chore: rename template restart requirement to autostop requirement (#9295)

This commit is contained in:
Dean Sheather
2023-08-29 11:35:05 -07:00
committed by GitHub
parent fc4683d8b3
commit a572800d47
40 changed files with 925 additions and 891 deletions

View File

@@ -316,8 +316,8 @@ func TestScheduleOverride(t *testing.T) {
stdoutBuf = &bytes.Buffer{} stdoutBuf = &bytes.Buffer{}
) )
require.Zero(t, template.DefaultTTLMillis) require.Zero(t, template.DefaultTTLMillis)
require.Empty(t, template.RestartRequirement.DaysOfWeek) require.Empty(t, template.AutostopRequirement.DaysOfWeek)
require.Zero(t, template.RestartRequirement.Weeks) require.Zero(t, template.AutostopRequirement.Weeks)
// Unset the workspace TTL // Unset the workspace TTL
err = client.UpdateWorkspaceTTL(ctx, workspace.ID, codersdk.UpdateWorkspaceTTLRequest{TTLMillis: nil}) err = client.UpdateWorkspaceTTL(ctx, workspace.ID, codersdk.UpdateWorkspaceTTLRequest{TTLMillis: nil})

View File

@@ -21,8 +21,8 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
icon string icon string
defaultTTL time.Duration defaultTTL time.Duration
maxTTL time.Duration maxTTL time.Duration
restartRequirementDaysOfWeek []string autostopRequirementDaysOfWeek []string
restartRequirementWeeks int64 autostopRequirementWeeks int64
failureTTL time.Duration failureTTL time.Duration
inactivityTTL time.Duration inactivityTTL time.Duration
allowUserCancelWorkspaceJobs bool allowUserCancelWorkspaceJobs bool
@@ -51,9 +51,9 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
} }
} }
unsetRestartRequirementDaysOfWeek := len(restartRequirementDaysOfWeek) == 1 && restartRequirementDaysOfWeek[0] == "none" unsetAutostopRequirementDaysOfWeek := len(autostopRequirementDaysOfWeek) == 1 && autostopRequirementDaysOfWeek[0] == "none"
requiresEntitlement := (len(restartRequirementDaysOfWeek) > 0 && !unsetRestartRequirementDaysOfWeek) || requiresEntitlement := (len(autostopRequirementDaysOfWeek) > 0 && !unsetAutostopRequirementDaysOfWeek) ||
restartRequirementWeeks > 0 || autostopRequirementWeeks > 0 ||
!allowUserAutostart || !allowUserAutostart ||
!allowUserAutostop || !allowUserAutostop ||
maxTTL != 0 || maxTTL != 0 ||
@@ -84,11 +84,11 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
// Copy the default value if the list is empty, or if the user // Copy the default value if the list is empty, or if the user
// specified the "none" value clear the list. // specified the "none" value clear the list.
if len(restartRequirementDaysOfWeek) == 0 { if len(autostopRequirementDaysOfWeek) == 0 {
restartRequirementDaysOfWeek = template.RestartRequirement.DaysOfWeek autostopRequirementDaysOfWeek = template.AutostopRequirement.DaysOfWeek
} }
if unsetRestartRequirementDaysOfWeek { if unsetAutostopRequirementDaysOfWeek {
restartRequirementDaysOfWeek = []string{} autostopRequirementDaysOfWeek = []string{}
} }
// NOTE: coderd will ignore empty fields. // NOTE: coderd will ignore empty fields.
@@ -99,9 +99,9 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
Icon: icon, Icon: icon,
DefaultTTLMillis: defaultTTL.Milliseconds(), DefaultTTLMillis: defaultTTL.Milliseconds(),
MaxTTLMillis: maxTTL.Milliseconds(), MaxTTLMillis: maxTTL.Milliseconds(),
RestartRequirement: &codersdk.TemplateRestartRequirement{ AutostopRequirement: &codersdk.TemplateAutostopRequirement{
DaysOfWeek: restartRequirementDaysOfWeek, DaysOfWeek: autostopRequirementDaysOfWeek,
Weeks: restartRequirementWeeks, Weeks: autostopRequirementWeeks,
}, },
FailureTTLMillis: failureTTL.Milliseconds(), FailureTTLMillis: failureTTL.Milliseconds(),
TimeTilDormantMillis: inactivityTTL.Milliseconds(), TimeTilDormantMillis: inactivityTTL.Milliseconds(),
@@ -151,28 +151,28 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
Value: clibase.DurationOf(&maxTTL), Value: clibase.DurationOf(&maxTTL),
}, },
{ {
Flag: "restart-requirement-weekdays", Flag: "autostop-requirement-weekdays",
Description: "Edit the template restart requirement weekdays - workspaces created from this template must be restarted on the given weekdays. To unset this value for the template (and disable the restart requirement for the template), pass 'none'.", 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'.",
// TODO(@dean): unhide when we delete max_ttl // TODO(@dean): unhide when we delete max_ttl
Hidden: true, Hidden: true,
Value: clibase.Validate(clibase.StringArrayOf(&restartRequirementDaysOfWeek), func(value *clibase.StringArray) error { Value: clibase.Validate(clibase.StringArrayOf(&autostopRequirementDaysOfWeek), func(value *clibase.StringArray) error {
v := value.GetSlice() v := value.GetSlice()
if len(v) == 1 && v[0] == "none" { if len(v) == 1 && v[0] == "none" {
return nil return nil
} }
_, err := codersdk.WeekdaysToBitmap(v) _, err := codersdk.WeekdaysToBitmap(v)
if err != nil { if err != nil {
return xerrors.Errorf("invalid restart requirement days of week %q: %w", strings.Join(v, ","), err) return xerrors.Errorf("invalid autostop requirement days of week %q: %w", strings.Join(v, ","), err)
} }
return nil return nil
}), }),
}, },
{ {
Flag: "restart-requirement-weeks", Flag: "autostop-requirement-weeks",
Description: "Edit the template restart requirement weeks - workspaces created from this template must be restarted on an n-weekly basis.", Description: "Edit the template autostop requirement weeks - workspaces created from this template must be restarted on an n-weekly basis.",
// TODO(@dean): unhide when we delete max_ttl // TODO(@dean): unhide when we delete max_ttl
Hidden: true, Hidden: true,
Value: clibase.Int64Of(&restartRequirementWeeks), Value: clibase.Int64Of(&autostopRequirementWeeks),
}, },
{ {
Flag: "failure-ttl", Flag: "failure-ttl",

View File

@@ -242,7 +242,7 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, "", updated.Icon) assert.Equal(t, "", updated.Icon)
assert.Equal(t, "", updated.DisplayName) assert.Equal(t, "", updated.DisplayName)
}) })
t.Run("RestartRequirement", func(t *testing.T) { t.Run("AutostopRequirement", func(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("BlockedAGPL", func(t *testing.T) { t.Run("BlockedAGPL", func(t *testing.T) {
t.Parallel() t.Parallel()
@@ -252,7 +252,7 @@ func TestTemplateEdit(t *testing.T) {
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID) _ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.DefaultTTLMillis = nil ctr.DefaultTTLMillis = nil
ctr.RestartRequirement = nil ctr.AutostopRequirement = nil
}) })
cases := []struct { cases := []struct {
@@ -263,20 +263,20 @@ func TestTemplateEdit(t *testing.T) {
{ {
name: "Weekdays", name: "Weekdays",
flags: []string{ flags: []string{
"--restart-requirement-weekdays", "monday", "--autostop-requirement-weekdays", "monday",
}, },
}, },
{ {
name: "WeekdaysNoneAllowed", name: "WeekdaysNoneAllowed",
flags: []string{ flags: []string{
"--restart-requirement-weekdays", "none", "--autostop-requirement-weekdays", "none",
}, },
ok: true, ok: true,
}, },
{ {
name: "Weeks", name: "Weeks",
flags: []string{ flags: []string{
"--restart-requirement-weeks", "1", "--autostop-requirement-weeks", "1",
}, },
}, },
} }
@@ -312,8 +312,8 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.Icon, updated.Icon) assert.Equal(t, template.Icon, updated.Icon)
assert.Equal(t, template.DisplayName, updated.DisplayName) assert.Equal(t, template.DisplayName, updated.DisplayName)
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.RestartRequirement.DaysOfWeek, updated.RestartRequirement.DaysOfWeek) assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.RestartRequirement.Weeks, updated.RestartRequirement.Weeks) assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
}) })
} }
}) })
@@ -326,7 +326,7 @@ func TestTemplateEdit(t *testing.T) {
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID) _ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.DefaultTTLMillis = nil ctr.DefaultTTLMillis = nil
ctr.RestartRequirement = nil ctr.AutostopRequirement = nil
}) })
// Make a proxy server that will return a valid entitlements // Make a proxy server that will return a valid entitlements
@@ -377,20 +377,20 @@ func TestTemplateEdit(t *testing.T) {
{ {
name: "Weekdays", name: "Weekdays",
flags: []string{ flags: []string{
"--restart-requirement-weekdays", "monday", "--autostop-requirement-weekdays", "monday",
}, },
}, },
{ {
name: "WeekdaysNoneAllowed", name: "WeekdaysNoneAllowed",
flags: []string{ flags: []string{
"--restart-requirement-weekdays", "none", "--autostop-requirement-weekdays", "none",
}, },
ok: true, ok: true,
}, },
{ {
name: "Weeks", name: "Weeks",
flags: []string{ flags: []string{
"--restart-requirement-weeks", "1", "--autostop-requirement-weeks", "1",
}, },
}, },
} }
@@ -426,8 +426,8 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.Icon, updated.Icon) assert.Equal(t, template.Icon, updated.Icon)
assert.Equal(t, template.DisplayName, updated.DisplayName) assert.Equal(t, template.DisplayName, updated.DisplayName)
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.RestartRequirement.DaysOfWeek, updated.RestartRequirement.DaysOfWeek) assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.RestartRequirement.Weeks, updated.RestartRequirement.Weeks) assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
}) })
} }
}) })
@@ -439,7 +439,7 @@ func TestTemplateEdit(t *testing.T) {
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID) _ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.DefaultTTLMillis = nil ctr.DefaultTTLMillis = nil
ctr.RestartRequirement = nil ctr.AutostopRequirement = nil
}) })
// Make a proxy server that will return a valid entitlements // Make a proxy server that will return a valid entitlements
@@ -475,8 +475,8 @@ func TestTemplateEdit(t *testing.T) {
var req codersdk.UpdateTemplateMeta var req codersdk.UpdateTemplateMeta
err = json.Unmarshal(body, &req) err = json.Unmarshal(body, &req)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, req.RestartRequirement.DaysOfWeek, []string{"monday", "tuesday"}) assert.Equal(t, req.AutostopRequirement.DaysOfWeek, []string{"monday", "tuesday"})
assert.EqualValues(t, req.RestartRequirement.Weeks, 3) assert.EqualValues(t, req.AutostopRequirement.Weeks, 3)
r.Body = io.NopCloser(bytes.NewReader(body)) r.Body = io.NopCloser(bytes.NewReader(body))
atomic.AddInt64(&updateTemplateCalled, 1) atomic.AddInt64(&updateTemplateCalled, 1)
@@ -504,8 +504,8 @@ func TestTemplateEdit(t *testing.T) {
"templates", "templates",
"edit", "edit",
template.Name, template.Name,
"--restart-requirement-weekdays", "monday,tuesday", "--autostop-requirement-weekdays", "monday,tuesday",
"--restart-requirement-weeks", "3", "--autostop-requirement-weeks", "3",
} }
inv, root := clitest.New(t, cmdArgs...) inv, root := clitest.New(t, cmdArgs...)
clitest.SetupConfig(t, proxyClient, root) clitest.SetupConfig(t, proxyClient, root)
@@ -525,8 +525,8 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.Icon, updated.Icon) assert.Equal(t, template.Icon, updated.Icon)
assert.Equal(t, template.DisplayName, updated.DisplayName) assert.Equal(t, template.DisplayName, updated.DisplayName)
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.RestartRequirement.DaysOfWeek, updated.RestartRequirement.DaysOfWeek) assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.RestartRequirement.Weeks, updated.RestartRequirement.Weeks) assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
}) })
}) })
// TODO(@dean): remove this test when we remove max_ttl // TODO(@dean): remove this test when we remove max_ttl
@@ -750,7 +750,7 @@ func TestTemplateEdit(t *testing.T) {
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID) _ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.DefaultTTLMillis = nil ctr.DefaultTTLMillis = nil
ctr.RestartRequirement = nil ctr.AutostopRequirement = nil
ctr.FailureTTLMillis = nil ctr.FailureTTLMillis = nil
ctr.TimeTilDormantMillis = nil ctr.TimeTilDormantMillis = nil
}) })
@@ -793,8 +793,8 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.Icon, updated.Icon) assert.Equal(t, template.Icon, updated.Icon)
assert.Equal(t, template.DisplayName, updated.DisplayName) assert.Equal(t, template.DisplayName, updated.DisplayName)
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.RestartRequirement.DaysOfWeek, updated.RestartRequirement.DaysOfWeek) assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.RestartRequirement.Weeks, updated.RestartRequirement.Weeks) assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart) assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart)
assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop) assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop)
assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis) assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis)
@@ -887,8 +887,8 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.Icon, updated.Icon) assert.Equal(t, template.Icon, updated.Icon)
assert.Equal(t, template.DisplayName, updated.DisplayName) assert.Equal(t, template.DisplayName, updated.DisplayName)
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.RestartRequirement.DaysOfWeek, updated.RestartRequirement.DaysOfWeek) assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.RestartRequirement.Weeks, updated.RestartRequirement.Weeks) assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart) assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart)
assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop) assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop)
assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis) assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis)
@@ -985,8 +985,8 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, template.Icon, updated.Icon) assert.Equal(t, template.Icon, updated.Icon)
assert.Equal(t, template.DisplayName, updated.DisplayName) assert.Equal(t, template.DisplayName, updated.DisplayName)
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
assert.Equal(t, template.RestartRequirement.DaysOfWeek, updated.RestartRequirement.DaysOfWeek) assert.Equal(t, template.AutostopRequirement.DaysOfWeek, updated.AutostopRequirement.DaysOfWeek)
assert.Equal(t, template.RestartRequirement.Weeks, updated.RestartRequirement.Weeks) assert.Equal(t, template.AutostopRequirement.Weeks, updated.AutostopRequirement.Weeks)
assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart) assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart)
assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop) assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop)
assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis) assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis)

View File

@@ -26,7 +26,7 @@ func TestWorkspaceActivityBump(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// deadline allows you to forcibly set a max_deadline on the build. This // deadline allows you to forcibly set a max_deadline on the build. This
// doesn't use template restart requirements and instead edits the // doesn't use template autostop requirements and instead edits the
// max_deadline on the build directly in the database. // max_deadline on the build directly in the database.
setupActivityTest := func(t *testing.T, deadline ...time.Duration) (client *codersdk.Client, workspace codersdk.Workspace, assertBumped func(want bool)) { setupActivityTest := func(t *testing.T, deadline ...time.Duration) (client *codersdk.Client, workspace codersdk.Workspace, assertBumped func(want bool)) {
const ttl = time.Minute const ttl = time.Minute
@@ -49,7 +49,7 @@ func TestWorkspaceActivityBump(t *testing.T) {
UserAutostopEnabled: true, UserAutostopEnabled: true,
DefaultTTL: ttl, DefaultTTL: ttl,
// We set max_deadline manually below. // We set max_deadline manually below.
RestartRequirement: schedule.TemplateRestartRequirement{}, AutostopRequirement: schedule.TemplateAutostopRequirement{},
}, nil }, nil
}, },
}, },

90
coderd/apidoc/docs.go generated
View File

