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:
+58
-14
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/coder/coder/cli/clitest"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/util/ptr"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
@@ -62,6 +63,57 @@ func TestCreate(t *testing.T) {
|
||||
<-doneChan
|
||||
})
|
||||
|
||||
t.Run("AboveTemplateMaxTTL", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.MaxTTLMillis = ptr.Ref((12 * time.Hour).Milliseconds())
|
||||
})
|
||||
args := []string{
|
||||
"create",
|
||||
"my-workspace",
|
||||
"--template", template.Name,
|
||||
"--ttl", "12h1m",
|
||||
"-y", // don't bother with waiting
|
||||
}
|
||||
cmd, root := clitest.New(t, args...)
|
||||
clitest.SetupConfig(t, client, root)
|
||||
pty := ptytest.New(t)
|
||||
cmd.SetIn(pty.Input())
|
||||
cmd.SetOut(pty.Output())
|
||||
err := cmd.Execute()
|
||||
assert.ErrorContains(t, err, "TTL must be below template maximum 12h0m0s")
|
||||
})
|
||||
|
||||
t.Run("BelowTemplateMinAutostartInterval", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds())
|
||||
})
|
||||
args := []string{
|
||||
"create",
|
||||
"my-workspace",
|
||||
"--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)
|
||||
pty := ptytest.New(t)
|
||||
cmd.SetIn(pty.Input())
|
||||
cmd.SetOut(pty.Output())
|
||||
err := cmd.Execute()
|
||||
assert.ErrorContains(t, err, "minimum autostart interval 1m0s is above template constraint 1h0m0s")
|
||||
})
|
||||
|
||||
t.Run("CreateErrInvalidTz", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
|
||||
@@ -74,19 +126,15 @@ 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, "Invalid autostart schedule: Invalid workspace autostart timezone: unknown time zone invalid")
|
||||
})
|
||||
|
||||
t.Run("CreateErrInvalidTTL", func(t *testing.T) {
|
||||
@@ -101,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")
|
||||
})
|
||||
|
||||
t.Run("CreateFromListWithSkip", func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user