Skip to content

Commit fe7645b

Browse files
authored
feat: add templates delete command (#1443)
1 parent 19335df commit fe7645b

File tree

5 files changed

+185
-0
lines changed

5 files changed

+185
-0
lines changed

cli/templatedelete.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
"golang.org/x/xerrors"
8+
9+
"github.com/coder/coder/cli/cliui"
10+
"github.com/coder/coder/codersdk"
11+
)
12+
13+
func templateDelete() *cobra.Command {
14+
return &cobra.Command{
15+
Use: "delete [name...]",
16+
Short: "Delete templates",
17+
RunE: func(cmd *cobra.Command, args []string) error {
18+
var (
19+
ctx = cmd.Context()
20+
templateNames = []string{}
21+
templates = []codersdk.Template{}
22+
)
23+
24+
client, err := createClient(cmd)
25+
if err != nil {
26+
return err
27+
}
28+
organization, err := currentOrganization(cmd, client)
29+
if err != nil {
30+
return err
31+
}
32+
33+
if len(args) > 0 {
34+
templateNames = args
35+
} else {
36+
allTemplates, err := client.TemplatesByOrganization(ctx, organization.ID)
37+
if err != nil {
38+
return xerrors.Errorf("get templates by organization: %w", err)
39+
}
40+
41+
if len(allTemplates) == 0 {
42+
return xerrors.Errorf("no templates exist in the current organization %q", organization.Name)
43+
}
44+
45+
opts := make([]string, 0, len(allTemplates))
46+
for _, template := range allTemplates {
47+
opts = append(opts, template.Name)
48+
}
49+
50+
selection, err := cliui.Select(cmd, cliui.SelectOptions{
51+
Options: opts,
52+
})
53+
if err != nil {
54+
return xerrors.Errorf("select template: %w", err)
55+
}
56+
57+
for _, template := range allTemplates {
58+
if template.Name == selection {
59+
templates = append(templates, template)
60+
}
61+
}
62+
}
63+
64+
for _, templateName := range templateNames {
65+
template, err := client.TemplateByName(ctx, organization.ID, templateName)
66+
if err != nil {
67+
return xerrors.Errorf("get template by name: %w", err)
68+
}
69+
70+
templates = append(templates, template)
71+
}
72+
73+
for _, template := range templates {
74+
err := client.DeleteTemplate(ctx, template.ID)
75+
if err != nil {
76+
return xerrors.Errorf("delete template %q: %w", template.Name, err)
77+
}
78+
79+
_, _ = fmt.Fprintln(cmd.ErrOrStderr(), "Deleted template "+cliui.Styles.Code.Render(template.Name)+"!")
80+
}
81+
82+
return nil
83+
},
84+
}
85+
}

cli/templatedelete_test.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package cli_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/coder/coder/cli/clitest"
8+
"github.com/coder/coder/coderd/coderdtest"
9+
"github.com/coder/coder/codersdk"
10+
"github.com/coder/coder/pty/ptytest"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestTemplateDelete(t *testing.T) {
15+
t.Parallel()
16+
17+
t.Run("Ok", func(t *testing.T) {
18+
t.Parallel()
19+
20+
client := coderdtest.New(t, nil)
21+
user := coderdtest.CreateFirstUser(t, client)
22+
_ = coderdtest.NewProvisionerDaemon(t, client)
23+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
24+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
25+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
26+
27+
cmd, root := clitest.New(t, "templates", "delete", template.Name)
28+
clitest.SetupConfig(t, client, root)
29+
require.NoError(t, cmd.Execute())
30+
31+
_, err := client.Template(context.Background(), template.ID)
32+
require.Error(t, err, "template should not exist")
33+
})
34+
35+
t.Run("Multiple", func(t *testing.T) {
36+
t.Parallel()
37+
38+
client := coderdtest.New(t, nil)
39+
user := coderdtest.CreateFirstUser(t, client)
40+
_ = coderdtest.NewProvisionerDaemon(t, client)
41+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
42+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
43+
templates := []codersdk.Template{
44+
coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID),
45+
coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID),
46+
coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID),
47+
}
48+
templateNames := []string{}
49+
for _, template := range templates {
50+
templateNames = append(templateNames, template.Name)
51+
}
52+
53+
cmd, root := clitest.New(t, append([]string{"templates", "delete"}, templateNames...)...)
54+
clitest.SetupConfig(t, client, root)
55+
require.NoError(t, cmd.Execute())
56+
57+
for _, template := range templates {
58+
_, err := client.Template(context.Background(), template.ID)
59+
require.Error(t, err, "template should not exist")
60+
}
61+
})
62+
63+
t.Run("Selector", func(t *testing.T) {
64+
t.Parallel()
65+
66+
client := coderdtest.New(t, nil)
67+
user := coderdtest.CreateFirstUser(t, client)
68+
_ = coderdtest.NewProvisionerDaemon(t, client)
69+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
70+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
71+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
72+
73+
cmd, root := clitest.New(t, "templates", "delete")
74+
clitest.SetupConfig(t, client, root)
75+
76+
pty := ptytest.New(t)
77+
cmd.SetIn(pty.Input())
78+
cmd.SetOut(pty.Output())
79+
80+
execDone := make(chan error)
81+
go func() {
82+
execDone <- cmd.Execute()
83+
}()
84+
85+
pty.WriteLine("docker-local")
86+
require.NoError(t, <-execDone)
87+
88+
_, err := client.Template(context.Background(), template.ID)
89+
require.Error(t, err, "template should not exist")
90+
})
91+
}

cli/templates.go

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func templates() *cobra.Command {
3030
templatePlan(),
3131
templateUpdate(),
3232
templateVersions(),
33+
templateDelete(),
3334
)
3435

3536
return cmd

coderd/httpmw/templateparam.go

+7
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ func ExtractTemplateParam(db database.Store) func(http.Handler) http.Handler {
4545
return
4646
}
4747

48+
if template.Deleted {
49+
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
50+
Message: fmt.Sprintf("template %q does not exist", templateID),
51+
})
52+
return
53+
}
54+
4855
ctx := context.WithValue(r.Context(), templateParamContextKey{}, template)
4956
chi.RouteContext(ctx).URLParams.Add("organization", template.OrganizationID.String())
5057
next.ServeHTTP(rw, r.WithContext(ctx))

coderd/templates.go

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
// Returns a single template.
1919
func (api *api) template(rw http.ResponseWriter, r *http.Request) {
2020
template := httpmw.TemplateParam(r)
21+
2122
workspaceCounts, err := api.Database.GetWorkspaceOwnerCountsByTemplateIDs(r.Context(), []uuid.UUID{template.ID})
2223
if errors.Is(err, sql.ErrNoRows) {
2324
err = nil

0 commit comments

Comments
 (0)