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:
Steven Masley
2024-03-20 10:37:57 -05:00
committed by GitHub
parent d82e20152b
commit d789a60d47
51 changed files with 151 additions and 1304 deletions
+2 -10
View File
@@ -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
View File
@@ -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",
-213
View File
@@ -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
View File
@@ -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
-5
View File
@@ -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
View File
@@ -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
View File
@@ -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