Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions cli/templateedit.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func (r *RootCmd) templateEdit() *serpent.Command {
icon string
defaultTTL time.Duration
activityBump time.Duration
timeTilAutostopNotify time.Duration
autostopRequirementDaysOfWeek []string
autostopRequirementWeeks int64
autostartRequirementDaysOfWeek []string
Expand Down Expand Up @@ -113,6 +114,10 @@ func (r *RootCmd) templateEdit() *serpent.Command {
activityBump = time.Duration(template.ActivityBumpMillis) * time.Millisecond
}

if !userSetOption(inv, "autostop-reminder") {
timeTilAutostopNotify = time.Duration(template.TimeTilAutostopNotifyMillis) * time.Millisecond
}

if !userSetOption(inv, "allow-user-autostop") {
allowUserAutostop = template.AllowUserAutostop
}
Expand Down Expand Up @@ -174,12 +179,13 @@ func (r *RootCmd) templateEdit() *serpent.Command {
}

req := codersdk.UpdateTemplateMeta{
Name: &name,
DisplayName: &displayName,
Description: &description,
Icon: &icon,
DefaultTTLMillis: ptr.Ref(defaultTTL.Milliseconds()),
ActivityBumpMillis: ptr.Ref(activityBump.Milliseconds()),
Name: &name,
DisplayName: &displayName,
Description: &description,
Icon: &icon,
DefaultTTLMillis: ptr.Ref(defaultTTL.Milliseconds()),
ActivityBumpMillis: ptr.Ref(activityBump.Milliseconds()),
TimeTilAutostopNotifyMillis: ptr.Ref(timeTilAutostopNotify.Milliseconds()),
AutostopRequirement: &codersdk.TemplateAutostopRequirement{
DaysOfWeek: autostopRequirementDaysOfWeek,
Weeks: autostopRequirementWeeks,
Expand Down Expand Up @@ -248,6 +254,11 @@ func (r *RootCmd) templateEdit() *serpent.Command {
Description: "Edit the template activity bump - workspaces created from this template will have their shutdown time bumped by this value when activity is detected. Maps to \"Activity bump\" in the UI.",
Value: serpent.DurationOf(&activityBump),
},
{
Flag: "autostop-reminder",
Description: "Edit how long before the autostop deadline a reminder notification is sent for workspaces created from this template. Set to 0 to disable.",
Value: serpent.DurationOf(&timeTilAutostopNotify),
},
{
Flag: "autostart-requirement-weekdays",
Description: "Edit the template autostart requirement weekdays - workspaces created from this template can only autostart on the given weekdays. To unset this value for the template (and allow autostart on all days), pass 'all'.",
Expand Down
3 changes: 3 additions & 0 deletions cli/templateedit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func TestTemplateEdit(t *testing.T) {
desc := "lorem ipsum dolor sit amet et cetera"
icon := "/icon/new-icon.png"
defaultTTL := 12 * time.Hour
timeTilAutostopNotify := 5 * time.Minute
allowUserCancelWorkspaceJobs := false

cmdArgs := []string{
Expand All @@ -55,6 +56,7 @@ func TestTemplateEdit(t *testing.T) {
"--description", desc,
"--icon", icon,
"--default-ttl", defaultTTL.String(),
"--autostop-reminder", timeTilAutostopNotify.String(),
"--allow-user-cancel-workspace-jobs=" + strconv.FormatBool(allowUserCancelWorkspaceJobs),
}
inv, root := clitest.New(t, cmdArgs...)
Expand All @@ -73,6 +75,7 @@ func TestTemplateEdit(t *testing.T) {
assert.Equal(t, desc, updated.Description)
assert.Equal(t, icon, updated.Icon)
assert.Equal(t, defaultTTL.Milliseconds(), updated.DefaultTTLMillis)
assert.Equal(t, timeTilAutostopNotify.Milliseconds(), updated.TimeTilAutostopNotifyMillis)
assert.Equal(t, allowUserCancelWorkspaceJobs, updated.AllowUserCancelWorkspaceJobs)
})
t.Run("FirstEmptyThenNotModified", func(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions cli/testdata/coder_templates_edit_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ OPTIONS:
this value for the template (and allow autostart on all days), pass
'all'.

--autostop-reminder duration
Edit how long before the autostop deadline a reminder notification is
sent for workspaces created from this template. Set to 0 to disable.

--autostop-requirement-weekdays [monday|tuesday|wednesday|thursday|friday|saturday|sunday|none]
Edit the template autostop requirement weekdays - workspaces created
from this template must be restarted on the given weekdays. To unset
Expand Down
12 changes: 12 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion coderd/database/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion coderd/database/queries/templates.sql
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ SET
autostart_block_days_of_week = $9,
failure_ttl = $10,
time_til_dormant = $11,
time_til_dormant_autodelete = $12
time_til_dormant_autodelete = $12,
time_til_autostop_notify = $13
WHERE
id = $1
;
Expand Down
26 changes: 17 additions & 9 deletions coderd/schedule/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ type TemplateScheduleOptions struct {
// ActivityBump dictates the duration to bump the workspace's deadline by if
// Coder detects activity from the user. A value of 0 means no bumping.
ActivityBump time.Duration
// TimeTilAutostopNotify dictates how long before the workspace's autostop
// deadline a reminder notification should be sent. A value of 0 means
// disabled.
TimeTilAutostopNotify time.Duration
// AutostopRequirement dictates when the workspace must be restarted. This
// used to be handled by MaxTTL.
AutostopRequirement TemplateAutostopRequirement
Expand Down Expand Up @@ -178,10 +182,11 @@ func (*agplTemplateScheduleStore) Get(ctx context.Context, db database.Store, te
return TemplateScheduleOptions{
// Disregard the values in the database, since user scheduling is an
// enterprise feature.
UserAutostartEnabled: true,
UserAutostopEnabled: true,
DefaultTTL: time.Duration(tpl.DefaultTTL),
ActivityBump: time.Duration(tpl.ActivityBump),
UserAutostartEnabled: true,
UserAutostopEnabled: true,
DefaultTTL: time.Duration(tpl.DefaultTTL),
ActivityBump: time.Duration(tpl.ActivityBump),
TimeTilAutostopNotify: time.Duration(tpl.TimeTilAutostopNotify),
// Disregard the values in the database, since AutostopRequirement,
// FailureTTL, TimeTilDormant, and TimeTilDormantAutoDelete are enterprise features.
AutostartRequirement: TemplateAutostartRequirement{
Expand All @@ -204,18 +209,21 @@ func (*agplTemplateScheduleStore) Set(ctx context.Context, db database.Store, tp
ctx, span := tracing.StartSpan(ctx)
defer span.End()

if int64(opts.DefaultTTL) == tpl.DefaultTTL && int64(opts.ActivityBump) == tpl.ActivityBump {
if int64(opts.DefaultTTL) == tpl.DefaultTTL &&
int64(opts.ActivityBump) == tpl.ActivityBump &&
int64(opts.TimeTilAutostopNotify) == tpl.TimeTilAutostopNotify {
// Avoid updating the UpdatedAt timestamp if nothing will be changed.
return tpl, nil
}

var template database.Template
err := db.InTx(func(db database.Store) error {
err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{
ID: tpl.ID,
UpdatedAt: dbtime.Now(),
DefaultTTL: int64(opts.DefaultTTL),
ActivityBump: int64(opts.ActivityBump),
ID: tpl.ID,
UpdatedAt: dbtime.Now(),
DefaultTTL: int64(opts.DefaultTTL),
ActivityBump: int64(opts.ActivityBump),
TimeTilAutostopNotify: int64(opts.TimeTilAutostopNotify),
// Don't allow changing these settings, but keep the value in the DB (to
// avoid clearing settings if the license has an issue).
AutostopRequirementDaysOfWeek: tpl.AutostopRequirementDaysOfWeek,
Expand Down
34 changes: 26 additions & 8 deletions coderd/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,13 +303,17 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
failureTTL time.Duration
dormantTTL time.Duration
dormantAutoDeletionTTL time.Duration
timeTilAutostopNotify time.Duration
)
if createTemplate.DefaultTTLMillis != nil {
defaultTTL = time.Duration(*createTemplate.DefaultTTLMillis) * time.Millisecond
}
if createTemplate.ActivityBumpMillis != nil {
activityBump = time.Duration(*createTemplate.ActivityBumpMillis) * time.Millisecond
}
if createTemplate.TimeTilAutostopNotifyMillis != nil {
timeTilAutostopNotify = time.Duration(*createTemplate.TimeTilAutostopNotifyMillis) * time.Millisecond
}
if createTemplate.AutostopRequirement != nil {
autostopRequirementDaysOfWeek = createTemplate.AutostopRequirement.DaysOfWeek
autostopRequirementWeeks = createTemplate.AutostopRequirement.Weeks
Expand Down Expand Up @@ -343,6 +347,11 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
if activityBump < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "activity_bump_ms", Detail: "Must be a positive integer."})
}
if timeTilAutostopNotify < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "time_til_autostop_notify_ms", Detail: "Must be a positive integer."})
} else if timeTilAutostopNotify != 0 && timeTilAutostopNotify < time.Minute {
validErrs = append(validErrs, codersdk.ValidationError{Field: "time_til_autostop_notify_ms", Detail: "Must be 0 (disabled) or at least one minute."})
}

if len(autostopRequirementDaysOfWeek) > 0 {
autostopRequirementDaysOfWeekParsed, err = codersdk.WeekdaysToBitmap(autostopRequirementDaysOfWeek)
Expand Down Expand Up @@ -458,10 +467,11 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
}

dbTemplate, err = (*api.TemplateScheduleStore.Load()).Set(ctx, tx, dbTemplate, schedule.TemplateScheduleOptions{
UserAutostartEnabled: allowUserAutostart,
UserAutostopEnabled: allowUserAutostop,
DefaultTTL: defaultTTL,
ActivityBump: activityBump,
UserAutostartEnabled: allowUserAutostart,
UserAutostopEnabled: allowUserAutostop,
DefaultTTL: defaultTTL,
ActivityBump: activityBump,
TimeTilAutostopNotify: timeTilAutostopNotify,
// Some of these values are enterprise-only, but the
// TemplateScheduleStore will handle avoiding setting them if
// unlicensed.
Expand Down Expand Up @@ -693,6 +703,11 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
if resolved.activityBumpMillis < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "activity_bump_ms", Detail: "Must be a positive integer."})
}
if resolved.timeTilAutostopNotifyMillis < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "time_til_autostop_notify_ms", Detail: "Must be a positive integer."})
} else if resolved.timeTilAutostopNotifyMillis != 0 && time.Duration(resolved.timeTilAutostopNotifyMillis)*time.Millisecond < time.Minute {
validErrs = append(validErrs, codersdk.ValidationError{Field: "time_til_autostop_notify_ms", Detail: "Must be 0 (disabled) or at least one minute."})
}
if resolved.autostopRequirementWeeks > schedule.MaxTemplateAutostopRequirementWeeks {
validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.weeks", Detail: fmt.Sprintf("Must be less than %d.", schedule.MaxTemplateAutostopRequirementWeeks)})
}
Expand Down Expand Up @@ -793,6 +808,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {

defaultTTL := time.Duration(resolved.defaultTTLMillis) * time.Millisecond
activityBump := time.Duration(resolved.activityBumpMillis) * time.Millisecond
timeTilAutostopNotify := time.Duration(resolved.timeTilAutostopNotifyMillis) * time.Millisecond
failureTTL := time.Duration(resolved.failureTTLMillis) * time.Millisecond
inactivityTTL := time.Duration(resolved.timeTilDormantMillis) * time.Millisecond
timeTilDormantAutoDelete := time.Duration(resolved.timeTilDormantAutoDeleteMillis) * time.Millisecond
Expand All @@ -808,10 +824,11 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
// Some of these values are enterprise-only, but the
// TemplateScheduleStore will handle avoiding setting them if
// unlicensed.
UserAutostartEnabled: resolved.allowUserAutostart,
UserAutostopEnabled: resolved.allowUserAutostop,
DefaultTTL: defaultTTL,
ActivityBump: activityBump,
UserAutostartEnabled: resolved.allowUserAutostart,
UserAutostopEnabled: resolved.allowUserAutostop,
DefaultTTL: defaultTTL,
ActivityBump: activityBump,
TimeTilAutostopNotify: timeTilAutostopNotify,
AutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: resolved.autostopRequirementDaysOfWeekParsed,
Weeks: resolved.autostopRequirementWeeks,
Expand Down Expand Up @@ -1020,6 +1037,7 @@ func (api *API) convertTemplate(
Icon: template.Icon,
DefaultTTLMillis: time.Duration(template.DefaultTTL).Milliseconds(),
ActivityBumpMillis: time.Duration(template.ActivityBump).Milliseconds(),
TimeTilAutostopNotifyMillis: time.Duration(template.TimeTilAutostopNotify).Milliseconds(),
CreatedByID: template.CreatedBy,
CreatedByName: template.CreatedByUsername,
AllowUserAutostart: template.AllowUserAutostart,
Expand Down
2 changes: 2 additions & 0 deletions coderd/templates_meta_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type templateMetaUpdate struct {
icon string
defaultTTLMillis int64
activityBumpMillis int64
timeTilAutostopNotifyMillis int64
failureTTLMillis int64
timeTilDormantMillis int64
timeTilDormantAutoDeleteMillis int64
Expand Down Expand Up @@ -73,6 +74,7 @@ func resolveTemplateMetaUpdate(
icon: ptr.NilToDefault(req.Icon, template.Icon),
defaultTTLMillis: ptr.NilToDefault(req.DefaultTTLMillis, time.Duration(template.DefaultTTL).Milliseconds()),
activityBumpMillis: ptr.NilToDefault(req.ActivityBumpMillis, time.Duration(template.ActivityBump).Milliseconds()),
timeTilAutostopNotifyMillis: ptr.NilToDefault(req.TimeTilAutostopNotifyMillis, time.Duration(template.TimeTilAutostopNotify).Milliseconds()),
failureTTLMillis: ptr.NilToDefault(req.FailureTTLMillis, time.Duration(template.FailureTTL).Milliseconds()),
timeTilDormantMillis: ptr.NilToDefault(req.TimeTilDormantMillis, time.Duration(template.TimeTilDormant).Milliseconds()),
timeTilDormantAutoDeleteMillis: ptr.NilToDefault(req.TimeTilDormantAutoDeleteMillis, time.Duration(template.TimeTilDormantAutoDelete).Milliseconds()),
Expand Down
Loading
Loading