Skip to content

Commit c0db364

Browse files
authored
feat(cli): add provisioner list and provisioner jobs list (#16030)
1 parent 91204c2 commit c0db364

34 files changed

+881
-30
lines changed

cli/provisionerjobs.go

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"slices"
6+
7+
"golang.org/x/xerrors"
8+
9+
"github.com/coder/coder/v2/cli/cliui"
10+
"github.com/coder/coder/v2/coderd/util/slice"
11+
"github.com/coder/coder/v2/codersdk"
12+
"github.com/coder/serpent"
13+
)
14+
15+
func (r *RootCmd) provisionerJobs() *serpent.Command {
16+
cmd := &serpent.Command{
17+
Use: "jobs",
18+
Short: "View and manage provisioner jobs",
19+
Handler: func(inv *serpent.Invocation) error {
20+
return inv.Command.HelpHandler(inv)
21+
},
22+
Aliases: []string{"job"},
23+
Children: []*serpent.Command{
24+
r.provisionerJobsList(),
25+
},
26+
}
27+
return cmd
28+
}
29+
30+
func (r *RootCmd) provisionerJobsList() *serpent.Command {
31+
type provisionerJobRow struct {
32+
codersdk.ProvisionerJob `table:"provisioner_job,recursive_inline,nosort"`
33+
OrganizationName string `json:"organization_name" table:"organization"`
34+
Queue string `json:"-" table:"queue"`
35+
}
36+
37+
var (
38+
client = new(codersdk.Client)
39+
orgContext = NewOrganizationContext()
40+
formatter = cliui.NewOutputFormatter(
41+
cliui.TableFormat([]provisionerJobRow{}, []string{"created at", "id", "organization", "status", "type", "queue", "tags"}),
42+
cliui.JSONFormat(),
43+
)
44+
status []string
45+
limit int64
46+
)
47+
48+
cmd := &serpent.Command{
49+
Use: "list",
50+
Short: "List provisioner jobs",
51+
Aliases: []string{"ls"},
52+
Middleware: serpent.Chain(
53+
serpent.RequireNArgs(0),
54+
r.InitClient(client),
55+
),
56+
Handler: func(inv *serpent.Invocation) error {
57+
ctx := inv.Context()
58+
org, err := orgContext.Selected(inv, client)
59+
if err != nil {
60+
return xerrors.Errorf("current organization: %w", err)
61+
}
62+
63+
jobs, err := client.OrganizationProvisionerJobs(ctx, org.ID, &codersdk.OrganizationProvisionerJobsOptions{
64+
Status: slice.StringEnums[codersdk.ProvisionerJobStatus](status),
65+
Limit: int(limit),
66+
})
67+
if err != nil {
68+
return xerrors.Errorf("list provisioner jobs: %w", err)
69+
}
70+
71+
if len(jobs) == 0 {
72+
_, _ = fmt.Fprintln(inv.Stdout, "No provisioner jobs found")
73+
return nil
74+
}
75+
76+
var rows []provisionerJobRow
77+
for _, job := range jobs {
78+
row := provisionerJobRow{
79+
ProvisionerJob: job,
80+
OrganizationName: org.HumanName(),
81+
}
82+
if job.Status == codersdk.ProvisionerJobPending {
83+
row.Queue = fmt.Sprintf("%d/%d", job.QueuePosition, job.QueueSize)
84+
}
85+
rows = append(rows, row)
86+
}
87+
// Sort manually because the cliui table truncates timestamps and
88+
// produces an unstable sort with timestamps that are all the same.
89+
slices.SortStableFunc(rows, func(a provisionerJobRow, b provisionerJobRow) int {
90+
return a.CreatedAt.Compare(b.CreatedAt)
91+
})
92+
93+
out, err := formatter.Format(ctx, rows)
94+
if err != nil {
95+
return xerrors.Errorf("display provisioner daemons: %w", err)
96+
}
97+
98+
_, _ = fmt.Fprintln(inv.Stdout, out)
99+
100+
return nil
101+
},
102+
}
103+
104+
cmd.Options = append(cmd.Options, []serpent.Option{
105+
{
106+
Flag: "status",
107+
FlagShorthand: "s",
108+
Env: "CODER_PROVISIONER_JOB_LIST_STATUS",
109+
Description: "Filter by job status.",
110+
Value: serpent.EnumArrayOf(&status, slice.ToStrings(codersdk.ProvisionerJobStatusEnums())...),
111+
},
112+
{
113+
Flag: "limit",
114+
FlagShorthand: "l",
115+
Env: "CODER_PROVISIONER_JOB_LIST_LIMIT",
116+
Description: "Limit the number of jobs returned.",
117+
Default: "50",
118+
Value: serpent.Int64Of(&limit),
119+
},
120+
}...)
121+
122+
orgContext.AttachOptions(cmd)
123+
formatter.AttachOptions(&cmd.Options)
124+
125+
return cmd
126+
}

