From 9259e38ead9376efd8c935317466fa5bdc99a38e Mon Sep 17 00:00:00 2001 From: joobisb Date: Sun, 13 Oct 2024 00:23:04 +0530 Subject: [PATCH 1/2] feat: allow promoting an existing template version to active from CLI --- cli/templateversions.go | 64 ++++++++++++++ cli/templateversions_test.go | 85 +++++++++++++++++++ .../coder_templates_versions_--help.golden | 1 + ...r_templates_versions_promote_--help.golden | 22 +++++ docs/manifest.json | 5 ++ docs/reference/cli/templates_versions.md | 1 + .../cli/templates_versions_promote.md | 46 ++++++++++ 7 files changed, 224 insertions(+) create mode 100644 cli/testdata/coder_templates_versions_promote_--help.golden create mode 100644 docs/reference/cli/templates_versions_promote.md diff --git a/cli/templateversions.go b/cli/templateversions.go index 1f6cb11d4a8dd..f30cf46c7bdaf 100644 --- a/cli/templateversions.go +++ b/cli/templateversions.go @@ -32,6 +32,7 @@ func (r *RootCmd) templateVersions() *serpent.Command { r.templateVersionsList(), r.archiveTemplateVersion(), r.unarchiveTemplateVersion(), + r.templateVersionsPromote(), }, } @@ -169,3 +170,66 @@ func templateVersionsToRows(activeVersionID uuid.UUID, templateVersions ...coder return rows } + +func (r *RootCmd) templateVersionsPromote() *serpent.Command { + var ( + templateName string + templateVersionName string + orgContext = NewOrganizationContext() + ) + client := new(codersdk.Client) + cmd := &serpent.Command{ + Use: "promote", + Short: "Promote a template version to active.", + Long: "Promote an existing template version to be the active version for the specified template.", + Middleware: serpent.Chain( + r.InitClient(client), + ), + Handler: func(inv *serpent.Invocation) error { + organization, err := orgContext.Selected(inv, client) + if err != nil { + return err + } + + template, err := client.TemplateByName(inv.Context(), organization.ID, templateName) + if err != nil { + return xerrors.Errorf("get template by name: %w", err) + } + + version, err := client.TemplateVersionByName(inv.Context(), template.ID, templateVersionName) + if err != nil { + return xerrors.Errorf("get template version by name: %w", err) + } + + err = client.UpdateActiveTemplateVersion(inv.Context(), template.ID, codersdk.UpdateActiveTemplateVersion{ + ID: version.ID, + }) + if err != nil { + return xerrors.Errorf("update active template version: %w", err) + } + + _, _ = fmt.Fprintf(inv.Stdout, "Successfully promoted version %q to active for template %q\n", templateVersionName, templateName) + return nil + }, + } + + cmd.Options = serpent.OptionSet{ + { + Flag: "template", + FlagShorthand: "t", + Env: "CODER_TEMPLATE_NAME", + Description: "Specify the template name.", + Required: true, + Value: serpent.StringOf(&templateName), + }, + { + Flag: "template-version", + Description: "Specify the template version name to promote.", + Env: "CODER_TEMPLATE_VERSION_NAME", + Required: true, + Value: serpent.StringOf(&templateVersionName), + }, + } + orgContext.AttachOptions(cmd) + return cmd +} diff --git a/cli/templateversions_test.go b/cli/templateversions_test.go index 8a017fb15da62..f2e2f8a38f884 100644 --- a/cli/templateversions_test.go +++ b/cli/templateversions_test.go @@ -1,12 +1,15 @@ package cli_test import ( + "context" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/coderd/coderdtest" + "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/pty/ptytest" ) @@ -38,3 +41,85 @@ func TestTemplateVersions(t *testing.T) { pty.ExpectMatch("Active") }) } + +func TestTemplateVersionsPromote(t *testing.T) { + t.Parallel() + + t.Run("PromoteVersion", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + owner := coderdtest.CreateFirstUser(t, client) + + // Create a template with two versions + version1 := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, completeWithAgent()) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID) + + template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version1.ID) + + version2 := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, completeWithAgent(), func(ctvr *codersdk.CreateTemplateVersionRequest) { + ctvr.TemplateID = template.ID + ctvr.Name = "2.0.0" + }) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version2.ID) + + // Ensure version1 is active + updatedTemplate, err := client.Template(context.Background(), template.ID) + assert.NoError(t, err) + assert.Equal(t, version1.ID, updatedTemplate.ActiveVersionID) + + args := []string{ + "templates", + "versions", + "promote", + "--template", template.Name, + "--template-version", version2.Name, + } + + inv, root := clitest.New(t, args...) + //nolint:gocritic // Creating a workspace for another user requires owner permissions. + clitest.SetupConfig(t, client, root) + errC := make(chan error) + go func() { + errC <- inv.Run() + }() + + require.NoError(t, <-errC) + + // Verify that version2 is now the active version + updatedTemplate, err = client.Template(context.Background(), template.ID) + require.NoError(t, err) + assert.Equal(t, version2.ID, updatedTemplate.ActiveVersionID) + }) + + t.Run("PromoteNonExistentVersion", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + owner := coderdtest.CreateFirstUser(t, client) + member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) + + version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil) + _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID) + + inv, root := clitest.New(t, "templates", "versions", "promote", "--template", template.Name, "--template-version", "non-existent-version") + clitest.SetupConfig(t, member, root) + + err := inv.Run() + require.Error(t, err) + require.Contains(t, err.Error(), "get template version by name") + }) + + t.Run("PromoteVersionInvalidTemplate", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + owner := coderdtest.CreateFirstUser(t, client) + member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) + + inv, root := clitest.New(t, "templates", "versions", "promote", "--template", "non-existent-template", "--template-version", "some-version") + clitest.SetupConfig(t, member, root) + + err := inv.Run() + require.Error(t, err) + require.Contains(t, err.Error(), "get template by name") + }) +} diff --git a/cli/testdata/coder_templates_versions_--help.golden b/cli/testdata/coder_templates_versions_--help.golden index 8d10e4a0f8d00..fa276999563d2 100644 --- a/cli/testdata/coder_templates_versions_--help.golden +++ b/cli/testdata/coder_templates_versions_--help.golden @@ -14,6 +14,7 @@ USAGE: SUBCOMMANDS: archive Archive a template version(s). list List all the versions of the specified template + promote Promote a template version to active. unarchive Unarchive a template version(s). ——— diff --git a/cli/testdata/coder_templates_versions_promote_--help.golden b/cli/testdata/coder_templates_versions_promote_--help.golden new file mode 100644 index 0000000000000..d7ef21f51f1e9 --- /dev/null +++ b/cli/testdata/coder_templates_versions_promote_--help.golden @@ -0,0 +1,22 @@ +coder v0.0.0-devel + +USAGE: + coder templates versions promote [flags] + + Promote a template version to active. + + Promote an existing template version to be the active version for the + specified template. + +OPTIONS: + -O, --org string, $CODER_ORGANIZATION + Select which organization (uuid or name) to use. + + -t, --template string, $CODER_TEMPLATE_NAME + Specify the template name. + + --template-version string, $CODER_TEMPLATE_VERSION_NAME + Specify the template version name to promote. + +——— +Run `coder --help` for a list of global options. diff --git a/docs/manifest.json b/docs/manifest.json index a3ebdf9b98987..9dc9e4d37dfa2 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1314,6 +1314,11 @@ "description": "List all the versions of the specified template", "path": "reference/cli/templates_versions_list.md" }, + { + "title": "templates versions promote", + "description": "Promote a template version to active.", + "path": "reference/cli/templates_versions_promote.md" + }, { "title": "templates versions unarchive", "description": "Unarchive a template version(s).", diff --git a/docs/reference/cli/templates_versions.md b/docs/reference/cli/templates_versions.md index 21e49faa61485..5b1c3b2c2cfb8 100644 --- a/docs/reference/cli/templates_versions.md +++ b/docs/reference/cli/templates_versions.md @@ -29,3 +29,4 @@ coder templates versions | [list](./templates_versions_list.md) | List all the versions of the specified template | | [archive](./templates_versions_archive.md) | Archive a template version(s). | | [unarchive](./templates_versions_unarchive.md) | Unarchive a template version(s). | +| [promote](./templates_versions_promote.md) | Promote a template version to active. | diff --git a/docs/reference/cli/templates_versions_promote.md b/docs/reference/cli/templates_versions_promote.md new file mode 100644 index 0000000000000..9566fd6f4d209 --- /dev/null +++ b/docs/reference/cli/templates_versions_promote.md @@ -0,0 +1,46 @@ + + +# templates versions promote + +Promote a template version to active. + +## Usage + +```console +coder templates versions promote [flags] +``` + +## Description + +```console +Promote an existing template version to be the active version for the specified template. +``` + +## Options + +### -t, --template + +| | | +| ----------- | --------------------------------- | +| Type | string | +| Environment | $CODER_TEMPLATE_NAME | + +Specify the template name. + +### --template-version + +| | | +| ----------- | ----------------------------------------- | +| Type | string | +| Environment | $CODER_TEMPLATE_VERSION_NAME | + +Specify the template version name to promote. + +### -O, --org + +| | | +| ----------- | -------------------------------- | +| Type | string | +| Environment | $CODER_ORGANIZATION | + +Select which organization (uuid or name) to use. From 26d185fb4a9526ee7f2402fb5274a08acbf6654b Mon Sep 17 00:00:00 2001 From: joobisb Date: Tue, 15 Oct 2024 16:02:35 +0530 Subject: [PATCH 2/2] make required flags explicit --- cli/templateversions.go | 2 +- cli/testdata/coder_templates_versions_promote_--help.golden | 3 ++- docs/reference/cli/templates_versions_promote.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/templateversions.go b/cli/templateversions.go index f30cf46c7bdaf..c90903a7c4f93 100644 --- a/cli/templateversions.go +++ b/cli/templateversions.go @@ -179,7 +179,7 @@ func (r *RootCmd) templateVersionsPromote() *serpent.Command { ) client := new(codersdk.Client) cmd := &serpent.Command{ - Use: "promote", + Use: "promote --template= --template-version=", Short: "Promote a template version to active.", Long: "Promote an existing template version to be the active version for the specified template.", Middleware: serpent.Chain( diff --git a/cli/testdata/coder_templates_versions_promote_--help.golden b/cli/testdata/coder_templates_versions_promote_--help.golden index d7ef21f51f1e9..afa652aca5a3f 100644 --- a/cli/testdata/coder_templates_versions_promote_--help.golden +++ b/cli/testdata/coder_templates_versions_promote_--help.golden @@ -1,7 +1,8 @@ coder v0.0.0-devel USAGE: - coder templates versions promote [flags] + coder templates versions promote [flags] --template= + --template-version= Promote a template version to active. diff --git a/docs/reference/cli/templates_versions_promote.md b/docs/reference/cli/templates_versions_promote.md index 9566fd6f4d209..30b5f1e8776c6 100644 --- a/docs/reference/cli/templates_versions_promote.md +++ b/docs/reference/cli/templates_versions_promote.md @@ -7,7 +7,7 @@ Promote a template version to active. ## Usage ```console -coder templates versions promote [flags] +coder templates versions promote [flags] --template= --template-version= ``` ## Description