mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: enforce template-level constraints for TTL and autostart (#2018)
This PR adds fields to templates that constrain values for workspaces derived from that template. - Autostop: Adds a field max_ttl on the template which limits the maximum value of ttl on all workspaces derived from that template. Defaulting to 168 hours, enforced on edits to workspace metadata. New workspaces will default to the templates's `max_ttl` if not specified. - Autostart: Adds a field min_autostart_duration which limits the minimum duration between successive autostarts of a template, measured from a single reference time. Defaulting to 1 hour, enforced on edits to workspace metadata.
This commit is contained in:
@@ -108,6 +108,35 @@ func (s Schedule) Next(t time.Time) time.Time {
|
||||
return s.sched.Next(t)
|
||||
}
|
||||
|
||||
var t0 = time.Date(1970, 1, 1, 1, 1, 1, 0, time.UTC)
|
||||
var tMax = t0.Add(168 * time.Hour)
|
||||
|
||||
// Min returns the minimum duration of the schedule.
|
||||
// This is calculated as follows:
|
||||
// - Let t(0) be a given point in time (1970-01-01T01:01:01Z00:00)
|
||||
// - Let t(max) be 168 hours after t(0).
|
||||
// - Let t(1) be the next scheduled time after t(0).
|
||||
// - Let t(n) be the next scheduled time after t(n-1).
|
||||
// - Then, the minimum duration of s d(min)
|
||||
// = min( t(n) - t(n-1) ∀ n ∈ N, t(n) < t(max) )
|
||||
func (s Schedule) Min() time.Duration {
|
||||
durMin := tMax.Sub(t0)
|
||||
tPrev := s.Next(t0)
|
||||
tCurr := s.Next(tPrev)
|
||||
for {
|
||||
dur := tCurr.Sub(tPrev)
|
||||
if dur < durMin {
|
||||
durMin = dur
|
||||
}
|
||||
tPrev = tCurr
|
||||
tCurr = s.Next(tCurr)
|
||||
if tCurr.After(tMax) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return durMin
|
||||
}
|
||||
|
||||
// validateWeeklySpec ensures that the day-of-month and month options of
|
||||
// spec are both set to *
|
||||
func validateWeeklySpec(spec string) error {
|
||||
|
||||
@@ -16,6 +16,7 @@ func Test_Weekly(t *testing.T) {
|
||||
spec string
|
||||
at time.Time
|
||||
expectedNext time.Time
|
||||
expectedMin time.Duration
|
||||
expectedError string
|
||||
expectedCron string
|
||||
expectedTz string
|
||||
@@ -26,6 +27,7 @@ func Test_Weekly(t *testing.T) {
|
||||
spec: "CRON_TZ=US/Central 30 9 * * 1-5",
|
||||
at: time.Date(2022, 4, 1, 14, 29, 0, 0, time.UTC),
|
||||
expectedNext: time.Date(2022, 4, 1, 14, 30, 0, 0, time.UTC),
|
||||
expectedMin: 24 * time.Hour,
|
||||
expectedError: "",
|
||||
expectedCron: "30 9 * * 1-5",
|
||||
expectedTz: "US/Central",
|
||||
@@ -36,11 +38,34 @@ func Test_Weekly(t *testing.T) {
|
||||
spec: "30 9 * * 1-5",
|
||||
at: time.Date(2022, 4, 1, 9, 29, 0, 0, time.UTC),
|
||||
expectedNext: time.Date(2022, 4, 1, 9, 30, 0, 0, time.UTC),
|
||||
expectedMin: 24 * time.Hour,
|
||||
expectedError: "",
|
||||
expectedCron: "30 9 * * 1-5",
|
||||
expectedTz: "UTC",
|
||||
expectedString: "CRON_TZ=UTC 30 9 * * 1-5",
|
||||
},
|
||||
{
|
||||
name: "convoluted with timezone",
|
||||
spec: "CRON_TZ=US/Central */5 12-18 * * 1,3,6",
|
||||
at: time.Date(2022, 4, 1, 14, 29, 0, 0, time.UTC),
|
||||
expectedNext: time.Date(2022, 4, 2, 17, 0, 0, 0, time.UTC), // Apr 1 was a Friday in 2022
|
||||
expectedMin: 5 * time.Minute,
|
||||
expectedError: "",
|
||||
expectedCron: "*/5 12-18 * * 1,3,6",
|
||||
expectedTz: "US/Central",
|
||||
expectedString: "CRON_TZ=US/Central */5 12-18 * * 1,3,6",
|
||||
},
|
||||
{
|
||||
name: "another convoluted example",
|
||||
spec: "CRON_TZ=US/Central 10,20,40-50 * * * *",
|
||||
at: time.Date(2022, 4, 1, 14, 29, 0, 0, time.UTC),
|
||||
expectedNext: time.Date(2022, 4, 1, 14, 40, 0, 0, time.UTC),
|
||||
expectedMin: time.Minute,
|
||||
expectedError: "",
|
||||
expectedCron: "10,20,40-50 * * * *",
|
||||
expectedTz: "US/Central",
|
||||
expectedString: "CRON_TZ=US/Central 10,20,40-50 * * * *",
|
||||
},
|
||||
{
|
||||
name: "time.Local will bite you",
|
||||
spec: "CRON_TZ=Local 30 9 * * 1-5",
|
||||
@@ -104,6 +129,7 @@ func Test_Weekly(t *testing.T) {
|
||||
require.Equal(t, testCase.expectedCron, actual.Cron())
|
||||
require.Equal(t, testCase.expectedTz, actual.Timezone())
|
||||
require.Equal(t, testCase.expectedString, actual.String())
|
||||
require.Equal(t, testCase.expectedMin, actual.Min())
|
||||
} else {
|
||||
require.EqualError(t, err, testCase.expectedError)
|
||||
require.Nil(t, actual)
|
||||
|
||||
Reference in New Issue
Block a user