Skip to content

chore!: remove max_ttl from templates #12644

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

Merged
merged 10 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions cli/templatecreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ func (r *RootCmd) templateCreate() *serpent.Command {
failureTTL time.Duration
dormancyThreshold time.Duration
dormancyAutoDeletion time.Duration
maxTTL time.Duration

uploadFlags templateUploadFlags
)
Expand All @@ -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())
Expand All @@ -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")
}
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.",
Expand Down
20 changes: 2 additions & 18 deletions cli/templateedit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -53,7 +52,6 @@ func (r *RootCmd) templateEdit() *serpent.Command {
autostopRequirementWeeks > 0 ||
!allowUserAutostart ||
!allowUserAutostop ||
maxTTL != 0 ||
failureTTL != 0 ||
dormancyThreshold != 0 ||
dormancyAutoDeletion != 0 ||
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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'.
Expand All @@ -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" {
Expand All @@ -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",
Expand Down
213 changes: 0 additions & 213 deletions cli/templateedit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion cli/testdata/coder_server_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 0 additions & 5 deletions cli/testdata/coder_templates_create_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 10 additions & 6 deletions cli/testdata/coder_templates_edit_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.

Expand Down
Loading