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

Lines changed: 126 additions & 0 deletions
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

Lines changed: 93 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy