mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: remove max_ttl from templates (#12644)
* chore: remove max_ttl from templates Completely removing max_ttl as a feature on template scheduling. Must use other template scheduling features to achieve autostop.
This commit is contained in:
+2
-10
@@ -29,7 +29,6 @@ func (r *RootCmd) templateCreate() *serpent.Command {
|
||||
failureTTL time.Duration
|
||||
dormancyThreshold time.Duration
|
||||
dormancyAutoDeletion time.Duration
|
||||
maxTTL time.Duration
|
||||
|
||||
uploadFlags templateUploadFlags
|
||||
)
|
||||
@@ -46,7 +45,7 @@ func (r *RootCmd) templateCreate() *serpent.Command {
|
||||
r.InitClient(client),
|
||||
),
|
||||
Handler: func(inv *serpent.Invocation) error {
|
||||
isTemplateSchedulingOptionsSet := failureTTL != 0 || dormancyThreshold != 0 || dormancyAutoDeletion != 0 || maxTTL != 0
|
||||
isTemplateSchedulingOptionsSet := failureTTL != 0 || dormancyThreshold != 0 || dormancyAutoDeletion != 0
|
||||
|
||||
if isTemplateSchedulingOptionsSet || requireActiveVersion {
|
||||
entitlements, err := client.Entitlements(inv.Context())
|
||||
@@ -58,7 +57,7 @@ func (r *RootCmd) templateCreate() *serpent.Command {
|
||||
|
||||
if isTemplateSchedulingOptionsSet {
|
||||
if !entitlements.Features[codersdk.FeatureAdvancedTemplateScheduling].Enabled {
|
||||
return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --failure-ttl, --inactivity-ttl, or --max-ttl")
|
||||
return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --failure-ttl, or --inactivity-ttl")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +153,6 @@ func (r *RootCmd) templateCreate() *serpent.Command {
|
||||
VersionID: job.ID,
|
||||
DefaultTTLMillis: ptr.Ref(defaultTTL.Milliseconds()),
|
||||
FailureTTLMillis: ptr.Ref(failureTTL.Milliseconds()),
|
||||
MaxTTLMillis: ptr.Ref(maxTTL.Milliseconds()),
|
||||
TimeTilDormantMillis: ptr.Ref(dormancyThreshold.Milliseconds()),
|
||||
TimeTilDormantAutoDeleteMillis: ptr.Ref(dormancyAutoDeletion.Milliseconds()),
|
||||
DisableEveryoneGroupAccess: disableEveryone,
|
||||
@@ -229,12 +227,6 @@ func (r *RootCmd) templateCreate() *serpent.Command {
|
||||
Default: "0h",
|
||||
Value: serpent.DurationOf(&dormancyAutoDeletion),
|
||||
},
|
||||
|
||||
{
|
||||
Flag: "max-ttl",
|
||||
Description: "Edit the template maximum time before shutdown - workspaces created from this template must shutdown within the given duration after starting. This is an enterprise-only feature.",
|
||||
Value: serpent.DurationOf(&maxTTL),
|
||||
},
|
||||
{
|
||||
Flag: "test.provisioner",
|
||||
Description: "Customize the provisioner backend.",
|
||||
|
||||
+2
-18
@@ -24,7 +24,6 @@ func (r *RootCmd) templateEdit() *serpent.Command {
|
||||
icon string
|
||||
defaultTTL time.Duration
|
||||
activityBump time.Duration
|
||||
maxTTL time.Duration
|
||||
autostopRequirementDaysOfWeek []string
|
||||
autostopRequirementWeeks int64
|
||||
autostartRequirementDaysOfWeek []string
|
||||
@@ -53,7 +52,6 @@ func (r *RootCmd) templateEdit() *serpent.Command {
|
||||
autostopRequirementWeeks > 0 ||
|
||||
!allowUserAutostart ||
|
||||
!allowUserAutostop ||
|
||||
maxTTL != 0 ||
|
||||
failureTTL != 0 ||
|
||||
dormancyThreshold != 0 ||
|
||||
dormancyAutoDeletion != 0 ||
|
||||
@@ -69,7 +67,7 @@ func (r *RootCmd) templateEdit() *serpent.Command {
|
||||
}
|
||||
|
||||
if requiresScheduling && !entitlements.Features[codersdk.FeatureAdvancedTemplateScheduling].Enabled {
|
||||
return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --max-ttl, --failure-ttl, --inactivityTTL, --allow-user-autostart=false or --allow-user-autostop=false")
|
||||
return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --failure-ttl, --inactivityTTL, --allow-user-autostart=false or --allow-user-autostop=false")
|
||||
}
|
||||
|
||||
if requireActiveVersion {
|
||||
@@ -101,10 +99,6 @@ func (r *RootCmd) templateEdit() *serpent.Command {
|
||||
displayName = template.DisplayName
|
||||
}
|
||||
|
||||
if !userSetOption(inv, "max-ttl") {
|
||||
maxTTL = time.Duration(template.MaxTTLMillis) * time.Millisecond
|
||||
}
|
||||
|
||||
if !userSetOption(inv, "default-ttl") {
|
||||
defaultTTL = time.Duration(template.DefaultTTLMillis) * time.Millisecond
|
||||
}
|
||||
@@ -179,7 +173,6 @@ func (r *RootCmd) templateEdit() *serpent.Command {
|
||||
Icon: icon,
|
||||
DefaultTTLMillis: defaultTTL.Milliseconds(),
|
||||
ActivityBumpMillis: activityBump.Milliseconds(),
|
||||
MaxTTLMillis: maxTTL.Milliseconds(),
|
||||
AutostopRequirement: &codersdk.TemplateAutostopRequirement{
|
||||
DaysOfWeek: autostopRequirementDaysOfWeek,
|
||||
Weeks: autostopRequirementWeeks,
|
||||
@@ -244,11 +237,6 @@ 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: "max-ttl",
|
||||
Description: "Edit the template maximum time before shutdown - workspaces created from this template must shutdown within the given duration after starting, regardless of user activity. This is an enterprise-only feature. Maps to \"Max lifetime\" in the UI.",
|
||||
Value: serpent.DurationOf(&maxTTL),
|
||||
},
|
||||
{
|
||||
Flag: "autostart-requirement-weekdays",
|
||||
// workspaces created from this template must be restarted on the given weekdays. To unset this value for the template (and disable the autostop requirement for the template), pass 'none'.
|
||||
@@ -268,8 +256,6 @@ func (r *RootCmd) templateEdit() *serpent.Command {
|
||||
{
|
||||
Flag: "autostop-requirement-weekdays",
|
||||
Description: "Edit the template autostop requirement weekdays - workspaces created from this template must be restarted on the given weekdays. To unset this value for the template (and disable the autostop requirement for the template), pass 'none'.",
|
||||
// TODO(@dean): unhide when we delete max_ttl
|
||||
Hidden: true,
|
||||
Value: serpent.Validate(serpent.StringArrayOf(&autostopRequirementDaysOfWeek), func(value *serpent.StringArray) error {
|
||||
v := value.GetSlice()
|
||||
if len(v) == 1 && v[0] == "none" {
|
||||
@@ -285,9 +271,7 @@ func (r *RootCmd) templateEdit() *serpent.Command {
|
||||
{
|
||||
Flag: "autostop-requirement-weeks",
|
||||
Description: "Edit the template autostop requirement weeks - workspaces created from this template must be restarted on an n-weekly basis.",
|
||||
// TODO(@dean): unhide when we delete max_ttl
|
||||
Hidden: true,
|
||||
Value: serpent.Int64Of(&autostopRequirementWeeks),
|
||||
Value: serpent.Int64Of(&autostopRequirementWeeks),
|
||||
},
|
||||
{
|
||||
Flag: "failure-ttl",
|
||||
|
||||
@@ -552,220 +552,7 @@ func TestTemplateEdit(t *testing.T) {
|
||||
assert.Equal(t, template.AutostartRequirement.DaysOfWeek, updated.AutostartRequirement.DaysOfWeek)
|
||||
})
|
||||
})
|
||||
// TODO(@dean): remove this test when we remove max_ttl
|
||||
t.Run("MaxTTL", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("BlockedAGPL", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
owner := coderdtest.CreateFirstUser(t, client)
|
||||
templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin())
|
||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.DefaultTTLMillis = nil
|
||||
ctr.MaxTTLMillis = nil
|
||||
})
|
||||
|
||||
// Test the cli command.
|
||||
cmdArgs := []string{
|
||||
"templates",
|
||||
"edit",
|
||||
template.Name,
|
||||
"--max-ttl", "1h",
|
||||
}
|
||||
inv, root := clitest.New(t, cmdArgs...)
|
||||
clitest.SetupConfig(t, templateAdmin, root)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
err := inv.WithContext(ctx).Run()
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "appears to be an AGPL deployment")
|
||||
|
||||
// Assert that the template metadata did not change.
|
||||
updated, err := client.Template(context.Background(), template.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, template.Name, updated.Name)
|
||||
assert.Equal(t, template.Description, updated.Description)
|
||||
assert.Equal(t, template.Icon, updated.Icon)
|
||||
assert.Equal(t, template.DisplayName, updated.DisplayName)
|
||||
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
|
||||
assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis)
|
||||
})
|
||||
|
||||
t.Run("BlockedNotEntitled", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
owner := coderdtest.CreateFirstUser(t, client)
|
||||
templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin())
|
||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.DefaultTTLMillis = nil
|
||||
ctr.MaxTTLMillis = nil
|
||||
})
|
||||
|
||||
// Make a proxy server that will return a valid entitlements
|
||||
// response, but without advanced scheduling entitlement.
|
||||
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/api/v2/entitlements" {
|
||||
res := codersdk.Entitlements{
|
||||
Features: map[codersdk.FeatureName]codersdk.Feature{},
|
||||
Warnings: []string{},
|
||||
Errors: []string{},
|
||||
HasLicense: true,
|
||||
Trial: true,
|
||||
RequireTelemetry: false,
|
||||
}
|
||||
for _, feature := range codersdk.FeatureNames {
|
||||
res.Features[feature] = codersdk.Feature{
|
||||
Entitlement: codersdk.EntitlementNotEntitled,
|
||||
Enabled: false,
|
||||
Limit: nil,
|
||||
Actual: nil,
|
||||
}
|
||||
}
|
||||
httpapi.Write(r.Context(), w, http.StatusOK, res)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, proxy the request to the real API server.
|
||||
rp := httputil.NewSingleHostReverseProxy(client.URL)
|
||||
tp := &http.Transport{}
|
||||
defer tp.CloseIdleConnections()
|
||||
rp.Transport = tp
|
||||
rp.ServeHTTP(w, r)
|
||||
}))
|
||||
defer proxy.Close()
|
||||
|
||||
// Create a new client that uses the proxy server.
|
||||
proxyURL, err := url.Parse(proxy.URL)
|
||||
require.NoError(t, err)
|
||||
proxyClient := codersdk.New(proxyURL)
|
||||
proxyClient.SetSessionToken(templateAdmin.SessionToken())
|
||||
t.Cleanup(proxyClient.HTTPClient.CloseIdleConnections)
|
||||
|
||||
// Test the cli command.
|
||||
cmdArgs := []string{
|
||||
"templates",
|
||||
"edit",
|
||||
template.Name,
|
||||
"--max-ttl", "1h",
|
||||
}
|
||||
inv, root := clitest.New(t, cmdArgs...)
|
||||
clitest.SetupConfig(t, proxyClient, root)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
err = inv.WithContext(ctx).Run()
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "license is not entitled")
|
||||
|
||||
// Assert that the template metadata did not change.
|
||||
updated, err := client.Template(context.Background(), template.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, template.Name, updated.Name)
|
||||
assert.Equal(t, template.Description, updated.Description)
|
||||
assert.Equal(t, template.Icon, updated.Icon)
|
||||
assert.Equal(t, template.DisplayName, updated.DisplayName)
|
||||
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
|
||||
assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis)
|
||||
})
|
||||
t.Run("Entitled", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
owner := coderdtest.CreateFirstUser(t, client)
|
||||
templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin())
|
||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.DefaultTTLMillis = nil
|
||||
ctr.MaxTTLMillis = nil
|
||||
})
|
||||
|
||||
// Make a proxy server that will return a valid entitlements
|
||||
// response, including a valid advanced scheduling entitlement.
|
||||
var updateTemplateCalled int64
|
||||
proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/api/v2/entitlements" {
|
||||
res := codersdk.Entitlements{
|
||||
Features: map[codersdk.FeatureName]codersdk.Feature{},
|
||||
Warnings: []string{},
|
||||
Errors: []string{},
|
||||
HasLicense: true,
|
||||
Trial: true,
|
||||
RequireTelemetry: false,
|
||||
}
|
||||
for _, feature := range codersdk.FeatureNames {
|
||||
var one int64 = 1
|
||||
res.Features[feature] = codersdk.Feature{
|
||||
Entitlement: codersdk.EntitlementNotEntitled,
|
||||
Enabled: true,
|
||||
Limit: &one,
|
||||
Actual: &one,
|
||||
}
|
||||
}
|
||||
httpapi.Write(r.Context(), w, http.StatusOK, res)
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(r.URL.Path, "/api/v2/templates/") {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
_ = r.Body.Close()
|
||||
|
||||
var req codersdk.UpdateTemplateMeta
|
||||
err = json.Unmarshal(body, &req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, time.Hour.Milliseconds(), req.MaxTTLMillis)
|
||||
|
||||
r.Body = io.NopCloser(bytes.NewReader(body))
|
||||
atomic.AddInt64(&updateTemplateCalled, 1)
|
||||
// We still want to call the real route.
|
||||
}
|
||||
|
||||
// Otherwise, proxy the request to the real API server.
|
||||
rp := httputil.NewSingleHostReverseProxy(client.URL)
|
||||
tp := &http.Transport{}
|
||||
defer tp.CloseIdleConnections()
|
||||
rp.Transport = tp
|
||||
rp.ServeHTTP(w, r)
|
||||
}))
|
||||
defer proxy.Close()
|
||||
|
||||
// Create a new client that uses the proxy server.
|
||||
proxyURL, err := url.Parse(proxy.URL)
|
||||
require.NoError(t, err)
|
||||
proxyClient := codersdk.New(proxyURL)
|
||||
proxyClient.SetSessionToken(templateAdmin.SessionToken())
|
||||
t.Cleanup(proxyClient.HTTPClient.CloseIdleConnections)
|
||||
|
||||
// Test the cli command.
|
||||
cmdArgs := []string{
|
||||
"templates",
|
||||
"edit",
|
||||
template.Name,
|
||||
"--max-ttl", "1h",
|
||||
}
|
||||
inv, root := clitest.New(t, cmdArgs...)
|
||||
clitest.SetupConfig(t, proxyClient, root)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
err = inv.WithContext(ctx).Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, 1, atomic.LoadInt64(&updateTemplateCalled))
|
||||
|
||||
// Assert that the template metadata did not change. We verify the
|
||||
// correct request gets sent to the server already.
|
||||
updated, err := client.Template(context.Background(), template.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, template.Name, updated.Name)
|
||||
assert.Equal(t, template.Description, updated.Description)
|
||||
assert.Equal(t, template.Icon, updated.Icon)
|
||||
assert.Equal(t, template.DisplayName, updated.DisplayName)
|
||||
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
|
||||
assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis)
|
||||
})
|
||||
})
|
||||
t.Run("AllowUserScheduling", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("BlockedAGPL", func(t *testing.T) {
|
||||
|
||||
+1
-1
@@ -467,7 +467,7 @@ telemetrywhen required by your organization's security policy.
|
||||
|
||||
USER QUIET HOURS SCHEDULE OPTIONS:
|
||||
Allow users to set quiet hours schedules each day for workspaces to avoid
|
||||
workspaces stopping during the day due to template max TTL.
|
||||
workspaces stopping during the day due to template scheduling.
|
||||
|
||||
--allow-custom-quiet-hours bool, $CODER_ALLOW_CUSTOM_QUIET_HOURS (default: true)
|
||||
Allow users to set their own quiet hours schedule for workspaces to
|
||||
|
||||
@@ -35,11 +35,6 @@ OPTIONS:
|
||||
Ignore warnings about not having a .terraform.lock.hcl file present in
|
||||
the template.
|
||||
|
||||
--max-ttl duration
|
||||
Edit the template maximum time before shutdown - workspaces created
|
||||
from this template must shutdown within the given duration after
|
||||
starting. This is an enterprise-only feature.
|
||||
|
||||
-m, --message string
|
||||
Specify a message describing the changes in this version of the
|
||||
template. Messages longer than 72 characters will be displayed as
|
||||
|
||||
+10
-6
@@ -28,6 +28,16 @@ OPTIONS:
|
||||
this value for the template (and allow autostart on all days), pass
|
||||
'all'.
|
||||
|
||||
--autostop-requirement-weekdays string-array
|
||||
Edit the template autostop requirement weekdays - workspaces created
|
||||
from this template must be restarted on the given weekdays. To unset
|
||||
this value for the template (and disable the autostop requirement for
|
||||
the template), pass 'none'.
|
||||
|
||||
--autostop-requirement-weeks int
|
||||
Edit the template autostop requirement weeks - workspaces created from
|
||||
this template must be restarted on an n-weekly basis.
|
||||
|
||||
--default-ttl duration
|
||||
Edit the template default time before shutdown - workspaces created
|
||||
from this template default to this value. Maps to "Default autostop"
|
||||
@@ -62,12 +72,6 @@ OPTIONS:
|
||||
--icon string
|
||||
Edit the template icon path.
|
||||
|
||||
--max-ttl duration
|
||||
Edit the template maximum time before shutdown - workspaces created
|
||||
from this template must shutdown within the given duration after
|
||||
starting, regardless of user activity. This is an enterprise-only
|
||||
feature. Maps to "Max lifetime" in the UI.
|
||||
|
||||
--name string
|
||||
Edit the template name.
|
||||
|
||||
|
||||
+1
-1
@@ -462,7 +462,7 @@ externalAuthProviders: []
|
||||
# (default: <unset>, type: string)
|
||||
wgtunnelHost: ""
|
||||
# Allow users to set quiet hours schedules each day for workspaces to avoid
|
||||
# workspaces stopping during the day due to template max TTL.
|
||||
# workspaces stopping during the day due to template scheduling.
|
||||
userQuietHoursSchedule:
|
||||
# The default daily cron schedule applied to users that haven't set a custom quiet
|
||||
# hours schedule themselves. The quiet hours schedule determines when workspaces
|
||||
|
||||
Reference in New Issue
Block a user