@@ -7390,6 +7390,14 @@ const docTemplate = `{
"description": "Allow users to cancel in-progress workspace jobs.\n*bool as the default value is \"true\".", "description": "Allow users to cancel in-progress workspace jobs.\n*bool as the default value is \"true\".",
"type": "boolean" "type": "boolean"
}, },
"autostop_requirement": {
"description": "AutostopRequirement allows optionally specifying the autostop requirement\nfor workspaces created from this template. This is an enterprise feature.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateAutostopRequirement"
}
]
},
"default_ttl_ms": { "default_ttl_ms": {
"description": "DefaultTTLMillis allows optionally specifying the default TTL\nfor all workspaces created from this template.", "description": "DefaultTTLMillis allows optionally specifying the default TTL\nfor all workspaces created from this template.",
"type": "integer" "type": "integer"
@@ -7423,21 +7431,13 @@ const docTemplate = `{
"type": "string" "type": "string"
}, },
"max_ttl_ms": { "max_ttl_ms": {
"description": "TODO(@dean): remove max_ttl once restart_requirement is matured", "description": "TODO(@dean): remove max_ttl once autostop_requirement is matured",
"type": "integer" "type": "integer"
}, },
"name": { "name": {
"description": "Name is the name of the template.", "description": "Name is the name of the template.",
"type": "string" "type": "string"
}, },
"restart_requirement": {
"description": "RestartRequirement allows optionally specifying the restart requirement\nfor workspaces created from this template. This is an enterprise feature.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateRestartRequirement"
}
]
},
"template_version_id": { "template_version_id": {
"description": "VersionID is an in-progress or completed job to use as an initial version\nof the template.\n\nThis is required on creation to enable a user-flow of validating a\ntemplate works. There is no reason the data-model cannot support empty\ntemplates, but it doesn't make sense for users.", "description": "VersionID is an in-progress or completed job to use as an initial version\nof the template.\n\nThis is required on creation to enable a user-flow of validating a\ntemplate works. There is no reason the data-model cannot support empty\ntemplates, but it doesn't make sense for users.",
"type": "string", "type": "string",
@@ -8124,7 +8124,7 @@ const docTemplate = `{
"workspace_actions", "workspace_actions",
"tailnet_pg_coordinator", "tailnet_pg_coordinator",
"single_tailnet", "single_tailnet",
"template_restart_requirement", "template_autostop_requirement",
"deployment_health_page", "deployment_health_page",
"workspaces_batch_actions" "workspaces_batch_actions"
], ],
@@ -8133,7 +8133,7 @@ const docTemplate = `{
"ExperimentWorkspaceActions", "ExperimentWorkspaceActions",
"ExperimentTailnetPGCoordinator", "ExperimentTailnetPGCoordinator",
"ExperimentSingleTailnet", "ExperimentSingleTailnet",
"ExperimentTemplateRestartRequirement", "ExperimentTemplateAutostopRequirement",
"ExperimentDeploymentHealthPage", "ExperimentDeploymentHealthPage",
"ExperimentWorkspacesBatchActions" "ExperimentWorkspacesBatchActions"
] ]
@@ -9516,6 +9516,14 @@ const docTemplate = `{
"allow_user_cancel_workspace_jobs": { "allow_user_cancel_workspace_jobs": {
"type": "boolean" "type": "boolean"
}, },
"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.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateAutostopRequirement"
}
]
},
"build_time_stats": { "build_time_stats": {
"$ref": "#/definitions/codersdk.TemplateBuildTimeStats" "$ref": "#/definitions/codersdk.TemplateBuildTimeStats"
}, },
@@ -9551,7 +9559,7 @@ const docTemplate = `{
"format": "uuid" "format": "uuid"
}, },
"max_ttl_ms": { "max_ttl_ms": {
"description": "TODO(@dean): remove max_ttl once restart_requirement is matured", "description": "TODO(@dean): remove max_ttl once autostop_requirement is matured",
"type": "integer" "type": "integer"
}, },
"name": { "name": {
@@ -9567,14 +9575,6 @@ const docTemplate = `{
"terraform" "terraform"
] ]
}, },
"restart_requirement": {
"description": "RestartRequirement is an enterprise feature. Its value is only used if\nyour license is entitled to use the advanced template scheduling feature.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateRestartRequirement"
}
]
},
"time_til_dormant_autodelete_ms": { "time_til_dormant_autodelete_ms": {
"type": "integer" "type": "integer"
}, },
@@ -9633,6 +9633,31 @@ const docTemplate = `{
"TemplateAppsTypeApp" "TemplateAppsTypeApp"
] ]
}, },
"codersdk.TemplateAutostopRequirement": {
"type": "object",
"properties": {
"days_of_week": {
"description": "DaysOfWeek is a list of days of the week on which restarts are required.\nRestarts happen within the user's quiet hours (in their configured\ntimezone). If no days are specified, restarts are not required. Weekdays\ncannot be specified twice.\n\nRestarts will only happen on weekdays in this list on weeks which line up\nwith Weeks.",
"type": "array",
"items": {
"type": "string",
"enum": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday"
]
}
},
"weeks": {
"description": "Weeks is the number of weeks between required restarts. Weeks are synced\nacross all workspaces (and Coder deployments) using modulo math on a\nhardcoded epoch week of January 2nd, 2023 (the first Monday of 2023).\nValues of 0 or 1 indicate weekly restarts. Values of 2 indicate\nfortnightly restarts, etc.",
"type": "integer"
}
}
},
"codersdk.TemplateBuildTimeStats": { "codersdk.TemplateBuildTimeStats": {
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
@@ -9793,31 +9818,6 @@ const docTemplate = `{
} }
} }
}, },
"codersdk.TemplateRestartRequirement": {
"type": "object",
"properties": {
"days_of_week": {
"description": "DaysOfWeek is a list of days of the week on which restarts are required.\nRestarts happen within the user's quiet hours (in their configured\ntimezone). If no days are specified, restarts are not required. Weekdays\ncannot be specified twice.\n\nRestarts will only happen on weekdays in this list on weeks which line up\nwith Weeks.",
"type": "array",
"items": {
"type": "string",
"enum": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday"
]
}
},
"weeks": {
"description": "Weeks is the number of weeks between required restarts. Weeks are synced\nacross all workspaces (and Coder deployments) using modulo math on a\nhardcoded epoch week of January 2nd, 2023 (the first Monday of 2023).\nValues of 0 or 1 indicate weekly restarts. Values of 2 indicate\nfortnightly restarts, etc.",
"type": "integer"
}
}
},
"codersdk.TemplateRole": { "codersdk.TemplateRole": {
"type": "string", "type": "string",
"enum": [ "enum": [

View File

@@ -6583,6 +6583,14 @@
"description": "Allow users to cancel in-progress workspace jobs.\n*bool as the default value is \"true\".", "description": "Allow users to cancel in-progress workspace jobs.\n*bool as the default value is \"true\".",
"type": "boolean" "type": "boolean"
}, },
"autostop_requirement": {
"description": "AutostopRequirement allows optionally specifying the autostop requirement\nfor workspaces created from this template. This is an enterprise feature.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateAutostopRequirement"
}
]
},
"default_ttl_ms": { "default_ttl_ms": {
"description": "DefaultTTLMillis allows optionally specifying the default TTL\nfor all workspaces created from this template.", "description": "DefaultTTLMillis allows optionally specifying the default TTL\nfor all workspaces created from this template.",
"type": "integer" "type": "integer"
@@ -6616,21 +6624,13 @@
"type": "string" "type": "string"
}, },
"max_ttl_ms": { "max_ttl_ms": {
"description": "TODO(@dean): remove max_ttl once restart_requirement is matured", "description": "TODO(@dean): remove max_ttl once autostop_requirement is matured",
"type": "integer" "type": "integer"
}, },
"name": { "name": {
"description": "Name is the name of the template.", "description": "Name is the name of the template.",
"type": "string" "type": "string"
}, },
"restart_requirement": {
"description": "RestartRequirement allows optionally specifying the restart requirement\nfor workspaces created from this template. This is an enterprise feature.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateRestartRequirement"
}
]
},
"template_version_id": { "template_version_id": {
"description": "VersionID is an in-progress or completed job to use as an initial version\nof the template.\n\nThis is required on creation to enable a user-flow of validating a\ntemplate works. There is no reason the data-model cannot support empty\ntemplates, but it doesn't make sense for users.", "description": "VersionID is an in-progress or completed job to use as an initial version\nof the template.\n\nThis is required on creation to enable a user-flow of validating a\ntemplate works. There is no reason the data-model cannot support empty\ntemplates, but it doesn't make sense for users.",
"type": "string", "type": "string",
@@ -7275,7 +7275,7 @@
"workspace_actions", "workspace_actions",
"tailnet_pg_coordinator", "tailnet_pg_coordinator",
"single_tailnet", "single_tailnet",
"template_restart_requirement", "template_autostop_requirement",
"deployment_health_page", "deployment_health_page",
"workspaces_batch_actions" "workspaces_batch_actions"
], ],
@@ -7284,7 +7284,7 @@
"ExperimentWorkspaceActions", "ExperimentWorkspaceActions",
"ExperimentTailnetPGCoordinator", "ExperimentTailnetPGCoordinator",
"ExperimentSingleTailnet", "ExperimentSingleTailnet",
"ExperimentTemplateRestartRequirement", "ExperimentTemplateAutostopRequirement",
"ExperimentDeploymentHealthPage", "ExperimentDeploymentHealthPage",
"ExperimentWorkspacesBatchActions" "ExperimentWorkspacesBatchActions"
] ]
@@ -8585,6 +8585,14 @@
"allow_user_cancel_workspace_jobs": { "allow_user_cancel_workspace_jobs": {
"type": "boolean" "type": "boolean"
}, },
"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.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateAutostopRequirement"
}
]
},
"build_time_stats": { "build_time_stats": {
"$ref": "#/definitions/codersdk.TemplateBuildTimeStats" "$ref": "#/definitions/codersdk.TemplateBuildTimeStats"
}, },
@@ -8620,7 +8628,7 @@
"format": "uuid" "format": "uuid"
}, },
"max_ttl_ms": { "max_ttl_ms": {
"description": "TODO(@dean): remove max_ttl once restart_requirement is matured", "description": "TODO(@dean): remove max_ttl once autostop_requirement is matured",
"type": "integer" "type": "integer"
}, },
"name": { "name": {
@@ -8634,14 +8642,6 @@
"type": "string", "type": "string",
"enum": ["terraform"] "enum": ["terraform"]
}, },
"restart_requirement": {
"description": "RestartRequirement is an enterprise feature. Its value is only used if\nyour license is entitled to use the advanced template scheduling feature.",
"allOf": [
{
"$ref": "#/definitions/codersdk.TemplateRestartRequirement"
}
]
},
"time_til_dormant_autodelete_ms": { "time_til_dormant_autodelete_ms": {
"type": "integer" "type": "integer"
}, },
@@ -8694,6 +8694,31 @@
"enum": ["builtin", "app"], "enum": ["builtin", "app"],
"x-enum-varnames": ["TemplateAppsTypeBuiltin", "TemplateAppsTypeApp"] "x-enum-varnames": ["TemplateAppsTypeBuiltin", "TemplateAppsTypeApp"]
}, },
"codersdk.TemplateAutostopRequirement": {
"type": "object",
"properties": {
"days_of_week": {
"description": "DaysOfWeek is a list of days of the week on which restarts are required.\nRestarts happen within the user's quiet hours (in their configured\ntimezone). If no days are specified, restarts are not required. Weekdays\ncannot be specified twice.\n\nRestarts will only happen on weekdays in this list on weeks which line up\nwith Weeks.",
"type": "array",
"items": {
"type": "string",
"enum": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday"
]
}
},
"weeks": {
"description": "Weeks is the number of weeks between required restarts. Weeks are synced\nacross all workspaces (and Coder deployments) using modulo math on a\nhardcoded epoch week of January 2nd, 2023 (the first Monday of 2023).\nValues of 0 or 1 indicate weekly restarts. Values of 2 indicate\nfortnightly restarts, etc.",
"type": "integer"
}
}
},
"codersdk.TemplateBuildTimeStats": { "codersdk.TemplateBuildTimeStats": {
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
@@ -8854,31 +8879,6 @@
} }
} }
}, },
"codersdk.TemplateRestartRequirement": {
"type": "object",
"properties": {
"days_of_week": {
"description": "DaysOfWeek is a list of days of the week on which restarts are required.\nRestarts happen within the user's quiet hours (in their configured\ntimezone). If no days are specified, restarts are not required. Weekdays\ncannot be specified twice.\n\nRestarts will only happen on weekdays in this list on weeks which line up\nwith Weeks.",
"type": "array",
"items": {
"type": "string",
"enum": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday"
]
}
},
"weeks": {
"description": "Weeks is the number of weeks between required restarts. Weeks are synced\nacross all workspaces (and Coder deployments) using modulo math on a\nhardcoded epoch week of January 2nd, 2023 (the first Monday of 2023).\nValues of 0 or 1 indicate weekly restarts. Values of 2 indicate\nfortnightly restarts, etc.",
"type": "integer"
}
}
},
"codersdk.TemplateRole": { "codersdk.TemplateRole": {
"type": "string", "type": "string",
"enum": ["admin", "use", ""], "enum": ["admin", "use", ""],

View File

@@ -623,7 +623,7 @@ func TestExecutorAutostartTemplateDisabled(t *testing.T) {
UserAutostartEnabled: false, UserAutostartEnabled: false,
UserAutostopEnabled: true, UserAutostopEnabled: true,
DefaultTTL: 0, DefaultTTL: 0,
RestartRequirement: schedule.TemplateRestartRequirement{}, AutostopRequirement: schedule.TemplateAutostopRequirement{},
}, nil }, nil
}, },
}, },

View File

@@ -5127,8 +5127,8 @@ func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database
tpl.UpdatedAt = database.Now() tpl.UpdatedAt = database.Now()
tpl.DefaultTTL = arg.DefaultTTL tpl.DefaultTTL = arg.DefaultTTL
tpl.MaxTTL = arg.MaxTTL tpl.MaxTTL = arg.MaxTTL
tpl.RestartRequirementDaysOfWeek = arg.RestartRequirementDaysOfWeek tpl.AutostopRequirementDaysOfWeek = arg.AutostopRequirementDaysOfWeek
tpl.RestartRequirementWeeks = arg.RestartRequirementWeeks tpl.AutostopRequirementWeeks = arg.AutostopRequirementWeeks
tpl.FailureTTL = arg.FailureTTL tpl.FailureTTL = arg.FailureTTL
tpl.TimeTilDormant = arg.TimeTilDormant tpl.TimeTilDormant = arg.TimeTilDormant
tpl.TimeTilDormantAutoDelete = arg.TimeTilDormantAutoDelete tpl.TimeTilDormantAutoDelete = arg.TimeTilDormantAutoDelete

View File

@@ -637,8 +637,8 @@ CREATE TABLE templates (
failure_ttl bigint DEFAULT 0 NOT NULL, failure_ttl bigint DEFAULT 0 NOT NULL,
time_til_dormant bigint DEFAULT 0 NOT NULL, time_til_dormant bigint DEFAULT 0 NOT NULL,
time_til_dormant_autodelete bigint DEFAULT 0 NOT NULL, time_til_dormant_autodelete bigint DEFAULT 0 NOT NULL,
restart_requirement_days_of_week smallint DEFAULT 0 NOT NULL, autostop_requirement_days_of_week smallint DEFAULT 0 NOT NULL,
restart_requirement_weeks bigint DEFAULT 0 NOT NULL autostop_requirement_weeks bigint DEFAULT 0 NOT NULL
); );
COMMENT ON COLUMN templates.default_ttl IS 'The default duration for autostop for workspaces created from this template.'; COMMENT ON COLUMN templates.default_ttl IS 'The default duration for autostop for workspaces created from this template.';
@@ -651,9 +651,9 @@ COMMENT ON COLUMN templates.allow_user_autostart IS 'Allow users to specify an a
COMMENT ON COLUMN templates.allow_user_autostop IS 'Allow users to specify custom autostop values for workspaces (enterprise).'; COMMENT ON COLUMN templates.allow_user_autostop IS 'Allow users to specify custom autostop values for workspaces (enterprise).';
COMMENT ON COLUMN templates.restart_requirement_days_of_week IS 'A bitmap of days of week to restart the workspace on, starting with Monday as the 0th bit, and Sunday as the 6th bit. The 7th bit is unused.'; COMMENT ON COLUMN templates.autostop_requirement_days_of_week IS 'A bitmap of days of week to restart the workspace on, starting with Monday as the 0th bit, and Sunday as the 6th bit. The 7th bit is unused.';
COMMENT ON COLUMN templates.restart_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.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.';
CREATE VIEW template_with_users AS CREATE VIEW template_with_users AS
SELECT templates.id, SELECT templates.id,
@@ -678,8 +678,8 @@ CREATE VIEW template_with_users AS
templates.failure_ttl, templates.failure_ttl,
templates.time_til_dormant, templates.time_til_dormant,
templates.time_til_dormant_autodelete, templates.time_til_dormant_autodelete,
templates.restart_requirement_days_of_week, templates.autostop_requirement_days_of_week,
templates.restart_requirement_weeks, templates.autostop_requirement_weeks,
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url, COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS created_by_username COALESCE(visible_users.username, ''::text) AS created_by_username
FROM (public.templates FROM (public.templates

View File

@@ -0,0 +1,7 @@
BEGIN;
ALTER TABLE templates RENAME COLUMN autostop_requirement_days_of_week TO restart_requirement_days_of_week;
ALTER TABLE templates RENAME COLUMN autostop_requirement_weeks TO restart_requirement_weeks;
COMMIT;

View File

@@ -0,0 +1,25 @@
BEGIN;
ALTER TABLE templates RENAME COLUMN restart_requirement_days_of_week TO autostop_requirement_days_of_week;
ALTER TABLE templates RENAME COLUMN restart_requirement_weeks TO autostop_requirement_weeks;
DROP VIEW template_with_users;
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

@@ -83,8 +83,8 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate
&i.FailureTTL, &i.FailureTTL,
&i.TimeTilDormant, &i.TimeTilDormant,
&i.TimeTilDormantAutoDelete, &i.TimeTilDormantAutoDelete,
&i.RestartRequirementDaysOfWeek, &i.AutostopRequirementDaysOfWeek,
&i.RestartRequirementWeeks, &i.AutostopRequirementWeeks,
&i.CreatedByAvatarURL, &i.CreatedByAvatarURL,
&i.CreatedByUsername, &i.CreatedByUsername,
); err != nil { ); err != nil {

View File

@@ -1731,8 +1731,8 @@ type Template struct {
FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"`
TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"` TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"`
TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"` TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"`
RestartRequirementDaysOfWeek int16 `db:"restart_requirement_days_of_week" json:"restart_requirement_days_of_week"` AutostopRequirementDaysOfWeek int16 `db:"autostop_requirement_days_of_week" json:"autostop_requirement_days_of_week"`
RestartRequirementWeeks int64 `db:"restart_requirement_weeks" json:"restart_requirement_weeks"` AutostopRequirementWeeks int64 `db:"autostop_requirement_weeks" json:"autostop_requirement_weeks"`
CreatedByAvatarURL sql.NullString `db:"created_by_avatar_url" json:"created_by_avatar_url"` CreatedByAvatarURL sql.NullString `db:"created_by_avatar_url" json:"created_by_avatar_url"`
CreatedByUsername string `db:"created_by_username" json:"created_by_username"` CreatedByUsername string `db:"created_by_username" json:"created_by_username"`
} }
@@ -1766,9 +1766,9 @@ type TemplateTable struct {
TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"` TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"`
TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"` TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"`
// A bitmap of days of week to restart the workspace on, starting with Monday as the 0th bit, and Sunday as the 6th bit. The 7th bit is unused. // A bitmap of days of week to restart the workspace on, starting with Monday as the 0th bit, and Sunday as the 6th bit. The 7th bit is unused.
RestartRequirementDaysOfWeek int16 `db:"restart_requirement_days_of_week" json:"restart_requirement_days_of_week"` 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. // 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.
RestartRequirementWeeks int64 `db:"restart_requirement_weeks" json:"restart_requirement_weeks"` AutostopRequirementWeeks int64 `db:"autostop_requirement_weeks" json:"autostop_requirement_weeks"`
} }
// Joins in the username + avatar url of the created by user. // Joins in the username + avatar url of the created by user.

View File

@@ -4317,7 +4317,7 @@ func (q *sqlQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg GetTem
const getTemplateByID = `-- name: GetTemplateByID :one const getTemplateByID = `-- name: GetTemplateByID :one
SELECT 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, restart_requirement_days_of_week, restart_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, created_by_avatar_url, created_by_username
FROM FROM
template_with_users template_with_users
WHERE WHERE
@@ -4352,8 +4352,8 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
&i.FailureTTL, &i.FailureTTL,
&i.TimeTilDormant, &i.TimeTilDormant,
&i.TimeTilDormantAutoDelete, &i.TimeTilDormantAutoDelete,
&i.RestartRequirementDaysOfWeek, &i.AutostopRequirementDaysOfWeek,
&i.RestartRequirementWeeks, &i.AutostopRequirementWeeks,
&i.CreatedByAvatarURL, &i.CreatedByAvatarURL,
&i.CreatedByUsername, &i.CreatedByUsername,
) )
@@ -4362,7 +4362,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one
SELECT 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, restart_requirement_days_of_week, restart_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, created_by_avatar_url, created_by_username
FROM FROM
template_with_users AS templates template_with_users AS templates
WHERE WHERE
@@ -4405,8 +4405,8 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
&i.FailureTTL, &i.FailureTTL,
&i.TimeTilDormant, &i.TimeTilDormant,
&i.TimeTilDormantAutoDelete, &i.TimeTilDormantAutoDelete,
&i.RestartRequirementDaysOfWeek, &i.AutostopRequirementDaysOfWeek,
&i.RestartRequirementWeeks, &i.AutostopRequirementWeeks,
&i.CreatedByAvatarURL, &i.CreatedByAvatarURL,
&i.CreatedByUsername, &i.CreatedByUsername,
) )
@@ -4414,7 +4414,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
} }
const getTemplates = `-- name: GetTemplates :many 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, restart_requirement_days_of_week, restart_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, created_by_avatar_url, created_by_username FROM template_with_users AS templates
ORDER BY (name, id) ASC ORDER BY (name, id) ASC
` `
@@ -4450,8 +4450,8 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
&i.FailureTTL, &i.FailureTTL,
&i.TimeTilDormant, &i.TimeTilDormant,
&i.TimeTilDormantAutoDelete, &i.TimeTilDormantAutoDelete,
&i.RestartRequirementDaysOfWeek, &i.AutostopRequirementDaysOfWeek,
&i.RestartRequirementWeeks, &i.AutostopRequirementWeeks,
&i.CreatedByAvatarURL, &i.CreatedByAvatarURL,
&i.CreatedByUsername, &i.CreatedByUsername,
); err != nil { ); err != nil {
@@ -4470,7 +4470,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many
SELECT 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, restart_requirement_days_of_week, restart_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, created_by_avatar_url, created_by_username
FROM FROM
template_with_users AS templates template_with_users AS templates
WHERE WHERE
@@ -4543,8 +4543,8 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate
&i.FailureTTL, &i.FailureTTL,
&i.TimeTilDormant, &i.TimeTilDormant,
&i.TimeTilDormantAutoDelete, &i.TimeTilDormantAutoDelete,
&i.RestartRequirementDaysOfWeek, &i.AutostopRequirementDaysOfWeek,
&i.RestartRequirementWeeks, &i.AutostopRequirementWeeks,
&i.CreatedByAvatarURL, &i.CreatedByAvatarURL,
&i.CreatedByUsername, &i.CreatedByUsername,
); err != nil { ); err != nil {
@@ -4729,8 +4729,8 @@ SET
allow_user_autostop = $4, allow_user_autostop = $4,
default_ttl = $5, default_ttl = $5,
max_ttl = $6, max_ttl = $6,
restart_requirement_days_of_week = $7, autostop_requirement_days_of_week = $7,
restart_requirement_weeks = $8, autostop_requirement_weeks = $8,
failure_ttl = $9, failure_ttl = $9,
time_til_dormant = $10, time_til_dormant = $10,
time_til_dormant_autodelete = $11 time_til_dormant_autodelete = $11
@@ -4745,8 +4745,8 @@ type UpdateTemplateScheduleByIDParams struct {
AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"`
DefaultTTL int64 `db:"default_ttl" json:"default_ttl"` DefaultTTL int64 `db:"default_ttl" json:"default_ttl"`
MaxTTL int64 `db:"max_ttl" json:"max_ttl"` MaxTTL int64 `db:"max_ttl" json:"max_ttl"`
RestartRequirementDaysOfWeek int16 `db:"restart_requirement_days_of_week" json:"restart_requirement_days_of_week"` AutostopRequirementDaysOfWeek int16 `db:"autostop_requirement_days_of_week" json:"autostop_requirement_days_of_week"`
RestartRequirementWeeks int64 `db:"restart_requirement_weeks" json:"restart_requirement_weeks"` AutostopRequirementWeeks int64 `db:"autostop_requirement_weeks" json:"autostop_requirement_weeks"`
FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"`
TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"` TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"`
TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"` TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"`
@@ -4760,8 +4760,8 @@ func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateT
arg.AllowUserAutostop, arg.AllowUserAutostop,
arg.DefaultTTL, arg.DefaultTTL,
arg.MaxTTL, arg.MaxTTL,
arg.RestartRequirementDaysOfWeek, arg.AutostopRequirementDaysOfWeek,
arg.RestartRequirementWeeks, arg.AutostopRequirementWeeks,
arg.FailureTTL, arg.FailureTTL,
arg.TimeTilDormant, arg.TimeTilDormant,
arg.TimeTilDormantAutoDelete, arg.TimeTilDormantAutoDelete,

