Skip to content

Commit 7f3d805

Browse files
committed
add template version prune command
1 parent 2e2c092 commit 7f3d805

13 files changed

+458
-87
lines changed

cli/templatedelete.go

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -48,33 +48,13 @@ func (r *RootCmd) templateDelete() *clibase.Cmd {
4848
templates = append(templates, template)
4949
}
5050
} else {
51-
allTemplates, err := client.TemplatesByOrganization(ctx, organization.ID)
51+
template, err := selectTemplate(inv, client, organization)
5252
if err != nil {
53-
return xerrors.Errorf("get templates by organization: %w", err)
53+
return err
5454
}
5555

56-
if len(allTemplates) == 0 {
57-
return xerrors.Errorf("no templates exist in the current organization %q", organization.Name)
58-
}
59-
60-
opts := make([]string, 0, len(allTemplates))
61-
for _, template := range allTemplates {
62-
opts = append(opts, template.Name)
63-
}
64-
65-
selection, err := cliui.Select(inv, cliui.SelectOptions{
66-
Options: opts,
67-
})
68-
if err != nil {
69-
return xerrors.Errorf("select template: %w", err)
70-
}
71-
72-
for _, template := range allTemplates {
73-
if template.Name == selection {
74-
templates = append(templates, template)
75-
templateNames = append(templateNames, template.Name)
76-
}
77-
}
56+
templates = append(templates, template)
57+
templateNames = append(templateNames, template.Name)
7858
}
7959

8060
// Confirm deletion of the template.

cli/templateprune.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package cli
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"strings"
7+
"time"
8+
9+
"golang.org/x/xerrors"
10+
11+
"github.com/coder/pretty"
12+
13+
"github.com/coder/coder/v2/cli/clibase"
14+
"github.com/coder/coder/v2/cli/cliui"
15+
"github.com/coder/coder/v2/codersdk"
16+
)
17+
18+
func (r *RootCmd) templatePrune() *clibase.Cmd {
19+
var (
20+
all clibase.Bool
21+
)
22+
client := new(codersdk.Client)
23+
cmd := &clibase.Cmd{
24+
Use: "prune [name...]",
25+
Short: "Prune unused failed template versions from a given template(s)",
26+
Middleware: clibase.Chain(
27+
r.InitClient(client),
28+
),
29+
Options: clibase.OptionSet{
30+
cliui.SkipPromptOption(),
31+
clibase.Option{
32+
Name: "all",
33+
Description: "Include all unused template versions. By default, only failed template versions are pruned.",
34+
Flag: "all",
35+
Value: &all,
36+
},
37+
},
38+
Handler: func(inv *clibase.Invocation) error {
39+
var (
40+
ctx = inv.Context()
41+
templateNames = []string{}
42+
templates = []codersdk.Template{}
43+
)
44+
45+
organization, err := CurrentOrganization(inv, client)
46+
if err != nil {
47+
return err
48+
}
49+
50+
if len(inv.Args) > 0 {
51+
templateNames = inv.Args
52+
53+
for _, templateName := range templateNames {
54+
template, err := client.TemplateByName(ctx, organization.ID, templateName)
55+
if err != nil {
56+
return xerrors.Errorf("get template by name: %w", err)
57+
}
58+
templates = append(templates, template)
59+
}
60+
} else {
61+
template, err := selectTemplate(inv, client, organization)
62+
if err != nil {
63+
return err
64+
}
65+
66+
templates = append(templates, template)
67+
templateNames = append(templateNames, template.Name)
68+
}
69+
70+
// Confirm prune of the template.
71+
_, err = cliui.Prompt(inv, cliui.PromptOptions{
72+
Text: fmt.Sprintf("Prune template versions of these templates: %s?", pretty.Sprint(cliui.DefaultStyles.Code, strings.Join(templateNames, ", "))),
73+
IsConfirm: true,
74+
Default: cliui.ConfirmNo,
75+
})
76+
if err != nil {
77+
return err
78+
}
79+
80+
for _, template := range templates {
81+
resp, err := client.PruneTemplateVersions(ctx, template.ID, all.Value())
82+
if err != nil {
83+
return xerrors.Errorf("delete template %q: %w", template.Name, err)
84+
}
85+
86+
_, _ = fmt.Fprintln(
87+
inv.Stdout, fmt.Sprintf("Deleted %s versions from "+pretty.Sprint(cliui.DefaultStyles.Keyword, template.Name)+" at "+cliui.Timestamp(time.Now()), len(resp.DeletedIDs)),
88+
)
89+
90+
if ok, _ := inv.ParsedFlags().GetBool("verbose"); err == nil && ok {
91+
data, err := json.Marshal(resp)
92+
if err != nil {
93+
return xerrors.Errorf("marshal verbose response: %w", err)
94+
}
95+
_, _ = fmt.Fprintln(
96+
inv.Stdout, string(data),
97+
)
98+
}
99+
}
100+
101+
return nil
102+
},
103+
}
104+
105+
return cmd
106+
}

