Skip to content
Prev Previous commit
Next Next commit
fixup! Merge branch 'main' into dean/max-ttl-comeback
  • Loading branch information
deansheather committed Dec 8, 2023
commit dbd74d5db4a8a4b3eb7b1ab8dcc626158c084c7b
11 changes: 11 additions & 0 deletions coderd/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
if createTemplate.MaxTTLMillis != nil {
maxTTL = time.Duration(*createTemplate.MaxTTLMillis) * time.Millisecond
}
if maxTTL != 0 && len(autostopRequirementDaysOfWeek) > 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.days_of_week", Detail: "Cannot be set if max_ttl_ms is set."})
}
if autostopRequirementWeeks < 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.weeks", Detail: "Must be a positive integer."})
}
Expand Down Expand Up @@ -364,6 +367,7 @@ 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,
UseMaxTTL: maxTTL > 0,
DefaultTTL: defaultTTL,
MaxTTL: maxTTL,
// Some of these values are enterprise-only, but the
Expand Down Expand Up @@ -568,6 +572,10 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
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."})
}
if req.MaxTTLMillis != 0 && req.AutostopRequirement != nil && len(req.AutostopRequirement.DaysOfWeek) > 0 {
validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.days_of_week", Detail: "Cannot be set if max_ttl_ms is set."})
}
useMaxTTL := req.MaxTTLMillis > 0
if req.AutostopRequirement == nil {
req.AutostopRequirement = &codersdk.TemplateAutostopRequirement{
DaysOfWeek: codersdk.BitmapToWeekdays(scheduleOpts.AutostopRequirement.DaysOfWeek),
Expand Down Expand Up @@ -641,6 +649,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
req.AllowUserAutostop == template.AllowUserAutostop &&
req.AllowUserCancelWorkspaceJobs == template.AllowUserCancelWorkspaceJobs &&
req.DefaultTTLMillis == time.Duration(template.DefaultTTL).Milliseconds() &&
useMaxTTL == scheduleOpts.UseMaxTTL &&
req.MaxTTLMillis == time.Duration(template.MaxTTL).Milliseconds() &&
autostopRequirementDaysOfWeekParsed == scheduleOpts.AutostopRequirement.DaysOfWeek &&
autostartRequirementDaysOfWeekParsed == scheduleOpts.AutostartRequirement.DaysOfWeek &&
Expand Down Expand Up @@ -695,6 +704,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
timeTilDormantAutoDelete := time.Duration(req.TimeTilDormantAutoDeleteMillis) * time.Millisecond

if defaultTTL != time.Duration(template.DefaultTTL) ||
useMaxTTL != scheduleOpts.UseMaxTTL ||
maxTTL != time.Duration(template.MaxTTL) ||
autostopRequirementDaysOfWeekParsed != scheduleOpts.AutostopRequirement.DaysOfWeek ||
autostartRequirementDaysOfWeekParsed != scheduleOpts.AutostartRequirement.DaysOfWeek ||
Expand All @@ -711,6 +721,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
UserAutostartEnabled: req.AllowUserAutostart,
UserAutostopEnabled: req.AllowUserAutostop,
DefaultTTL: defaultTTL,
UseMaxTTL: useMaxTTL,
MaxTTL: maxTTL,
AutostopRequirement: schedule.TemplateAutostopRequirement{
DaysOfWeek: autostopRequirementDaysOfWeekParsed,
Expand Down
61 changes: 61 additions & 0 deletions coderd/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ func TestPostTemplateByOrganization(t *testing.T) {
})
require.NoError(t, err)

require.False(t, got.UseMaxTTL) // default
require.EqualValues(t, 1, atomic.LoadInt64(&setCalled))
require.Empty(t, got.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 1, got.AutostopRequirement.Weeks)
Expand Down Expand Up @@ -351,11 +352,13 @@ func TestPostTemplateByOrganization(t *testing.T) {
require.NoError(t, err)

require.EqualValues(t, 1, atomic.LoadInt64(&setCalled))
require.False(t, got.UseMaxTTL)
require.Equal(t, []string{"friday", "saturday"}, got.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 2, got.AutostopRequirement.Weeks)

got, err = client.Template(ctx, got.ID)
require.NoError(t, err)
require.False(t, got.UseMaxTTL)
require.Equal(t, []string{"friday", "saturday"}, got.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 2, got.AutostopRequirement.Weeks)
})
Expand All @@ -380,10 +383,36 @@ func TestPostTemplateByOrganization(t *testing.T) {
})
require.NoError(t, err)
// ignored and use AGPL defaults
require.False(t, got.UseMaxTTL)
require.Empty(t, got.AutostopRequirement.DaysOfWeek)
require.EqualValues(t, 1, got.AutostopRequirement.Weeks)
})
})

