feat: add controls to template for determining startup days (#10226)

* feat: template controls which days can autostart
* Add unit test to test blocking autostart with DaysOfWeek
This commit is contained in:
Steven Masley 2023-10-13 11:57:18 -05:00 committed by GitHub
parent 98b6c8bcb0
commit 39c0539d42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 825 additions and 144 deletions

View File

@ -17,19 +17,20 @@ import (
func (r *RootCmd) templateEdit() *clibase.Cmd {
var (
name string
displayName string
description string
icon string
defaultTTL time.Duration
maxTTL time.Duration
autostopRequirementDaysOfWeek []string
autostopRequirementWeeks int64
failureTTL time.Duration
inactivityTTL time.Duration
allowUserCancelWorkspaceJobs bool
allowUserAutostart bool
allowUserAutostop bool
name string
displayName string
description string
icon string
defaultTTL time.Duration
maxTTL time.Duration
autostopRequirementDaysOfWeek []string
autostopRequirementWeeks int64
autostartRequirementDaysOfWeek []string
failureTTL time.Duration
inactivityTTL time.Duration
allowUserCancelWorkspaceJobs bool
allowUserAutostart bool
allowUserAutostop bool
)
client := new(codersdk.Client)
@ -48,7 +49,9 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
!allowUserAutostop ||
maxTTL != 0 ||
failureTTL != 0 ||
inactivityTTL != 0
inactivityTTL != 0 ||
len(autostartRequirementDaysOfWeek) > 0
if requiresEntitlement {
entitlements, err := client.Entitlements(inv.Context())
var sdkErr *codersdk.Error
@ -77,6 +80,12 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
if len(autostopRequirementDaysOfWeek) == 0 {
autostopRequirementDaysOfWeek = template.AutostopRequirement.DaysOfWeek
}
if len(autostartRequirementDaysOfWeek) == 1 && autostartRequirementDaysOfWeek[0] == "all" {
// Set it to every day of the week
autostartRequirementDaysOfWeek = []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"}
} else if len(autostartRequirementDaysOfWeek) == 0 {
autostartRequirementDaysOfWeek = template.AutostartRequirement.DaysOfWeek
}
if unsetAutostopRequirementDaysOfWeek {
autostopRequirementDaysOfWeek = []string{}
}
@ -93,6 +102,9 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
DaysOfWeek: autostopRequirementDaysOfWeek,
Weeks: autostopRequirementWeeks,
},
AutostartRequirement: &codersdk.TemplateAutostartRequirement{
DaysOfWeek: autostartRequirementDaysOfWeek,
},
FailureTTLMillis: failureTTL.Milliseconds(),
TimeTilDormantMillis: inactivityTTL.Milliseconds(),
AllowUserCancelWorkspaceJobs: allowUserCancelWorkspaceJobs,
@ -140,6 +152,22 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
Description: "Edit the template maximum time before shutdown - workspaces created from this template must shutdown within the given duration after starting, regardless of user activity. This is an enterprise-only feature. Maps to \"Max lifetime\" in the UI.",
Value: clibase.DurationOf(&maxTTL),
},
{
Flag: "autostart-requirement-weekdays",
// workspaces created from this template must be restarted on the given weekdays. To unset this value for the template (and disable the autostop requirement for the template), pass 'none'.
Description: "Edit the template autostart requirement weekdays - workspaces created from this template can only autostart on the given weekdays. To unset this value for the template (and allow autostart on all days), pass 'all'.",
Value: clibase.Validate(clibase.StringArrayOf(&autostartRequirementDaysOfWeek), func(value *clibase.StringArray) error {
v := value.GetSlice()
if len(v) == 1 && v[0] == "all" {
return nil
}
_, err := codersdk.WeekdaysToBitmap(v)
if err != nil {
return xerrors.Errorf("invalid autostart requirement days of week %q: %w", strings.Join(v, ","), err)
}
return nil
}),
},
{
Flag: "autostop-requirement-weekdays",
Description: "Edit the template autostop requirement weekdays - workspaces created from this template must be restarted on the given weekdays. To unset this value for the template (and disable the autostop requirement for the template), pass 'none'.",

View File

@ -248,7 +248,7 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, "", updated.Icon)
assert.Equal(t, "", updated.DisplayName)
})
t.Run("AutostopRequirement", func(t *testing.T) {
t.Run("Autostop/startRequirement", func(t *testing.T) {
t.Parallel()
t.Run("BlockedAGPL", func(t *testing.T) {
t.Parallel()
@ -286,6 +286,12 @@ func TestTemplateEdit(t *testing.T) {
"--autostop-requirement-weeks", "1",
},
},
{
name: "AutostartDays",
flags: []string{
"--autostart-requirement-weekdays", "monday",
},
},
}
for _, c := range cases {
@ -321,6 +327,8 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
assert.Equal(t, template.AutostartRequirement.DaysOfWeek, updated.AutostartRequirement.DaysOfWeek)
assert.Equal(t, template.AutostartRequirement.DaysOfWeek, updated.AutostartRequirement.DaysOfWeek)
})
}
})
@ -436,6 +444,7 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
assert.Equal(t, template.AutostartRequirement.DaysOfWeek, updated.AutostartRequirement.DaysOfWeek)
})
}
})
@ -536,6 +545,7 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
assert.Equal(t, template.AutostartRequirement.DaysOfWeek, updated.AutostartRequirement.DaysOfWeek)
})
})
// TODO(@dean): remove this test when we remove max_ttl
@ -808,6 +818,7 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
assert.Equal(t, template.AutostartRequirement.DaysOfWeek, updated.AutostartRequirement.DaysOfWeek)
assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart)
assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop)
assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis)
@ -903,6 +914,7 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
assert.Equal(t, template.AutostartRequirement.DaysOfWeek, updated.AutostartRequirement.DaysOfWeek)
assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart)
assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop)
assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis)
@ -1002,6 +1014,7 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
assert.Equal(t, template.AutostartRequirement.DaysOfWeek, updated.AutostartRequirement.DaysOfWeek)
assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart)
assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop)
assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis)

View File

@ -17,6 +17,12 @@ OPTIONS:
--allow-user-cancel-workspace-jobs bool (default: true)
Allow users to cancel in-progress workspace jobs.
--autostart-requirement-weekdays string-array
Edit the template autostart requirement weekdays - workspaces created
from this template can only autostart on the given weekdays. To unset
this value for the template (and allow autostart on all days), pass
'all'.
--default-ttl duration
Edit the template default time before shutdown - workspaces created
from this template default to this value. Maps to "Default autostop"

34
coderd/apidoc/docs.go generated
View File

