diff --git a/cli/templatecreate.go b/cli/templatecreate.go index d1bc545181f..62ba06dde16 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -21,6 +21,7 @@ func (r *RootCmd) templateCreate() *serpent.Command { provisionerTags []string variablesFile string commandLineVariables []string + agentsAllowed bool disableEveryone bool requireActiveVersion bool @@ -159,6 +160,7 @@ func (r *RootCmd) templateCreate() *serpent.Command { TimeTilDormantAutoDeleteMillis: ptr.Ref(dormancyAutoDeletion.Milliseconds()), DisableEveryoneGroupAccess: disableEveryone, RequireActiveVersion: requireActiveVersion, + AgentsAllowed: &agentsAllowed, } template, err := client.CreateTemplate(inv.Context(), organization.ID, createReq) @@ -179,6 +181,12 @@ func (r *RootCmd) templateCreate() *serpent.Command { }, } cmd.Options = serpent.OptionSet{ + { + Flag: "agents-allowed", + Description: "Allow Coder Agents to create workspaces using this template.", + Default: "true", + Value: serpent.BoolOf(&agentsAllowed), + }, { Flag: "private", Description: "Disable the default behavior of granting template access to the 'everyone' group. " + diff --git a/cli/templatecreate_test.go b/cli/templatecreate_test.go index cb744800430..36c9eff055f 100644 --- a/cli/templatecreate_test.go +++ b/cli/templatecreate_test.go @@ -12,6 +12,7 @@ import ( "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/provisioner/echo" "github.com/coder/coder/v2/provisionersdk/proto" "github.com/coder/coder/v2/testutil" @@ -58,6 +59,56 @@ func TestCliTemplateCreate(t *testing.T) { } } }) + t.Run("AgentsAllowed", 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()) + + for _, tt := range []struct { + name string + flag string + agentsAllowed bool + }{ + { + name: "DefaultTrue", + agentsAllowed: true, + }, + { + name: "False", + flag: "--agents-allowed=false", + agentsAllowed: false, + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + templateName := coderdtest.RandomUsername(t) + source := clitest.CreateTemplateVersionSource(t, completeWithAgent()) + args := []string{ + "templates", + "create", + templateName, + "--yes", + "--directory", source, + "--test.provisioner", string(database.ProvisionerTypeEcho), + } + if tt.flag != "" { + args = append(args, tt.flag) + } + inv, root := clitest.New(t, args...) + clitest.SetupConfig(t, templateAdmin, root) + + require.NoError(t, inv.Run()) + + template, err := client.TemplateByName(t.Context(), owner.OrganizationID, templateName) + require.NoError(t, err) + require.Equal(t, tt.agentsAllowed, template.AgentsAllowed) + }) + } + }) + t.Run("CreateNoLockfile", func(t *testing.T) { t.Parallel() logger := testutil.Logger(t) diff --git a/cli/templateedit.go b/cli/templateedit.go index e25da3462c3..751bb633040 100644 --- a/cli/templateedit.go +++ b/cli/templateedit.go @@ -33,6 +33,7 @@ func (r *RootCmd) templateEdit() *serpent.Command { allowUserCancelWorkspaceJobs bool allowUserAutostart bool allowUserAutostop bool + agentsAllowed bool requireActiveVersion bool deprecationMessage string disableEveryone bool @@ -142,6 +143,10 @@ func (r *RootCmd) templateEdit() *serpent.Command { dormancyAutoDeletion = time.Duration(template.TimeTilDormantAutoDeleteMillis) * time.Millisecond } + if !userSetOption(inv, "agents-allowed") { + agentsAllowed = template.AgentsAllowed + } + if !userSetOption(inv, "require-active-version") { requireActiveVersion = template.RequireActiveVersion } @@ -199,6 +204,7 @@ func (r *RootCmd) templateEdit() *serpent.Command { AllowUserCancelWorkspaceJobs: &allowUserCancelWorkspaceJobs, AllowUserAutostart: &allowUserAutostart, AllowUserAutostop: &allowUserAutostop, + AgentsAllowed: &agentsAllowed, RequireActiveVersion: &requireActiveVersion, DeprecationMessage: deprecated, DisableEveryoneGroupAccess: &disableEveryoneGroup, @@ -292,6 +298,12 @@ func (r *RootCmd) templateEdit() *serpent.Command { Default: "0h", Value: serpent.DurationOf(&dormancyAutoDeletion), }, + { + Flag: "agents-allowed", + Description: "Allow Coder Agents to create workspaces using this template.", + Default: "true", + Value: serpent.BoolOf(&agentsAllowed), + }, { Flag: "allow-user-cancel-workspace-jobs", Description: "Allow users to cancel in-progress workspace jobs.", diff --git a/cli/templateedit_test.go b/cli/templateedit_test.go index d6c8af82b0f..3acc6513daf 100644 --- a/cli/templateedit_test.go +++ b/cli/templateedit_test.go @@ -115,6 +115,76 @@ func TestTemplateEdit(t *testing.T) { assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) assert.Equal(t, template.AllowUserCancelWorkspaceJobs, updated.AllowUserCancelWorkspaceJobs) }) + t.Run("AgentsAllowed", 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()) + + for _, tt := range []struct { + name string + initialAgentsAllowed bool + flag string + description string + wantAgentsAllowed bool + }{ + { + name: "ExplicitTrue", + initialAgentsAllowed: false, + flag: "--agents-allowed=true", + wantAgentsAllowed: true, + }, + { + name: "ExplicitFalse", + initialAgentsAllowed: true, + flag: "--agents-allowed=false", + wantAgentsAllowed: false, + }, + { + name: "OmittedPreservesTrue", + initialAgentsAllowed: true, + description: "updated description", + wantAgentsAllowed: true, + }, + { + name: "OmittedPreservesFalse", + initialAgentsAllowed: false, + description: "updated description", + wantAgentsAllowed: false, + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil) + _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID, func(req *codersdk.CreateTemplateRequest) { + req.AgentsAllowed = &tt.initialAgentsAllowed + }) + + cmdArgs := []string{"templates", "edit", template.Name} + if tt.flag != "" { + cmdArgs = append(cmdArgs, tt.flag) + } + if tt.description != "" { + cmdArgs = append(cmdArgs, "--description", tt.description) + } + inv, root := clitest.New(t, cmdArgs...) + clitest.SetupConfig(t, templateAdmin, root) + + require.NoError(t, inv.Run()) + + updated, err := client.Template(t.Context(), template.ID) + require.NoError(t, err) + require.Equal(t, tt.wantAgentsAllowed, updated.AgentsAllowed) + if tt.description != "" { + require.Equal(t, tt.description, updated.Description) + } + }) + } + }) + t.Run("InvalidDisplayName", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) diff --git a/cli/testdata/coder_templates_create_--help.golden b/cli/testdata/coder_templates_create_--help.golden index c0370d93d21..7ef71d7d1cf 100644 --- a/cli/testdata/coder_templates_create_--help.golden +++ b/cli/testdata/coder_templates_create_--help.golden @@ -10,6 +10,9 @@ OPTIONS: -O, --org string, $CODER_ORGANIZATION Select which organization (uuid or name) to use. + --agents-allowed bool (default: true) + Allow Coder Agents to create workspaces using this template. + --default-ttl duration (default: 24h) Specify a default TTL for workspaces created from this template. It is the default time before shutdown - workspaces created from this diff --git a/cli/testdata/coder_templates_edit_--help.golden b/cli/testdata/coder_templates_edit_--help.golden index 73760dadfcb..972437a8e8c 100644 --- a/cli/testdata/coder_templates_edit_--help.golden +++ b/cli/testdata/coder_templates_edit_--help.golden @@ -14,6 +14,9 @@ OPTIONS: template will have their shutdown time bumped by this value when activity is detected. Maps to "Activity bump" in the UI. + --agents-allowed bool (default: true) + Allow Coder Agents to create workspaces using this template. + --allow-user-autostart bool (default: true) Allow users to configure autostart for workspaces on this template. This can only be disabled in enterprise. diff --git a/docs/reference/cli/templates_create.md b/docs/reference/cli/templates_create.md index 3f46f3e759f..a3bba84d924 100644 --- a/docs/reference/cli/templates_create.md +++ b/docs/reference/cli/templates_create.md @@ -11,6 +11,15 @@ coder templates create [flags] [name] ## Options +### --agents-allowed + +| | | +|---------|-------------------| +| Type | bool | +| Default | true | + +Allow Coder Agents to create workspaces using this template. + ### --private | | | diff --git a/docs/reference/cli/templates_edit.md b/docs/reference/cli/templates_edit.md index 5e9fe477050..2e472d1600e 100644 --- a/docs/reference/cli/templates_edit.md +++ b/docs/reference/cli/templates_edit.md @@ -126,6 +126,15 @@ Specify a duration workspaces may be inactive prior to being moved to the dorman Specify a duration workspaces may be in the dormant state prior to being deleted. This licensed feature's default is 0h (off). Maps to "Dormancy Auto-Deletion" in the UI. +### --agents-allowed + +| | | +|---------|-------------------| +| Type | bool | +| Default | true | + +Allow Coder Agents to create workspaces using this template. + ### --allow-user-cancel-workspace-jobs | | |