View File

@@ -118,8 +118,8 @@ SET
allow_user_autostop = $4, allow_user_autostop = $4,
default_ttl = $5, default_ttl = $5,
max_ttl = $6, max_ttl = $6,
restart_requirement_days_of_week = $7, autostop_requirement_days_of_week = $7,
restart_requirement_weeks = $8, autostop_requirement_weeks = $8,
failure_ttl = $9, failure_ttl = $9,
time_til_dormant = $10, time_til_dormant = $10,
time_til_dormant_autodelete = $11 time_til_dormant_autodelete = $11

View File

@@ -1031,7 +1031,7 @@ func TestCompleteJob(t *testing.T) {
UserAutostopEnabled: c.templateAllowAutostop, UserAutostopEnabled: c.templateAllowAutostop,
DefaultTTL: c.templateDefaultTTL, DefaultTTL: c.templateDefaultTTL,
MaxTTL: c.templateMaxTTL, MaxTTL: c.templateMaxTTL,
UseRestartRequirement: false, UseAutostopRequirement: false,
}, nil }, nil
}, },
} }
@@ -1155,7 +1155,7 @@ func TestCompleteJob(t *testing.T) {
// Wednesday the 8th of February 2023 at midnight. This date was // Wednesday the 8th of February 2023 at midnight. This date was
// specifically chosen as it doesn't fall on a applicable week for both // specifically chosen as it doesn't fall on a applicable week for both
// fortnightly and triweekly restart requirements. // fortnightly and triweekly autostop requirements.
wednesdayMidnightUTC := time.Date(2023, 2, 8, 0, 0, 0, 0, time.UTC) wednesdayMidnightUTC := time.Date(2023, 2, 8, 0, 0, 0, 0, time.UTC)
sydneyQuietHours := "CRON_TZ=Australia/Sydney 0 0 * * *" sydneyQuietHours := "CRON_TZ=Australia/Sydney 0 0 * * *"
@@ -1176,7 +1176,7 @@ func TestCompleteJob(t *testing.T) {
// These fields are only used when testing max deadline. // These fields are only used when testing max deadline.
userQuietHoursSchedule string userQuietHoursSchedule string
templateRestartRequirement schedule.TemplateRestartRequirement templateAutostopRequirement schedule.TemplateAutostopRequirement
expectedDeadline time.Time expectedDeadline time.Time
expectedMaxDeadline time.Time expectedMaxDeadline time.Time
@@ -1184,7 +1184,7 @@ func TestCompleteJob(t *testing.T) {
{ {
name: "OK", name: "OK",
now: now, now: now,
templateRestartRequirement: schedule.TemplateRestartRequirement{}, templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
workspaceTTL: 0, workspaceTTL: 0,
transition: database.WorkspaceTransitionStart, transition: database.WorkspaceTransitionStart,
expectedDeadline: time.Time{}, expectedDeadline: time.Time{},
@@ -1193,7 +1193,7 @@ func TestCompleteJob(t *testing.T) {
{ {
name: "Delete", name: "Delete",
now: now, now: now,
templateRestartRequirement: schedule.TemplateRestartRequirement{}, templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
workspaceTTL: 0, workspaceTTL: 0,
transition: database.WorkspaceTransitionDelete, transition: database.WorkspaceTransitionDelete,
expectedDeadline: time.Time{}, expectedDeadline: time.Time{},
@@ -1202,17 +1202,17 @@ func TestCompleteJob(t *testing.T) {
{ {
name: "WorkspaceTTL", name: "WorkspaceTTL",
now: now, now: now,
templateRestartRequirement: schedule.TemplateRestartRequirement{}, templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
workspaceTTL: time.Hour, workspaceTTL: time.Hour,
transition: database.WorkspaceTransitionStart, transition: database.WorkspaceTransitionStart,
expectedDeadline: now.Add(time.Hour), expectedDeadline: now.Add(time.Hour),
expectedMaxDeadline: time.Time{}, expectedMaxDeadline: time.Time{},
}, },
{ {
name: "TemplateRestartRequirement", name: "TemplateAutostopRequirement",
now: wednesdayMidnightUTC, now: wednesdayMidnightUTC,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 0, // weekly Weeks: 0, // weekly
}, },
@@ -1244,8 +1244,8 @@ func TestCompleteJob(t *testing.T) {
UserAutostartEnabled: false, UserAutostartEnabled: false,
UserAutostopEnabled: true, UserAutostopEnabled: true,
DefaultTTL: 0, DefaultTTL: 0,
UseRestartRequirement: true, UseAutostopRequirement: true,
RestartRequirement: c.templateRestartRequirement, AutostopRequirement: c.templateAutostopRequirement,
}, nil }, nil
}, },
} }
@@ -1285,8 +1285,8 @@ func TestCompleteJob(t *testing.T) {
AllowUserAutostart: false, AllowUserAutostart: false,
AllowUserAutostop: true, AllowUserAutostop: true,
DefaultTTL: 0, DefaultTTL: 0,
RestartRequirementDaysOfWeek: int16(c.templateRestartRequirement.DaysOfWeek), AutostopRequirementDaysOfWeek: int16(c.templateAutostopRequirement.DaysOfWeek),
RestartRequirementWeeks: c.templateRestartRequirement.Weeks, AutostopRequirementWeeks: c.templateAutostopRequirement.Weeks,
}) })
require.NoError(t, err) require.NoError(t, err)
template, err = srv.Database.GetTemplateByID(ctx, template.ID) template, err = srv.Database.GetTemplateByID(ctx, template.ID)

View File

@@ -13,17 +13,17 @@ import (
) )
const ( const (
// restartRequirementLeeway is the duration of time before a restart // autostopRequirementLeeway is the duration of time before a autostop
// requirement where we skip the requirement and fall back to the next // requirement where we skip the requirement and fall back to the next
// scheduled restart. This avoids workspaces being restarted too soon. // scheduled stop. This avoids workspaces being stopped too soon.
// //
// E.g. If the workspace is started within an hour of the quiet hours, we // E.g. If the workspace is started within an hour of the quiet hours, we
// will skip the restart requirement and use the next scheduled restart // will skip the autostop requirement and use the next scheduled
// requirement. // stop time instead.
restartRequirementLeeway = 1 * time.Hour autostopRequirementLeeway = 1 * time.Hour
// restartRequirementBuffer is the duration of time we subtract from the // autostopRequirementBuffer is the duration of time we subtract from the
// time when calculating the next scheduled restart time. This avoids issues // time when calculating the next scheduled stop time. This avoids issues
// where autostart happens on the hour and the scheduled quiet hours are // where autostart happens on the hour and the scheduled quiet hours are
// also on the hour. // also on the hour.
// //
@@ -37,7 +37,7 @@ const (
// //
// This resolves that problem by subtracting 15 minutes from midnight // This resolves that problem by subtracting 15 minutes from midnight
// when we check the next cron time. // when we check the next cron time.
restartRequirementBuffer = -15 * time.Minute autostopRequirementBuffer = -15 * time.Minute
) )
type CalculateAutostopParams struct { type CalculateAutostopParams struct {
@@ -68,7 +68,7 @@ type AutostopTime struct {
// //
// MaxDeadline is the maximum value for deadline. The deadline cannot be bumped // MaxDeadline is the maximum value for deadline. The deadline cannot be bumped
// past this value, so it denotes the absolute deadline that the workspace build // past this value, so it denotes the absolute deadline that the workspace build
// must be stopped by. MaxDeadline is calculated using the template's "restart // must be stopped by. MaxDeadline is calculated using the template's "autostop
// requirement" settings and the user's "quiet hours" settings to pick a time // requirement" settings and the user's "quiet hours" settings to pick a time
// outside of working hours. // outside of working hours.
// //
@@ -113,13 +113,13 @@ func CalculateAutostop(ctx context.Context, params CalculateAutostopParams) (Aut
// Use the old algorithm for calculating max_deadline if the instance isn't // Use the old algorithm for calculating max_deadline if the instance isn't
// configured or entitled to use the new feature flag yet. // configured or entitled to use the new feature flag yet.
// TODO(@dean): remove this once the feature flag is enabled for all // TODO(@dean): remove this once the feature flag is enabled for all
if !templateSchedule.UseRestartRequirement && templateSchedule.MaxTTL > 0 { if !templateSchedule.UseAutostopRequirement && templateSchedule.MaxTTL > 0 {
autostop.MaxDeadline = now.Add(templateSchedule.MaxTTL) autostop.MaxDeadline = now.Add(templateSchedule.MaxTTL)
} }
// TODO(@dean): remove extra conditional // TODO(@dean): remove extra conditional
if templateSchedule.UseRestartRequirement && templateSchedule.RestartRequirement.DaysOfWeek != 0 { if templateSchedule.UseAutostopRequirement && templateSchedule.AutostopRequirement.DaysOfWeek != 0 {
// The template has a restart requirement, so determine the max deadline // The template has a autostop requirement, so determine the max deadline
// of this workspace build. // of this workspace build.
// First, get the user's quiet hours schedule (this will return the // First, get the user's quiet hours schedule (this will return the
@@ -137,13 +137,13 @@ func CalculateAutostop(ctx context.Context, params CalculateAutostopParams) (Aut
now := now.In(loc) now := now.In(loc)
// Add the leeway here so we avoid checking today's quiet hours if // Add the leeway here so we avoid checking today's quiet hours if
// the workspace was started <1h before midnight. // the workspace was started <1h before midnight.
startOfStopDay := truncateMidnight(now.Add(restartRequirementLeeway)) startOfStopDay := truncateMidnight(now.Add(autostopRequirementLeeway))
// If the template schedule wants to only restart on n-th weeks then // If the template schedule wants to only autostop on n-th weeks
// change the startOfDay to be the Monday of the next applicable // then change the startOfDay to be the Monday of the next
// week. // applicable week.
if templateSchedule.RestartRequirement.Weeks > 1 { if templateSchedule.AutostopRequirement.Weeks > 1 {
startOfStopDay, err = GetNextApplicableMondayOfNWeeks(startOfStopDay, templateSchedule.RestartRequirement.Weeks) startOfStopDay, err = GetNextApplicableMondayOfNWeeks(startOfStopDay, templateSchedule.AutostopRequirement.Weeks)
if err != nil { if err != nil {
return autostop, xerrors.Errorf("determine start of stop week: %w", err) return autostop, xerrors.Errorf("determine start of stop week: %w", err)
} }
@@ -155,30 +155,30 @@ func CalculateAutostop(ctx context.Context, params CalculateAutostopParams) (Aut
// Allow an hour of leeway (i.e. any workspaces started within an // Allow an hour of leeway (i.e. any workspaces started within an
// hour of the scheduled stop time will always bounce to the next // hour of the scheduled stop time will always bounce to the next
// stop window). // stop window).
checkSchedule := userQuietHoursSchedule.Schedule.Next(startOfStopDay.Add(restartRequirementBuffer)) checkSchedule := userQuietHoursSchedule.Schedule.Next(startOfStopDay.Add(autostopRequirementBuffer))
if checkSchedule.Before(now.Add(restartRequirementLeeway)) { if checkSchedule.Before(now.Add(autostopRequirementLeeway)) {
// Set the first stop day we try to tomorrow because today's // Set the first stop day we try to tomorrow because today's
// schedule is too close to now or has already passed. // schedule is too close to now or has already passed.
startOfStopDay = nextDayMidnight(startOfStopDay) startOfStopDay = nextDayMidnight(startOfStopDay)
} }
// Iterate from 0 to 7, check if the current startOfDay is in the // Iterate from 0 to 7, check if the current startOfDay is in the
// restart requirement. If it isn't then add a day and try again. // autostop requirement. If it isn't then add a day and try again.
requirementDays := templateSchedule.RestartRequirement.DaysMap() requirementDays := templateSchedule.AutostopRequirement.DaysMap()
for i := 0; i < len(DaysOfWeek)+1; i++ { for i := 0; i < len(DaysOfWeek)+1; i++ {
if i == len(DaysOfWeek) { if i == len(DaysOfWeek) {
// We've wrapped, so somehow we couldn't find a day in the // We've wrapped, so somehow we couldn't find a day in the
// restart requirement in the next week. // autostop requirement in the next week.
// //
// This shouldn't be able to happen, as we've already // This shouldn't be able to happen, as we've already
// checked that there is a day in the restart requirement // checked that there is a day in the autostop requirement
// above with the // above with the
// `if templateSchedule.RestartRequirement.DaysOfWeek != 0` // `if templateSchedule.AutoStopRequirement.DaysOfWeek != 0`
// check. // check.
// //
// The eighth bit shouldn't be set, as we validate the // The eighth bit shouldn't be set, as we validate the
// bitmap in the enterprise TemplateScheduleStore. // bitmap in the enterprise TemplateScheduleStore.
return autostop, xerrors.New("could not find suitable day for template restart requirement in the next 7 days") return autostop, xerrors.New("could not find suitable day for template autostop requirement in the next 7 days")
} }
if requirementDays[startOfStopDay.Weekday()] { if requirementDays[startOfStopDay.Weekday()] {
break break
@@ -194,13 +194,13 @@ func CalculateAutostop(ctx context.Context, params CalculateAutostopParams) (Aut
// If it's not within an hour of now, subtract 15 minutes to // If it's not within an hour of now, subtract 15 minutes to
// give a little leeway. This prevents skipped stop events // give a little leeway. This prevents skipped stop events
// because autostart perfectly lines up with autostop. // because autostart perfectly lines up with autostop.
checkTime = checkTime.Add(restartRequirementBuffer) checkTime = checkTime.Add(autostopRequirementBuffer)
} }
// Get the next occurrence of the restart schedule. // Get the next occurrence of the schedule.
autostop.MaxDeadline = userQuietHoursSchedule.Schedule.Next(checkTime) autostop.MaxDeadline = userQuietHoursSchedule.Schedule.Next(checkTime)
if autostop.MaxDeadline.IsZero() { if autostop.MaxDeadline.IsZero() {
return autostop, xerrors.New("could not find next occurrence of template restart requirement in user quiet hours schedule") return autostop, xerrors.Errorf("could not find next occurrence of template autostop requirement in user quiet hours schedule, checked from time %q", checkTime)
} }
} }
} }
@@ -244,9 +244,9 @@ func nextDayMidnight(t time.Time) time.Time {
// //
// The timezone embedded in the time object is used to determine the epoch. // The timezone embedded in the time object is used to determine the epoch.
func WeeksSinceEpoch(now time.Time) (int64, error) { func WeeksSinceEpoch(now time.Time) (int64, error) {
epoch := TemplateRestartRequirementEpoch(now.Location()) epoch := TemplateAutostopRequirementEpoch(now.Location())
if now.Before(epoch) { if now.Before(epoch) {
return 0, xerrors.New("coder server system clock is incorrect, cannot calculate template restart requirement") return 0, xerrors.New("coder server system clock is incorrect, cannot calculate template autostop requirement")
} }
// This calculation needs to be done using YearDay, as dividing by the // This calculation needs to be done using YearDay, as dividing by the
@@ -290,7 +290,7 @@ func GetMondayOfWeek(loc *time.Location, n int64) (time.Time, error) {
if n < 0 { if n < 0 {
return time.Time{}, xerrors.New("weeks since epoch must be positive") return time.Time{}, xerrors.New("weeks since epoch must be positive")
} }
epoch := TemplateRestartRequirementEpoch(loc) epoch := TemplateAutostopRequirementEpoch(loc)
monday := epoch.AddDate(0, 0, int(n*7)) monday := epoch.AddDate(0, 0, int(n*7))
y, m, d := monday.Date() y, m, d := monday.Date()

View File

@@ -25,7 +25,7 @@ func TestCalculateAutoStop(t *testing.T) {
// Wednesday the 8th of February 2023 at midnight. This date was // Wednesday the 8th of February 2023 at midnight. This date was
// specifically chosen as it doesn't fall on a applicable week for both // specifically chosen as it doesn't fall on a applicable week for both
// fortnightly and triweekly restart requirements. // fortnightly and triweekly autostop requirements.
wednesdayMidnightUTC := time.Date(2023, 2, 8, 0, 0, 0, 0, time.UTC) wednesdayMidnightUTC := time.Date(2023, 2, 8, 0, 0, 0, 0, time.UTC)
sydneyQuietHours := "CRON_TZ=Australia/Sydney 0 0 * * *" sydneyQuietHours := "CRON_TZ=Australia/Sydney 0 0 * * *"
@@ -75,7 +75,7 @@ func TestCalculateAutoStop(t *testing.T) {
// TODO(@dean): remove max_ttl tests // TODO(@dean): remove max_ttl tests
useMaxTTL bool useMaxTTL bool
templateMaxTTL time.Duration templateMaxTTL time.Duration
templateRestartRequirement schedule.TemplateRestartRequirement templateAutostopRequirement schedule.TemplateAutostopRequirement
userQuietHoursSchedule string userQuietHoursSchedule string
// workspaceTTL is usually copied from the template's TTL when the // workspaceTTL is usually copied from the template's TTL when the
// workspace is made, so it takes precedence unless // workspace is made, so it takes precedence unless
@@ -92,7 +92,7 @@ func TestCalculateAutoStop(t *testing.T) {
now: now, now: now,
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
templateRestartRequirement: schedule.TemplateRestartRequirement{}, templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
workspaceTTL: 0, workspaceTTL: 0,
expectedDeadline: time.Time{}, expectedDeadline: time.Time{},
expectedMaxDeadline: time.Time{}, expectedMaxDeadline: time.Time{},
@@ -102,7 +102,7 @@ func TestCalculateAutoStop(t *testing.T) {
now: now, now: now,
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
templateRestartRequirement: schedule.TemplateRestartRequirement{}, templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
workspaceTTL: 0, workspaceTTL: 0,
expectedDeadline: time.Time{}, expectedDeadline: time.Time{},
expectedMaxDeadline: time.Time{}, expectedMaxDeadline: time.Time{},
@@ -112,7 +112,7 @@ func TestCalculateAutoStop(t *testing.T) {
now: now, now: now,
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
templateRestartRequirement: schedule.TemplateRestartRequirement{}, templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
workspaceTTL: time.Hour, workspaceTTL: time.Hour,
expectedDeadline: now.Add(time.Hour), expectedDeadline: now.Add(time.Hour),
expectedMaxDeadline: time.Time{}, expectedMaxDeadline: time.Time{},
@@ -122,7 +122,7 @@ func TestCalculateAutoStop(t *testing.T) {
now: now, now: now,
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: time.Hour, templateDefaultTTL: time.Hour,
templateRestartRequirement: schedule.TemplateRestartRequirement{}, templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
workspaceTTL: 0, workspaceTTL: 0,
expectedDeadline: time.Time{}, expectedDeadline: time.Time{},
expectedMaxDeadline: time.Time{}, expectedMaxDeadline: time.Time{},
@@ -132,7 +132,7 @@ func TestCalculateAutoStop(t *testing.T) {
now: now, now: now,
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 2 * time.Hour, templateDefaultTTL: 2 * time.Hour,
templateRestartRequirement: schedule.TemplateRestartRequirement{}, templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
workspaceTTL: time.Hour, workspaceTTL: time.Hour,
expectedDeadline: now.Add(time.Hour), expectedDeadline: now.Add(time.Hour),
expectedMaxDeadline: time.Time{}, expectedMaxDeadline: time.Time{},
@@ -142,18 +142,18 @@ func TestCalculateAutoStop(t *testing.T) {
now: now, now: now,
templateAllowAutostop: false, templateAllowAutostop: false,
templateDefaultTTL: 3 * time.Hour, templateDefaultTTL: 3 * time.Hour,
templateRestartRequirement: schedule.TemplateRestartRequirement{}, templateAutostopRequirement: schedule.TemplateAutostopRequirement{},
workspaceTTL: 4 * time.Hour, workspaceTTL: 4 * time.Hour,
expectedDeadline: now.Add(3 * time.Hour), expectedDeadline: now.Add(3 * time.Hour),
expectedMaxDeadline: time.Time{}, expectedMaxDeadline: time.Time{},
}, },
{ {
name: "TemplateRestartRequirement", name: "TemplateAutostopRequirement",
now: wednesdayMidnightUTC, now: wednesdayMidnightUTC,
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 0, // weekly Weeks: 0, // weekly
}, },
@@ -162,12 +162,12 @@ func TestCalculateAutoStop(t *testing.T) {
expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC), expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC),
}, },
{ {
name: "TemplateRestartRequirement1HourSkip", name: "TemplateAutostopRequirement1HourSkip",
now: saturdayMidnightSydney.Add(-59 * time.Minute), now: saturdayMidnightSydney.Add(-59 * time.Minute),
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 1, // 1 also means weekly Weeks: 1, // 1 also means weekly
}, },
@@ -176,14 +176,14 @@ func TestCalculateAutoStop(t *testing.T) {
expectedMaxDeadline: saturdayMidnightSydney.Add(7 * 24 * time.Hour).In(time.UTC), expectedMaxDeadline: saturdayMidnightSydney.Add(7 * 24 * time.Hour).In(time.UTC),
}, },
{ {
// The next restart requirement should be skipped if the // The next autostop requirement should be skipped if the
// workspace is started within 1 hour of it. // workspace is started within 1 hour of it.
name: "TemplateRestartRequirementDaily", name: "TemplateAutostopRequirementDaily",
now: fridayEveningSydney, now: fridayEveningSydney,
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b01111111, // daily DaysOfWeek: 0b01111111, // daily
Weeks: 0, // all weeks Weeks: 0, // all weeks
}, },
@@ -192,12 +192,12 @@ func TestCalculateAutoStop(t *testing.T) {
expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC), expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC),
}, },
{ {
name: "TemplateRestartRequirementFortnightly/Skip", name: "TemplateAutostopRequirementFortnightly/Skip",
now: wednesdayMidnightUTC, now: wednesdayMidnightUTC,
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 2, // every 2 weeks Weeks: 2, // every 2 weeks
}, },
@@ -206,12 +206,12 @@ func TestCalculateAutoStop(t *testing.T) {
expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC), expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC),
}, },
{ {
name: "TemplateRestartRequirementFortnightly/NoSkip", name: "TemplateAutostopRequirementFortnightly/NoSkip",
now: wednesdayMidnightUTC.AddDate(0, 0, 7), now: wednesdayMidnightUTC.AddDate(0, 0, 7),
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 2, // every 2 weeks Weeks: 2, // every 2 weeks
}, },
@@ -220,28 +220,28 @@ func TestCalculateAutoStop(t *testing.T) {
expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC), expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC),
}, },
{ {
name: "TemplateRestartRequirementTriweekly/Skip", name: "TemplateAutostopRequirementTriweekly/Skip",
now: wednesdayMidnightUTC, now: wednesdayMidnightUTC,
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 3, // every 3 weeks Weeks: 3, // every 3 weeks
}, },
workspaceTTL: 0, workspaceTTL: 0,
// expectedDeadline is copied from expectedMaxDeadline. // expectedDeadline is copied from expectedMaxDeadline.
// The next triweekly restart requirement happens next week // The next triweekly autostop requirement happens next week
// according to the epoch. // according to the epoch.
expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC), expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC),
}, },
{ {
name: "TemplateRestartRequirementTriweekly/NoSkip", name: "TemplateAutostopRequirementTriweekly/NoSkip",
now: wednesdayMidnightUTC.AddDate(0, 0, 7), now: wednesdayMidnightUTC.AddDate(0, 0, 7),
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 3, // every 3 weeks Weeks: 3, // every 3 weeks
}, },
@@ -250,14 +250,14 @@ func TestCalculateAutoStop(t *testing.T) {
expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC), expectedMaxDeadline: saturdayMidnightSydney.AddDate(0, 0, 7).In(time.UTC),
}, },
{ {
name: "TemplateRestartRequirementOverridesWorkspaceTTL", name: "TemplateAutostopRequirementOverridesWorkspaceTTL",
// now doesn't have to be UTC, but it helps us ensure that // now doesn't have to be UTC, but it helps us ensure that
// timezones are compared correctly in this test. // timezones are compared correctly in this test.
now: fridayEveningSydney.In(time.UTC), now: fridayEveningSydney.In(time.UTC),
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 0, // weekly Weeks: 0, // weekly
}, },
@@ -266,12 +266,12 @@ func TestCalculateAutoStop(t *testing.T) {
expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC), expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC),
}, },
{ {
name: "TemplateRestartRequirementOverridesTemplateDefaultTTL", name: "TemplateAutostopRequirementOverridesTemplateDefaultTTL",
now: fridayEveningSydney.In(time.UTC), now: fridayEveningSydney.In(time.UTC),
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 3 * time.Hour, templateDefaultTTL: 3 * time.Hour,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 0, // weekly Weeks: 0, // weekly
}, },
@@ -288,7 +288,7 @@ func TestCalculateAutoStop(t *testing.T) {
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 2, // every fortnight Weeks: 2, // every fortnight
}, },
@@ -301,7 +301,7 @@ func TestCalculateAutoStop(t *testing.T) {
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 1, // weekly Weeks: 1, // weekly
}, },
@@ -315,7 +315,7 @@ func TestCalculateAutoStop(t *testing.T) {
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 1, // weekly Weeks: 1, // weekly
}, },
@@ -329,7 +329,7 @@ func TestCalculateAutoStop(t *testing.T) {
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 1, // weekly Weeks: 1, // weekly
}, },
@@ -343,7 +343,7 @@ func TestCalculateAutoStop(t *testing.T) {
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: dstInQuietHours, userQuietHoursSchedule: dstInQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b01000000, // Sunday DaysOfWeek: 0b01000000, // Sunday
Weeks: 1, // weekly Weeks: 1, // weekly
}, },
@@ -357,7 +357,7 @@ func TestCalculateAutoStop(t *testing.T) {
templateAllowAutostop: true, templateAllowAutostop: true,
templateDefaultTTL: 0, templateDefaultTTL: 0,
userQuietHoursSchedule: dstOutQuietHours, userQuietHoursSchedule: dstOutQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b01000000, // Sunday DaysOfWeek: 0b01000000, // Sunday
Weeks: 1, // weekly Weeks: 1, // weekly
}, },
@@ -368,14 +368,14 @@ func TestCalculateAutoStop(t *testing.T) {
// TODO(@dean): remove max_ttl tests // TODO(@dean): remove max_ttl tests
{ {
name: "RestartRequirementIgnoresMaxTTL", name: "AutostopRequirementIgnoresMaxTTL",
now: fridayEveningSydney.In(time.UTC), now: fridayEveningSydney.In(time.UTC),
templateAllowAutostop: false, templateAllowAutostop: false,
templateDefaultTTL: 0, templateDefaultTTL: 0,
useMaxTTL: false, useMaxTTL: false,
templateMaxTTL: time.Hour, // should be ignored templateMaxTTL: time.Hour, // should be ignored
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 0, // weekly Weeks: 0, // weekly
}, },
@@ -384,14 +384,14 @@ func TestCalculateAutoStop(t *testing.T) {
expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC), expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC),
}, },
{ {
name: "MaxTTLIgnoresRestartRequirement", name: "MaxTTLIgnoresAutostopRequirement",
now: fridayEveningSydney.In(time.UTC), now: fridayEveningSydney.In(time.UTC),
templateAllowAutostop: false, templateAllowAutostop: false,
templateDefaultTTL: 0, templateDefaultTTL: 0,
useMaxTTL: true, useMaxTTL: true,
templateMaxTTL: time.Hour, // should NOT be ignored templateMaxTTL: time.Hour, // should NOT be ignored
userQuietHoursSchedule: sydneyQuietHours, userQuietHoursSchedule: sydneyQuietHours,
templateRestartRequirement: schedule.TemplateRestartRequirement{ templateAutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: 0b00100000, // Saturday DaysOfWeek: 0b00100000, // Saturday
Weeks: 0, // weekly Weeks: 0, // weekly
}, },
@@ -417,8 +417,8 @@ func TestCalculateAutoStop(t *testing.T) {
UserAutostopEnabled: c.templateAllowAutostop, UserAutostopEnabled: c.templateAllowAutostop,
DefaultTTL: c.templateDefaultTTL, DefaultTTL: c.templateDefaultTTL,
MaxTTL: c.templateMaxTTL, MaxTTL: c.templateMaxTTL,
UseRestartRequirement: !c.useMaxTTL, UseAutostopRequirement: !c.useMaxTTL,
RestartRequirement: c.templateRestartRequirement, AutostopRequirement: c.templateAutostopRequirement,
}, nil }, nil
}, },
} }
@@ -457,8 +457,8 @@ func TestCalculateAutoStop(t *testing.T) {
ID: template.ID, ID: template.ID,
UpdatedAt: database.Now(), UpdatedAt: database.Now(),
AllowUserAutostart: c.templateAllowAutostop, AllowUserAutostart: c.templateAllowAutostop,
RestartRequirementDaysOfWeek: int16(c.templateRestartRequirement.DaysOfWeek), AutostopRequirementDaysOfWeek: int16(c.templateAutostopRequirement.DaysOfWeek),
RestartRequirementWeeks: c.templateRestartRequirement.Weeks, AutostopRequirementWeeks: c.templateAutostopRequirement.Weeks,
}) })
require.NoError(t, err) require.NoError(t, err)
template, err = db.GetTemplateByID(ctx, template.ID) template, err = db.GetTemplateByID(ctx, template.ID)