t.Run("BothMaxTTLAndAutostopRequirement", func(t *testing.T) {
t.Parallel()

// Fake template schedule store is unneeded for this test since the
// route fails before it is called.
client := coderdtest.New(t, nil)
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

_, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{
Name: "testing",
VersionID: version.ID,
MaxTTLMillis: ptr.Ref(24 * time.Hour.Milliseconds()),
AutostopRequirement: &codersdk.TemplateAutostopRequirement{
DaysOfWeek: []string{"friday", "saturday"},
Weeks: 2,
},
})
require.Error(t, err)
require.ErrorContains(t, err, "max_ttl_ms")
})
}

func TestTemplatesByOrganization(t *testing.T) {
Expand Down Expand Up @@ -1238,6 +1267,38 @@ func TestPatchTemplateMeta(t *testing.T) {
require.False(t, template.Deprecated)
})
})

t.Run("BothMaxTTLAndAutostopRequirement", func(t *testing.T) {
t.Parallel()

// Fake template schedule store is unneeded for this test since the
// route fails before it is called.
client := coderdtest.New(t, nil)
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)

req := codersdk.UpdateTemplateMeta{
Name: template.Name,
DisplayName: template.DisplayName,
Description: template.Description,
Icon: template.Icon,
AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs,
DefaultTTLMillis: time.Hour.Milliseconds(),
MaxTTLMillis: time.Hour.Milliseconds(),
AutostopRequirement: &codersdk.TemplateAutostopRequirement{
DaysOfWeek: []string{"monday"},
Weeks: 2,
},
}

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

_, err := client.UpdateTemplateMeta(ctx, template.ID, req)
require.Error(t, err)
require.ErrorContains(t, err, "max_ttl_ms")
})
}

func TestDeleteTemplate(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions codersdk/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,11 @@ type CreateTemplateRequest struct {
// for all workspaces created from this template.
DefaultTTLMillis *int64 `json:"default_ttl_ms,omitempty"`
// TODO(@dean): remove max_ttl once autostop_requirement is matured
// Only one of MaxTTLMillis or AutostopRequirement can be specified.
MaxTTLMillis *int64 `json:"max_ttl_ms,omitempty"`
// AutostopRequirement allows optionally specifying the autostop requirement
// for workspaces created from this template. This is an enterprise feature.
// Only one of MaxTTLMillis or AutostopRequirement can be specified.
AutostopRequirement *TemplateAutostopRequirement `json:"autostop_requirement,omitempty"`
// AutostartRequirement allows optionally specifying the autostart allowed days
// for workspaces created from this template. This is an enterprise feature.
Expand Down
5 changes: 5 additions & 0 deletions codersdk/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ type Template struct {
DeprecationMessage string `json:"deprecation_message"`
Icon string `json:"icon"`
DefaultTTLMillis int64 `json:"default_ttl_ms"`
// UseMaxTTL picks whether to use the deprecated max TTL for the template or
// the new autostop requirement.
UseMaxTTL bool `json:"use_max_ttl"`
// TODO(@dean): remove max_ttl once autostop_requirement is matured
MaxTTLMillis int64 `json:"max_ttl_ms"`
// AutostopRequirement and AutostartRequirement are enterprise features. Its
Expand Down Expand Up @@ -206,10 +209,12 @@ type UpdateTemplateMeta struct {
Icon string `json:"icon,omitempty"`
DefaultTTLMillis int64 `json:"default_ttl_ms,omitempty"`
// TODO(@dean): remove max_ttl once autostop_requirement is matured
// Only one of MaxTTLMillis or AutostopRequirement can be specified.
MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"`
// AutostopRequirement and AutostartRequirement can only be set if your license
// includes the advanced template scheduling feature. If you attempt to set this
// value while unlicensed, it will be ignored.
// Only one of MaxTTLMillis or AutostopRequirement can be specified.
AutostopRequirement *TemplateAutostopRequirement `json:"autostop_requirement,omitempty"`
AutostartRequirement *TemplateAutostartRequirement `json:"autostart_requirement,omitempty"`
AllowUserAutostart bool `json:"allow_user_autostart,omitempty"`
Expand Down