-
Notifications
You must be signed in to change notification settings - Fork 915
feat: add provisioner daemon and jobs endpoints and commands #15940
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
650cbaf
449df5d
fa261b1
c5a0429
b138faf
a0af6eb
8975d41
96a278e
e9b8725
c5211ef
c6e1cc6
9e64cdd
8fd1a6a
b57bc8c
32fde66
38116d7
e7ac032
f6c2a3f
876bd80
0788d56
24d49da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
|
||
"golang.org/x/xerrors" | ||
|
||
"github.com/coder/coder/v2/cli/cliui" | ||
"github.com/coder/coder/v2/coderd/util/slice" | ||
"github.com/coder/coder/v2/codersdk" | ||
"github.com/coder/serpent" | ||
) | ||
|
||
func (r *RootCmd) provisionerJobs() *serpent.Command { | ||
cmd := &serpent.Command{ | ||
Use: "jobs", | ||
Short: "View and manage provisioner jobs", | ||
Handler: func(inv *serpent.Invocation) error { | ||
return inv.Command.HelpHandler(inv) | ||
}, | ||
Aliases: []string{"job"}, | ||
Children: []*serpent.Command{ | ||
r.provisionerJobsList(), | ||
}, | ||
} | ||
return cmd | ||
} | ||
|
||
func (r *RootCmd) provisionerJobsList() *serpent.Command { | ||
type provisionerJobRow struct { | ||
codersdk.ProvisionerJob `table:"provisioner_job,recursive_inline"` | ||
OrganizationName string `json:"organization_name" table:"organization"` | ||
Queue string `json:"-" table:"queue"` | ||
} | ||
|
||
var ( | ||
client = new(codersdk.Client) | ||
orgContext = NewOrganizationContext() | ||
formatter = cliui.NewOutputFormatter( | ||
cliui.TableFormat([]provisionerJobRow{}, []string{"created at", "id", "organization", "status", "type", "queue", "tags"}), | ||
cliui.JSONFormat(), | ||
) | ||
status []string | ||
limit int64 | ||
) | ||
|
||
cmd := &serpent.Command{ | ||
Use: "list", | ||
Short: "List provisioner jobs", | ||
Aliases: []string{"ls"}, | ||
Middleware: serpent.Chain( | ||
serpent.RequireNArgs(0), | ||
r.InitClient(client), | ||
), | ||
Handler: func(inv *serpent.Invocation) error { | ||
ctx := inv.Context() | ||
org, err := orgContext.Selected(inv, client) | ||
if err != nil { | ||
return xerrors.Errorf("current organization: %w", err) | ||
} | ||
|
||
jobs, err := client.OrganizationProvisionerJobs(ctx, org.ID, &codersdk.OrganizationProvisionerJobsOptions{ | ||
Status: slice.ToStringEnums[codersdk.ProvisionerJobStatus](status), | ||
Limit: int(limit), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: paging? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to keep it simple for now, I realized we don't have a precedent on the CLI for paging so I didn't want to introduce it here. Granted The original issue #15084 mentioned we should have "created at" (or rather "created after"), and I added that initially, but then I realized we don't have a precedent for filtering on time on the CLI either. (There's even a lack of serpent support there.) For this reason both pagination and created at filter have been omitted and should IMO be part of a grander plan for how to support these in a unified way on the CLI. |
||
}) | ||
if err != nil { | ||
return xerrors.Errorf("list provisioner jobs: %w", err) | ||
} | ||
|
||
if len(jobs) == 0 { | ||
_, _ = fmt.Fprintln(inv.Stdout, "No provisioner jobs found") | ||
return nil | ||
} | ||
|
||
var rows []provisionerJobRow | ||
for _, job := range jobs { | ||
row := provisionerJobRow{ | ||
ProvisionerJob: job, | ||
OrganizationName: org.HumanName(), | ||
} | ||
if job.Status == codersdk.ProvisionerJobPending { | ||
row.Queue = fmt.Sprintf("%d/%d", job.QueuePosition, job.QueueSize) | ||
} | ||
rows = append(rows, row) | ||
} | ||
|
||
out, err := formatter.Format(ctx, rows) | ||
if err != nil { | ||
return xerrors.Errorf("display provisioner daemons: %w", err) | ||
} | ||
|
||
_, _ = fmt.Fprintln(inv.Stdout, out) | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
cmd.Options = append(cmd.Options, []serpent.Option{ | ||
{ | ||
Flag: "status", | ||
FlagShorthand: "s", | ||
Env: "CODER_PROVISIONER_JOB_LIST_STATUS", | ||
Description: "Filter by job status.", | ||
Value: serpent.EnumArrayOf(&status, slice.ToStrings(codersdk.ProvisionerJobStatusEnums())...), | ||
}, | ||
{ | ||
Flag: "limit", | ||
FlagShorthand: "l", | ||
Env: "CODER_PROVISIONER_JOB_LIST_LIMIT", | ||
Description: "Limit the number of jobs returned.", | ||
Default: "50", | ||
Value: serpent.Int64Of(&limit), | ||
}, | ||
}...) | ||
|
||
orgContext.AttachOptions(cmd) | ||
formatter.AttachOptions(&cmd.Options) | ||
|
||
return cmd | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
|
||
"golang.org/x/xerrors" | ||
|
||
"github.com/coder/coder/v2/cli/cliui" | ||
"github.com/coder/coder/v2/codersdk" | ||
"github.com/coder/serpent" | ||
) | ||
|
||
func (r *RootCmd) Provisioners() *serpent.Command { | ||
cmd := &serpent.Command{ | ||
Use: "provisioner", | ||
Short: "View and manage provisioner daemons and jobs", | ||
Handler: func(inv *serpent.Invocation) error { | ||
return inv.Command.HelpHandler(inv) | ||
}, | ||
Aliases: []string{"provisioners"}, | ||
Children: []*serpent.Command{ | ||
r.provisionerList(), | ||
r.provisionerJobs(), | ||
}, | ||
} | ||
|
||
return cmd | ||
} | ||
|
||
func (r *RootCmd) provisionerList() *serpent.Command { | ||
type provisionerDaemonRow struct { | ||
codersdk.ProvisionerDaemonWithStatus `table:"provisioner_daemon,recursive_inline"` | ||
OrganizationName string `json:"organization_name" table:"organization"` | ||
} | ||
var ( | ||
client = new(codersdk.Client) | ||
orgContext = NewOrganizationContext() | ||
formatter = cliui.NewOutputFormatter( | ||
cliui.TableFormat([]provisionerDaemonRow{}, []string{"name", "organization", "status", "key name", "created at", "last seen at", "version", "tags"}), | ||
cliui.JSONFormat(), | ||
) | ||
) | ||
|
||
cmd := &serpent.Command{ | ||
Use: "list", | ||
Short: "List provisioner daemons in an organization", | ||
Aliases: []string{"ls"}, | ||
Middleware: serpent.Chain( | ||
serpent.RequireNArgs(0), | ||
r.InitClient(client), | ||
), | ||
Handler: func(inv *serpent.Invocation) error { | ||
ctx := inv.Context() | ||
|
||
org, err := orgContext.Selected(inv, client) | ||
if err != nil { | ||
return xerrors.Errorf("current organization: %w", err) | ||
} | ||
|
||
daemons, err := client.OrganizationProvisionerDaemons(ctx, org.ID, nil) | ||
if err != nil { | ||
return xerrors.Errorf("list provisioner daemons: %w", err) | ||
} | ||
|
||
if len(daemons) == 0 { | ||
_, _ = fmt.Fprintln(inv.Stdout, "No provisioner daemons found") | ||
return nil | ||
} | ||
|
||
var rows []provisionerDaemonRow | ||
for _, daemon := range daemons { | ||
rows = append(rows, provisionerDaemonRow{ | ||
ProvisionerDaemonWithStatus: daemon, | ||
OrganizationName: org.HumanName(), | ||
}) | ||
} | ||
|
||
out, err := formatter.Format(ctx, rows) | ||
if err != nil { | ||
return xerrors.Errorf("display provisioner daemons: %w", err) | ||
} | ||
|
||
_, _ = fmt.Fprintln(inv.Stdout, out) | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
orgContext.AttachOptions(cmd) | ||
formatter.AttachOptions(&cmd.Options) | ||
|
||
return cmd | ||
} |
Uh oh!
There was an error while loading. Please reload this page.