@ -7732,6 +7732,14 @@ const docTemplate = `{
"description": "Allow users to cancel in-progress workspace jobs.\n*bool as the default value is \"true\".",
"type": "boolean"
},
"autostart_requirement": {
"description": "AutostartRequirement allows optionally specifying the autostart allowed days\nfor workspaces created from this template. This is an enterprise feature.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateAutostartRequirement"
}
]
},
"autostop_requirement": {
"description": "AutostopRequirement allows optionally specifying the autostop requirement\nfor workspaces created from this template. This is an enterprise feature.",
"allOf": [
@ -9896,8 +9904,11 @@ const docTemplate = `{
"allow_user_cancel_workspace_jobs": {
"type": "boolean"
},
"autostart_requirement": {
"$ref": "#/definitions/codersdk.TemplateAutostartRequirement"
},
"autostop_requirement": {
"description": "AutostopRequirement is an enterprise feature. Its value is only used if\nyour license is entitled to use the advanced template scheduling feature.",
"description": "AutostopRequirement and AutostartRequirement are enterprise features. Its\nvalue is only used if your license is entitled to use the advanced template\nscheduling feature.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateAutostopRequirement"
@ -10013,6 +10024,27 @@ const docTemplate = `{
"TemplateAppsTypeApp"
]
},
"codersdk.TemplateAutostartRequirement": {
"type": "object",
"properties": {
"days_of_week": {
"description": "DaysOfWeek is a list of days of the week in which autostart is allowed\nto happen. If no days are specified, autostart is not allowed.",
"type": "array",
"items": {
"type": "string",
"enum": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday"
]
}
}
}
},
"codersdk.TemplateAutostopRequirement": {
"type": "object",
"properties": {

View File

@ -6889,6 +6889,14 @@
"description": "Allow users to cancel in-progress workspace jobs.\n*bool as the default value is \"true\".",
"type": "boolean"
},
"autostart_requirement": {
"description": "AutostartRequirement allows optionally specifying the autostart allowed days\nfor workspaces created from this template. This is an enterprise feature.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateAutostartRequirement"
}
]
},
"autostop_requirement": {
"description": "AutostopRequirement allows optionally specifying the autostop requirement\nfor workspaces created from this template. This is an enterprise feature.",
"allOf": [
@ -8936,8 +8944,11 @@
"allow_user_cancel_workspace_jobs": {
"type": "boolean"
},
"autostart_requirement": {
"$ref": "#/definitions/codersdk.TemplateAutostartRequirement"
},
"autostop_requirement": {
"description": "AutostopRequirement is an enterprise feature. Its value is only used if\nyour license is entitled to use the advanced template scheduling feature.",
"description": "AutostopRequirement and AutostartRequirement are enterprise features. Its\nvalue is only used if your license is entitled to use the advanced template\nscheduling feature.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateAutostopRequirement"
@ -9045,6 +9056,27 @@
"enum": ["builtin", "app"],
"x-enum-varnames": ["TemplateAppsTypeBuiltin", "TemplateAppsTypeApp"]
},
"codersdk.TemplateAutostartRequirement": {
"type": "object",
"properties": {
"days_of_week": {
"description": "DaysOfWeek is a list of days of the week in which autostart is allowed\nto happen. If no days are specified, autostart is not allowed.",
"type": "array",
"items": {
"type": "string",
"enum": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday"
]
}
}
}
},
"codersdk.TemplateAutostopRequirement": {
"type": "object",
"properties": {

View File

@ -354,6 +354,17 @@ func isEligibleForAutostart(ws database.Workspace, build database.WorkspaceBuild
// Truncate is probably not necessary here, but doing it anyway to be sure.
nextTransition := sched.Next(build.CreatedAt).Truncate(time.Minute)
// The nextTransition is when the auto start should kick off. If it lands on a
// forbidden day, do not allow the auto start. We use the time location of the
// schedule to determine the weekday. So if "Saturday" is disallowed, the
// definition of "Saturday" depends on the location of the schedule.
zonedTransition := nextTransition.In(sched.Location())
allowed := templateSchedule.AutostartRequirement.DaysMap()[zonedTransition.Weekday()]
if !allowed {
return false
}
// Must used '.Before' vs '.After' so equal times are considered "valid for autostart".
return !currentTick.Before(nextTransition)
}

View File

@ -0,0 +1,146 @@
package autobuild
import (
"database/sql"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/schedule"
)
func Test_isEligibleForAutostart(t *testing.T) {
t.Parallel()
// okXXX should be set to values that make 'isEligibleForAutostart' return true.
// Intentionally chosen to be a non UTC time that changes the day of the week
// when converted to UTC.
localLocation, err := time.LoadLocation("America/Chicago")
if err != nil {
t.Fatal(err)
}
// 5s after the autostart in UTC.
okTick := time.Date(2021, 1, 1, 20, 0, 5, 0, localLocation).UTC()
okWorkspace := database.Workspace{
DormantAt: sql.NullTime{Valid: false},
AutostartSchedule: sql.NullString{
Valid: true,
// Every day at 8pm America/Chicago, which is 2am UTC the next day.
String: "CRON_TZ=America/Chicago 0 20 * * *",
},
}
okBuild := database.WorkspaceBuild{
Transition: database.WorkspaceTransitionStop,
// Put 24hr before the tick so it's eligible for autostart.
CreatedAt: okTick.Add(time.Hour * -24),
}
okJob := database.ProvisionerJob{
JobStatus: database.ProvisionerJobStatusSucceeded,
}
okTemplateSchedule := schedule.TemplateScheduleOptions{
UserAutostartEnabled: true,
AutostartRequirement: schedule.TemplateAutostartRequirement{
DaysOfWeek: 0b01111111,
},
}
var okWeekdayBit uint8
for i, weekday := range schedule.DaysOfWeek {
// Find the local weekday
if okTick.In(localLocation).Weekday() == weekday {
okWeekdayBit = 1 << uint(i)
}
}
testCases := []struct {
Name string
Workspace database.Workspace
Build database.WorkspaceBuild
Job database.ProvisionerJob
TemplateSchedule schedule.TemplateScheduleOptions
Tick time.Time
ExpectedResponse bool
}{
{
Name: "Ok",
Workspace: okWorkspace,
Build: okBuild,
Job: okJob,
TemplateSchedule: okTemplateSchedule,
Tick: okTick,
ExpectedResponse: true,
},
{
Name: "AutostartOnlyDayEnabled",
Workspace: okWorkspace,
Build: okBuild,
Job: okJob,
TemplateSchedule: schedule.TemplateScheduleOptions{
UserAutostartEnabled: true,
AutostartRequirement: schedule.TemplateAutostartRequirement{
// Specific day of week is allowed
DaysOfWeek: okWeekdayBit,
},
},
Tick: okTick,
ExpectedResponse: true,
},
{
Name: "AutostartOnlyDayDisabled",
Workspace: okWorkspace,
Build: okBuild,
Job: okJob,
TemplateSchedule: schedule.TemplateScheduleOptions{
UserAutostartEnabled: true,
AutostartRequirement: schedule.TemplateAutostartRequirement{
// Specific day of week is disallowed
DaysOfWeek: 0b01111111 & (^okWeekdayBit),
},
},
Tick: okTick,
ExpectedResponse: false,
},
{
Name: "AutostartAllDaysDisabled",
Workspace: okWorkspace,
Build: okBuild,
Job: okJob,
TemplateSchedule: schedule.TemplateScheduleOptions{
UserAutostartEnabled: true,
AutostartRequirement: schedule.TemplateAutostartRequirement{
// All days disabled
DaysOfWeek: 0,
},
},
Tick: okTick,
ExpectedResponse: false,
},
{
Name: "BuildTransitionNotStop",
Workspace: okWorkspace,
Build: func(b database.WorkspaceBuild) database.WorkspaceBuild {
cpy := b
cpy.Transition = database.WorkspaceTransitionStart
return cpy
}(okBuild),
Job: okJob,
TemplateSchedule: okTemplateSchedule,
Tick: okTick,
ExpectedResponse: false,
},
}
for _, c := range testCases {
c := c
t.Run(c.Name, func(t *testing.T) {
t.Parallel()
autostart := isEligibleForAutostart(c.Workspace, c.Build, c.Job, c.TemplateSchedule, c.Tick)
require.Equal(t, c.ExpectedResponse, autostart, "autostart not expected")
})
}
}

View File

@ -23,6 +23,7 @@ import (
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/provisioner/echo"
"github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/testutil"
)
func TestExecutorAutostartOK(t *testing.T) {
@ -60,6 +61,12 @@ func TestExecutorAutostartOK(t *testing.T) {
workspace = coderdtest.MustWorkspace(t, client, workspace.ID)
assert.Equal(t, codersdk.BuildReasonAutostart, workspace.LatestBuild.Reason)
// Assert some template props. If this is not set correctly, the test
// will fail.
ctx := testutil.Context(t, testutil.WaitShort)
template, err := client.Template(ctx, workspace.TemplateID)
require.NoError(t, err)
require.Equal(t, template.AutostartRequirement.DaysOfWeek, []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"})
}
func TestExecutorAutostartTemplateUpdated(t *testing.T) {

View File

@ -5725,6 +5725,7 @@ func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database
tpl.MaxTTL = arg.MaxTTL
tpl.AutostopRequirementDaysOfWeek = arg.AutostopRequirementDaysOfWeek
tpl.AutostopRequirementWeeks = arg.AutostopRequirementWeeks
tpl.AutostartBlockDaysOfWeek = arg.AutostartBlockDaysOfWeek
tpl.FailureTTL = arg.FailureTTL
tpl.TimeTilDormant = arg.TimeTilDormant
tpl.TimeTilDormantAutoDelete = arg.TimeTilDormantAutoDelete

View File

@ -754,7 +754,8 @@ CREATE TABLE templates (
time_til_dormant bigint DEFAULT 0 NOT NULL,
time_til_dormant_autodelete bigint DEFAULT 0 NOT NULL,
autostop_requirement_days_of_week smallint DEFAULT 0 NOT NULL,
autostop_requirement_weeks bigint DEFAULT 0 NOT NULL
autostop_requirement_weeks bigint DEFAULT 0 NOT NULL,
autostart_block_days_of_week smallint DEFAULT 0 NOT NULL
);
COMMENT ON COLUMN templates.default_ttl IS 'The default duration for autostop for workspaces created from this template.';
@ -771,6 +772,8 @@ COMMENT ON COLUMN templates.autostop_requirement_days_of_week IS 'A bitmap of da
COMMENT ON COLUMN templates.autostop_requirement_weeks IS 'The number of weeks between restarts. 0 or 1 weeks means "every week", 2 week means "every second week", etc. Weeks are counted from January 2, 2023, which is the first Monday of 2023. This is to ensure workspaces are started consistently for all customers on the same n-week cycles.';
COMMENT ON COLUMN templates.autostart_block_days_of_week IS 'A bitmap of days of week that autostart of a workspace is not allowed. Default allows all days. This is intended as a cost savings measure to prevent auto start on weekends (for example).';
CREATE VIEW template_with_users AS
SELECT templates.id,
templates.created_at,
@ -796,6 +799,7 @@ CREATE VIEW template_with_users AS
templates.time_til_dormant_autodelete,
templates.autostop_requirement_days_of_week,
templates.autostop_requirement_weeks,
templates.autostart_block_days_of_week,
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS created_by_username
FROM (public.templates

View File

@ -0,0 +1,25 @@
BEGIN;
DROP VIEW template_with_users;
ALTER TABLE templates
DROP COLUMN autostart_block_days_of_week;
-- Recreate view
CREATE VIEW
template_with_users
AS
SELECT
templates.*,
coalesce(visible_users.avatar_url, '') AS created_by_avatar_url,
coalesce(visible_users.username, '') AS created_by_username
FROM
templates
LEFT JOIN
visible_users
ON
templates.created_by = visible_users.id;
COMMENT ON VIEW template_with_users IS 'Joins in the username + avatar url of the created by user.';
COMMIT;

View File

@ -0,0 +1,27 @@
BEGIN;
DROP VIEW template_with_users;
ALTER TABLE templates
ADD COLUMN autostart_block_days_of_week smallint NOT NULL DEFAULT 0;
COMMENT ON COLUMN templates.autostart_block_days_of_week IS 'A bitmap of days of week that autostart of a workspace is not allowed. Default allows all days. This is intended as a cost savings measure to prevent auto start on weekends (for example).';
-- Recreate view
CREATE VIEW
template_with_users
AS
SELECT
templates.*,
coalesce(visible_users.avatar_url, '') AS created_by_avatar_url,
coalesce(visible_users.username, '') AS created_by_username
FROM
templates
LEFT JOIN
visible_users
ON
templates.created_by = visible_users.id;
COMMENT ON VIEW template_with_users IS 'Joins in the username + avatar url of the created by user.';
COMMIT;

View File

@ -124,6 +124,15 @@ func (t Template) DeepCopy() Template {
return cpy
}
// AutostartAllowedDays returns the inverse of 'AutostartBlockDaysOfWeek'.
// It is more useful to have the days that are allowed to autostart from a UX
// POV. The database prefers the 0 value being 'all days allowed'.
func (t Template) AutostartAllowedDays() uint8 {
// Just flip the binary 0s to 1s and vice versa.
// There is an extra day with the 8th bit that needs to be zeroed.
return ^uint8(t.AutostartBlockDaysOfWeek) & 0b01111111
}
func (TemplateVersion) RBACObject(template Template) rbac.Object {
// Just use the parent template resource for controlling versions
return template.RBACObject()

View File

@ -85,6 +85,7 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate
&i.TimeTilDormantAutoDelete,
&i.AutostopRequirementDaysOfWeek,
&i.AutostopRequirementWeeks,
&i.AutostartBlockDaysOfWeek,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {

View File

@ -1891,6 +1891,7 @@ type Template struct {
TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"`
AutostopRequirementDaysOfWeek int16 `db:"autostop_requirement_days_of_week" json:"autostop_requirement_days_of_week"`
AutostopRequirementWeeks int64 `db:"autostop_requirement_weeks" json:"autostop_requirement_weeks"`
AutostartBlockDaysOfWeek int16 `db:"autostart_block_days_of_week" json:"autostart_block_days_of_week"`
CreatedByAvatarURL sql.NullString `db:"created_by_avatar_url" json:"created_by_avatar_url"`
CreatedByUsername string `db:"created_by_username" json:"created_by_username"`
}
@ -1927,6 +1928,8 @@ type TemplateTable struct {
AutostopRequirementDaysOfWeek int16 `db:"autostop_requirement_days_of_week" json:"autostop_requirement_days_of_week"`
// The number of weeks between restarts. 0 or 1 weeks means "every week", 2 week means "every second week", etc. Weeks are counted from January 2, 2023, which is the first Monday of 2023. This is to ensure workspaces are started consistently for all customers on the same n-week cycles.
AutostopRequirementWeeks int64 `db:"autostop_requirement_weeks" json:"autostop_requirement_weeks"`
// A bitmap of days of week that autostart of a workspace is not allowed. Default allows all days. This is intended as a cost savings measure to prevent auto start on weekends (for example).
AutostartBlockDaysOfWeek int16 `db:"autostart_block_days_of_week" json:"autostart_block_days_of_week"`
}
// Joins in the username + avatar url of the created by user.

View File

@ -4726,7 +4726,7 @@ func (q *sqlQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg GetTem
const getTemplateByID = `-- name: GetTemplateByID :one
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, created_by_avatar_url, created_by_username
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, created_by_avatar_url, created_by_username
FROM
template_with_users
WHERE
@ -4763,6 +4763,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
&i.TimeTilDormantAutoDelete,
&i.AutostopRequirementDaysOfWeek,
&i.AutostopRequirementWeeks,
&i.AutostartBlockDaysOfWeek,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
@ -4771,7 +4772,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, created_by_avatar_url, created_by_username
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, created_by_avatar_url, created_by_username
FROM
template_with_users AS templates
WHERE
@ -4816,6 +4817,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
&i.TimeTilDormantAutoDelete,
&i.AutostopRequirementDaysOfWeek,
&i.AutostopRequirementWeeks,
&i.AutostartBlockDaysOfWeek,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
@ -4823,7 +4825,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
}
const getTemplates = `-- name: GetTemplates :many
SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, created_by_avatar_url, created_by_username FROM template_with_users AS templates
SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, created_by_avatar_url, created_by_username FROM template_with_users AS templates
ORDER BY (name, id) ASC
`
@ -4861,6 +4863,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
&i.TimeTilDormantAutoDelete,
&i.AutostopRequirementDaysOfWeek,
&i.AutostopRequirementWeeks,
&i.AutostartBlockDaysOfWeek,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {
@ -4879,7 +4882,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, created_by_avatar_url, created_by_username
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, created_by_avatar_url, created_by_username
FROM
template_with_users AS templates
WHERE
@ -4954,6 +4957,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate
&i.TimeTilDormantAutoDelete,
&i.AutostopRequirementDaysOfWeek,
&i.AutostopRequirementWeeks,
&i.AutostartBlockDaysOfWeek,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {
@ -5140,9 +5144,10 @@ SET
max_ttl = $6,
autostop_requirement_days_of_week = $7,
autostop_requirement_weeks = $8,
failure_ttl = $9,
time_til_dormant = $10,
time_til_dormant_autodelete = $11
autostart_block_days_of_week = $9,
failure_ttl = $10,
time_til_dormant = $11,
time_til_dormant_autodelete = $12
WHERE
id = $1
`
@ -5156,6 +5161,7 @@ type UpdateTemplateScheduleByIDParams struct {
MaxTTL int64 `db:"max_ttl" json:"max_ttl"`
AutostopRequirementDaysOfWeek int16 `db:"autostop_requirement_days_of_week" json:"autostop_requirement_days_of_week"`
AutostopRequirementWeeks int64 `db:"autostop_requirement_weeks" json:"autostop_requirement_weeks"`
AutostartBlockDaysOfWeek int16 `db:"autostart_block_days_of_week" json:"autostart_block_days_of_week"`
FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"`
TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"`
TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"`
@ -5171,6 +5177,7 @@ func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateT
arg.MaxTTL,
arg.AutostopRequirementDaysOfWeek,
arg.AutostopRequirementWeeks,
arg.AutostartBlockDaysOfWeek,
arg.FailureTTL,
arg.TimeTilDormant,
arg.TimeTilDormantAutoDelete,

View File

@ -120,9 +120,10 @@ SET
max_ttl = $6,
autostop_requirement_days_of_week = $7,
autostop_requirement_weeks = $8,
failure_ttl = $9,
time_til_dormant = $10,
time_til_dormant_autodelete = $11
autostart_block_days_of_week = $9,
failure_ttl = $10,
time_til_dormant = $11,
time_til_dormant_autodelete = $12
WHERE
id = $1
;

View File

@ -35,6 +35,18 @@ var DaysOfWeek = []time.Weekday{
time.Sunday,
}
type TemplateAutostartRequirement struct {
// DaysOfWeek is a bitmap of which days of the week the workspace is allowed
// to be auto started. If fully zero, the workspace is not allowed to be auto started.
//
// First bit is Monday, ..., seventh bit is Sunday, eighth bit is unused.
DaysOfWeek uint8
}
func (r TemplateAutostartRequirement) DaysMap() map[time.Weekday]bool {
return daysMap(r.DaysOfWeek)
}
type TemplateAutostopRequirement struct {
// DaysOfWeek is a bitmap of which days of the week the workspace must be
// restarted. If fully zero, the workspace is not required to be restarted
@ -57,9 +69,15 @@ type TemplateAutostopRequirement struct {
// DaysMap returns a map of the days of the week that the workspace must be
// restarted.
func (r TemplateAutostopRequirement) DaysMap() map[time.Weekday]bool {
return daysMap(r.DaysOfWeek)
}
// daysMap returns a map of the days of the week that are specified in the
// bitmap.
func daysMap(daysOfWeek uint8) map[time.Weekday]bool {
days := make(map[time.Weekday]bool)
for i, day := range DaysOfWeek {
days[day] = r.DaysOfWeek&(1<<uint(i)) != 0
days[day] = daysOfWeek&(1<<uint(i)) != 0
}
return days
}
@ -82,6 +100,19 @@ func VerifyTemplateAutostopRequirement(days uint8, weeks int64) error {
return nil
}
// VerifyTemplateAutostartRequirement returns an error if the autostart
// requirement is invalid.
func VerifyTemplateAutostartRequirement(days uint8) error {
if days&0b10000000 != 0 {
return xerrors.New("invalid autostart requirement days, last bit is set")
}
if days > 0b11111111 {
return xerrors.New("invalid autostart requirement days, too large")
}
return nil
}
type TemplateScheduleOptions struct {
UserAutostartEnabled bool `json:"user_autostart_enabled"`
UserAutostopEnabled bool `json:"user_autostop_enabled"`
@ -97,6 +128,8 @@ type TemplateScheduleOptions struct {
// AutostopRequirement dictates when the workspace must be restarted. This
// used to be handled by MaxTTL.
AutostopRequirement TemplateAutostopRequirement `json:"autostop_requirement"`
// AutostartRequirement dictates when the workspace can be auto started.
AutostartRequirement TemplateAutostartRequirement `json:"autostart_requirement"`
// FailureTTL dictates the duration after which failed workspaces will be
// stopped automatically.
FailureTTL time.Duration `json:"failure_ttl"`
@ -154,6 +187,10 @@ func (*agplTemplateScheduleStore) Get(ctx context.Context, db database.Store, te
// FailureTTL, TimeTilDormant, and TimeTilDormantAutoDelete are enterprise features.
UseAutostopRequirement: false,
MaxTTL: 0,
AutostartRequirement: TemplateAutostartRequirement{
// Default to allowing all days for AGPL
DaysOfWeek: 0b01111111,
},
AutostopRequirement: TemplateAutostopRequirement{
// No days means never. The weeks value should always be greater
// than zero though.
@ -186,6 +223,7 @@ func (*agplTemplateScheduleStore) Set(ctx context.Context, db database.Store, tp
MaxTTL: tpl.MaxTTL,
AutostopRequirementDaysOfWeek: tpl.AutostopRequirementDaysOfWeek,
AutostopRequirementWeeks: tpl.AutostopRequirementWeeks,
AutostartBlockDaysOfWeek: tpl.AutostartBlockDaysOfWeek,
AllowUserAutostart: tpl.AllowUserAutostart,
AllowUserAutostop: tpl.AllowUserAutostop,
FailureTTL: tpl.FailureTTL,

View File

@ -225,12 +225,13 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
var (
defaultTTL time.Duration
// TODO(@dean): remove max_ttl once autostop_requirement is ready
maxTTL time.Duration
autostopRequirementDaysOfWeek []string
autostopRequirementWeeks int64
failureTTL time.Duration
dormantTTL time.Duration
dormantAutoDeletionTTL time.Duration
maxTTL time.Duration
autostopRequirementDaysOfWeek []string
autostartRequirementDaysOfWeek []string
autostopRequirementWeeks int64
failureTTL time.Duration
dormantTTL time.Duration
dormantAutoDeletionTTL time.Duration
)
if createTemplate.DefaultTTLMillis != nil {
defaultTTL = time.Duration(*createTemplate.DefaultTTLMillis) * time.Millisecond
@ -239,6 +240,12 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
autostopRequirementDaysOfWeek = createTemplate.AutostopRequirement.DaysOfWeek
autostopRequirementWeeks = createTemplate.AutostopRequirement.Weeks
}
if createTemplate.AutostartRequirement != nil {
autostartRequirementDaysOfWeek = createTemplate.AutostartRequirement.DaysOfWeek
} else {
// By default, we want to allow all days of the week to be autostarted.
autostartRequirementDaysOfWeek = codersdk.BitmapToWeekdays(0b01111111)
}
if createTemplate.FailureTTLMillis != nil {
failureTTL = time.Duration(*createTemplate.FailureTTLMillis) * time.Millisecond
}
@ -250,8 +257,9 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
}
var (
validErrs []codersdk.ValidationError
autostopRequirementDaysOfWeekParsed uint8
validErrs []codersdk.ValidationError
autostopRequirementDaysOfWeekParsed uint8
autostartRequirementDaysOfWeekParsed uint8
)
if defaultTTL < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be a positive integer."})
@ -268,6 +276,12 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.days_of_week", Detail: err.Error()})
}
}
if len(autostartRequirementDaysOfWeek) > 0 {
autostartRequirementDaysOfWeekParsed, err = codersdk.WeekdaysToBitmap(autostartRequirementDaysOfWeek)
if err != nil {
validErrs = append(validErrs, codersdk.ValidationError{Field: "autostart_requirement.days_of_week", Detail: err.Error()})
}
}
if createTemplate.MaxTTLMillis != nil {
maxTTL = time.Duration(*createTemplate.MaxTTLMillis) * time.Millisecond
}
@ -350,6 +364,9 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
DaysOfWeek: autostopRequirementDaysOfWeekParsed,
Weeks: autostopRequirementWeeks,
},
AutostartRequirement: schedule.TemplateAutostartRequirement{
DaysOfWeek: autostartRequirementDaysOfWeekParsed,
},
FailureTTL: failureTTL,
TimeTilDormant: dormantTTL,
TimeTilDormantAutoDelete: dormantAutoDeletionTTL,
@ -510,8 +527,9 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
}
var (
validErrs []codersdk.ValidationError
autostopRequirementDaysOfWeekParsed uint8
validErrs []codersdk.ValidationError
autostopRequirementDaysOfWeekParsed uint8
autostartRequirementDaysOfWeekParsed uint8
)
if req.DefaultTTLMillis < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be a positive integer."})
@ -534,6 +552,17 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.days_of_week", Detail: err.Error()})
}
}
if req.AutostartRequirement == nil {
req.AutostartRequirement = &codersdk.TemplateAutostartRequirement{
DaysOfWeek: codersdk.BitmapToWeekdays(scheduleOpts.AutostartRequirement.DaysOfWeek),
}
}
if len(req.AutostartRequirement.DaysOfWeek) > 0 {
autostartRequirementDaysOfWeekParsed, err = codersdk.WeekdaysToBitmap(req.AutostartRequirement.DaysOfWeek)
if err != nil {
validErrs = append(validErrs, codersdk.ValidationError{Field: "autostart_requirement.days_of_week", Detail: err.Error()})
}
}
if req.AutostopRequirement.Weeks < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.weeks", Detail: "Must be a positive integer."})
}
@ -622,6 +651,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
if defaultTTL != time.Duration(template.DefaultTTL) ||
maxTTL != time.Duration(template.MaxTTL) ||
autostopRequirementDaysOfWeekParsed != scheduleOpts.AutostopRequirement.DaysOfWeek ||
autostartRequirementDaysOfWeekParsed != scheduleOpts.AutostartRequirement.DaysOfWeek ||
req.AutostopRequirement.Weeks != scheduleOpts.AutostopRequirement.Weeks ||
failureTTL != time.Duration(template.FailureTTL) ||
inactivityTTL != time.Duration(template.TimeTilDormant) ||
@ -640,6 +670,9 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
DaysOfWeek: autostopRequirementDaysOfWeekParsed,
Weeks: req.AutostopRequirement.Weeks,
},
AutostartRequirement: schedule.TemplateAutostartRequirement{
DaysOfWeek: autostartRequirementDaysOfWeekParsed,
},
FailureTTL: failureTTL,
TimeTilDormant: inactivityTTL,
TimeTilDormantAutoDelete: timeTilDormantAutoDelete,
@ -787,5 +820,8 @@ func (api *API) convertTemplate(
DaysOfWeek: codersdk.BitmapToWeekdays(uint8(template.AutostopRequirementDaysOfWeek)),
Weeks: autostopRequirementWeeks,
},
AutostartRequirement: codersdk.TemplateAutostartRequirement{
DaysOfWeek: codersdk.BitmapToWeekdays(template.AutostartAllowedDays()),
},
}
}

