-
Notifications
You must be signed in to change notification settings - Fork 894
feat: enforce template-level constraints for TTL and autostart #2018
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
1713431
f11f942
c5c9a7a
74f4dee
7da27a0
5ad048b
65b980e
aa298a6
a5ec9bf
a6255d6
245795e
709db5b
c4a0927
244e479
60ef479
c9aab8b
f997af2
d7f697b
ebe4c15
08ef2eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,6 @@ import ( | |
|
||
"github.com/coder/coder/cli/cliflag" | ||
"github.com/coder/coder/cli/cliui" | ||
"github.com/coder/coder/coderd/autobuild/schedule" | ||
"github.com/coder/coder/coderd/util/ptr" | ||
"github.com/coder/coder/codersdk" | ||
) | ||
|
@@ -61,20 +60,6 @@ func create() *cobra.Command { | |
} | ||
} | ||
|
||
tz, err := time.LoadLocation(tzName) | ||
if err != nil { | ||
return xerrors.Errorf("Invalid workspace autostart timezone: %w", err) | ||
} | ||
schedSpec := fmt.Sprintf("CRON_TZ=%s %s %s * * %s", tz.String(), autostartMinute, autostartHour, autostartDow) | ||
_, err = schedule.Weekly(schedSpec) | ||
if err != nil { | ||
return xerrors.Errorf("invalid workspace autostart schedule: %w", err) | ||
} | ||
|
||
if ttl == 0 { | ||
return xerrors.Errorf("TTL must be at least 1 minute") | ||
} | ||
|
||
_, err = client.WorkspaceByOwnerAndName(cmd.Context(), organization.ID, codersdk.Me, workspaceName) | ||
if err == nil { | ||
return xerrors.Errorf("A workspace already exists named %q!", workspaceName) | ||
|
@@ -129,6 +114,11 @@ func create() *cobra.Command { | |
} | ||
} | ||
|
||
schedSpec := buildSchedule(autostartMinute, autostartHour, autostartDow, tzName) | ||
if ttl < time.Minute { | ||
return xerrors.Errorf("TTL must be at least 1 minute") | ||
} | ||
|
||
templateVersion, err := client.TemplateVersion(cmd.Context(), template.ActiveVersionID) | ||
if err != nil { | ||
return err | ||
|
@@ -226,7 +216,7 @@ func create() *cobra.Command { | |
workspace, err := client.CreateWorkspace(cmd.Context(), organization.ID, codersdk.CreateWorkspaceRequest{ | ||
TemplateID: template.ID, | ||
Name: workspaceName, | ||
AutostartSchedule: &schedSpec, | ||
AutostartSchedule: schedSpec, | ||
TTLMillis: ptr.Ref(ttl.Milliseconds()), | ||
ParameterValues: parameters, | ||
}) | ||
|
@@ -262,7 +252,16 @@ func create() *cobra.Command { | |
cliflag.StringVarP(cmd.Flags(), &autostartMinute, "autostart-minute", "", "CODER_WORKSPACE_AUTOSTART_MINUTE", "0", "Specify the minute(s) at which the workspace should autostart (e.g. 0).") | ||
cliflag.StringVarP(cmd.Flags(), &autostartHour, "autostart-hour", "", "CODER_WORKSPACE_AUTOSTART_HOUR", "9", "Specify the hour(s) at which the workspace should autostart (e.g. 9).") | ||
cliflag.StringVarP(cmd.Flags(), &autostartDow, "autostart-day-of-week", "", "CODER_WORKSPACE_AUTOSTART_DOW", "MON-FRI", "Specify the days(s) on which the workspace should autostart (e.g. MON,TUE,WED,THU,FRI)") | ||
cliflag.StringVarP(cmd.Flags(), &tzName, "tz", "", "TZ", "", "Specify your timezone location for workspace autostart (e.g. US/Central).") | ||
cliflag.StringVarP(cmd.Flags(), &tzName, "tz", "", "TZ", "UTC", "Specify your timezone location for workspace autostart (e.g. US/Central).") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review: defaulting TZ to UTC here explicitly. Will hopefully have some fancier auto-detection in a separate issue. |
||
cliflag.DurationVarP(cmd.Flags(), &ttl, "ttl", "", "CODER_WORKSPACE_TTL", 8*time.Hour, "Specify a time-to-live (TTL) for the workspace (e.g. 8h).") | ||
return cmd | ||
} | ||
|
||
func buildSchedule(minute, hour, dow, tzName string) *string { | ||
if minute == "" || hour == "" || dow == "" { | ||
return nil | ||
} | ||
|
||
schedSpec := fmt.Sprintf("CRON_TZ=%s %s %s * * %s", tzName, minute, hour, dow) | ||
return &schedSpec | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,6 @@ import ( | |
|
||
func TestCreate(t *testing.T) { | ||
t.Parallel() | ||
t.Fatal("CIAN FIX THIS") | ||
t.Run("Create", func(t *testing.T) { | ||
t.Parallel() | ||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true}) | ||
|
@@ -66,7 +65,6 @@ func TestCreate(t *testing.T) { | |
|
||
t.Run("AboveTemplateMaxTTL", func(t *testing.T) { | ||
t.Parallel() | ||
t.Fatal("CIAN FIX THIS") | ||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true}) | ||
user := coderdtest.CreateFirstUser(t, client) | ||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) | ||
|
@@ -78,24 +76,20 @@ func TestCreate(t *testing.T) { | |
"create", | ||
"my-workspace", | ||
"--template", template.Name, | ||
"--ttl", "24h", | ||
"--ttl", "12h1m", | ||
"-y", // don't bother with waiting | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review: I'm slightly side-stepping here because I don't want to deal with confirms |
||
} | ||
cmd, root := clitest.New(t, args...) | ||
clitest.SetupConfig(t, client, root) | ||
errCh := make(chan error) | ||
pty := ptytest.New(t) | ||
cmd.SetIn(pty.Input()) | ||
cmd.SetOut(pty.Output()) | ||
go func() { | ||
defer close(errCh) | ||
errCh <- cmd.Execute() | ||
}() | ||
require.EqualError(t, <-errCh, "TODO what is the error") | ||
err := cmd.Execute() | ||
assert.ErrorContains(t, err, "ttl_ms: ttl must be below template maximum 12h0m0s") | ||
}) | ||
|
||
t.Run("BelowTemplateMinAutostartInterval", func(t *testing.T) { | ||
t.Parallel() | ||
t.Fatal("CIAN FIX THIS") | ||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true}) | ||
user := coderdtest.CreateFirstUser(t, client) | ||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) | ||
|
@@ -109,18 +103,15 @@ func TestCreate(t *testing.T) { | |
"--template", template.Name, | ||
"--autostart-minute", "*", // Every minute | ||
"--autostart-hour", "*", // Every hour | ||
"-y", // don't bother with waiting | ||
} | ||
cmd, root := clitest.New(t, args...) | ||
clitest.SetupConfig(t, client, root) | ||
errCh := make(chan error) | ||
pty := ptytest.New(t) | ||
cmd.SetIn(pty.Input()) | ||
cmd.SetOut(pty.Output()) | ||
go func() { | ||
defer close(errCh) | ||
errCh <- cmd.Execute() | ||
}() | ||
require.EqualError(t, <-errCh, "TODO what is the error") | ||
err := cmd.Execute() | ||
assert.ErrorContains(t, err, "Minimum autostart interval 1m0s below template minimum 1h0m0s") | ||
}) | ||
|
||
t.Run("CreateErrInvalidTz", func(t *testing.T) { | ||
|
@@ -135,24 +126,19 @@ func TestCreate(t *testing.T) { | |
"my-workspace", | ||
"--template", template.Name, | ||
"--tz", "invalid", | ||
"-y", | ||
} | ||
cmd, root := clitest.New(t, args...) | ||
clitest.SetupConfig(t, client, root) | ||
doneChan := make(chan struct{}) | ||
pty := ptytest.New(t) | ||
cmd.SetIn(pty.Input()) | ||
cmd.SetOut(pty.Output()) | ||
go func() { | ||
defer close(doneChan) | ||
err := cmd.Execute() | ||
assert.EqualError(t, err, "Invalid workspace autostart timezone: unknown time zone invalid") | ||
}() | ||
<-doneChan | ||
err := cmd.Execute() | ||
assert.ErrorContains(t, err, "schedule: parse schedule: provided bad location invalid: unknown time zone invalid") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review: the parallelism isn't strictly necessary in this test as I don't need to do I/O with the pty, so just leaving it out for simplicity. |
||
}) | ||
|
||
t.Run("CreateErrInvalidTTL", func(t *testing.T) { | ||
t.Parallel() | ||
t.Fatal("CIAN FIX THIS") | ||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true}) | ||
user := coderdtest.CreateFirstUser(t, client) | ||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) | ||
|
@@ -163,19 +149,15 @@ func TestCreate(t *testing.T) { | |
"my-workspace", | ||
"--template", template.Name, | ||
"--ttl", "0s", | ||
"-y", | ||
} | ||
cmd, root := clitest.New(t, args...) | ||
clitest.SetupConfig(t, client, root) | ||
doneChan := make(chan struct{}) | ||
pty := ptytest.New(t) | ||
cmd.SetIn(pty.Input()) | ||
cmd.SetOut(pty.Output()) | ||
go func() { | ||
defer close(doneChan) | ||
err := cmd.Execute() | ||
assert.EqualError(t, err, "TTL must be at least 1 minute") | ||
}() | ||
<-doneChan | ||
err := cmd.Execute() | ||
assert.EqualError(t, err, "TTL must be at least 1 minute") | ||
Comment on lines
-111
to
+160
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review: as above, removing parallelism from this test as it's not strictly necessary. |
||
}) | ||
|
||
t.Run("CreateFromListWithSkip", func(t *testing.T) { | ||
|
Uh oh!
There was an error while loading. Please reload this page.