View File

@@ -11,9 +11,9 @@ import (
"github.com/coder/coder/v2/coderd/tracing" "github.com/coder/coder/v2/coderd/tracing"
) )
const MaxTemplateRestartRequirementWeeks = 16 const MaxTemplateAutostopRequirementWeeks = 16
func TemplateRestartRequirementEpoch(loc *time.Location) time.Time { func TemplateAutostopRequirementEpoch(loc *time.Location) time.Time {
// The "first week" starts on January 2nd, 2023, which is the first Monday // The "first week" starts on January 2nd, 2023, which is the first Monday
// of 2023. All other weeks are counted using modulo arithmetic from that // of 2023. All other weeks are counted using modulo arithmetic from that
// date. // date.
@@ -34,7 +34,7 @@ var DaysOfWeek = []time.Weekday{
time.Sunday, time.Sunday,
} }
type TemplateRestartRequirement struct { type TemplateAutostopRequirement struct {
// DaysOfWeek is a bitmap of which days of the week the workspace must be // 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 // restarted. If fully zero, the workspace is not required to be restarted
// ever. // ever.
@@ -55,7 +55,7 @@ type TemplateRestartRequirement struct {
// DaysMap returns a map of the days of the week that the workspace must be // DaysMap returns a map of the days of the week that the workspace must be
// restarted. // restarted.
func (r TemplateRestartRequirement) DaysMap() map[time.Weekday]bool { func (r TemplateAutostopRequirement) DaysMap() map[time.Weekday]bool {
days := make(map[time.Weekday]bool) days := make(map[time.Weekday]bool)
for i, day := range DaysOfWeek { for i, day := range DaysOfWeek {
days[day] = r.DaysOfWeek&(1<<uint(i)) != 0 days[day] = r.DaysOfWeek&(1<<uint(i)) != 0
@@ -63,20 +63,20 @@ func (r TemplateRestartRequirement) DaysMap() map[time.Weekday]bool {
return days return days
} }
// VerifyTemplateRestartRequirement returns an error if the restart requirement // VerifyTemplateAutostopRequirement returns an error if the autostop
// is invalid. // requirement is invalid.
func VerifyTemplateRestartRequirement(days uint8, weeks int64) error { func VerifyTemplateAutostopRequirement(days uint8, weeks int64) error {
if days&0b10000000 != 0 { if days&0b10000000 != 0 {
return xerrors.New("invalid restart requirement days, last bit is set") return xerrors.New("invalid autostop requirement days, last bit is set")
} }
if days > 0b11111111 { if days > 0b11111111 {
return xerrors.New("invalid restart requirement days, too large") return xerrors.New("invalid autostop requirement days, too large")
} }
if weeks < 0 { if weeks < 0 {
return xerrors.New("invalid restart requirement weeks, negative") return xerrors.New("invalid autostop requirement weeks, negative")
} }
if weeks > MaxTemplateRestartRequirementWeeks { if weeks > MaxTemplateAutostopRequirementWeeks {
return xerrors.New("invalid restart requirement weeks, too large") return xerrors.New("invalid autostop requirement weeks, too large")
} }
return nil return nil
} }
@@ -85,17 +85,17 @@ type TemplateScheduleOptions struct {
UserAutostartEnabled bool `json:"user_autostart_enabled"` UserAutostartEnabled bool `json:"user_autostart_enabled"`
UserAutostopEnabled bool `json:"user_autostop_enabled"` UserAutostopEnabled bool `json:"user_autostop_enabled"`
DefaultTTL time.Duration `json:"default_ttl"` DefaultTTL time.Duration `json:"default_ttl"`
// TODO(@dean): remove MaxTTL once restart_requirement is matured and the // TODO(@dean): remove MaxTTL once autostop_requirement is matured and the
// default // default
MaxTTL time.Duration `json:"max_ttl"` MaxTTL time.Duration `json:"max_ttl"`
// UseRestartRequirement dictates whether the restart requirement should be // UseAutostopRequirement dictates whether the autostop requirement should
// used instead of MaxTTL. This is governed by the feature flag and // be used instead of MaxTTL. This is governed by the feature flag and
// licensing. // licensing.
// TODO(@dean): remove this when we remove max_tll // TODO(@dean): remove this when we remove max_tll
UseRestartRequirement bool UseAutostopRequirement bool
// RestartRequirement dictates when the workspace must be restarted. This // AutostopRequirement dictates when the workspace must be restarted. This
// used to be handled by MaxTTL. // used to be handled by MaxTTL.
RestartRequirement TemplateRestartRequirement `json:"restart_requirement"` AutostopRequirement TemplateAutostopRequirement `json:"autostop_requirement"`
// FailureTTL dictates the duration after which failed workspaces will be // FailureTTL dictates the duration after which failed workspaces will be
// stopped automatically. // stopped automatically.
FailureTTL time.Duration `json:"failure_ttl"` FailureTTL time.Duration `json:"failure_ttl"`
@@ -149,11 +149,11 @@ func (*agplTemplateScheduleStore) Get(ctx context.Context, db database.Store, te
UserAutostartEnabled: true, UserAutostartEnabled: true,
UserAutostopEnabled: true, UserAutostopEnabled: true,
DefaultTTL: time.Duration(tpl.DefaultTTL), DefaultTTL: time.Duration(tpl.DefaultTTL),
// Disregard the values in the database, since RestartRequirement, // Disregard the values in the database, since AutostopRequirement,
// FailureTTL, TimeTilDormant, and TimeTilDormantAutoDelete are enterprise features. // FailureTTL, TimeTilDormant, and TimeTilDormantAutoDelete are enterprise features.
UseRestartRequirement: false, UseAutostopRequirement: false,
MaxTTL: 0, MaxTTL: 0,
RestartRequirement: TemplateRestartRequirement{ AutostopRequirement: TemplateAutostopRequirement{
DaysOfWeek: 0, DaysOfWeek: 0,
Weeks: 0, Weeks: 0,
}, },
@@ -181,8 +181,8 @@ func (*agplTemplateScheduleStore) Set(ctx context.Context, db database.Store, tp
// Don't allow changing these settings, but keep the value in the DB (to // Don't allow changing these settings, but keep the value in the DB (to
// avoid clearing settings if the license has an issue). // avoid clearing settings if the license has an issue).
MaxTTL: tpl.MaxTTL, MaxTTL: tpl.MaxTTL,
RestartRequirementDaysOfWeek: tpl.RestartRequirementDaysOfWeek, AutostopRequirementDaysOfWeek: tpl.AutostopRequirementDaysOfWeek,
RestartRequirementWeeks: tpl.RestartRequirementWeeks, AutostopRequirementWeeks: tpl.AutostopRequirementWeeks,
AllowUserAutostart: tpl.AllowUserAutostart, AllowUserAutostart: tpl.AllowUserAutostart,
AllowUserAutostop: tpl.AllowUserAutostop, AllowUserAutostop: tpl.AllowUserAutostop,
FailureTTL: tpl.FailureTTL, FailureTTL: tpl.FailureTTL,

View File

@@ -214,10 +214,10 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
var ( var (
defaultTTL time.Duration defaultTTL time.Duration
// TODO(@dean): remove max_ttl once restart_requirement is ready // TODO(@dean): remove max_ttl once autostop_requirement is ready
maxTTL time.Duration maxTTL time.Duration
restartRequirementDaysOfWeek []string autostopRequirementDaysOfWeek []string
restartRequirementWeeks int64 autostopRequirementWeeks int64
failureTTL time.Duration failureTTL time.Duration
dormantTTL time.Duration dormantTTL time.Duration
dormantAutoDeletionTTL time.Duration dormantAutoDeletionTTL time.Duration
@@ -225,9 +225,9 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
if createTemplate.DefaultTTLMillis != nil { if createTemplate.DefaultTTLMillis != nil {
defaultTTL = time.Duration(*createTemplate.DefaultTTLMillis) * time.Millisecond defaultTTL = time.Duration(*createTemplate.DefaultTTLMillis) * time.Millisecond
} }
if createTemplate.RestartRequirement != nil { if createTemplate.AutostopRequirement != nil {
restartRequirementDaysOfWeek = createTemplate.RestartRequirement.DaysOfWeek autostopRequirementDaysOfWeek = createTemplate.AutostopRequirement.DaysOfWeek
restartRequirementWeeks = createTemplate.RestartRequirement.Weeks autostopRequirementWeeks = createTemplate.AutostopRequirement.Weeks
} }
if createTemplate.FailureTTLMillis != nil { if createTemplate.FailureTTLMillis != nil {
failureTTL = time.Duration(*createTemplate.FailureTTLMillis) * time.Millisecond failureTTL = time.Duration(*createTemplate.FailureTTLMillis) * time.Millisecond
@@ -241,7 +241,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
var ( var (
validErrs []codersdk.ValidationError validErrs []codersdk.ValidationError
restartRequirementDaysOfWeekParsed uint8 autostopRequirementDaysOfWeekParsed uint8
) )
if defaultTTL < 0 { if defaultTTL < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be a positive integer."}) validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be a positive integer."})
@@ -252,20 +252,20 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
if maxTTL != 0 && defaultTTL > maxTTL { if maxTTL != 0 && defaultTTL > maxTTL {
validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be less than or equal to max_ttl_ms if max_ttl_ms is set."}) validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be less than or equal to max_ttl_ms if max_ttl_ms is set."})
} }
if len(restartRequirementDaysOfWeek) > 0 { if len(autostopRequirementDaysOfWeek) > 0 {
restartRequirementDaysOfWeekParsed, err = codersdk.WeekdaysToBitmap(restartRequirementDaysOfWeek) autostopRequirementDaysOfWeekParsed, err = codersdk.WeekdaysToBitmap(autostopRequirementDaysOfWeek)
if err != nil { if err != nil {
validErrs = append(validErrs, codersdk.ValidationError{Field: "restart_requirement.days_of_week", Detail: err.Error()}) validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.days_of_week", Detail: err.Error()})
} }
} }
if createTemplate.MaxTTLMillis != nil { if createTemplate.MaxTTLMillis != nil {
maxTTL = time.Duration(*createTemplate.MaxTTLMillis) * time.Millisecond maxTTL = time.Duration(*createTemplate.MaxTTLMillis) * time.Millisecond
} }
if restartRequirementWeeks < 0 { if autostopRequirementWeeks < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "restart_requirement.weeks", Detail: "Must be a positive integer."}) validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.weeks", Detail: "Must be a positive integer."})
} }
if restartRequirementWeeks > schedule.MaxTemplateRestartRequirementWeeks { if autostopRequirementWeeks > schedule.MaxTemplateAutostopRequirementWeeks {
validErrs = append(validErrs, codersdk.ValidationError{Field: "restart_requirement.weeks", Detail: fmt.Sprintf("Must be less than %d.", schedule.MaxTemplateRestartRequirementWeeks)}) validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.weeks", Detail: fmt.Sprintf("Must be less than %d.", schedule.MaxTemplateAutostopRequirementWeeks)})
} }
if failureTTL < 0 { if failureTTL < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "failure_ttl_ms", Detail: "Must be a positive integer."}) validErrs = append(validErrs, codersdk.ValidationError{Field: "failure_ttl_ms", Detail: "Must be a positive integer."})
@@ -336,9 +336,9 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
// Some of these values are enterprise-only, but the // Some of these values are enterprise-only, but the
// TemplateScheduleStore will handle avoiding setting them if // TemplateScheduleStore will handle avoiding setting them if
// unlicensed. // unlicensed.
RestartRequirement: schedule.TemplateRestartRequirement{ AutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: restartRequirementDaysOfWeekParsed, DaysOfWeek: autostopRequirementDaysOfWeekParsed,
Weeks: restartRequirementWeeks, Weeks: autostopRequirementWeeks,
}, },
FailureTTL: failureTTL, FailureTTL: failureTTL,
TimeTilDormant: dormantTTL, TimeTilDormant: dormantTTL,
@@ -501,7 +501,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
var ( var (
validErrs []codersdk.ValidationError validErrs []codersdk.ValidationError
restartRequirementDaysOfWeekParsed uint8 autostopRequirementDaysOfWeekParsed uint8
) )
if req.DefaultTTLMillis < 0 { if req.DefaultTTLMillis < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be a positive integer."}) validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be a positive integer."})
@@ -512,23 +512,23 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
if req.MaxTTLMillis != 0 && req.DefaultTTLMillis > req.MaxTTLMillis { if req.MaxTTLMillis != 0 && req.DefaultTTLMillis > req.MaxTTLMillis {
validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be less than or equal to max_ttl_ms if max_ttl_ms is set."}) validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be less than or equal to max_ttl_ms if max_ttl_ms is set."})
} }
if req.RestartRequirement == nil { if req.AutostopRequirement == nil {
req.RestartRequirement = &codersdk.TemplateRestartRequirement{ req.AutostopRequirement = &codersdk.TemplateAutostopRequirement{
DaysOfWeek: codersdk.BitmapToWeekdays(scheduleOpts.RestartRequirement.DaysOfWeek), DaysOfWeek: codersdk.BitmapToWeekdays(scheduleOpts.AutostopRequirement.DaysOfWeek),
Weeks: scheduleOpts.RestartRequirement.Weeks, Weeks: scheduleOpts.AutostopRequirement.Weeks,
} }
} }
if len(req.RestartRequirement.DaysOfWeek) > 0 { if len(req.AutostopRequirement.DaysOfWeek) > 0 {
restartRequirementDaysOfWeekParsed, err = codersdk.WeekdaysToBitmap(req.RestartRequirement.DaysOfWeek) autostopRequirementDaysOfWeekParsed, err = codersdk.WeekdaysToBitmap(req.AutostopRequirement.DaysOfWeek)
if err != nil { if err != nil {
validErrs = append(validErrs, codersdk.ValidationError{Field: "restart_requirement.days_of_week", Detail: err.Error()}) validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.days_of_week", Detail: err.Error()})
} }
} }
if req.RestartRequirement.Weeks < 0 { if req.AutostopRequirement.Weeks < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "restart_requirement.weeks", Detail: "Must be a positive integer."}) validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.weeks", Detail: "Must be a positive integer."})
} }
if req.RestartRequirement.Weeks > schedule.MaxTemplateRestartRequirementWeeks { if req.AutostopRequirement.Weeks > schedule.MaxTemplateAutostopRequirementWeeks {
validErrs = append(validErrs, codersdk.ValidationError{Field: "restart_requirement.weeks", Detail: fmt.Sprintf("Must be less than %d.", schedule.MaxTemplateRestartRequirementWeeks)}) validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.weeks", Detail: fmt.Sprintf("Must be less than %d.", schedule.MaxTemplateAutostopRequirementWeeks)})
} }
if req.FailureTTLMillis < 0 { if req.FailureTTLMillis < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "failure_ttl_ms", Detail: "Must be a positive integer."}) validErrs = append(validErrs, codersdk.ValidationError{Field: "failure_ttl_ms", Detail: "Must be a positive integer."})
@@ -562,8 +562,8 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
req.AllowUserCancelWorkspaceJobs == template.AllowUserCancelWorkspaceJobs && req.AllowUserCancelWorkspaceJobs == template.AllowUserCancelWorkspaceJobs &&
req.DefaultTTLMillis == time.Duration(template.DefaultTTL).Milliseconds() && req.DefaultTTLMillis == time.Duration(template.DefaultTTL).Milliseconds() &&
req.MaxTTLMillis == time.Duration(template.MaxTTL).Milliseconds() && req.MaxTTLMillis == time.Duration(template.MaxTTL).Milliseconds() &&
restartRequirementDaysOfWeekParsed == scheduleOpts.RestartRequirement.DaysOfWeek && autostopRequirementDaysOfWeekParsed == scheduleOpts.AutostopRequirement.DaysOfWeek &&
req.RestartRequirement.Weeks == scheduleOpts.RestartRequirement.Weeks && req.AutostopRequirement.Weeks == scheduleOpts.AutostopRequirement.Weeks &&
req.FailureTTLMillis == time.Duration(template.FailureTTL).Milliseconds() && req.FailureTTLMillis == time.Duration(template.FailureTTL).Milliseconds() &&
req.TimeTilDormantMillis == time.Duration(template.TimeTilDormant).Milliseconds() && req.TimeTilDormantMillis == time.Duration(template.TimeTilDormant).Milliseconds() &&
req.TimeTilDormantAutoDeleteMillis == time.Duration(template.TimeTilDormantAutoDelete).Milliseconds() { req.TimeTilDormantAutoDeleteMillis == time.Duration(template.TimeTilDormantAutoDelete).Milliseconds() {
@@ -603,8 +603,8 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
if defaultTTL != time.Duration(template.DefaultTTL) || if defaultTTL != time.Duration(template.DefaultTTL) ||
maxTTL != time.Duration(template.MaxTTL) || maxTTL != time.Duration(template.MaxTTL) ||
restartRequirementDaysOfWeekParsed != scheduleOpts.RestartRequirement.DaysOfWeek || autostopRequirementDaysOfWeekParsed != scheduleOpts.AutostopRequirement.DaysOfWeek ||
req.RestartRequirement.Weeks != scheduleOpts.RestartRequirement.Weeks || req.AutostopRequirement.Weeks != scheduleOpts.AutostopRequirement.Weeks ||
failureTTL != time.Duration(template.FailureTTL) || failureTTL != time.Duration(template.FailureTTL) ||
inactivityTTL != time.Duration(template.TimeTilDormant) || inactivityTTL != time.Duration(template.TimeTilDormant) ||
timeTilDormantAutoDelete != time.Duration(template.TimeTilDormantAutoDelete) || timeTilDormantAutoDelete != time.Duration(template.TimeTilDormantAutoDelete) ||
@@ -618,9 +618,9 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
UserAutostopEnabled: req.AllowUserAutostop, UserAutostopEnabled: req.AllowUserAutostop,
DefaultTTL: defaultTTL, DefaultTTL: defaultTTL,
MaxTTL: maxTTL, MaxTTL: maxTTL,
RestartRequirement: schedule.TemplateRestartRequirement{ AutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: restartRequirementDaysOfWeekParsed, DaysOfWeek: autostopRequirementDaysOfWeekParsed,
Weeks: req.RestartRequirement.Weeks, Weeks: req.AutostopRequirement.Weeks,
}, },
FailureTTL: failureTTL, FailureTTL: failureTTL,
TimeTilDormant: inactivityTTL, TimeTilDormant: inactivityTTL,
@@ -760,9 +760,9 @@ func (api *API) convertTemplate(
FailureTTLMillis: time.Duration(template.FailureTTL).Milliseconds(), FailureTTLMillis: time.Duration(template.FailureTTL).Milliseconds(),
TimeTilDormantMillis: time.Duration(template.TimeTilDormant).Milliseconds(), TimeTilDormantMillis: time.Duration(template.TimeTilDormant).Milliseconds(),
TimeTilDormantAutoDeleteMillis: time.Duration(template.TimeTilDormantAutoDelete).Milliseconds(), TimeTilDormantAutoDeleteMillis: time.Duration(template.TimeTilDormantAutoDelete).Milliseconds(),
RestartRequirement: codersdk.TemplateRestartRequirement{ AutostopRequirement: codersdk.TemplateAutostopRequirement{
DaysOfWeek: codersdk.BitmapToWeekdays(uint8(template.RestartRequirementDaysOfWeek)), DaysOfWeek: codersdk.BitmapToWeekdays(uint8(template.AutostopRequirementDaysOfWeek)),
Weeks: template.RestartRequirementWeeks, Weeks: template.AutostopRequirementWeeks,
}, },
} }
} }