View File

@ -89,6 +89,9 @@ type CreateTemplateRequest struct {
// AutostopRequirement allows optionally specifying the autostop requirement
// for workspaces created from this template. This is an enterprise feature.
AutostopRequirement *TemplateAutostopRequirement `json:"autostop_requirement,omitempty"`
// AutostartRequirement allows optionally specifying the autostart allowed days
// for workspaces created from this template. This is an enterprise feature.
AutostartRequirement *TemplateAutostartRequirement `json:"autostart_requirement,omitempty"`
// Allow users to cancel in-progress workspace jobs.
// *bool as the default value is "true".

View File

@ -31,11 +31,13 @@ type Template struct {
DefaultTTLMillis int64 `json:"default_ttl_ms"`
// TODO(@dean): remove max_ttl once autostop_requirement is matured
MaxTTLMillis int64 `json:"max_ttl_ms"`
// AutostopRequirement is an enterprise feature. Its value is only used if
// your license is entitled to use the advanced template scheduling feature.
AutostopRequirement TemplateAutostopRequirement `json:"autostop_requirement"`
CreatedByID uuid.UUID `json:"created_by_id" format:"uuid"`
CreatedByName string `json:"created_by_name"`
// AutostopRequirement and AutostartRequirement are enterprise features. Its
// value is only used if your license is entitled to use the advanced template
// scheduling feature.
AutostopRequirement TemplateAutostopRequirement `json:"autostop_requirement"`
AutostartRequirement TemplateAutostartRequirement `json:"autostart_requirement"`
CreatedByID uuid.UUID `json:"created_by_id" format:"uuid"`
CreatedByName string `json:"created_by_name"`
// AllowUserAutostart and AllowUserAutostop are enterprise-only. Their
// values are only used if your license is entitled to use the advanced
@ -107,6 +109,12 @@ func BitmapToWeekdays(bitmap uint8) []string {
return days
}
type TemplateAutostartRequirement struct {
// DaysOfWeek is a list of days of the week in which autostart is allowed
// to happen. If no days are specified, autostart is not allowed.
DaysOfWeek []string `json:"days_of_week" enums:"monday,tuesday,wednesday,thursday,friday,saturday,sunday"`
}
type TemplateAutostopRequirement struct {
// DaysOfWeek is a list of days of the week on which restarts are required.
// Restarts happen within the user's quiet hours (in their configured
@ -193,16 +201,17 @@ type UpdateTemplateMeta struct {
DefaultTTLMillis int64 `json:"default_ttl_ms,omitempty"`
// TODO(@dean): remove max_ttl once autostop_requirement is matured
MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"`
// AutostopRequirement can only be set if your license includes the advanced
// template scheduling feature. If you attempt to set this value while
// unlicensed, it will be ignored.
AutostopRequirement *TemplateAutostopRequirement `json:"autostop_requirement,omitempty"`
AllowUserAutostart bool `json:"allow_user_autostart,omitempty"`
AllowUserAutostop bool `json:"allow_user_autostop,omitempty"`
AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs,omitempty"`
FailureTTLMillis int64 `json:"failure_ttl_ms,omitempty"`
TimeTilDormantMillis int64 `json:"time_til_dormant_ms,omitempty"`
TimeTilDormantAutoDeleteMillis int64 `json:"time_til_dormant_autodelete_ms,omitempty"`
// AutostopRequirement and AutostartRequirement can only be set if your license
// includes the advanced template scheduling feature. If you attempt to set this
// value while unlicensed, it will be ignored.
AutostopRequirement *TemplateAutostopRequirement `json:"autostop_requirement,omitempty"`
AutostartRequirement *TemplateAutostartRequirement `json:"autostart_requirement,omitempty"`
AllowUserAutostart bool `json:"allow_user_autostart,omitempty"`
AllowUserAutostop bool `json:"allow_user_autostop,omitempty"`
AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs,omitempty"`
FailureTTLMillis int64 `json:"failure_ttl_ms,omitempty"`
TimeTilDormantMillis int64 `json:"time_til_dormant_ms,omitempty"`
TimeTilDormantAutoDeleteMillis int64 `json:"time_til_dormant_autodelete_ms,omitempty"`
// UpdateWorkspaceLastUsedAt updates the last_used_at field of workspaces
// spawned from the template. This is useful for preventing workspaces being
// immediately locked when updating the inactivity_ttl field to a new, shorter

View File

@ -8,19 +8,19 @@ We track the following resources:
<!-- Code generated by 'make docs/admin/audit-logs.md'. DO NOT EDIT -->
| <b>Resource<b> | |
| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| APIKey<br><i>login, logout, register, create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>expires_at</td><td>true</td></tr><tr><td>hashed_secret</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>ip_address</td><td>false</td></tr><tr><td>last_used</td><td>true</td></tr><tr><td>lifetime_seconds</td><td>false</td></tr><tr><td>login_type</td><td>false</td></tr><tr><td>scope</td><td>false</td></tr><tr><td>token_name</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| AuditOAuthConvertState<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>expires_at</td><td>true</td></tr><tr><td>from_login_type</td><td>true</td></tr><tr><td>to_login_type</td><td>true</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| Group<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>members</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>quota_allowance</td><td>true</td></tr><tr><td>source</td><td>false</td></tr></tbody></table> |
| GitSSHKey<br><i>create</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>private_key</td><td>true</td></tr><tr><td>public_key</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| License<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>exp</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>jwt</td><td>false</td></tr><tr><td>uploaded_at</td><td>true</td></tr><tr><td>uuid</td><td>true</td></tr></tbody></table> |
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>active_version_id</td><td>true</td></tr><tr><td>allow_user_autostart</td><td>true</td></tr><tr><td>allow_user_autostop</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>autostop_requirement_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_weeks</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>failure_ttl</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>max_ttl</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>time_til_dormant</td><td>true</td></tr><tr><td>time_til_dormant_autodelete</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
| TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>archived</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>external_auth_providers</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>message</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| User<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>email</td><td>true</td></tr><tr><td>hashed_password</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_seen_at</td><td>false</td></tr><tr><td>login_type</td><td>true</td></tr><tr><td>quiet_hours_schedule</td><td>true</td></tr><tr><td>rbac_roles</td><td>true</td></tr><tr><td>status</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
| Workspace<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>automatic_updates</td><td>true</td></tr><tr><td>autostart_schedule</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deleting_at</td><td>true</td></tr><tr><td>dormant_at</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>ttl</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| WorkspaceBuild<br><i>start, stop</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>build_number</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>daily_cost</td><td>false</td></tr><tr><td>deadline</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>initiator_by_avatar_url</td><td>false</td></tr><tr><td>initiator_by_username</td><td>false</td></tr><tr><td>initiator_id</td><td>false</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>max_deadline</td><td>false</td></tr><tr><td>provisioner_state</td><td>false</td></tr><tr><td>reason</td><td>false</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>transition</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>workspace_id</td><td>false</td></tr></tbody></table> |
| WorkspaceProxy<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>derp_enabled</td><td>true</td></tr><tr><td>derp_only</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>region_id</td><td>true</td></tr><tr><td>token_hashed_secret</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>url</td><td>true</td></tr><tr><td>wildcard_hostname</td><td>true</td></tr></tbody></table> |
| <b>Resource<b> | |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| APIKey<br><i>login, logout, register, create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>expires_at</td><td>true</td></tr><tr><td>hashed_secret</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>ip_address</td><td>false</td></tr><tr><td>last_used</td><td>true</td></tr><tr><td>lifetime_seconds</td><td>false</td></tr><tr><td>login_type</td><td>false</td></tr><tr><td>scope</td><td>false</td></tr><tr><td>token_name</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| AuditOAuthConvertState<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>expires_at</td><td>true</td></tr><tr><td>from_login_type</td><td>true</td></tr><tr><td>to_login_type</td><td>true</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| Group<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>members</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>quota_allowance</td><td>true</td></tr><tr><td>source</td><td>false</td></tr></tbody></table> |
| GitSSHKey<br><i>create</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>private_key</td><td>true</td></tr><tr><td>public_key</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| License<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>exp</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>jwt</td><td>false</td></tr><tr><td>uploaded_at</td><td>true</td></tr><tr><td>uuid</td><td>true</td></tr></tbody></table> |
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>active_version_id</td><td>true</td></tr><tr><td>allow_user_autostart</td><td>true</td></tr><tr><td>allow_user_autostop</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>autostart_block_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_weeks</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>failure_ttl</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>max_ttl</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>time_til_dormant</td><td>true</td></tr><tr><td>time_til_dormant_autodelete</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
| TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>archived</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>external_auth_providers</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>message</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| User<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>email</td><td>true</td></tr><tr><td>hashed_password</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_seen_at</td><td>false</td></tr><tr><td>login_type</td><td>true</td></tr><tr><td>quiet_hours_schedule</td><td>true</td></tr><tr><td>rbac_roles</td><td>true</td></tr><tr><td>status</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
| Workspace<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>automatic_updates</td><td>true</td></tr><tr><td>autostart_schedule</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deleting_at</td><td>true</td></tr><tr><td>dormant_at</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>ttl</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| WorkspaceBuild<br><i>start, stop</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>build_number</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>daily_cost</td><td>false</td></tr><tr><td>deadline</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>initiator_by_avatar_url</td><td>false</td></tr><tr><td>initiator_by_username</td><td>false</td></tr><tr><td>initiator_id</td><td>false</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>max_deadline</td><td>false</td></tr><tr><td>provisioner_state</td><td>false</td></tr><tr><td>reason</td><td>false</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>transition</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>workspace_id</td><td>false</td></tr></tbody></table> |
| WorkspaceProxy<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>derp_enabled</td><td>true</td></tr><tr><td>derp_only</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>region_id</td><td>true</td></tr><tr><td>token_hashed_secret</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>url</td><td>true</td></tr><tr><td>wildcard_hostname</td><td>true</td></tr></tbody></table> |
<!-- End generated by 'make docs/admin/audit-logs.md'. -->

106
docs/api/schemas.md generated
View File

@ -1567,6 +1567,9 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"autostart_requirement": {
"days_of_week": ["monday"]
},
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
@ -1587,23 +1590,24 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
### Properties
| Name | Type | Required | Restrictions | Description |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allow_user_autostart` | boolean | false | | Allow user autostart allows users to set a schedule for autostarting their workspace. By default this is true. This can only be disabled when using an enterprise license. |
| `allow_user_autostop` | boolean | false | | Allow user autostop allows users to set a custom workspace TTL to use in place of the template's DefaultTTL field. By default this is true. If false, the DefaultTTL will always be used. This can only be disabled when using an enterprise license. |
| `allow_user_cancel_workspace_jobs` | boolean | false | | Allow users to cancel in-progress workspace jobs. \*bool as the default value is "true". |
| `autostop_requirement` | [codersdk.TemplateAutostopRequirement](#codersdktemplateautostoprequirement) | false | | Autostop requirement allows optionally specifying the autostop requirement for workspaces created from this template. This is an enterprise feature. |
| `default_ttl_ms` | integer | false | | Default ttl ms allows optionally specifying the default TTL for all workspaces created from this template. |
| `delete_ttl_ms` | integer | false | | Delete ttl ms allows optionally specifying the max lifetime before Coder permanently deletes dormant workspaces created from this template. |
| `description` | string | false | | Description is a description of what the template contains. It must be less than 128 bytes. |
| `disable_everyone_group_access` | boolean | false | | Disable everyone group access allows optionally disabling the default behavior of granting the 'everyone' group access to use the template. If this is set to true, the template will not be available to all users, and must be explicitly granted to users or groups in the permissions settings of the template. |
| `display_name` | string | false | | Display name is the displayed name of the template. |
| `dormant_ttl_ms` | integer | false | | Dormant ttl ms allows optionally specifying the max lifetime before Coder locks inactive workspaces created from this template. |
| `failure_ttl_ms` | integer | false | | Failure ttl ms allows optionally specifying the max lifetime before Coder stops all resources for failed workspaces created from this template. |
| `icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. |
| `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured |
| `name` | string | true | | Name is the name of the template. |
| `template_version_id` | string | true | | Template version ID is an in-progress or completed job to use as an initial version of the template. |
| Name | Type | Required | Restrictions | Description |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allow_user_autostart` | boolean | false | | Allow user autostart allows users to set a schedule for autostarting their workspace. By default this is true. This can only be disabled when using an enterprise license. |
| `allow_user_autostop` | boolean | false | | Allow user autostop allows users to set a custom workspace TTL to use in place of the template's DefaultTTL field. By default this is true. If false, the DefaultTTL will always be used. This can only be disabled when using an enterprise license. |
| `allow_user_cancel_workspace_jobs` | boolean | false | | Allow users to cancel in-progress workspace jobs. \*bool as the default value is "true". |
| `autostart_requirement` | [codersdk.TemplateAutostartRequirement](#codersdktemplateautostartrequirement) | false | | Autostart requirement allows optionally specifying the autostart allowed days for workspaces created from this template. This is an enterprise feature. |
| `autostop_requirement` | [codersdk.TemplateAutostopRequirement](#codersdktemplateautostoprequirement) | false | | Autostop requirement allows optionally specifying the autostop requirement for workspaces created from this template. This is an enterprise feature. |
| `default_ttl_ms` | integer | false | | Default ttl ms allows optionally specifying the default TTL for all workspaces created from this template. |
| `delete_ttl_ms` | integer | false | | Delete ttl ms allows optionally specifying the max lifetime before Coder permanently deletes dormant workspaces created from this template. |
| `description` | string | false | | Description is a description of what the template contains. It must be less than 128 bytes. |
| `disable_everyone_group_access` | boolean | false | | Disable everyone group access allows optionally disabling the default behavior of granting the 'everyone' group access to use the template. If this is set to true, the template will not be available to all users, and must be explicitly granted to users or groups in the permissions settings of the template. |
| `display_name` | string | false | | Display name is the displayed name of the template. |
| `dormant_ttl_ms` | integer | false | | Dormant ttl ms allows optionally specifying the max lifetime before Coder locks inactive workspaces created from this template. |
| `failure_ttl_ms` | integer | false | | Failure ttl ms allows optionally specifying the max lifetime before Coder stops all resources for failed workspaces created from this template. |
| `icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. |
| `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured |
| `name` | string | true | | Name is the name of the template. |
| `template_version_id` | string | true | | Template version ID is an in-progress or completed job to use as an initial version of the template. |
| This is required on creation to enable a user-flow of validating a template works. There is no reason the data-model cannot support empty templates, but it doesn't make sense for users. |
## codersdk.CreateTemplateVersionDryRunRequest
@ -4336,6 +4340,9 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"autostart_requirement": {
"days_of_week": ["monday"]
},
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
@ -4371,31 +4378,32 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
### Properties
| Name | Type | Required | Restrictions | Description |
| ---------------------------------- | ---------------------------------------------------------------------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `active_user_count` | integer | false | | Active user count is set to -1 when loading. |
| `active_version_id` | string | false | | |
| `allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. |
| `allow_user_autostop` | boolean | false | | |
| `allow_user_cancel_workspace_jobs` | boolean | false | | |
| `autostop_requirement` | [codersdk.TemplateAutostopRequirement](#codersdktemplateautostoprequirement) | false | | Autostop requirement is an enterprise feature. Its value is only used if your license is entitled to use the advanced template scheduling feature. |
| `build_time_stats` | [codersdk.TemplateBuildTimeStats](#codersdktemplatebuildtimestats) | false | | |
| `created_at` | string | false | | |
| `created_by_id` | string | false | | |
| `created_by_name` | string | false | | |
| `default_ttl_ms` | integer | false | | |
| `description` | string | false | | |
| `display_name` | string | false | | |
| `failure_ttl_ms` | integer | false | | Failure ttl ms TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. |
| `icon` | string | false | | |
| `id` | string | false | | |
| `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured |
| `name` | string | false | | |
| `organization_id` | string | false | | |
| `provisioner` | string | false | | |
| `time_til_dormant_autodelete_ms` | integer | false | | |
| `time_til_dormant_ms` | integer | false | | |
| `updated_at` | string | false | | |
| Name | Type | Required | Restrictions | Description |
| ---------------------------------- | ------------------------------------------------------------------------------ | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `active_user_count` | integer | false | | Active user count is set to -1 when loading. |
| `active_version_id` | string | false | | |
| `allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. |
| `allow_user_autostop` | boolean | false | | |
| `allow_user_cancel_workspace_jobs` | boolean | false | | |
| `autostart_requirement` | [codersdk.TemplateAutostartRequirement](#codersdktemplateautostartrequirement) | false | | |
| `autostop_requirement` | [codersdk.TemplateAutostopRequirement](#codersdktemplateautostoprequirement) | false | | Autostop requirement and AutostartRequirement are enterprise features. Its value is only used if your license is entitled to use the advanced template scheduling feature. |
| `build_time_stats` | [codersdk.TemplateBuildTimeStats](#codersdktemplatebuildtimestats) | false | | |
| `created_at` | string | false | | |
| `created_by_id` | string | false | | |
| `created_by_name` | string | false | | |
| `default_ttl_ms` | integer | false | | |
| `description` | string | false | | |
| `display_name` | string | false | | |
| `failure_ttl_ms` | integer | false | | Failure ttl ms TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. |
| `icon` | string | false | | |
| `id` | string | false | | |
| `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured |
| `name` | string | false | | |
| `organization_id` | string | false | | |
| `provisioner` | string | false | | |
| `time_til_dormant_autodelete_ms` | integer | false | | |
| `time_til_dormant_ms` | integer | false | | |
| `updated_at` | string | false | | |
#### Enumerated Values
@ -4442,6 +4450,20 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `builtin` |
| `app` |
## codersdk.TemplateAutostartRequirement
```json
{
"days_of_week": ["monday"]
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
| -------------- | --------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| `days_of_week` | array of string | false | | Days of week is a list of days of the week in which autostart is allowed to happen. If no days are specified, autostart is not allowed. |
## codersdk.TemplateAutostopRequirement
```json

82
docs/api/templates.md generated
View File

@ -31,6 +31,9 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"autostart_requirement": {
"days_of_week": ["monday"]
},
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
@ -75,38 +78,40 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
Status Code **200**
| Name | Type | Required | Restrictions | Description |
| ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[array item]` | array | false | | |
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
| `» active_version_id` | string(uuid) | false | | |
| `» allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. |
| `» allow_user_autostop` | boolean | false | | |
| `» allow_user_cancel_workspace_jobs` | boolean | false | | |
| `» autostop_requirement` | [codersdk.TemplateAutostopRequirement](schemas.md#codersdktemplateautostoprequirement) | false | | Autostop requirement is an enterprise feature. Its value is only used if your license is entitled to use the advanced template scheduling feature. |
| `»» days_of_week` | array | false | | Days of week is a list of days of the week on which restarts are required. Restarts happen within the user's quiet hours (in their configured timezone). If no days are specified, restarts are not required. Weekdays cannot be specified twice. |
| Name | Type | Required | Restrictions | Description |
| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[array item]` | array | false | | |
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
| `» active_version_id` | string(uuid) | false | | |
| `» allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. |
| `» allow_user_autostop` | boolean | false | | |
| `» allow_user_cancel_workspace_jobs` | boolean | false | | |
| `» autostart_requirement` | [codersdk.TemplateAutostartRequirement](schemas.md#codersdktemplateautostartrequirement) | false | | |
| `»» days_of_week` | array | false | | Days of week is a list of days of the week in which autostart is allowed to happen. If no days are specified, autostart is not allowed. |
| `» autostop_requirement` | [codersdk.TemplateAutostopRequirement](schemas.md#codersdktemplateautostoprequirement) | false | | Autostop requirement and AutostartRequirement are enterprise features. Its value is only used if your license is entitled to use the advanced template scheduling feature. |
| `»» days_of_week` | array | false | | Days of week is a list of days of the week on which restarts are required. Restarts happen within the user's quiet hours (in their configured timezone). If no days are specified, restarts are not required. Weekdays cannot be specified twice. |
| Restarts will only happen on weekdays in this list on weeks which line up with Weeks. |
| `»» weeks` | integer | false | | Weeks is the number of weeks between required restarts. Weeks are synced across all workspaces (and Coder deployments) using modulo math on a hardcoded epoch week of January 2nd, 2023 (the first Monday of 2023). Values of 0 or 1 indicate weekly restarts. Values of 2 indicate fortnightly restarts, etc. |
| `» build_time_stats` | [codersdk.TemplateBuildTimeStats](schemas.md#codersdktemplatebuildtimestats) | false | | |
| `»» [any property]` | [codersdk.TransitionStats](schemas.md#codersdktransitionstats) | false | | |
| `»»» p50` | integer | false | | |
| `»»» p95` | integer | false | | |
| `» created_at` | string(date-time) | false | | |
| `» created_by_id` | string(uuid) | false | | |
| `» created_by_name` | string | false | | |
| `» default_ttl_ms` | integer | false | | |
| `» description` | string | false | | |
| `» display_name` | string | false | | |
| `» failure_ttl_ms` | integer | false | | Failure ttl ms TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. |
| `» icon` | string | false | | |
| `» id` | string(uuid) | false | | |
| `» max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured |
| `» name` | string | false | | |
| `» organization_id` | string(uuid) | false | | |
| `» provisioner` | string | false | | |
| `» time_til_dormant_autodelete_ms` | integer | false | | |
| `» time_til_dormant_ms` | integer | false | | |
| `» updated_at` | string(date-time) | false | | |
| `»» weeks` | integer | false | | Weeks is the number of weeks between required restarts. Weeks are synced across all workspaces (and Coder deployments) using modulo math on a hardcoded epoch week of January 2nd, 2023 (the first Monday of 2023). Values of 0 or 1 indicate weekly restarts. Values of 2 indicate fortnightly restarts, etc. |
| `» build_time_stats` | [codersdk.TemplateBuildTimeStats](schemas.md#codersdktemplatebuildtimestats) | false | | |
| `»» [any property]` | [codersdk.TransitionStats](schemas.md#codersdktransitionstats) | false | | |
| `»»» p50` | integer | false | | |
| `»»» p95` | integer | false | | |
| `» created_at` | string(date-time) | false | | |
| `» created_by_id` | string(uuid) | false | | |
| `» created_by_name` | string | false | | |
| `» default_ttl_ms` | integer | false | | |
| `» description` | string | false | | |
| `» display_name` | string | false | | |
| `» failure_ttl_ms` | integer | false | | Failure ttl ms TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. |
| `» icon` | string | false | | |
| `» id` | string(uuid) | false | | |
| `» max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured |
| `» name` | string | false | | |
| `» organization_id` | string(uuid) | false | | |
| `» provisioner` | string | false | | |
| `» time_til_dormant_autodelete_ms` | integer | false | | |
| `» time_til_dormant_ms` | integer | false | | |
| `» updated_at` | string(date-time) | false | | |
#### Enumerated Values
@ -137,6 +142,9 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"autostart_requirement": {
"days_of_week": ["monday"]
},
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
@ -173,6 +181,9 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"autostart_requirement": {
"days_of_week": ["monday"]
},
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
@ -305,6 +316,9 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"autostart_requirement": {
"days_of_week": ["monday"]
},
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
@ -613,6 +627,9 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"autostart_requirement": {
"days_of_week": ["monday"]
},
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
@ -728,6 +745,9 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"autostart_requirement": {
"days_of_week": ["monday"]
},
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0

View File

@ -39,6 +39,14 @@ Allow users to customize the autostop TTL for workspaces on this template. This
Allow users to cancel in-progress workspace jobs.
### --autostart-requirement-weekdays
| | |
| ---- | ------------------------- |
| Type | <code>string-array</code> |
Edit the template autostart requirement weekdays - workspaces created from this template can only autostart on the given weekdays. To unset this value for the template (and allow autostart on all days), pass 'all'.
### --default-ttl
| | |

View File

@ -71,6 +71,7 @@ var auditableResourcesTypes = map[any]map[string]Action{
"icon": ActionTrack,
"default_ttl": ActionTrack,
"max_ttl": ActionTrack,
"autostart_block_days_of_week": ActionTrack,
"autostop_requirement_days_of_week": ActionTrack,
"autostop_requirement_weeks": ActionTrack,
"created_by": ActionTrack,

View File

@ -86,6 +86,9 @@ func (s *EnterpriseTemplateScheduleStore) Get(ctx context.Context, db database.S
DaysOfWeek: uint8(tpl.AutostopRequirementDaysOfWeek),
Weeks: tpl.AutostopRequirementWeeks,
},
AutostartRequirement: agpl.TemplateAutostartRequirement{
DaysOfWeek: tpl.AutostartAllowedDays(),
},
FailureTTL: time.Duration(tpl.FailureTTL),
TimeTilDormant: time.Duration(tpl.TimeTilDormant),
TimeTilDormantAutoDelete: time.Duration(tpl.TimeTilDormantAutoDelete),
@ -107,6 +110,7 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
if int64(opts.DefaultTTL) == tpl.DefaultTTL &&
int64(opts.MaxTTL) == tpl.MaxTTL &&
int16(opts.AutostopRequirement.DaysOfWeek) == tpl.AutostopRequirementDaysOfWeek &&
opts.AutostartRequirement.DaysOfWeek == tpl.AutostartAllowedDays() &&
opts.AutostopRequirement.Weeks == tpl.AutostopRequirementWeeks &&
int64(opts.FailureTTL) == tpl.FailureTTL &&
int64(opts.TimeTilDormant) == tpl.TimeTilDormant &&
@ -119,7 +123,12 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
err := agpl.VerifyTemplateAutostopRequirement(opts.AutostopRequirement.DaysOfWeek, opts.AutostopRequirement.Weeks)
if err != nil {
return database.Template{}, err
return database.Template{}, xerrors.Errorf("verify autostop requirement: %w", err)
}
err = agpl.VerifyTemplateAutostartRequirement(opts.AutostartRequirement.DaysOfWeek)
if err != nil {
return database.Template{}, xerrors.Errorf("verify autostart requirement: %w", err)
}
var template database.Template
@ -136,9 +145,12 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
MaxTTL: int64(opts.MaxTTL),
AutostopRequirementDaysOfWeek: int16(opts.AutostopRequirement.DaysOfWeek),
AutostopRequirementWeeks: opts.AutostopRequirement.Weeks,
FailureTTL: int64(opts.FailureTTL),
TimeTilDormant: int64(opts.TimeTilDormant),
TimeTilDormantAutoDelete: int64(opts.TimeTilDormantAutoDelete),
// Database stores the inverse of the allowed days of the week.
// Make sure the 8th bit is always zeroed out, as there is no 8th day of the week.
AutostartBlockDaysOfWeek: int16(^opts.AutostartRequirement.DaysOfWeek & 0b01111111),
FailureTTL: int64(opts.FailureTTL),
TimeTilDormant: int64(opts.TimeTilDormant),
TimeTilDormantAutoDelete: int64(opts.TimeTilDormantAutoDelete),
})
if err != nil {
return xerrors.Errorf("update template schedule: %w", err)

View File

@ -140,6 +140,89 @@ func TestTemplates(t *testing.T) {
require.EqualValues(t, exp, *ws.TTLMillis)
})
t.Run("SetAutostartRequirement", func(t *testing.T) {
t.Parallel()
client, user := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureAdvancedTemplateScheduling: 1,
},
},
})
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
require.Equal(t, []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"}, template.AutostartRequirement.DaysOfWeek)
ctx := testutil.Context(t, testutil.WaitLong)
updated, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
Name: template.Name,
DisplayName: template.DisplayName,
Description: template.Description,
Icon: template.Icon,
AutostartRequirement: &codersdk.TemplateAutostartRequirement{
DaysOfWeek: []string{"monday", "saturday"},
},
})
require.NoError(t, err)
require.Equal(t, []string{"monday", "saturday"}, updated.AutostartRequirement.DaysOfWeek)
template, err = client.Template(ctx, template.ID)
require.NoError(t, err)
require.Equal(t, []string{"monday", "saturday"}, template.AutostartRequirement.DaysOfWeek)
// Ensure a missing field is a noop
updated, err = client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
Name: template.Name,
DisplayName: template.DisplayName,
Description: template.Description,
Icon: template.Icon + "something",
})
require.NoError(t, err)
require.Equal(t, []string{"monday", "saturday"}, updated.AutostartRequirement.DaysOfWeek)
template, err = client.Template(ctx, template.ID)
require.NoError(t, err)
require.Equal(t, []string{"monday", "saturday"}, template.AutostartRequirement.DaysOfWeek)
})
t.Run("SetInvalidAutostartRequirement", func(t *testing.T) {
t.Parallel()
client, user := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureAdvancedTemplateScheduling: 1,
},
},
})
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
require.Equal(t, []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"}, template.AutostartRequirement.DaysOfWeek)
ctx := testutil.Context(t, testutil.WaitLong)
_, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
Name: template.Name,
DisplayName: template.DisplayName,
Description: template.Description,
Icon: template.Icon,
AutostartRequirement: &codersdk.TemplateAutostartRequirement{
DaysOfWeek: []string{"foobar", "saturday"},
},
})
require.Error(t, err)
})
t.Run("SetAutostopRequirement", func(t *testing.T) {
t.Parallel()

View File

@ -736,6 +736,65 @@ func TestWorkspaceAutobuild(t *testing.T) {
})
}
// Blocked by autostart requirements
func TestExecutorAutostartBlocked(t *testing.T) {
t.Parallel()
now := time.Now()
var allowed []string
for _, day := range agplschedule.DaysOfWeek {
// Skip the day the workspace was created on and if the next day is within 2
// hours, skip that too. The cron scheduler will start the workspace every hour,
// so it can span into the next day.
if day != now.UTC().Weekday() &&
day != now.UTC().Add(time.Hour*2).Weekday() {
allowed = append(allowed, day.String())
}
}
var (
sched = must(cron.Weekly("CRON_TZ=UTC 0 * * * *"))
tickCh = make(chan time.Time)
statsCh = make(chan autobuild.Stats)
client, owner = coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
AutobuildTicker: tickCh,
IncludeProvisionerDaemon: true,
AutobuildStats: statsCh,
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore()),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{codersdk.FeatureAdvancedTemplateScheduling: 1},
},
})
version = coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
template = coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID, func(request *codersdk.CreateTemplateRequest) {
request.AutostartRequirement = &codersdk.TemplateAutostartRequirement{
DaysOfWeek: allowed,
}
})
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
workspace = coderdtest.CreateWorkspace(t, client, owner.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
cwr.AutostartSchedule = ptr.Ref(sched.String())
})
_ = coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
)
// Given: workspace is stopped
workspace = coderdtest.MustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
// When: the autobuild executor ticks way into the future
go func() {
tickCh <- workspace.LatestBuild.CreatedAt.Add(24 * time.Hour)
close(tickCh)
}()
// Then: the workspace should not be started.
stats := <-statsCh
require.NoError(t, stats.Error)
require.Len(t, stats.Transitions, 0)
}
func TestWorkspacesFiltering(t *testing.T) {
t.Parallel()
@ -911,3 +970,10 @@ func TestWorkspaceLock(t *testing.T) {
require.True(t, workspace.LastUsedAt.After(lastUsedAt))
})
}
func must[T any](value T, err error) T {
if err != nil {
panic(err)
}
return value
}