cli/templates.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ package cli
33
import (
44
"time"
55

6-
"github.com/google/uuid"
7-
86
"github.com/coder/pretty"
7+
"github.com/google/uuid"
8+
"golang.org/x/xerrors"
99

1010
"github.com/coder/coder/v2/cli/clibase"
1111
"github.com/coder/coder/v2/cli/cliui"
@@ -43,12 +43,45 @@ func (r *RootCmd) templates() *clibase.Cmd {
4343
r.templateVersions(),
4444
r.templateDelete(),
4545
r.templatePull(),
46+
r.templatePrune(),
4647
},
4748
}
4849

4950
return cmd
5051
}
5152

53+
func selectTemplate(inv *clibase.Invocation, client *codersdk.Client, organization codersdk.Organization) (codersdk.Template, error) {
54+
var empty codersdk.Template
55+
ctx := inv.Context()
56+
allTemplates, err := client.TemplatesByOrganization(ctx, organization.ID)
57+
if err != nil {
58+
return empty, xerrors.Errorf("get templates by organization: %w", err)
59+
}
60+
61+
if len(allTemplates) == 0 {
62+
return empty, xerrors.Errorf("no templates exist in the current organization %q", organization.Name)
63+
}
64+
65+
opts := make([]string, 0, len(allTemplates))
66+
for _, template := range allTemplates {
67+
opts = append(opts, template.Name)
68+
}
69+
70+
selection, err := cliui.Select(inv, cliui.SelectOptions{
71+
Options: opts,
72+
})
73+
if err != nil {
74+
return empty, xerrors.Errorf("select template: %w", err)
75+
}
76+
77+
for _, template := range allTemplates {
78+
if template.Name == selection {
79+
return template, nil
80+
}
81+
}
82+
return empty, xerrors.Errorf("no template selected")
83+
}
84+
5285
type templateTableRow struct {
5386
// Used by json format:
5487
Template codersdk.Template

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ func New(options *Options) *API {
671671
r.Delete("/", api.deleteTemplate)
672672
r.Patch("/", api.patchTemplateMeta)
673673
r.Route("/versions", func(r chi.Router) {
674+
r.Delete("/prune", api.deletePruneTemplateVersions)
674675
r.Get("/", api.templateVersionsByTemplate)
675676
r.Patch("/", api.patchActiveTemplateVersion)
676677
r.Get("/{templateversionname}", api.templateVersionByName)

coderd/database/dbfake/dbfake.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2758,6 +2758,9 @@ func (q *FakeQuerier) GetTemplateVersionsByTemplateID(_ context.Context, arg dat
27582758
if templateVersion.TemplateID.UUID != arg.TemplateID {
27592759
continue
27602760
}
2761+
if arg.Deleted.Valid && arg.Deleted.Bool != templateVersion.Deleted {
2762+
continue
2763+
}
27612764
version = append(version, q.templateVersionWithUserNoLock(templateVersion))
27622765
}
27632766

@@ -5234,7 +5237,26 @@ func (q *FakeQuerier) PruneUnusedTemplateVersions(_ context.Context, arg databas
52345237
if v.Deleted {
52355238
continue
52365239
}
5240+
52375241
if _, ok := usedVersions[v.ID]; !ok {
5242+
var job *database.ProvisionerJob
5243+
for _, j := range q.provisionerJobs {
5244+
if v.JobID == j.ID {
5245+
job = &j
5246+
break
5247+
}
5248+
}
5249+
5250+
if arg.JobStatus.Valid {
5251+
if job.JobStatus != arg.JobStatus.ProvisionerJobStatus {
5252+
continue
5253+
}
5254+
}
5255+
5256+
if job.JobStatus == database.ProvisionerJobStatusRunning || job.JobStatus == database.ProvisionerJobStatusPending {
5257+
continue
5258+
}
5259+
52385260
v.Deleted = true
52395261
q.templateVersions[i] = v
52405262
deleted = append(deleted, v.ID)

coderd/database/querier.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 54 additions & 44 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)