View File

@@ -246,7 +246,7 @@ func TestPostTemplateByOrganization(t *testing.T) {
require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
}) })
t.Run("RestartRequirement", func(t *testing.T) { t.Run("AutostopRequirement", func(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("None", func(t *testing.T) { t.Run("None", func(t *testing.T) {
@@ -257,8 +257,8 @@ func TestPostTemplateByOrganization(t *testing.T) {
TemplateScheduleStore: schedule.MockTemplateScheduleStore{ TemplateScheduleStore: schedule.MockTemplateScheduleStore{
SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) { SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) {
atomic.AddInt64(&setCalled, 1) atomic.AddInt64(&setCalled, 1)
assert.Zero(t, options.RestartRequirement.DaysOfWeek) assert.Zero(t, options.AutostopRequirement.DaysOfWeek)
assert.Zero(t, options.RestartRequirement.Weeks) assert.Zero(t, options.AutostopRequirement.Weeks)
err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{
ID: template.ID, ID: template.ID,
@@ -267,8 +267,8 @@ func TestPostTemplateByOrganization(t *testing.T) {
AllowUserAutostop: options.UserAutostopEnabled, AllowUserAutostop: options.UserAutostopEnabled,
DefaultTTL: int64(options.DefaultTTL), DefaultTTL: int64(options.DefaultTTL),
MaxTTL: int64(options.MaxTTL), MaxTTL: int64(options.MaxTTL),
RestartRequirementDaysOfWeek: int16(options.RestartRequirement.DaysOfWeek), AutostopRequirementDaysOfWeek: int16(options.AutostopRequirement.DaysOfWeek),
RestartRequirementWeeks: options.RestartRequirement.Weeks, AutostopRequirementWeeks: options.AutostopRequirement.Weeks,
FailureTTL: int64(options.FailureTTL), FailureTTL: int64(options.FailureTTL),
TimeTilDormant: int64(options.TimeTilDormant), TimeTilDormant: int64(options.TimeTilDormant),
TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete),
@@ -290,13 +290,13 @@ func TestPostTemplateByOrganization(t *testing.T) {
got, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{ got, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{
Name: "testing", Name: "testing",
VersionID: version.ID, VersionID: version.ID,
RestartRequirement: nil, AutostopRequirement: nil,
}) })
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, 1, atomic.LoadInt64(&setCalled)) require.EqualValues(t, 1, atomic.LoadInt64(&setCalled))
require.Empty(t, got.RestartRequirement.DaysOfWeek) require.Empty(t, got.AutostopRequirement.DaysOfWeek)
require.Zero(t, got.RestartRequirement.Weeks) require.Zero(t, got.AutostopRequirement.Weeks)
}) })
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
@@ -307,8 +307,8 @@ func TestPostTemplateByOrganization(t *testing.T) {
TemplateScheduleStore: schedule.MockTemplateScheduleStore{ TemplateScheduleStore: schedule.MockTemplateScheduleStore{
SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) { SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) {
atomic.AddInt64(&setCalled, 1) atomic.AddInt64(&setCalled, 1)
assert.EqualValues(t, 0b00110000, options.RestartRequirement.DaysOfWeek) assert.EqualValues(t, 0b00110000, options.AutostopRequirement.DaysOfWeek)
assert.EqualValues(t, 2, options.RestartRequirement.Weeks) assert.EqualValues(t, 2, options.AutostopRequirement.Weeks)
err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{
ID: template.ID, ID: template.ID,
@@ -317,8 +317,8 @@ func TestPostTemplateByOrganization(t *testing.T) {
AllowUserAutostop: options.UserAutostopEnabled, AllowUserAutostop: options.UserAutostopEnabled,
DefaultTTL: int64(options.DefaultTTL), DefaultTTL: int64(options.DefaultTTL),
MaxTTL: int64(options.MaxTTL), MaxTTL: int64(options.MaxTTL),
RestartRequirementDaysOfWeek: int16(options.RestartRequirement.DaysOfWeek), AutostopRequirementDaysOfWeek: int16(options.AutostopRequirement.DaysOfWeek),
RestartRequirementWeeks: options.RestartRequirement.Weeks, AutostopRequirementWeeks: options.AutostopRequirement.Weeks,
FailureTTL: int64(options.FailureTTL), FailureTTL: int64(options.FailureTTL),
TimeTilDormant: int64(options.TimeTilDormant), TimeTilDormant: int64(options.TimeTilDormant),
TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete),
@@ -340,7 +340,7 @@ func TestPostTemplateByOrganization(t *testing.T) {
got, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{ got, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{
Name: "testing", Name: "testing",
VersionID: version.ID, VersionID: version.ID,
RestartRequirement: &codersdk.TemplateRestartRequirement{ AutostopRequirement: &codersdk.TemplateAutostopRequirement{
// wrong order // wrong order
DaysOfWeek: []string{"saturday", "friday"}, DaysOfWeek: []string{"saturday", "friday"},
Weeks: 2, Weeks: 2,
@@ -349,13 +349,13 @@ func TestPostTemplateByOrganization(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, 1, atomic.LoadInt64(&setCalled)) require.EqualValues(t, 1, atomic.LoadInt64(&setCalled))
require.Equal(t, []string{"friday", "saturday"}, got.RestartRequirement.DaysOfWeek) require.Equal(t, []string{"friday", "saturday"}, got.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 2, got.RestartRequirement.Weeks) require.EqualValues(t, 2, got.AutostopRequirement.Weeks)
got, err = client.Template(ctx, got.ID) got, err = client.Template(ctx, got.ID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, []string{"friday", "saturday"}, got.RestartRequirement.DaysOfWeek) require.Equal(t, []string{"friday", "saturday"}, got.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 2, got.RestartRequirement.Weeks) require.EqualValues(t, 2, got.AutostopRequirement.Weeks)
}) })
t.Run("IgnoredUnlicensed", func(t *testing.T) { t.Run("IgnoredUnlicensed", func(t *testing.T) {
@@ -371,15 +371,15 @@ func TestPostTemplateByOrganization(t *testing.T) {
got, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{ got, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{
Name: "testing", Name: "testing",
VersionID: version.ID, VersionID: version.ID,
RestartRequirement: &codersdk.TemplateRestartRequirement{ AutostopRequirement: &codersdk.TemplateAutostopRequirement{
DaysOfWeek: []string{"friday", "saturday"}, DaysOfWeek: []string{"friday", "saturday"},
Weeks: 2, Weeks: 2,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
// ignored and use AGPL defaults // ignored and use AGPL defaults
require.Empty(t, got.RestartRequirement.DaysOfWeek) require.Empty(t, got.AutostopRequirement.DaysOfWeek)
require.Zero(t, got.RestartRequirement.Weeks) require.Zero(t, got.AutostopRequirement.Weeks)
}) })
}) })
} }
@@ -595,8 +595,8 @@ func TestPatchTemplateMeta(t *testing.T) {
AllowUserAutostop: options.UserAutostopEnabled, AllowUserAutostop: options.UserAutostopEnabled,
DefaultTTL: int64(options.DefaultTTL), DefaultTTL: int64(options.DefaultTTL),
MaxTTL: int64(options.MaxTTL), MaxTTL: int64(options.MaxTTL),
RestartRequirementDaysOfWeek: int16(options.RestartRequirement.DaysOfWeek), AutostopRequirementDaysOfWeek: int16(options.AutostopRequirement.DaysOfWeek),
RestartRequirementWeeks: options.RestartRequirement.Weeks, AutostopRequirementWeeks: options.AutostopRequirement.Weeks,
FailureTTL: int64(options.FailureTTL), FailureTTL: int64(options.FailureTTL),
TimeTilDormant: int64(options.TimeTilDormant), TimeTilDormant: int64(options.TimeTilDormant),
TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete),
@@ -738,7 +738,7 @@ func TestPatchTemplateMeta(t *testing.T) {
Description: template.Description, Description: template.Description,
Icon: template.Icon, Icon: template.Icon,
DefaultTTLMillis: 0, DefaultTTLMillis: 0,
RestartRequirement: &template.RestartRequirement, AutostopRequirement: &template.AutostopRequirement,
AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs,
FailureTTLMillis: failureTTL.Milliseconds(), FailureTTLMillis: failureTTL.Milliseconds(),
TimeTilDormantMillis: inactivityTTL.Milliseconds(), TimeTilDormantMillis: inactivityTTL.Milliseconds(),
@@ -773,7 +773,7 @@ func TestPatchTemplateMeta(t *testing.T) {
Description: template.Description, Description: template.Description,
Icon: template.Icon, Icon: template.Icon,
DefaultTTLMillis: template.DefaultTTLMillis, DefaultTTLMillis: template.DefaultTTLMillis,
RestartRequirement: &template.RestartRequirement, AutostopRequirement: &template.AutostopRequirement,
AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs,
FailureTTLMillis: failureTTL.Milliseconds(), FailureTTLMillis: failureTTL.Milliseconds(),
TimeTilDormantMillis: inactivityTTL.Milliseconds(), TimeTilDormantMillis: inactivityTTL.Milliseconds(),
@@ -832,7 +832,7 @@ func TestPatchTemplateMeta(t *testing.T) {
Description: template.Description, Description: template.Description,
Icon: template.Icon, Icon: template.Icon,
DefaultTTLMillis: template.DefaultTTLMillis, DefaultTTLMillis: template.DefaultTTLMillis,
RestartRequirement: &template.RestartRequirement, AutostopRequirement: &template.AutostopRequirement,
AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs,
AllowUserAutostart: allowAutostart.Load(), AllowUserAutostart: allowAutostart.Load(),
AllowUserAutostop: allowAutostop.Load(), AllowUserAutostop: allowAutostop.Load(),
@@ -864,7 +864,7 @@ func TestPatchTemplateMeta(t *testing.T) {
Icon: template.Icon, Icon: template.Icon,
// Increase the default TTL to avoid error "not modified". // Increase the default TTL to avoid error "not modified".
DefaultTTLMillis: template.DefaultTTLMillis + 1, DefaultTTLMillis: template.DefaultTTLMillis + 1,
RestartRequirement: &template.RestartRequirement, AutostopRequirement: &template.AutostopRequirement,
AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs,
AllowUserAutostart: false, AllowUserAutostart: false,
AllowUserAutostop: false, AllowUserAutostop: false,
@@ -895,7 +895,7 @@ func TestPatchTemplateMeta(t *testing.T) {
Description: template.Description, Description: template.Description,
Icon: template.Icon, Icon: template.Icon,
DefaultTTLMillis: template.DefaultTTLMillis, DefaultTTLMillis: template.DefaultTTLMillis,
RestartRequirement: nil, AutostopRequirement: nil,
AllowUserAutostart: template.AllowUserAutostart, AllowUserAutostart: template.AllowUserAutostart,
AllowUserAutostop: template.AllowUserAutostop, AllowUserAutostop: template.AllowUserAutostop,
} }
@@ -964,7 +964,7 @@ func TestPatchTemplateMeta(t *testing.T) {
assert.Equal(t, updated.Icon, "") assert.Equal(t, updated.Icon, "")
}) })
t.Run("RestartRequirement", func(t *testing.T) { t.Run("AutostopRequirement", func(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
@@ -975,8 +975,8 @@ func TestPatchTemplateMeta(t *testing.T) {
TemplateScheduleStore: schedule.MockTemplateScheduleStore{ TemplateScheduleStore: schedule.MockTemplateScheduleStore{
SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) { SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) {
if atomic.AddInt64(&setCalled, 1) == 2 { if atomic.AddInt64(&setCalled, 1) == 2 {
assert.EqualValues(t, 0b0110000, options.RestartRequirement.DaysOfWeek) assert.EqualValues(t, 0b0110000, options.AutostopRequirement.DaysOfWeek)
assert.EqualValues(t, 2, options.RestartRequirement.Weeks) assert.EqualValues(t, 2, options.AutostopRequirement.Weeks)
} }
err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{
@@ -986,8 +986,8 @@ func TestPatchTemplateMeta(t *testing.T) {
AllowUserAutostop: options.UserAutostopEnabled, AllowUserAutostop: options.UserAutostopEnabled,
DefaultTTL: int64(options.DefaultTTL), DefaultTTL: int64(options.DefaultTTL),
MaxTTL: int64(options.MaxTTL), MaxTTL: int64(options.MaxTTL),
RestartRequirementDaysOfWeek: int16(options.RestartRequirement.DaysOfWeek), AutostopRequirementDaysOfWeek: int16(options.AutostopRequirement.DaysOfWeek),
RestartRequirementWeeks: options.RestartRequirement.Weeks, AutostopRequirementWeeks: options.AutostopRequirement.Weeks,
FailureTTL: int64(options.FailureTTL), FailureTTL: int64(options.FailureTTL),
TimeTilDormant: int64(options.TimeTilDormant), TimeTilDormant: int64(options.TimeTilDormant),
TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete),
@@ -1005,8 +1005,8 @@ func TestPatchTemplateMeta(t *testing.T) {
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
require.EqualValues(t, 1, atomic.LoadInt64(&setCalled)) require.EqualValues(t, 1, atomic.LoadInt64(&setCalled))
require.Empty(t, template.RestartRequirement.DaysOfWeek) require.Empty(t, template.AutostopRequirement.DaysOfWeek)
require.Zero(t, template.RestartRequirement.Weeks) require.Zero(t, template.AutostopRequirement.Weeks)
req := codersdk.UpdateTemplateMeta{ req := codersdk.UpdateTemplateMeta{
Name: template.Name, Name: template.Name,
DisplayName: template.DisplayName, DisplayName: template.DisplayName,
@@ -1014,7 +1014,7 @@ func TestPatchTemplateMeta(t *testing.T) {
Icon: template.Icon, Icon: template.Icon,
AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs,
DefaultTTLMillis: time.Hour.Milliseconds(), DefaultTTLMillis: time.Hour.Milliseconds(),
RestartRequirement: &codersdk.TemplateRestartRequirement{ AutostopRequirement: &codersdk.TemplateAutostopRequirement{
// wrong order // wrong order
DaysOfWeek: []string{"saturday", "friday"}, DaysOfWeek: []string{"saturday", "friday"},
Weeks: 2, Weeks: 2,
@@ -1027,13 +1027,13 @@ func TestPatchTemplateMeta(t *testing.T) {
updated, err := client.UpdateTemplateMeta(ctx, template.ID, req) updated, err := client.UpdateTemplateMeta(ctx, template.ID, req)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, 2, atomic.LoadInt64(&setCalled)) require.EqualValues(t, 2, atomic.LoadInt64(&setCalled))
require.Equal(t, []string{"friday", "saturday"}, updated.RestartRequirement.DaysOfWeek) require.Equal(t, []string{"friday", "saturday"}, updated.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 2, updated.RestartRequirement.Weeks) require.EqualValues(t, 2, updated.AutostopRequirement.Weeks)
template, err = client.Template(ctx, template.ID) template, err = client.Template(ctx, template.ID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, []string{"friday", "saturday"}, template.RestartRequirement.DaysOfWeek) require.Equal(t, []string{"friday", "saturday"}, template.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 2, template.RestartRequirement.Weeks) require.EqualValues(t, 2, template.AutostopRequirement.Weeks)
}) })
t.Run("Unset", func(t *testing.T) { t.Run("Unset", func(t *testing.T) {
@@ -1044,8 +1044,8 @@ func TestPatchTemplateMeta(t *testing.T) {
TemplateScheduleStore: schedule.MockTemplateScheduleStore{ TemplateScheduleStore: schedule.MockTemplateScheduleStore{
SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) { SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) {
if atomic.AddInt64(&setCalled, 1) == 2 { if atomic.AddInt64(&setCalled, 1) == 2 {
assert.EqualValues(t, 0, options.RestartRequirement.DaysOfWeek) assert.EqualValues(t, 0, options.AutostopRequirement.DaysOfWeek)
assert.EqualValues(t, 0, options.RestartRequirement.Weeks) assert.EqualValues(t, 0, options.AutostopRequirement.Weeks)
} }
err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{
@@ -1055,8 +1055,8 @@ func TestPatchTemplateMeta(t *testing.T) {
AllowUserAutostop: options.UserAutostopEnabled, AllowUserAutostop: options.UserAutostopEnabled,
DefaultTTL: int64(options.DefaultTTL), DefaultTTL: int64(options.DefaultTTL),
MaxTTL: int64(options.MaxTTL), MaxTTL: int64(options.MaxTTL),
RestartRequirementDaysOfWeek: int16(options.RestartRequirement.DaysOfWeek), AutostopRequirementDaysOfWeek: int16(options.AutostopRequirement.DaysOfWeek),
RestartRequirementWeeks: options.RestartRequirement.Weeks, AutostopRequirementWeeks: options.AutostopRequirement.Weeks,
FailureTTL: int64(options.FailureTTL), FailureTTL: int64(options.FailureTTL),
TimeTilDormant: int64(options.TimeTilDormant), TimeTilDormant: int64(options.TimeTilDormant),
TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete),
@@ -1073,15 +1073,15 @@ func TestPatchTemplateMeta(t *testing.T) {
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.RestartRequirement = &codersdk.TemplateRestartRequirement{ ctr.AutostopRequirement = &codersdk.TemplateAutostopRequirement{
// wrong order // wrong order
DaysOfWeek: []string{"sunday", "saturday", "friday", "thursday", "wednesday", "tuesday", "monday"}, DaysOfWeek: []string{"sunday", "saturday", "friday", "thursday", "wednesday", "tuesday", "monday"},
Weeks: 2, Weeks: 2,
} }
}) })
require.EqualValues(t, 1, atomic.LoadInt64(&setCalled)) require.EqualValues(t, 1, atomic.LoadInt64(&setCalled))
require.Equal(t, []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"}, template.RestartRequirement.DaysOfWeek) require.Equal(t, []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"}, template.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 2, template.RestartRequirement.Weeks) require.EqualValues(t, 2, template.AutostopRequirement.Weeks)
req := codersdk.UpdateTemplateMeta{ req := codersdk.UpdateTemplateMeta{
Name: template.Name, Name: template.Name,
DisplayName: template.DisplayName, DisplayName: template.DisplayName,
@@ -1089,7 +1089,7 @@ func TestPatchTemplateMeta(t *testing.T) {
Icon: template.Icon, Icon: template.Icon,
AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs,
DefaultTTLMillis: time.Hour.Milliseconds(), DefaultTTLMillis: time.Hour.Milliseconds(),
RestartRequirement: &codersdk.TemplateRestartRequirement{ AutostopRequirement: &codersdk.TemplateAutostopRequirement{
DaysOfWeek: []string{}, DaysOfWeek: []string{},
Weeks: 0, Weeks: 0,
}, },
@@ -1101,13 +1101,13 @@ func TestPatchTemplateMeta(t *testing.T) {
updated, err := client.UpdateTemplateMeta(ctx, template.ID, req) updated, err := client.UpdateTemplateMeta(ctx, template.ID, req)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, 2, atomic.LoadInt64(&setCalled)) require.EqualValues(t, 2, atomic.LoadInt64(&setCalled))
require.Empty(t, updated.RestartRequirement.DaysOfWeek) require.Empty(t, updated.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 0, updated.RestartRequirement.Weeks) require.EqualValues(t, 0, updated.AutostopRequirement.Weeks)
template, err = client.Template(ctx, template.ID) template, err = client.Template(ctx, template.ID)
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, template.RestartRequirement.DaysOfWeek) require.Empty(t, template.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 0, template.RestartRequirement.Weeks) require.EqualValues(t, 0, template.AutostopRequirement.Weeks)
}) })
t.Run("EnterpriseOnly", func(t *testing.T) { t.Run("EnterpriseOnly", func(t *testing.T) {
@@ -1117,8 +1117,8 @@ func TestPatchTemplateMeta(t *testing.T) {
user := coderdtest.CreateFirstUser(t, client) user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
require.Empty(t, template.RestartRequirement.DaysOfWeek) require.Empty(t, template.AutostopRequirement.DaysOfWeek)
require.Zero(t, template.RestartRequirement.Weeks) require.Zero(t, template.AutostopRequirement.Weeks)
req := codersdk.UpdateTemplateMeta{ req := codersdk.UpdateTemplateMeta{
Name: template.Name, Name: template.Name,
DisplayName: template.DisplayName, DisplayName: template.DisplayName,
@@ -1126,7 +1126,7 @@ func TestPatchTemplateMeta(t *testing.T) {
Icon: template.Icon, Icon: template.Icon,
AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs,
DefaultTTLMillis: time.Hour.Milliseconds(), DefaultTTLMillis: time.Hour.Milliseconds(),
RestartRequirement: &codersdk.TemplateRestartRequirement{ AutostopRequirement: &codersdk.TemplateAutostopRequirement{
DaysOfWeek: []string{"monday"}, DaysOfWeek: []string{"monday"},
Weeks: 2, Weeks: 2,
}, },
@@ -1137,13 +1137,13 @@ func TestPatchTemplateMeta(t *testing.T) {
updated, err := client.UpdateTemplateMeta(ctx, template.ID, req) updated, err := client.UpdateTemplateMeta(ctx, template.ID, req)
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, updated.RestartRequirement.DaysOfWeek) require.Empty(t, updated.AutostopRequirement.DaysOfWeek)
require.Zero(t, updated.RestartRequirement.Weeks) require.Zero(t, updated.AutostopRequirement.Weeks)
template, err = client.Template(ctx, template.ID) template, err = client.Template(ctx, template.ID)
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, template.RestartRequirement.DaysOfWeek) require.Empty(t, template.AutostopRequirement.DaysOfWeek)
require.Zero(t, template.RestartRequirement.Weeks) require.Zero(t, template.AutostopRequirement.Weeks)
}) })
}) })
} }

View File

@@ -384,8 +384,8 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
} }
maxTTL := templateSchedule.MaxTTL maxTTL := templateSchedule.MaxTTL
if templateSchedule.UseRestartRequirement { if templateSchedule.UseAutostopRequirement {
// If we're using restart requirements, there isn't a max TTL. // If we're using autostop requirements, there isn't a max TTL.
maxTTL = 0 maxTTL = 0
} }
@@ -721,8 +721,8 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
} }
maxTTL := templateSchedule.MaxTTL maxTTL := templateSchedule.MaxTTL
if templateSchedule.UseRestartRequirement { if templateSchedule.UseAutostopRequirement {
// If we're using restart requirements, there isn't a max TTL. // If we're using autostop requirements, there isn't a max TTL.
maxTTL = 0 maxTTL = 0
} }

View File

@@ -1839,7 +1839,7 @@ func TestWorkspaceUpdateAutostart(t *testing.T) {
UserAutostartEnabled: false, UserAutostartEnabled: false,
UserAutostopEnabled: false, UserAutostopEnabled: false,
DefaultTTL: 0, DefaultTTL: 0,
RestartRequirement: schedule.TemplateRestartRequirement{}, AutostopRequirement: schedule.TemplateAutostopRequirement{},
}, nil }, nil
}, },
SetFn: func(_ context.Context, _ database.Store, tpl database.Template, _ schedule.TemplateScheduleOptions) (database.Template, error) { SetFn: func(_ context.Context, _ database.Store, tpl database.Template, _ schedule.TemplateScheduleOptions) (database.Template, error) {
@@ -2006,7 +2006,7 @@ func TestWorkspaceUpdateTTL(t *testing.T) {
UserAutostartEnabled: false, UserAutostartEnabled: false,
UserAutostopEnabled: false, UserAutostopEnabled: false,
DefaultTTL: 0, DefaultTTL: 0,
RestartRequirement: schedule.TemplateRestartRequirement{}, AutostopRequirement: schedule.TemplateAutostopRequirement{},
}, nil }, nil
}, },
SetFn: func(_ context.Context, _ database.Store, tpl database.Template, _ schedule.TemplateScheduleOptions) (database.Template, error) { SetFn: func(_ context.Context, _ database.Store, tpl database.Template, _ schedule.TemplateScheduleOptions) (database.Template, error) {

View File

@@ -46,7 +46,7 @@ const (
FeatureExternalProvisionerDaemons FeatureName = "external_provisioner_daemons" FeatureExternalProvisionerDaemons FeatureName = "external_provisioner_daemons"
FeatureAppearance FeatureName = "appearance" FeatureAppearance FeatureName = "appearance"
FeatureAdvancedTemplateScheduling FeatureName = "advanced_template_scheduling" FeatureAdvancedTemplateScheduling FeatureName = "advanced_template_scheduling"
FeatureTemplateRestartRequirement FeatureName = "template_restart_requirement" FeatureTemplateAutostopRequirement FeatureName = "template_autostop_requirement"
FeatureWorkspaceProxy FeatureName = "workspace_proxy" FeatureWorkspaceProxy FeatureName = "workspace_proxy"
) )
@@ -1924,17 +1924,19 @@ const (
// WARNING: This cannot be enabled when using HA. // WARNING: This cannot be enabled when using HA.
ExperimentSingleTailnet Experiment = "single_tailnet" ExperimentSingleTailnet Experiment = "single_tailnet"
// ExperimentTemplateRestartRequirement allows template admins to have more // ExperimentTemplateAutostopRequirement allows template admins to have more
// control over when workspaces created on a template are required to // control over when workspaces created on a template are required to
// restart, and allows users to ensure these restarts never happen during // stop, and allows users to ensure these restarts never happen during their
// their business hours. // business hours.
//
// This will replace the MaxTTL setting on templates.
// //
// Enables: // Enables:
// - User quiet hours schedule settings // - User quiet hours schedule settings
// - Template restart requirement settings // - Template autostop requirement settings
// - Changes the max_deadline algorithm to use restart requirement and user // - Changes the max_deadline algorithm to use autostop requirement and user
// quiet hours instead of max_ttl. // quiet hours instead of max_ttl.
ExperimentTemplateRestartRequirement Experiment = "template_restart_requirement" ExperimentTemplateAutostopRequirement Experiment = "template_autostop_requirement"
// Deployment health page // Deployment health page
ExperimentDeploymentHealthPage Experiment = "deployment_health_page" ExperimentDeploymentHealthPage Experiment = "deployment_health_page"

View File

@@ -84,11 +84,11 @@ type CreateTemplateRequest struct {
// DefaultTTLMillis allows optionally specifying the default TTL // DefaultTTLMillis allows optionally specifying the default TTL
// for all workspaces created from this template. // for all workspaces created from this template.
DefaultTTLMillis *int64 `json:"default_ttl_ms,omitempty"` DefaultTTLMillis *int64 `json:"default_ttl_ms,omitempty"`
// TODO(@dean): remove max_ttl once restart_requirement is matured // TODO(@dean): remove max_ttl once autostop_requirement is matured
MaxTTLMillis *int64 `json:"max_ttl_ms,omitempty"` MaxTTLMillis *int64 `json:"max_ttl_ms,omitempty"`
// RestartRequirement allows optionally specifying the restart requirement // AutostopRequirement allows optionally specifying the autostop requirement
// for workspaces created from this template. This is an enterprise feature. // for workspaces created from this template. This is an enterprise feature.
RestartRequirement *TemplateRestartRequirement `json:"restart_requirement,omitempty"` AutostopRequirement *TemplateAutostopRequirement `json:"autostop_requirement,omitempty"`
// Allow users to cancel in-progress workspace jobs. // Allow users to cancel in-progress workspace jobs.
// *bool as the default value is "true". // *bool as the default value is "true".

View File

@@ -29,11 +29,11 @@ type Template struct {
Description string `json:"description"` Description string `json:"description"`
Icon string `json:"icon"` Icon string `json:"icon"`
DefaultTTLMillis int64 `json:"default_ttl_ms"` DefaultTTLMillis int64 `json:"default_ttl_ms"`
// TODO(@dean): remove max_ttl once restart_requirement is matured // TODO(@dean): remove max_ttl once autostop_requirement is matured
MaxTTLMillis int64 `json:"max_ttl_ms"` MaxTTLMillis int64 `json:"max_ttl_ms"`
// RestartRequirement is an enterprise feature. Its value is only used if // AutostopRequirement is an enterprise feature. Its value is only used if
// your license is entitled to use the advanced template scheduling feature. // your license is entitled to use the advanced template scheduling feature.
RestartRequirement TemplateRestartRequirement `json:"restart_requirement"` AutostopRequirement TemplateAutostopRequirement `json:"autostop_requirement"`
CreatedByID uuid.UUID `json:"created_by_id" format:"uuid"` CreatedByID uuid.UUID `json:"created_by_id" format:"uuid"`
CreatedByName string `json:"created_by_name"` CreatedByName string `json:"created_by_name"`
@@ -107,7 +107,7 @@ func BitmapToWeekdays(bitmap uint8) []string {
return days return days
} }
type TemplateRestartRequirement struct { type TemplateAutostopRequirement struct {
// DaysOfWeek is a list of days of the week on which restarts are required. // 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 // Restarts happen within the user's quiet hours (in their configured
// timezone). If no days are specified, restarts are not required. Weekdays // timezone). If no days are specified, restarts are not required. Weekdays
@@ -180,12 +180,12 @@ type UpdateTemplateMeta struct {
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"` Icon string `json:"icon,omitempty"`
DefaultTTLMillis int64 `json:"default_ttl_ms,omitempty"` DefaultTTLMillis int64 `json:"default_ttl_ms,omitempty"`
// TODO(@dean): remove max_ttl once restart_requirement is matured // TODO(@dean): remove max_ttl once autostop_requirement is matured
MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"` MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"`
// RestartRequirement can only be set if your license includes the advanced // AutostopRequirement can only be set if your license includes the advanced
// template scheduling feature. If you attempt to set this value while // template scheduling feature. If you attempt to set this value while
// unlicensed, it will be ignored. // unlicensed, it will be ignored.
RestartRequirement *TemplateRestartRequirement `json:"restart_requirement,omitempty"` AutostopRequirement *TemplateAutostopRequirement `json:"autostop_requirement,omitempty"`
AllowUserAutostart bool `json:"allow_user_autostart,omitempty"` AllowUserAutostart bool `json:"allow_user_autostart,omitempty"`
AllowUserAutostop bool `json:"allow_user_autostop,omitempty"` AllowUserAutostop bool `json:"allow_user_autostop,omitempty"`
AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs,omitempty"` AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs,omitempty"`

View File

@@ -9,13 +9,13 @@ We track the following resources:
<!-- Code generated by 'make docs/admin/audit-logs.md'. DO NOT EDIT --> <!-- Code generated by 'make docs/admin/audit-logs.md'. DO NOT EDIT -->
| <b>Resource<b> | | | <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> | | 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> | | 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> | | 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> | | 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> | | 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>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>restart_requirement_days_of_week</td><td>true</td></tr><tr><td>restart_requirement_weeks</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> | | 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>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>git_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> | | TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><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>git_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> | | 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>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> | | Workspace<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><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> |

66
docs/api/schemas.md generated
View File

@@ -1478,6 +1478,10 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
"allow_user_autostart": true, "allow_user_autostart": true,
"allow_user_autostop": true, "allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true, "allow_user_cancel_workspace_jobs": true,
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"default_ttl_ms": 0, "default_ttl_ms": 0,
"delete_ttl_ms": 0, "delete_ttl_ms": 0,
"description": "string", "description": "string",
@@ -1488,10 +1492,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
"icon": "string", "icon": "string",
"max_ttl_ms": 0, "max_ttl_ms": 0,
"name": "string", "name": "string",
"restart_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1" "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1"
} }
``` ```
@@ -1499,10 +1499,11 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
### Properties ### Properties
| Name | Type | Required | Restrictions | Description | | 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_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_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". | | `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. | | `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. | | `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. | | `description` | string | false | | Description is a description of what the template contains. It must be less than 128 bytes. |
@@ -1511,9 +1512,8 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `dormant_ttl_ms` | integer | false | | Dormant ttl ms allows optionally specifying the max lifetime before Coder locks inactive workspaces created from this 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. | | `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. | | `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 restart_requirement is matured | | `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. | | `name` | string | true | | Name is the name of the template. |
| `restart_requirement` | [codersdk.TemplateRestartRequirement](#codersdktemplaterestartrequirement) | false | | Restart requirement allows optionally specifying the restart requirement for workspaces created from this template. This is an enterprise feature. |
| `template_version_id` | string | true | | Template version ID is an in-progress or completed job to use as an initial version 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. | | 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. |
@@ -2712,12 +2712,12 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
#### Enumerated Values #### Enumerated Values
| Value | | Value |
| ------------------------------ | | ------------------------------- |
| `moons` | | `moons` |
| `workspace_actions` | | `workspace_actions` |
| `tailnet_pg_coordinator` | | `tailnet_pg_coordinator` |
| `single_tailnet` | | `single_tailnet` |
| `template_restart_requirement` | | `template_autostop_requirement` |
| `deployment_health_page` | | `deployment_health_page` |
| `workspaces_batch_actions` | | `workspaces_batch_actions` |
@@ -4220,6 +4220,10 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
"allow_user_autostart": true, "allow_user_autostart": true,
"allow_user_autostop": true, "allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true, "allow_user_cancel_workspace_jobs": true,
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"build_time_stats": { "build_time_stats": {
"property1": { "property1": {
"p50": 123, "p50": 123,
@@ -4243,10 +4247,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
"name": "string", "name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"provisioner": "terraform", "provisioner": "terraform",
"restart_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"time_til_dormant_autodelete_ms": 0, "time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0, "time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z" "updated_at": "2019-08-24T14:15:22Z"
@@ -4256,12 +4256,13 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
### Properties ### Properties
| Name | Type | Required | Restrictions | Description | | Name | Type | Required | Restrictions | Description |
| ---------------------------------- | -------------------------------------------------------------------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ---------------------------------- | ---------------------------------------------------------------------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `active_user_count` | integer | false | | Active user count is set to -1 when loading. | | `active_user_count` | integer | false | | Active user count is set to -1 when loading. |
| `active_version_id` | string | false | | | | `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_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_autostop` | boolean | false | | |
| `allow_user_cancel_workspace_jobs` | 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 | | | | `build_time_stats` | [codersdk.TemplateBuildTimeStats](#codersdktemplatebuildtimestats) | false | | |
| `created_at` | string | false | | | | `created_at` | string | false | | |
| `created_by_id` | string | false | | | | `created_by_id` | string | false | | |
@@ -4272,11 +4273,10 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `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. | | `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 | | | | `icon` | string | false | | |
| `id` | string | false | | | | `id` | string | false | | |
| `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once restart_requirement is matured | | `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured |
| `name` | string | false | | | | `name` | string | false | | |
| `organization_id` | string | false | | | | `organization_id` | string | false | | |
| `provisioner` | string | false | | | | `provisioner` | string | false | | |
| `restart_requirement` | [codersdk.TemplateRestartRequirement](#codersdktemplaterestartrequirement) | false | | Restart requirement is an enterprise feature. Its value is only used if your license is entitled to use the advanced template scheduling feature. |
| `time_til_dormant_autodelete_ms` | integer | false | | | | `time_til_dormant_autodelete_ms` | integer | false | | |
| `time_til_dormant_ms` | integer | false | | | | `time_til_dormant_ms` | integer | false | | |
| `updated_at` | string | false | | | | `updated_at` | string | false | | |
@@ -4326,6 +4326,23 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `builtin` | | `builtin` |
| `app` | | `app` |
## codersdk.TemplateAutostopRequirement
```json
{
"days_of_week": ["monday"],
"weeks": 0
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
| ------------------------------------------------------------------------------------- | --------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `days_of_week` | array of string | 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. |
## codersdk.TemplateBuildTimeStats ## codersdk.TemplateBuildTimeStats
```json ```json
@@ -4566,23 +4583,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `count` | integer | false | | | | `count` | integer | false | | |
| `value` | string | false | | | | `value` | string | false | | |
## codersdk.TemplateRestartRequirement
```json
{
"days_of_week": ["monday"],
"weeks": 0
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
| ------------------------------------------------------------------------------------- | --------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `days_of_week` | array of string | 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. |
## codersdk.TemplateRole ## codersdk.TemplateRole
```json ```json

60
docs/api/templates.md generated
View File

@@ -31,6 +31,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
"allow_user_autostart": true, "allow_user_autostart": true,
"allow_user_autostop": true, "allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true, "allow_user_cancel_workspace_jobs": true,
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"build_time_stats": { "build_time_stats": {
"property1": { "property1": {
"p50": 123, "p50": 123,
@@ -54,10 +58,6 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
"name": "string", "name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"provisioner": "terraform", "provisioner": "terraform",
"restart_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"time_til_dormant_autodelete_ms": 0, "time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0, "time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z" "updated_at": "2019-08-24T14:15:22Z"
@@ -76,13 +76,17 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
Status Code **200** Status Code **200**
| Name | Type | Required | Restrictions | Description | | Name | Type | Required | Restrictions | Description |
| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[array item]` | array | false | | | | `[array item]` | array | false | | |
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. | | `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
| `» active_version_id` | string(uuid) | false | | | | `» 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_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_autostop` | boolean | false | | |
| `» allow_user_cancel_workspace_jobs` | 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. |
| 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 | | | | `» build_time_stats` | [codersdk.TemplateBuildTimeStats](schemas.md#codersdktemplatebuildtimestats) | false | | |
| `»» [any property]` | [codersdk.TransitionStats](schemas.md#codersdktransitionstats) | false | | | | `»» [any property]` | [codersdk.TransitionStats](schemas.md#codersdktransitionstats) | false | | |
| `»»» p50` | integer | false | | | | `»»» p50` | integer | false | | |
@@ -96,14 +100,10 @@ Status Code **200**
| `» 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. | | `» 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 | | | | `» icon` | string | false | | |
| `» id` | string(uuid) | false | | | | `» id` | string(uuid) | false | | |
| `» max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once restart_requirement is matured | | `» max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured |
| `» name` | string | false | | | | `» name` | string | false | | |
| `» organization_id` | string(uuid) | false | | | | `» organization_id` | string(uuid) | false | | |
| `» provisioner` | string | false | | | | `» provisioner` | string | false | | |
| `» restart_requirement` | [codersdk.TemplateRestartRequirement](schemas.md#codersdktemplaterestartrequirement) | false | | Restart 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. |
| 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. |
| `» time_til_dormant_autodelete_ms` | integer | false | | | | `» time_til_dormant_autodelete_ms` | integer | false | | |
| `» time_til_dormant_ms` | integer | false | | | | `» time_til_dormant_ms` | integer | false | | |
| `» updated_at` | string(date-time) | false | | | | `» updated_at` | string(date-time) | false | | |
@@ -137,6 +137,10 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"allow_user_autostart": true, "allow_user_autostart": true,
"allow_user_autostop": true, "allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true, "allow_user_cancel_workspace_jobs": true,
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"default_ttl_ms": 0, "default_ttl_ms": 0,
"delete_ttl_ms": 0, "delete_ttl_ms": 0,
"description": "string", "description": "string",
@@ -147,10 +151,6 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"icon": "string", "icon": "string",
"max_ttl_ms": 0, "max_ttl_ms": 0,
"name": "string", "name": "string",
"restart_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1" "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1"
} }
``` ```
@@ -173,6 +173,10 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"allow_user_autostart": true, "allow_user_autostart": true,
"allow_user_autostop": true, "allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true, "allow_user_cancel_workspace_jobs": true,
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"build_time_stats": { "build_time_stats": {
"property1": { "property1": {
"p50": 123, "p50": 123,
@@ -196,10 +200,6 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"name": "string", "name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"provisioner": "terraform", "provisioner": "terraform",
"restart_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"time_til_dormant_autodelete_ms": 0, "time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0, "time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z" "updated_at": "2019-08-24T14:15:22Z"
@@ -305,6 +305,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
"allow_user_autostart": true, "allow_user_autostart": true,
"allow_user_autostop": true, "allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true, "allow_user_cancel_workspace_jobs": true,
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"build_time_stats": { "build_time_stats": {
"property1": { "property1": {
"p50": 123, "p50": 123,
@@ -328,10 +332,6 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
"name": "string", "name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"provisioner": "terraform", "provisioner": "terraform",
"restart_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"time_til_dormant_autodelete_ms": 0, "time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0, "time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z" "updated_at": "2019-08-24T14:15:22Z"
@@ -610,6 +610,10 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \
"allow_user_autostart": true, "allow_user_autostart": true,
"allow_user_autostop": true, "allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true, "allow_user_cancel_workspace_jobs": true,
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"build_time_stats": { "build_time_stats": {
"property1": { "property1": {
"p50": 123, "p50": 123,
@@ -633,10 +637,6 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \
"name": "string", "name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"provisioner": "terraform", "provisioner": "terraform",
"restart_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"time_til_dormant_autodelete_ms": 0, "time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0, "time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z" "updated_at": "2019-08-24T14:15:22Z"
@@ -725,6 +725,10 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \
"allow_user_autostart": true, "allow_user_autostart": true,
"allow_user_autostop": true, "allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true, "allow_user_cancel_workspace_jobs": true,
"autostop_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"build_time_stats": { "build_time_stats": {
"property1": { "property1": {
"p50": 123, "p50": 123,
@@ -748,10 +752,6 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \
"name": "string", "name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"provisioner": "terraform", "provisioner": "terraform",
"restart_requirement": {
"days_of_week": ["monday"],
"weeks": 0
},
"time_til_dormant_autodelete_ms": 0, "time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0, "time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z" "updated_at": "2019-08-24T14:15:22Z"

View File

@@ -71,8 +71,8 @@ var auditableResourcesTypes = map[any]map[string]Action{
"icon": ActionTrack, "icon": ActionTrack,
"default_ttl": ActionTrack, "default_ttl": ActionTrack,
"max_ttl": ActionTrack, "max_ttl": ActionTrack,
"restart_requirement_days_of_week": ActionTrack, "autostop_requirement_days_of_week": ActionTrack,
"restart_requirement_weeks": ActionTrack, "autostop_requirement_weeks": ActionTrack,
"created_by": ActionTrack, "created_by": ActionTrack,
"created_by_username": ActionIgnore, "created_by_username": ActionIgnore,
"created_by_avatar_url": ActionIgnore, "created_by_avatar_url": ActionIgnore,

View File

@@ -265,7 +265,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
}) })
r.Route("/users/{user}/quiet-hours", func(r chi.Router) { r.Route("/users/{user}/quiet-hours", func(r chi.Router) {
r.Use( r.Use(
api.restartRequirementEnabledMW, api.autostopRequirementEnabledMW,
apiKeyMiddleware, apiKeyMiddleware,
httpmw.ExtractUserParam(options.Database, false), httpmw.ExtractUserParam(options.Database, false),
) )
@@ -432,9 +432,9 @@ func (api *API) updateEntitlements(ctx context.Context) error {
codersdk.FeatureTemplateRBAC: api.RBAC, codersdk.FeatureTemplateRBAC: api.RBAC,
codersdk.FeatureExternalProvisionerDaemons: true, codersdk.FeatureExternalProvisionerDaemons: true,
codersdk.FeatureAdvancedTemplateScheduling: true, codersdk.FeatureAdvancedTemplateScheduling: true,
// FeatureTemplateRestartRequirement depends on // FeatureTemplateAutostopRequirement depends on
// FeatureAdvancedTemplateScheduling. // FeatureAdvancedTemplateScheduling.
codersdk.FeatureTemplateRestartRequirement: api.DefaultQuietHoursSchedule != "", codersdk.FeatureTemplateAutostopRequirement: api.DefaultQuietHoursSchedule != "",
codersdk.FeatureWorkspaceProxy: true, codersdk.FeatureWorkspaceProxy: true,
codersdk.FeatureUserRoleManagement: true, codersdk.FeatureUserRoleManagement: true,
}) })
@@ -455,15 +455,15 @@ func (api *API) updateEntitlements(ctx context.Context) error {
return nil return nil
} }
if entitlements.Features[codersdk.FeatureTemplateRestartRequirement].Enabled && !entitlements.Features[codersdk.FeatureAdvancedTemplateScheduling].Enabled { if entitlements.Features[codersdk.FeatureTemplateAutostopRequirement].Enabled && !entitlements.Features[codersdk.FeatureAdvancedTemplateScheduling].Enabled {
api.entitlements.Errors = []string{ api.entitlements.Errors = []string{
`Your license is entitled to the feature "template restart ` + `Your license is entitled to the feature "template autostop ` +
`requirement" (and you have it enabled by setting the ` + `requirement" (and you have it enabled by setting the ` +
"default quiet hours schedule), but you are not entitled to " + "default quiet hours schedule), but you are not entitled to " +
`the dependency feature "advanced template scheduling". ` + `the dependency feature "advanced template scheduling". ` +
"Please contact support for a new license.", "Please contact support for a new license.",
} }
api.Logger.Error(ctx, "license is entitled to template restart requirement but not advanced template scheduling") api.Logger.Error(ctx, "license is entitled to template autostop requirement but not advanced template scheduling")
return nil return nil
} }
@@ -524,30 +524,30 @@ func (api *API) updateEntitlements(ctx context.Context) error {
} }
} }
if initial, changed, enabled := featureChanged(codersdk.FeatureTemplateRestartRequirement); shouldUpdate(initial, changed, enabled) { if initial, changed, enabled := featureChanged(codersdk.FeatureTemplateAutostopRequirement); shouldUpdate(initial, changed, enabled) {
if enabled { if enabled {
templateStore := *(api.AGPL.TemplateScheduleStore.Load()) templateStore := *(api.AGPL.TemplateScheduleStore.Load())
enterpriseTemplateStore, ok := templateStore.(*schedule.EnterpriseTemplateScheduleStore) enterpriseTemplateStore, ok := templateStore.(*schedule.EnterpriseTemplateScheduleStore)
if !ok { if !ok {
api.Logger.Error(ctx, "unable to set up enterprise template schedule store, template restart requirements will not be applied to workspace builds") api.Logger.Error(ctx, "unable to set up enterprise template schedule store, template autostop requirements will not be applied to workspace builds")
} }
enterpriseTemplateStore.UseRestartRequirement.Store(true) enterpriseTemplateStore.UseAutostopRequirement.Store(true)
quietHoursStore, err := schedule.NewEnterpriseUserQuietHoursScheduleStore(api.DefaultQuietHoursSchedule) quietHoursStore, err := schedule.NewEnterpriseUserQuietHoursScheduleStore(api.DefaultQuietHoursSchedule)
if err != nil { if err != nil {
api.Logger.Error(ctx, "unable to set up enterprise user quiet hours schedule store, template restart requirements will not be applied to workspace builds", slog.Error(err)) api.Logger.Error(ctx, "unable to set up enterprise user quiet hours schedule store, template autostop requirements will not be applied to workspace builds", slog.Error(err))
} else { } else {
api.AGPL.UserQuietHoursScheduleStore.Store(&quietHoursStore) api.AGPL.UserQuietHoursScheduleStore.Store(&quietHoursStore)
} }
} else { } else {
if api.DefaultQuietHoursSchedule != "" { if api.DefaultQuietHoursSchedule != "" {
api.Logger.Warn(ctx, "template restart requirements are not enabled (due to setting default quiet hours schedule) as your license is not entitled to this feature") api.Logger.Warn(ctx, "template autostop requirements are not enabled (due to setting default quiet hours schedule) as your license is not entitled to this feature")
} }
templateStore := *(api.AGPL.TemplateScheduleStore.Load()) templateStore := *(api.AGPL.TemplateScheduleStore.Load())
enterpriseTemplateStore, ok := templateStore.(*schedule.EnterpriseTemplateScheduleStore) enterpriseTemplateStore, ok := templateStore.(*schedule.EnterpriseTemplateScheduleStore)
if ok { if ok {
enterpriseTemplateStore.UseRestartRequirement.Store(false) enterpriseTemplateStore.UseAutostopRequirement.Store(false)
} }
quietHoursStore := agplschedule.NewAGPLUserQuietHoursScheduleStore() quietHoursStore := agplschedule.NewAGPLUserQuietHoursScheduleStore()

View File

@@ -21,11 +21,11 @@ import (
// EnterpriseTemplateScheduleStore provides an agpl.TemplateScheduleStore that // EnterpriseTemplateScheduleStore provides an agpl.TemplateScheduleStore that
// has all fields implemented for enterprise customers. // has all fields implemented for enterprise customers.
type EnterpriseTemplateScheduleStore struct { type EnterpriseTemplateScheduleStore struct {
// UseRestartRequirement decides whether the RestartRequirement field should // UseAutostopRequirement decides whether the AutostopRequirement field
// be used instead of the MaxTTL field for determining the max deadline of a // should be used instead of the MaxTTL field for determining the max
// workspace build. This value is determined by a feature flag, licensing, // deadline of a workspace build. This value is determined by a feature
// and whether a default user quiet hours schedule is set. // flag, licensing, and whether a default user quiet hours schedule is set.
UseRestartRequirement atomic.Bool UseAutostopRequirement atomic.Bool
// UserQuietHoursScheduleStore is used when recalculating build deadlines on // UserQuietHoursScheduleStore is used when recalculating build deadlines on
// update. // update.
@@ -62,13 +62,13 @@ func (s *EnterpriseTemplateScheduleStore) Get(ctx context.Context, db database.S
// These extra checks have to be done before the conversion because we lose // These extra checks have to be done before the conversion because we lose
// precision and signs when converting to the agpl types from the database. // precision and signs when converting to the agpl types from the database.
if tpl.RestartRequirementDaysOfWeek < 0 { if tpl.AutostopRequirementDaysOfWeek < 0 {
return agpl.TemplateScheduleOptions{}, xerrors.New("invalid restart requirement days, negative") return agpl.TemplateScheduleOptions{}, xerrors.New("invalid autostop requirement days, negative")
} }
if tpl.RestartRequirementDaysOfWeek > 0b11111111 { if tpl.AutostopRequirementDaysOfWeek > 0b11111111 {
return agpl.TemplateScheduleOptions{}, xerrors.New("invalid restart requirement days, too large") return agpl.TemplateScheduleOptions{}, xerrors.New("invalid autostop requirement days, too large")
} }
err = agpl.VerifyTemplateRestartRequirement(uint8(tpl.RestartRequirementDaysOfWeek), tpl.RestartRequirementWeeks) err = agpl.VerifyTemplateAutostopRequirement(uint8(tpl.AutostopRequirementDaysOfWeek), tpl.AutostopRequirementWeeks)
if err != nil { if err != nil {
return agpl.TemplateScheduleOptions{}, err return agpl.TemplateScheduleOptions{}, err
} }
@@ -78,10 +78,10 @@ func (s *EnterpriseTemplateScheduleStore) Get(ctx context.Context, db database.S
UserAutostopEnabled: tpl.AllowUserAutostop, UserAutostopEnabled: tpl.AllowUserAutostop,
DefaultTTL: time.Duration(tpl.DefaultTTL), DefaultTTL: time.Duration(tpl.DefaultTTL),
MaxTTL: time.Duration(tpl.MaxTTL), MaxTTL: time.Duration(tpl.MaxTTL),
UseRestartRequirement: s.UseRestartRequirement.Load(), UseAutostopRequirement: s.UseAutostopRequirement.Load(),
RestartRequirement: agpl.TemplateRestartRequirement{ AutostopRequirement: agpl.TemplateAutostopRequirement{
DaysOfWeek: uint8(tpl.RestartRequirementDaysOfWeek), DaysOfWeek: uint8(tpl.AutostopRequirementDaysOfWeek),
Weeks: tpl.RestartRequirementWeeks, Weeks: tpl.AutostopRequirementWeeks,
}, },
FailureTTL: time.Duration(tpl.FailureTTL), FailureTTL: time.Duration(tpl.FailureTTL),
TimeTilDormant: time.Duration(tpl.TimeTilDormant), TimeTilDormant: time.Duration(tpl.TimeTilDormant),
@@ -96,8 +96,8 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
if int64(opts.DefaultTTL) == tpl.DefaultTTL && if int64(opts.DefaultTTL) == tpl.DefaultTTL &&
int64(opts.MaxTTL) == tpl.MaxTTL && int64(opts.MaxTTL) == tpl.MaxTTL &&
int16(opts.RestartRequirement.DaysOfWeek) == tpl.RestartRequirementDaysOfWeek && int16(opts.AutostopRequirement.DaysOfWeek) == tpl.AutostopRequirementDaysOfWeek &&
opts.RestartRequirement.Weeks == tpl.RestartRequirementWeeks && opts.AutostopRequirement.Weeks == tpl.AutostopRequirementWeeks &&
int64(opts.FailureTTL) == tpl.FailureTTL && int64(opts.FailureTTL) == tpl.FailureTTL &&
int64(opts.TimeTilDormant) == tpl.TimeTilDormant && int64(opts.TimeTilDormant) == tpl.TimeTilDormant &&
int64(opts.TimeTilDormantAutoDelete) == tpl.TimeTilDormantAutoDelete && int64(opts.TimeTilDormantAutoDelete) == tpl.TimeTilDormantAutoDelete &&
@@ -107,7 +107,7 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
return tpl, nil return tpl, nil
} }
err := agpl.VerifyTemplateRestartRequirement(opts.RestartRequirement.DaysOfWeek, opts.RestartRequirement.Weeks) err := agpl.VerifyTemplateAutostopRequirement(opts.AutostopRequirement.DaysOfWeek, opts.AutostopRequirement.Weeks)
if err != nil { if err != nil {
return database.Template{}, err return database.Template{}, err
} }
@@ -124,8 +124,8 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
AllowUserAutostop: opts.UserAutostopEnabled, AllowUserAutostop: opts.UserAutostopEnabled,
DefaultTTL: int64(opts.DefaultTTL), DefaultTTL: int64(opts.DefaultTTL),
MaxTTL: int64(opts.MaxTTL), MaxTTL: int64(opts.MaxTTL),
RestartRequirementDaysOfWeek: int16(opts.RestartRequirement.DaysOfWeek), AutostopRequirementDaysOfWeek: int16(opts.AutostopRequirement.DaysOfWeek),
RestartRequirementWeeks: opts.RestartRequirement.Weeks, AutostopRequirementWeeks: opts.AutostopRequirement.Weeks,
FailureTTL: int64(opts.FailureTTL), FailureTTL: int64(opts.FailureTTL),
TimeTilDormant: int64(opts.TimeTilDormant), TimeTilDormant: int64(opts.TimeTilDormant),
TimeTilDormantAutoDelete: int64(opts.TimeTilDormantAutoDelete), TimeTilDormantAutoDelete: int64(opts.TimeTilDormantAutoDelete),
@@ -170,7 +170,7 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
// Recalculate max_deadline and deadline for all running workspace // Recalculate max_deadline and deadline for all running workspace
// builds on this template. // builds on this template.
if s.UseRestartRequirement.Load() { if s.UseAutostopRequirement.Load() {
err = s.updateWorkspaceBuilds(ctx, tx, template) err = s.updateWorkspaceBuilds(ctx, tx, template)
if err != nil { if err != nil {
return xerrors.Errorf("update workspace builds: %w", err) return xerrors.Errorf("update workspace builds: %w", err)
@@ -238,7 +238,7 @@ func (s *EnterpriseTemplateScheduleStore) updateWorkspaceBuild(ctx context.Conte
// If the job completed before the autostop epoch, then it must be skipped // If the job completed before the autostop epoch, then it must be skipped
// to avoid failures below. Add a week to account for timezones. // to avoid failures below. Add a week to account for timezones.
if job.CompletedAt.Time.Before(agpl.TemplateRestartRequirementEpoch(time.UTC).Add(time.Hour * 7 * 24)) { if job.CompletedAt.Time.Before(agpl.TemplateAutostopRequirementEpoch(time.UTC).Add(time.Hour * 7 * 24)) {
return nil return nil
} }

View File

@@ -210,7 +210,7 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
// Set the template policy. // Set the template policy.
templateScheduleStore := schedule.NewEnterpriseTemplateScheduleStore(userQuietHoursStorePtr) templateScheduleStore := schedule.NewEnterpriseTemplateScheduleStore(userQuietHoursStorePtr)
templateScheduleStore.UseRestartRequirement.Store(true) templateScheduleStore.UseAutostopRequirement.Store(true)
templateScheduleStore.TimeNowFn = func() time.Time { templateScheduleStore.TimeNowFn = func() time.Time {
return c.now return c.now
} }
@@ -219,8 +219,8 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
UserAutostopEnabled: false, UserAutostopEnabled: false,
DefaultTTL: 0, DefaultTTL: 0,
MaxTTL: 0, MaxTTL: 0,
UseRestartRequirement: true, UseAutostopRequirement: true,
RestartRequirement: agplschedule.TemplateRestartRequirement{ AutostopRequirement: agplschedule.TemplateAutostopRequirement{
// Every day // Every day
DaysOfWeek: 0b01111111, DaysOfWeek: 0b01111111,
Weeks: 0, Weeks: 0,
@@ -485,7 +485,7 @@ func TestTemplateUpdateBuildDeadlinesSkip(t *testing.T) {
// Set the template policy. // Set the template policy.
templateScheduleStore := schedule.NewEnterpriseTemplateScheduleStore(userQuietHoursStorePtr) templateScheduleStore := schedule.NewEnterpriseTemplateScheduleStore(userQuietHoursStorePtr)
templateScheduleStore.UseRestartRequirement.Store(true) templateScheduleStore.UseAutostopRequirement.Store(true)
templateScheduleStore.TimeNowFn = func() time.Time { templateScheduleStore.TimeNowFn = func() time.Time {
return now return now
} }
@@ -494,8 +494,8 @@ func TestTemplateUpdateBuildDeadlinesSkip(t *testing.T) {
UserAutostopEnabled: false, UserAutostopEnabled: false,
DefaultTTL: 0, DefaultTTL: 0,
MaxTTL: 0, MaxTTL: 0,
UseRestartRequirement: true, UseAutostopRequirement: true,
RestartRequirement: agplschedule.TemplateRestartRequirement{ AutostopRequirement: agplschedule.TemplateAutostopRequirement{
// Every day // Every day
DaysOfWeek: 0b01111111, DaysOfWeek: 0b01111111,
Weeks: 0, Weeks: 0,

View File

@@ -140,7 +140,7 @@ func TestTemplates(t *testing.T) {
require.EqualValues(t, exp, *ws.TTLMillis) require.EqualValues(t, exp, *ws.TTLMillis)
}) })
t.Run("SetRestartRequirement", func(t *testing.T) { t.Run("SetAutostopRequirement", func(t *testing.T) {
t.Parallel() t.Parallel()
client, user := coderdenttest.New(t, &coderdenttest.Options{ client, user := coderdenttest.New(t, &coderdenttest.Options{
@@ -157,8 +157,8 @@ func TestTemplates(t *testing.T) {
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitTemplateVersionJob(t, client, version.ID) coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
require.Empty(t, 0, template.RestartRequirement.DaysOfWeek) require.Empty(t, 0, template.AutostopRequirement.DaysOfWeek)
require.Zero(t, template.RestartRequirement.Weeks) require.Zero(t, template.AutostopRequirement.Weeks)
// ctx := testutil.Context(t, testutil.WaitLong) // ctx := testutil.Context(t, testutil.WaitLong)
ctx := context.Background() ctx := context.Background()
@@ -169,19 +169,19 @@ func TestTemplates(t *testing.T) {
Icon: template.Icon, Icon: template.Icon,
AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs,
DefaultTTLMillis: time.Hour.Milliseconds(), DefaultTTLMillis: time.Hour.Milliseconds(),
RestartRequirement: &codersdk.TemplateRestartRequirement{ AutostopRequirement: &codersdk.TemplateAutostopRequirement{
DaysOfWeek: []string{"monday", "saturday"}, DaysOfWeek: []string{"monday", "saturday"},
Weeks: 3, Weeks: 3,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, []string{"monday", "saturday"}, updated.RestartRequirement.DaysOfWeek) require.Equal(t, []string{"monday", "saturday"}, updated.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 3, updated.RestartRequirement.Weeks) require.EqualValues(t, 3, updated.AutostopRequirement.Weeks)
template, err = client.Template(ctx, template.ID) template, err = client.Template(ctx, template.ID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, []string{"monday", "saturday"}, template.RestartRequirement.DaysOfWeek) require.Equal(t, []string{"monday", "saturday"}, template.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 3, template.RestartRequirement.Weeks) require.EqualValues(t, 3, template.AutostopRequirement.Weeks)
}) })
t.Run("CleanupTTLs", func(t *testing.T) { t.Run("CleanupTTLs", func(t *testing.T) {

View File

@@ -11,28 +11,28 @@ import (
"github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk"
) )
func (api *API) restartRequirementEnabledMW(next http.Handler) http.Handler { func (api *API) autostopRequirementEnabledMW(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
// The experiment must be enabled. // The experiment must be enabled.
if !api.AGPL.Experiments.Enabled(codersdk.ExperimentTemplateRestartRequirement) { if !api.AGPL.Experiments.Enabled(codersdk.ExperimentTemplateAutostopRequirement) {
httpapi.RouteNotFound(rw) httpapi.RouteNotFound(rw)
return return
} }
// Entitlement must be enabled. // Entitlement must be enabled.
api.entitlementsMu.RLock() api.entitlementsMu.RLock()
entitled := api.entitlements.Features[codersdk.FeatureTemplateRestartRequirement].Entitlement != codersdk.EntitlementNotEntitled entitled := api.entitlements.Features[codersdk.FeatureTemplateAutostopRequirement].Entitlement != codersdk.EntitlementNotEntitled
enabled := api.entitlements.Features[codersdk.FeatureTemplateRestartRequirement].Enabled enabled := api.entitlements.Features[codersdk.FeatureTemplateAutostopRequirement].Enabled
api.entitlementsMu.RUnlock() api.entitlementsMu.RUnlock()
if !entitled { if !entitled {
httpapi.Write(r.Context(), rw, http.StatusForbidden, codersdk.Response{ httpapi.Write(r.Context(), rw, http.StatusForbidden, codersdk.Response{
Message: "Template restart requirement is an Enterprise feature. Contact sales!", Message: "Template autostop requirement is an Enterprise feature. Contact sales!",
}) })
return return
} }
if !enabled { if !enabled {
httpapi.Write(r.Context(), rw, http.StatusForbidden, codersdk.Response{ httpapi.Write(r.Context(), rw, http.StatusForbidden, codersdk.Response{
Message: "Template restart requirement feature is not enabled. Please specify a default user quiet hours schedule to use this feature.", Message: "Template autostop requirement feature is not enabled. Please specify a default user quiet hours schedule to use this feature.",
}) })
return return
} }

View File

@@ -35,7 +35,7 @@ func TestUserQuietHours(t *testing.T) {
dv := coderdtest.DeploymentValues(t) dv := coderdtest.DeploymentValues(t)
dv.UserQuietHoursSchedule.DefaultSchedule.Set(defaultQuietHoursSchedule) dv.UserQuietHoursSchedule.DefaultSchedule.Set(defaultQuietHoursSchedule)
dv.Experiments.Set(string(codersdk.ExperimentTemplateRestartRequirement)) dv.Experiments.Set(string(codersdk.ExperimentTemplateAutostopRequirement))
client, user := coderdenttest.New(t, &coderdenttest.Options{ client, user := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{ Options: &coderdtest.Options{
@@ -44,7 +44,7 @@ func TestUserQuietHours(t *testing.T) {
LicenseOptions: &coderdenttest.LicenseOptions{ LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{ Features: license.Features{
codersdk.FeatureAdvancedTemplateScheduling: 1, codersdk.FeatureAdvancedTemplateScheduling: 1,
codersdk.FeatureTemplateRestartRequirement: 1, codersdk.FeatureTemplateAutostopRequirement: 1,
}, },
}, },
}) })
@@ -133,7 +133,7 @@ func TestUserQuietHours(t *testing.T) {
dv := coderdtest.DeploymentValues(t) dv := coderdtest.DeploymentValues(t)
dv.UserQuietHoursSchedule.DefaultSchedule.Set("CRON_TZ=America/Chicago 0 0 * * *") dv.UserQuietHoursSchedule.DefaultSchedule.Set("CRON_TZ=America/Chicago 0 0 * * *")
dv.Experiments.Set(string(codersdk.ExperimentTemplateRestartRequirement)) dv.Experiments.Set(string(codersdk.ExperimentTemplateAutostopRequirement))
client, user := coderdenttest.New(t, &coderdenttest.Options{ client, user := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{ Options: &coderdtest.Options{
@@ -143,7 +143,7 @@ func TestUserQuietHours(t *testing.T) {
Features: license.Features{ Features: license.Features{
codersdk.FeatureAdvancedTemplateScheduling: 1, codersdk.FeatureAdvancedTemplateScheduling: 1,
// Not entitled. // Not entitled.
// codersdk.FeatureTemplateRestartRequirement: 1, // codersdk.FeatureTemplateAutostopRequirement: 1,
}, },
}, },
}) })
@@ -161,7 +161,7 @@ func TestUserQuietHours(t *testing.T) {
dv := coderdtest.DeploymentValues(t) dv := coderdtest.DeploymentValues(t)
dv.UserQuietHoursSchedule.DefaultSchedule.Set("") dv.UserQuietHoursSchedule.DefaultSchedule.Set("")
dv.Experiments.Set(string(codersdk.ExperimentTemplateRestartRequirement)) dv.Experiments.Set(string(codersdk.ExperimentTemplateAutostopRequirement))
client, user := coderdenttest.New(t, &coderdenttest.Options{ client, user := coderdenttest.New(t, &coderdenttest.Options{
NoDefaultQuietHoursSchedule: true, NoDefaultQuietHoursSchedule: true,
@@ -171,7 +171,7 @@ func TestUserQuietHours(t *testing.T) {
LicenseOptions: &coderdenttest.LicenseOptions{ LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{ Features: license.Features{
codersdk.FeatureAdvancedTemplateScheduling: 1, codersdk.FeatureAdvancedTemplateScheduling: 1,
codersdk.FeatureTemplateRestartRequirement: 1, codersdk.FeatureTemplateAutostopRequirement: 1,
}, },
}, },
}) })
@@ -198,7 +198,7 @@ func TestUserQuietHours(t *testing.T) {
LicenseOptions: &coderdenttest.LicenseOptions{ LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{ Features: license.Features{
codersdk.FeatureAdvancedTemplateScheduling: 1, codersdk.FeatureAdvancedTemplateScheduling: 1,
codersdk.FeatureTemplateRestartRequirement: 1, codersdk.FeatureTemplateAutostopRequirement: 1,
}, },
}, },
}) })

View File

@@ -195,7 +195,7 @@ export interface CreateTemplateRequest {
readonly template_version_id: string readonly template_version_id: string
readonly default_ttl_ms?: number readonly default_ttl_ms?: number
readonly max_ttl_ms?: number readonly max_ttl_ms?: number
readonly restart_requirement?: TemplateRestartRequirement readonly autostop_requirement?: TemplateAutostopRequirement
readonly allow_user_cancel_workspace_jobs?: boolean readonly allow_user_cancel_workspace_jobs?: boolean
readonly allow_user_autostart?: boolean readonly allow_user_autostart?: boolean
readonly allow_user_autostop?: boolean readonly allow_user_autostop?: boolean
@@ -910,7 +910,7 @@ export interface Template {
readonly icon: string readonly icon: string
readonly default_ttl_ms: number readonly default_ttl_ms: number
readonly max_ttl_ms: number readonly max_ttl_ms: number
readonly restart_requirement: TemplateRestartRequirement readonly autostop_requirement: TemplateAutostopRequirement
readonly created_by_id: string readonly created_by_id: string
readonly created_by_name: string readonly created_by_name: string
readonly allow_user_autostart: boolean readonly allow_user_autostart: boolean
@@ -937,6 +937,12 @@ export interface TemplateAppUsage {
readonly seconds: number readonly seconds: number
} }
// From codersdk/templates.go
export interface TemplateAutostopRequirement {
readonly days_of_week: string[]
readonly weeks: number
}
// From codersdk/templates.go // From codersdk/templates.go
export type TemplateBuildTimeStats = Record< export type TemplateBuildTimeStats = Record<
WorkspaceTransition, WorkspaceTransition,
@@ -1009,12 +1015,6 @@ export interface TemplateParameterValue {
readonly count: number readonly count: number
} }
// From codersdk/templates.go
export interface TemplateRestartRequirement {
readonly days_of_week: string[]
readonly weeks: number
}
// From codersdk/templates.go // From codersdk/templates.go
export interface TemplateUser extends User { export interface TemplateUser extends User {
readonly role: TemplateRole readonly role: TemplateRole
@@ -1147,7 +1147,7 @@ export interface UpdateTemplateMeta {
readonly icon?: string readonly icon?: string
readonly default_ttl_ms?: number readonly default_ttl_ms?: number
readonly max_ttl_ms?: number readonly max_ttl_ms?: number
readonly restart_requirement?: TemplateRestartRequirement readonly autostop_requirement?: TemplateAutostopRequirement
readonly allow_user_autostart?: boolean readonly allow_user_autostart?: boolean
readonly allow_user_autostop?: boolean readonly allow_user_autostop?: boolean
readonly allow_user_cancel_workspace_jobs?: boolean readonly allow_user_cancel_workspace_jobs?: boolean
@@ -1603,7 +1603,7 @@ export type Experiment =
| "moons" | "moons"
| "single_tailnet" | "single_tailnet"
| "tailnet_pg_coordinator" | "tailnet_pg_coordinator"
| "template_restart_requirement" | "template_autostop_requirement"
| "workspace_actions" | "workspace_actions"
| "workspaces_batch_actions" | "workspaces_batch_actions"
export const Experiments: Experiment[] = [ export const Experiments: Experiment[] = [
@@ -1611,7 +1611,7 @@ export const Experiments: Experiment[] = [
"moons", "moons",
"single_tailnet", "single_tailnet",
"tailnet_pg_coordinator", "tailnet_pg_coordinator",
"template_restart_requirement", "template_autostop_requirement",
"workspace_actions", "workspace_actions",
"workspaces_batch_actions", "workspaces_batch_actions",
] ]
@@ -1626,8 +1626,8 @@ export type FeatureName =
| "high_availability" | "high_availability"
| "multiple_git_auth" | "multiple_git_auth"
| "scim" | "scim"
| "template_autostop_requirement"
| "template_rbac" | "template_rbac"
| "template_restart_requirement"
| "user_limit" | "user_limit"
| "user_role_management" | "user_role_management"
| "workspace_proxy" | "workspace_proxy"
@@ -1640,8 +1640,8 @@ export const FeatureNames: FeatureName[] = [
"high_availability", "high_availability",
"multiple_git_auth", "multiple_git_auth",
"scim", "scim",
"template_autostop_requirement",
"template_rbac", "template_rbac",
"template_restart_requirement",
"user_limit", "user_limit",
"user_role_management", "user_role_management",
"workspace_proxy", "workspace_proxy",

View File

@@ -26,7 +26,7 @@ const validFormValues: FormValues = {
allow_user_cancel_workspace_jobs: false, allow_user_cancel_workspace_jobs: false,
allow_user_autostart: false, allow_user_autostart: false,
allow_user_autostop: false, allow_user_autostop: false,
restart_requirement: { autostop_requirement: {
days_of_week: [], days_of_week: [],
weeks: 1, weeks: 1,
}, },

View File

@@ -74,9 +74,9 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
? template.time_til_dormant_autodelete_ms / MS_DAY_CONVERSION ? template.time_til_dormant_autodelete_ms / MS_DAY_CONVERSION
: 0, : 0,
restart_requirement: { autostop_requirement: {
days_of_week: template.restart_requirement.days_of_week, days_of_week: template.autostop_requirement.days_of_week,
weeks: template.restart_requirement.weeks, weeks: template.autostop_requirement.weeks,
}, },
allow_user_autostart: template.allow_user_autostart, allow_user_autostart: template.allow_user_autostart,

View File

@@ -436,7 +436,7 @@ export const MockTemplate: TypesGen.Template = {
description: "This is a test description.", description: "This is a test description.",
default_ttl_ms: 24 * 60 * 60 * 1000, default_ttl_ms: 24 * 60 * 60 * 1000,
max_ttl_ms: 2 * 24 * 60 * 60 * 1000, max_ttl_ms: 2 * 24 * 60 * 60 * 1000,
restart_requirement: { autostop_requirement: {
days_of_week: [], days_of_week: [],
weeks: 1, weeks: 1,
}, },