View File

@ -208,6 +208,7 @@ export interface CreateTemplateRequest {
readonly default_ttl_ms?: number;
readonly max_ttl_ms?: number;
readonly autostop_requirement?: TemplateAutostopRequirement;
readonly autostart_requirement?: TemplateAutostartRequirement;
readonly allow_user_cancel_workspace_jobs?: boolean;
readonly allow_user_autostart?: boolean;
readonly allow_user_autostop?: boolean;
@ -901,6 +902,7 @@ export interface Template {
readonly default_ttl_ms: number;
readonly max_ttl_ms: number;
readonly autostop_requirement: TemplateAutostopRequirement;
readonly autostart_requirement: TemplateAutostartRequirement;
readonly created_by_id: string;
readonly created_by_name: string;
readonly allow_user_autostart: boolean;
@ -927,6 +929,11 @@ export interface TemplateAppUsage {
readonly seconds: number;
}
// From codersdk/templates.go
export interface TemplateAutostartRequirement {
readonly days_of_week: string[];
}
// From codersdk/templates.go
export interface TemplateAutostopRequirement {
readonly days_of_week: string[];
@ -1145,6 +1152,7 @@ export interface UpdateTemplateMeta {
readonly default_ttl_ms?: number;
readonly max_ttl_ms?: number;
readonly autostop_requirement?: TemplateAutostopRequirement;
readonly autostart_requirement?: TemplateAutostartRequirement;
readonly allow_user_autostart?: boolean;
readonly allow_user_autostop?: boolean;
readonly allow_user_cancel_workspace_jobs?: boolean;

View File

@ -27,6 +27,17 @@ const validFormValues: FormValues = {
days_of_week: [],
weeks: 1,
},
autostart_requirement: {
days_of_week: [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday",
],
},
failure_ttl_ms: 0,
time_til_dormant_ms: 0,
time_til_dormant_autodelete_ms: 0,

View File

@ -447,6 +447,17 @@ export const MockTemplate: TypesGen.Template = {
days_of_week: [],
weeks: 1,
},
autostart_requirement: {
days_of_week: [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday",
],
},
created_by_id: "test-creator-id",
created_by_name: "test_creator",
icon: "/icon/code.svg",