cli/provisioners.go

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
6+
"golang.org/x/xerrors"
7+
8+
"github.com/coder/coder/v2/cli/cliui"
9+
"github.com/coder/coder/v2/codersdk"
10+
"github.com/coder/serpent"
11+
)
12+
13+
func (r *RootCmd) Provisioners() *serpent.Command {
14+
cmd := &serpent.Command{
15+
Use: "provisioner",
16+
Short: "View and manage provisioner daemons and jobs",
17+
Handler: func(inv *serpent.Invocation) error {
18+
return inv.Command.HelpHandler(inv)
19+
},
20+
Aliases: []string{"provisioners"},
21+
Children: []*serpent.Command{
22+
r.provisionerList(),
23+
r.provisionerJobs(),
24+
},
25+
}
26+
27+
return cmd
28+
}
29+
30+
func (r *RootCmd) provisionerList() *serpent.Command {
31+
type provisionerDaemonRow struct {
32+
codersdk.ProvisionerDaemon `table:"provisioner_daemon,recursive_inline"`
33+
OrganizationName string `json:"organization_name" table:"organization"`
34+
}
35+
var (
36+
client = new(codersdk.Client)
37+
orgContext = NewOrganizationContext()
38+
formatter = cliui.NewOutputFormatter(
39+
cliui.TableFormat([]provisionerDaemonRow{}, []string{"name", "organization", "status", "key name", "created at", "last seen at", "version", "tags"}),
40+
cliui.JSONFormat(),
41+
)
42+
)
43+
44+
cmd := &serpent.Command{
45+
Use: "list",
46+
Short: "List provisioner daemons in an organization",
47+
Aliases: []string{"ls"},
48+
Middleware: serpent.Chain(
49+
serpent.RequireNArgs(0),
50+
r.InitClient(client),
51+
),
52+
Handler: func(inv *serpent.Invocation) error {
53+
ctx := inv.Context()
54+
55+
org, err := orgContext.Selected(inv, client)
56+
if err != nil {
57+
return xerrors.Errorf("current organization: %w", err)
58+
}
59+
60+
daemons, err := client.OrganizationProvisionerDaemons(ctx, org.ID, nil)
61+
if err != nil {
62+
return xerrors.Errorf("list provisioner daemons: %w", err)
63+
}
64+
65+
if len(daemons) == 0 {
66+
_, _ = fmt.Fprintln(inv.Stdout, "No provisioner daemons found")
67+
return nil
68+
}
69+
70+
var rows []provisionerDaemonRow
71+
for _, daemon := range daemons {
72+
rows = append(rows, provisionerDaemonRow{
73+
ProvisionerDaemon: daemon,
74+
OrganizationName: org.HumanName(),
75+
})
76+
}
77+
78+
out, err := formatter.Format(ctx, rows)
79+
if err != nil {
80+
return xerrors.Errorf("display provisioner daemons: %w", err)
81+
}
82+
83+
_, _ = fmt.Fprintln(inv.Stdout, out)
84+
85+
return nil
86+
},
87+
}
88+
89+
orgContext.AttachOptions(cmd)
90+
formatter.AttachOptions(&cmd.Options)
91+
92+
return cmd
93+
}

0 commit comments

Comments
 (0)