chore: refactor to directly create Client in Command Handlers (#19760)

Refactors the CLI to create the `*codersdk.Client` in the handlers. This is groundwork for changing the `rootCmd.InitClient()` to use the new `ClientOption`​s.

It also improves variable locality, scoping the Client to the handler. This makes misuse less likely and reduces the memory allocations to just the command being executed, rather than allocating a Client for every command regardless of whether it is executed.
This commit is contained in:
Spike Curtis
2025-09-22 17:14:07 +04:00
committed by GitHub
parent 12496830d6
commit 606ae897b7
67 changed files with 605 additions and 431 deletions
+6 -3
View File
@@ -12,18 +12,21 @@ import (
) )
func (r *RootCmd) autoupdate() *serpent.Command { func (r *RootCmd) autoupdate() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "autoupdate <workspace> <always|never>", Use: "autoupdate <workspace> <always|never>",
Short: "Toggle auto-update policy for a workspace", Short: "Toggle auto-update policy for a workspace",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(2), serpent.RequireNArgs(2),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
policy := strings.ToLower(inv.Args[1]) policy := strings.ToLower(inv.Args[1])
err := validateAutoUpdatePolicy(policy) err = validateAutoUpdatePolicy(policy)
if err != nil { if err != nil {
return xerrors.Errorf("validate policy: %w", err) return xerrors.Errorf("validate policy: %w", err)
} }
+5 -3
View File
@@ -236,7 +236,6 @@ func (r *RootCmd) configSSH() *serpent.Command {
dryRun bool dryRun bool
coderCliPath string coderCliPath string
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "config-ssh", Use: "config-ssh",
@@ -253,9 +252,13 @@ func (r *RootCmd) configSSH() *serpent.Command {
), ),
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
if sshConfigOpts.waitEnum != "auto" && sshConfigOpts.skipProxyCommand { if sshConfigOpts.waitEnum != "auto" && sshConfigOpts.skipProxyCommand {
@@ -280,7 +283,6 @@ func (r *RootCmd) configSSH() *serpent.Command {
out = inv.Stderr out = inv.Stderr
} }
var err error
coderBinary := coderCliPath coderBinary := coderCliPath
if coderBinary == "" { if coderBinary == "" {
coderBinary, err = currentBinPath(out) coderBinary, err = currentBinPath(out)
+5 -3
View File
@@ -50,7 +50,6 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command {
// shares the same name across multiple organizations. // shares the same name across multiple organizations.
orgContext = NewOrganizationContext() orgContext = NewOrganizationContext()
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "create [workspace]", Use: "create [workspace]",
@@ -61,9 +60,12 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command {
Command: "coder create <username>/<workspace_name>", Command: "coder create <username>/<workspace_name>",
}, },
), ),
Middleware: serpent.Chain(r.InitClient(client)),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var err error client, err := r.InitClient(inv)
if err != nil {
return err
}
workspaceOwner := codersdk.Me workspaceOwner := codersdk.Me
if len(inv.Args) >= 1 { if len(inv.Args) >= 1 {
workspaceOwner, workspaceName, err = splitNamedWorkspace(inv.Args[0]) workspaceOwner, workspaceName, err = splitNamedWorkspace(inv.Args[0])
+5 -2
View File
@@ -16,7 +16,6 @@ func (r *RootCmd) deleteWorkspace() *serpent.Command {
orphan bool orphan bool
prov buildFlags prov buildFlags
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "delete <workspace>", Use: "delete <workspace>",
@@ -29,9 +28,13 @@ func (r *RootCmd) deleteWorkspace() *serpent.Command {
), ),
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0]) workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
return err return err
+5 -4
View File
@@ -399,7 +399,6 @@ type mcpServer struct {
func (r *RootCmd) mcpServer() *serpent.Command { func (r *RootCmd) mcpServer() *serpent.Command {
var ( var (
client = new(codersdk.Client)
instructions string instructions string
allowedTools []string allowedTools []string
appStatusSlug string appStatusSlug string
@@ -409,6 +408,11 @@ func (r *RootCmd) mcpServer() *serpent.Command {
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "server", Use: "server",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.TryInitClient(inv)
if err != nil {
return err
}
var lastReport taskReport var lastReport taskReport
// Create a queue that skips duplicates and preserves summaries. // Create a queue that skips duplicates and preserves summaries.
queue := cliutil.NewQueue[taskReport](512).WithPredicate(func(report taskReport) (taskReport, bool) { queue := cliutil.NewQueue[taskReport](512).WithPredicate(func(report taskReport) (taskReport, bool) {
@@ -548,9 +552,6 @@ func (r *RootCmd) mcpServer() *serpent.Command {
return srv.startServer(ctx, inv, instructions, allowedTools) return srv.startServer(ctx, inv, instructions, allowedTools)
}, },
Short: "Start the Coder MCP server.", Short: "Start the Coder MCP server.",
Middleware: serpent.Chain(
r.TryInitClient(client),
),
Options: []serpent.Option{ Options: []serpent.Option{
{ {
Name: "instructions", Name: "instructions",
+5 -5
View File
@@ -22,16 +22,17 @@ import (
) )
func (r *RootCmd) rptyCommand() *serpent.Command { func (r *RootCmd) rptyCommand() *serpent.Command {
var ( var args handleRPTYArgs
client = new(codersdk.Client)
args handleRPTYArgs
)
cmd := &serpent.Command{ cmd := &serpent.Command{
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
if r.disableDirect { if r.disableDirect {
return xerrors.New("direct connections are disabled, but you can try websocat ;-)") return xerrors.New("direct connections are disabled, but you can try websocat ;-)")
} }
client, err := r.InitClient(inv)
if err != nil {
return err
}
args.NamedWorkspace = inv.Args[0] args.NamedWorkspace = inv.Args[0]
args.Command = inv.Args[1:] args.Command = inv.Args[1:]
return handleRPTY(inv, client, args) return handleRPTY(inv, client, args)
@@ -39,7 +40,6 @@ func (r *RootCmd) rptyCommand() *serpent.Command {
Long: "Establish an RPTY session with a workspace/agent. This uses the same mechanism as the Web Terminal.", Long: "Establish an RPTY session with a workspace/agent. This uses the same mechanism as the Web Terminal.",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(1, -1), serpent.RequireRangeArgs(1, -1),
r.InitClient(client),
), ),
Options: []serpent.Option{ Options: []serpent.Option{
{ {
+28 -26
View File
@@ -395,18 +395,17 @@ func (r *userCleanupRunner) Run(ctx context.Context, _ string, _ io.Writer) erro
func (r *RootCmd) scaletestCleanup() *serpent.Command { func (r *RootCmd) scaletestCleanup() *serpent.Command {
var template string var template string
cleanupStrategy := &scaletestStrategyFlags{cleanup: true} cleanupStrategy := &scaletestStrategyFlags{cleanup: true}
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "cleanup", Use: "cleanup",
Short: "Cleanup scaletest workspaces, then cleanup scaletest users.", Short: "Cleanup scaletest workspaces, then cleanup scaletest users.",
Long: "The strategy flags will apply to each stage of the cleanup process.", Long: "The strategy flags will apply to each stage of the cleanup process.",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
me, err := requireAdmin(ctx, client) me, err := requireAdmin(ctx, client)
@@ -551,14 +550,16 @@ func (r *RootCmd) scaletestCreateWorkspaces() *serpent.Command {
output = &scaletestOutputFlags{} output = &scaletestOutputFlags{}
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "create-workspaces", Use: "create-workspaces",
Short: "Creates many users, then creates a workspace for each user and waits for them finish building and fully come online. Optionally runs a command inside each workspace, and connects to the workspace over WireGuard.", Short: "Creates many users, then creates a workspace for each user and waits for them finish building and fully come online. Optionally runs a command inside each workspace, and connects to the workspace over WireGuard.",
Long: `It is recommended that all rate limits are disabled on the server before running this scaletest. This test generates many login events which will be rate limited against the (most likely single) IP.`, Long: `It is recommended that all rate limits are disabled on the server before running this scaletest. This test generates many login events which will be rate limited against the (most likely single) IP.`,
Middleware: r.InitClient(client),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
me, err := requireAdmin(ctx, client) me, err := requireAdmin(ctx, client)
@@ -861,7 +862,6 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
targetWorkspaces string targetWorkspaces string
workspaceProxyURL string workspaceProxyURL string
client = &codersdk.Client{}
tracingFlags = &scaletestTracingFlags{} tracingFlags = &scaletestTracingFlags{}
strategy = &scaletestStrategyFlags{} strategy = &scaletestStrategyFlags{}
cleanupStrategy = &scaletestStrategyFlags{cleanup: true} cleanupStrategy = &scaletestStrategyFlags{cleanup: true}
@@ -872,10 +872,12 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "workspace-traffic", Use: "workspace-traffic",
Short: "Generate traffic to scaletest workspaces through coderd", Short: "Generate traffic to scaletest workspaces through coderd",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) (err error) { Handler: func(inv *serpent.Invocation) (err error) {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
notifyCtx, stop := signal.NotifyContext(ctx, StopSignals...) // Checked later. notifyCtx, stop := signal.NotifyContext(ctx, StopSignals...) // Checked later.
@@ -1150,13 +1152,11 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
func (r *RootCmd) scaletestDashboard() *serpent.Command { func (r *RootCmd) scaletestDashboard() *serpent.Command {
var ( var (
interval time.Duration interval time.Duration
jitter time.Duration jitter time.Duration
headless bool headless bool
randSeed int64 randSeed int64
targetUsers string targetUsers string
client = &codersdk.Client{}
tracingFlags = &scaletestTracingFlags{} tracingFlags = &scaletestTracingFlags{}
strategy = &scaletestStrategyFlags{} strategy = &scaletestStrategyFlags{}
cleanupStrategy = &scaletestStrategyFlags{cleanup: true} cleanupStrategy = &scaletestStrategyFlags{cleanup: true}
@@ -1167,10 +1167,12 @@ func (r *RootCmd) scaletestDashboard() *serpent.Command {
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "dashboard", Use: "dashboard",
Short: "Generate traffic to the HTTP API to simulate use of the dashboard.", Short: "Generate traffic to the HTTP API to simulate use of the dashboard.",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
if !(interval > 0) { if !(interval > 0) {
return xerrors.Errorf("--interval must be greater than zero") return xerrors.Errorf("--interval must be greater than zero")
} }
+5 -2
View File
@@ -16,7 +16,6 @@ import (
func (r *RootCmd) taskCreate() *serpent.Command { func (r *RootCmd) taskCreate() *serpent.Command {
var ( var (
orgContext = NewOrganizationContext() orgContext = NewOrganizationContext()
client = new(codersdk.Client)
templateName string templateName string
templateVersionName string templateVersionName string
@@ -30,7 +29,6 @@ func (r *RootCmd) taskCreate() *serpent.Command {
Short: "Create an experimental task", Short: "Create an experimental task",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1), serpent.RequireRangeArgs(0, 1),
r.InitClient(client),
), ),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
{ {
@@ -67,6 +65,11 @@ func (r *RootCmd) taskCreate() *serpent.Command {
}, },
}, },
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
var ( var (
ctx = inv.Context() ctx = inv.Context()
expClient = codersdk.NewExperimentalClient(client) expClient = codersdk.NewExperimentalClient(client)
+5 -4
View File
@@ -16,20 +16,21 @@ import (
) )
func (r *RootCmd) taskDelete() *serpent.Command { func (r *RootCmd) taskDelete() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "delete <task> [<task> ...]", Use: "delete <task> [<task> ...]",
Short: "Delete experimental tasks", Short: "Delete experimental tasks",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(1, -1), serpent.RequireRangeArgs(1, -1),
r.InitClient(client),
), ),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
cliui.SkipPromptOption(), cliui.SkipPromptOption(),
}, },
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
exp := codersdk.NewExperimentalClient(client) exp := codersdk.NewExperimentalClient(client)
type toDelete struct { type toDelete struct {
@@ -70,7 +71,7 @@ func (r *RootCmd) taskDelete() *serpent.Command {
for _, it := range items { for _, it := range items {
displayList = append(displayList, it.Display) displayList = append(displayList, it.Display)
} }
_, err := cliui.Prompt(inv, cliui.PromptOptions{ _, err = cliui.Prompt(inv, cliui.PromptOptions{
Text: fmt.Sprintf("Delete these tasks: %s?", pretty.Sprint(cliui.DefaultStyles.Code, strings.Join(displayList, ", "))), Text: fmt.Sprintf("Delete these tasks: %s?", pretty.Sprint(cliui.DefaultStyles.Code, strings.Join(displayList, ", "))),
IsConfirm: true, IsConfirm: true,
Default: cliui.ConfirmNo, Default: cliui.ConfirmNo,
+5 -2
View File
@@ -38,7 +38,6 @@ func (r *RootCmd) taskList() *serpent.Command {
user string user string
quiet bool quiet bool
client = new(codersdk.Client)
formatter = cliui.NewOutputFormatter( formatter = cliui.NewOutputFormatter(
cliui.TableFormat( cliui.TableFormat(
[]taskListRow{}, []taskListRow{},
@@ -73,7 +72,6 @@ func (r *RootCmd) taskList() *serpent.Command {
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
{ {
@@ -108,6 +106,11 @@ func (r *RootCmd) taskList() *serpent.Command {
}, },
}, },
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
exp := codersdk.NewExperimentalClient(client) exp := codersdk.NewExperimentalClient(client)
+5 -2
View File
@@ -15,7 +15,6 @@ import (
func (r *RootCmd) taskStatus() *serpent.Command { func (r *RootCmd) taskStatus() *serpent.Command {
var ( var (
client = new(codersdk.Client)
formatter = cliui.NewOutputFormatter( formatter = cliui.NewOutputFormatter(
cliui.TableFormat( cliui.TableFormat(
[]taskStatusRow{}, []taskStatusRow{},
@@ -66,9 +65,13 @@ func (r *RootCmd) taskStatus() *serpent.Command {
}, },
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(i *serpent.Invocation) error { Handler: func(i *serpent.Invocation) error {
client, err := r.InitClient(i)
if err != nil {
return err
}
ctx := i.Context() ctx := i.Context()
ec := codersdk.NewExperimentalClient(client) ec := codersdk.NewExperimentalClient(client)
identifier := i.Args[0] identifier := i.Args[0]
+10 -5
View File
@@ -5,12 +5,10 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) favorite() *serpent.Command { func (r *RootCmd) favorite() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Aliases: []string{"fav", "favou" + "rite"}, Aliases: []string{"fav", "favou" + "rite"},
Annotations: workspaceCommand, Annotations: workspaceCommand,
@@ -18,9 +16,13 @@ func (r *RootCmd) favorite() *serpent.Command {
Short: "Add a workspace to your favorites", Short: "Add a workspace to your favorites",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ws, err := namedWorkspace(inv.Context(), client, inv.Args[0]) ws, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
return xerrors.Errorf("get workspace: %w", err) return xerrors.Errorf("get workspace: %w", err)
@@ -37,7 +39,6 @@ func (r *RootCmd) favorite() *serpent.Command {
} }
func (r *RootCmd) unfavorite() *serpent.Command { func (r *RootCmd) unfavorite() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Aliases: []string{"unfav", "unfavou" + "rite"}, Aliases: []string{"unfav", "unfavou" + "rite"},
Annotations: workspaceCommand, Annotations: workspaceCommand,
@@ -45,9 +46,13 @@ func (r *RootCmd) unfavorite() *serpent.Command {
Short: "Remove a workspace from your favorites", Short: "Remove a workspace from your favorites",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ws, err := namedWorkspace(inv.Context(), client, inv.Args[0]) ws, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
return xerrors.Errorf("get workspace: %w", err) return xerrors.Errorf("get workspace: %w", err)
+5 -2
View File
@@ -96,7 +96,6 @@ func (r *RootCmd) list() *serpent.Command {
cliui.JSONFormat(), cliui.JSONFormat(),
) )
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "list", Use: "list",
@@ -104,9 +103,13 @@ func (r *RootCmd) list() *serpent.Command {
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
res, err := QueryConvertWorkspaces(inv.Context(), client, filter.Filter(), WorkspaceListRowFromWorkspace) res, err := QueryConvertWorkspaces(inv.Context(), client, filter.Filter(), WorkspaceListRowFromWorkspace)
if err != nil { if err != nil {
return err return err
+5 -6
View File
@@ -8,24 +8,23 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) logout() *serpent.Command { func (r *RootCmd) logout() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "logout", Use: "logout",
Short: "Unauthenticate your local session", Short: "Unauthenticate your local session",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
var errors []error var errors []error
config := r.createConfig() config := r.createConfig()
var err error
_, err = cliui.Prompt(inv, cliui.PromptOptions{ _, err = cliui.Prompt(inv, cliui.PromptOptions{
Text: "Are you sure you want to log out?", Text: "Are you sure you want to log out?",
IsConfirm: true, IsConfirm: true,
+5 -6
View File
@@ -9,22 +9,21 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/healthcheck/derphealth" "github.com/coder/coder/v2/coderd/healthcheck/derphealth"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/healthsdk" "github.com/coder/coder/v2/codersdk/healthsdk"
"github.com/coder/coder/v2/codersdk/workspacesdk" "github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) netcheck() *serpent.Command { func (r *RootCmd) netcheck() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "netcheck", Use: "netcheck",
Short: "Print network debug information for DERP and STUN", Short: "Print network debug information for DERP and STUN",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(inv.Context(), 30*time.Second) ctx, cancel := context.WithTimeout(inv.Context(), 30*time.Second)
defer cancel() defer cancel()
+22 -11
View File
@@ -47,16 +47,19 @@ func (r *RootCmd) notifications() *serpent.Command {
} }
func (r *RootCmd) pauseNotifications() *serpent.Command { func (r *RootCmd) pauseNotifications() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "pause", Use: "pause",
Short: "Pause notifications", Short: "Pause notifications",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
err := client.PutNotificationsSettings(inv.Context(), codersdk.NotificationsSettings{ client, err := r.InitClient(inv)
if err != nil {
return err
}
err = client.PutNotificationsSettings(inv.Context(), codersdk.NotificationsSettings{
NotifierPaused: true, NotifierPaused: true,
}) })
if err != nil { if err != nil {
@@ -71,16 +74,19 @@ func (r *RootCmd) pauseNotifications() *serpent.Command {
} }
func (r *RootCmd) resumeNotifications() *serpent.Command { func (r *RootCmd) resumeNotifications() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "resume", Use: "resume",
Short: "Resume notifications", Short: "Resume notifications",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
err := client.PutNotificationsSettings(inv.Context(), codersdk.NotificationsSettings{ client, err := r.InitClient(inv)
if err != nil {
return err
}
err = client.PutNotificationsSettings(inv.Context(), codersdk.NotificationsSettings{
NotifierPaused: false, NotifierPaused: false,
}) })
if err != nil { if err != nil {
@@ -95,15 +101,18 @@ func (r *RootCmd) resumeNotifications() *serpent.Command {
} }
func (r *RootCmd) testNotifications() *serpent.Command { func (r *RootCmd) testNotifications() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "test", Use: "test",
Short: "Send a test notification", Short: "Send a test notification",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
if err := client.PostTestNotification(inv.Context()); err != nil { if err := client.PostTestNotification(inv.Context()); err != nil {
return xerrors.Errorf("unable to post test notification: %w", err) return xerrors.Errorf("unable to post test notification: %w", err)
} }
@@ -116,16 +125,18 @@ func (r *RootCmd) testNotifications() *serpent.Command {
} }
func (r *RootCmd) customNotifications() *serpent.Command { func (r *RootCmd) customNotifications() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "custom <title> <message>", Use: "custom <title> <message>",
Short: "Send a custom notification", Short: "Send a custom notification",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(2), serpent.RequireNArgs(2),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
err := client.PostCustomNotification(inv.Context(), codersdk.CustomNotificationRequest{ client, err := r.InitClient(inv)
if err != nil {
return err
}
err = client.PostCustomNotification(inv.Context(), codersdk.CustomNotificationRequest{
Content: &codersdk.CustomNotificationContent{ Content: &codersdk.CustomNotificationContent{
Title: inv.Args[0], Title: inv.Args[0],
Message: inv.Args[1], Message: inv.Args[1],
+12 -10
View File
@@ -41,24 +41,25 @@ const vscodeDesktopName = "VS Code Desktop"
func (r *RootCmd) openVSCode() *serpent.Command { func (r *RootCmd) openVSCode() *serpent.Command {
var ( var (
generateToken bool generateToken bool
testOpenError bool testOpenError bool
appearanceConfig codersdk.AppearanceConfig
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "vscode <workspace> [<directory in workspace>]", Use: "vscode <workspace> [<directory in workspace>]",
Short: fmt.Sprintf("Open a workspace in %s", vscodeDesktopName), Short: fmt.Sprintf("Open a workspace in %s", vscodeDesktopName),
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(1, 2), serpent.RequireRangeArgs(1, 2),
r.InitClient(client),
initAppearance(client, &appearanceConfig),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(inv.Context()) ctx, cancel := context.WithCancel(inv.Context())
defer cancel() defer cancel()
appearanceConfig := initAppearance(ctx, client)
// Check if we're inside a workspace, and especially inside _this_ // Check if we're inside a workspace, and especially inside _this_
// workspace so we can perform path resolution/expansion. Generally, // workspace so we can perform path resolution/expansion. Generally,
@@ -299,15 +300,16 @@ func (r *RootCmd) openApp() *serpent.Command {
testOpenError bool testOpenError bool
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "app <workspace> <app slug>", Use: "app <workspace> <app slug>",
Short: "Open a workspace application.", Short: "Open a workspace application.",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(inv.Context()) ctx, cancel := context.WithCancel(inv.Context())
defer cancel() defer cancel()
+5 -3
View File
@@ -37,7 +37,6 @@ func (r *RootCmd) organizations() *serpent.Command {
func (r *RootCmd) showOrganization(orgContext *OrganizationContext) *serpent.Command { func (r *RootCmd) showOrganization(orgContext *OrganizationContext) *serpent.Command {
var ( var (
stringFormat func(orgs []codersdk.Organization) (string, error) stringFormat func(orgs []codersdk.Organization) (string, error)
client = new(codersdk.Client)
formatter = cliui.NewOutputFormatter( formatter = cliui.NewOutputFormatter(
cliui.ChangeFormatterData(cliui.TextFormat(), func(data any) (any, error) { cliui.ChangeFormatterData(cliui.TextFormat(), func(data any) (any, error) {
typed, ok := data.([]codersdk.Organization) typed, ok := data.([]codersdk.Organization)
@@ -77,7 +76,6 @@ func (r *RootCmd) showOrganization(orgContext *OrganizationContext) *serpent.Com
}, },
), ),
Middleware: serpent.Chain( Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireRangeArgs(0, 1), serpent.RequireRangeArgs(0, 1),
), ),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
@@ -90,13 +88,17 @@ func (r *RootCmd) showOrganization(orgContext *OrganizationContext) *serpent.Com
}, },
}, },
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
orgArg := "selected" orgArg := "selected"
if len(inv.Args) >= 1 { if len(inv.Args) >= 1 {
orgArg = inv.Args[0] orgArg = inv.Args[0]
} }
var orgs []codersdk.Organization var orgs []codersdk.Organization
var err error
switch strings.ToLower(orgArg) { switch strings.ToLower(orgArg) {
case "selected": case "selected":
stringFormat = func(orgs []codersdk.Organization) (string, error) { stringFormat = func(orgs []codersdk.Organization) (string, error) {
+6 -4
View File
@@ -12,22 +12,24 @@ import (
) )
func (r *RootCmd) createOrganization() *serpent.Command { func (r *RootCmd) createOrganization() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "create <organization name>", Use: "create <organization name>",
Short: "Create a new organization.", Short: "Create a new organization.",
Middleware: serpent.Chain( Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
), ),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
cliui.SkipPromptOption(), cliui.SkipPromptOption(),
}, },
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
orgName := inv.Args[0] orgName := inv.Args[0]
err := codersdk.NameValid(orgName) err = codersdk.NameValid(orgName)
if err != nil { if err != nil {
return xerrors.Errorf("organization name %q is invalid: %w", orgName, err) return xerrors.Errorf("organization name %q is invalid: %w", orgName, err)
} }
+17 -13
View File
@@ -31,16 +31,17 @@ func (r *RootCmd) organizationMembers(orgContext *OrganizationContext) *serpent.
} }
func (r *RootCmd) removeOrganizationMember(orgContext *OrganizationContext) *serpent.Command { func (r *RootCmd) removeOrganizationMember(orgContext *OrganizationContext) *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "remove <username | user_id>", Use: "remove <username | user_id>",
Short: "Remove a new member to the current organization", Short: "Remove a new member to the current organization",
Middleware: serpent.Chain( Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
@@ -62,16 +63,17 @@ func (r *RootCmd) removeOrganizationMember(orgContext *OrganizationContext) *ser
} }
func (r *RootCmd) addOrganizationMember(orgContext *OrganizationContext) *serpent.Command { func (r *RootCmd) addOrganizationMember(orgContext *OrganizationContext) *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "add <username | user_id>", Use: "add <username | user_id>",
Short: "Add a new member to the current organization", Short: "Add a new member to the current organization",
Middleware: serpent.Chain( Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
@@ -93,16 +95,15 @@ func (r *RootCmd) addOrganizationMember(orgContext *OrganizationContext) *serpen
} }
func (r *RootCmd) assignOrganizationRoles(orgContext *OrganizationContext) *serpent.Command { func (r *RootCmd) assignOrganizationRoles(orgContext *OrganizationContext) *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "edit-roles <username | user_id> [roles...]", Use: "edit-roles <username | user_id> [roles...]",
Aliases: []string{"edit-role"}, Aliases: []string{"edit-role"},
Short: "Edit organization member's roles", Short: "Edit organization member's roles",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
@@ -141,15 +142,18 @@ func (r *RootCmd) listOrganizationMembers(orgContext *OrganizationContext) *serp
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "list", Use: "list",
Short: "List all organization members", Short: "List all organization members",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
+14 -8
View File
@@ -54,14 +54,15 @@ func (r *RootCmd) showOrganizationRoles(orgContext *OrganizationContext) *serpen
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "show [role_names ...]", Use: "show [role_names ...]",
Short: "Show role(s)", Short: "Show role(s)",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
@@ -117,7 +118,6 @@ func (r *RootCmd) createOrganizationRole(orgContext *OrganizationContext) *serpe
jsonInput bool jsonInput bool
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "create <role_name>", Use: "create <role_name>",
Short: "Create a new organization custom role", Short: "Create a new organization custom role",
@@ -144,10 +144,13 @@ func (r *RootCmd) createOrganizationRole(orgContext *OrganizationContext) *serpe
}, },
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1), serpent.RequireRangeArgs(0, 1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return err return err
@@ -240,7 +243,6 @@ func (r *RootCmd) updateOrganizationRole(orgContext *OrganizationContext) *serpe
jsonInput bool jsonInput bool
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "update <role_name>", Use: "update <role_name>",
Short: "Update an organization custom role", Short: "Update an organization custom role",
@@ -267,9 +269,13 @@ func (r *RootCmd) updateOrganizationRole(orgContext *OrganizationContext) *serpe
}, },
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1), serpent.RequireRangeArgs(0, 1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
+10 -9
View File
@@ -95,7 +95,6 @@ type organizationSetting struct {
} }
func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, settings []organizationSetting) *serpent.Command { func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, settings []organizationSetting) *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "set", Use: "set",
Short: "Update specified organization setting.", Short: "Update specified organization setting.",
@@ -108,7 +107,6 @@ func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, setti
Options: []serpent.Option{}, Options: []serpent.Option{},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
@@ -124,12 +122,15 @@ func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, setti
Options: []serpent.Option{}, Options: []serpent.Option{},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
var org codersdk.Organization var org codersdk.Organization
var err error
if !set.DisableOrgContext { if !set.DisableOrgContext {
org, err = orgContext.Selected(inv, client) org, err = orgContext.Selected(inv, client)
@@ -170,7 +171,6 @@ func (r *RootCmd) setOrganizationSettings(orgContext *OrganizationContext, setti
} }
func (r *RootCmd) printOrganizationSetting(orgContext *OrganizationContext, settings []organizationSetting) *serpent.Command { func (r *RootCmd) printOrganizationSetting(orgContext *OrganizationContext, settings []organizationSetting) *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "show", Use: "show",
Short: "Outputs specified organization setting.", Short: "Outputs specified organization setting.",
@@ -183,7 +183,6 @@ func (r *RootCmd) printOrganizationSetting(orgContext *OrganizationContext, sett
Options: []serpent.Option{}, Options: []serpent.Option{},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv) return inv.Command.HelpHandler(inv)
@@ -199,13 +198,15 @@ func (r *RootCmd) printOrganizationSetting(orgContext *OrganizationContext, sett
Options: []serpent.Option{}, Options: []serpent.Option{},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
var org codersdk.Organization var org codersdk.Organization
var err error
if !set.DisableOrgContext { if !set.DisableOrgContext {
org, err = orgContext.Selected(inv, client) org, err = orgContext.Selected(inv, client)
if err != nil { if err != nil {
+10 -10
View File
@@ -84,28 +84,28 @@ func (s *pingSummary) Write(w io.Writer) {
func (r *RootCmd) ping() *serpent.Command { func (r *RootCmd) ping() *serpent.Command {
var ( var (
pingNum int64 pingNum int64
pingTimeout time.Duration pingTimeout time.Duration
pingWait time.Duration pingWait time.Duration
pingTimeLocal bool pingTimeLocal bool
pingTimeUTC bool pingTimeUTC bool
appearanceConfig codersdk.AppearanceConfig
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "ping <workspace>", Use: "ping <workspace>",
Short: "Ping a workspace", Short: "Ping a workspace",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
initAppearance(client, &appearanceConfig),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(inv.Context()) ctx, cancel := context.WithCancel(inv.Context())
defer cancel() defer cancel()
appearanceConfig := initAppearance(ctx, client)
notifyCtx, notifyCancel := inv.SignalNotifyContext(ctx, StopSignals...) notifyCtx, notifyCancel := inv.SignalNotifyContext(ctx, StopSignals...)
defer notifyCancel() defer notifyCancel()
+5 -4
View File
@@ -38,9 +38,7 @@ func (r *RootCmd) portForward() *serpent.Command {
tcpForwards []string // <port>:<port> tcpForwards []string // <port>:<port>
udpForwards []string // <port>:<port> udpForwards []string // <port>:<port>
disableAutostart bool disableAutostart bool
appearanceConfig codersdk.AppearanceConfig
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "port-forward <workspace>", Use: "port-forward <workspace>",
Short: `Forward ports from a workspace to the local machine. For reverse port forwarding, use "coder ssh -R".`, Short: `Forward ports from a workspace to the local machine. For reverse port forwarding, use "coder ssh -R".`,
@@ -69,12 +67,15 @@ func (r *RootCmd) portForward() *serpent.Command {
), ),
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
initAppearance(client, &appearanceConfig),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(inv.Context()) ctx, cancel := context.WithCancel(inv.Context())
defer cancel() defer cancel()
appearanceConfig := initAppearance(ctx, client)
specs, err := parsePortForwards(tcpForwards, udpForwards) specs, err := parsePortForwards(tcpForwards, udpForwards)
if err != nil { if err != nil {
+9 -7
View File
@@ -38,7 +38,6 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command {
} }
var ( var (
client = new(codersdk.Client)
orgContext = NewOrganizationContext() orgContext = NewOrganizationContext()
formatter = cliui.NewOutputFormatter( formatter = cliui.NewOutputFormatter(
cliui.TableFormat([]provisionerJobRow{}, []string{"created at", "id", "type", "template display name", "status", "queue", "tags"}), cliui.TableFormat([]provisionerJobRow{}, []string{"created at", "id", "type", "template display name", "status", "queue", "tags"}),
@@ -54,10 +53,13 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command {
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return xerrors.Errorf("current organization: %w", err) return xerrors.Errorf("current organization: %w", err)
@@ -129,19 +131,19 @@ func (r *RootCmd) provisionerJobsList() *serpent.Command {
} }
func (r *RootCmd) provisionerJobsCancel() *serpent.Command { func (r *RootCmd) provisionerJobsCancel() *serpent.Command {
var ( orgContext := NewOrganizationContext()
client = new(codersdk.Client)
orgContext = NewOrganizationContext()
)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "cancel <job_id>", Use: "cancel <job_id>",
Short: "Cancel a provisioner job", Short: "Cancel a provisioner job",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return xerrors.Errorf("current organization: %w", err) return xerrors.Errorf("current organization: %w", err)
+4 -3
View File
@@ -35,7 +35,6 @@ func (r *RootCmd) provisionerList() *serpent.Command {
OrganizationName string `json:"organization_name" table:"organization"` OrganizationName string `json:"organization_name" table:"organization"`
} }
var ( var (
client = new(codersdk.Client)
orgContext = NewOrganizationContext() orgContext = NewOrganizationContext()
formatter = cliui.NewOutputFormatter( formatter = cliui.NewOutputFormatter(
cliui.TableFormat([]provisionerDaemonRow{}, []string{"created at", "last seen at", "key name", "name", "version", "status", "tags"}), cliui.TableFormat([]provisionerDaemonRow{}, []string{"created at", "last seen at", "key name", "name", "version", "status", "tags"}),
@@ -53,11 +52,13 @@ func (r *RootCmd) provisionerList() *serpent.Command {
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return xerrors.Errorf("current organization: %w", err) return xerrors.Errorf("current organization: %w", err)
+7 -5
View File
@@ -14,13 +14,15 @@ import (
func (r *RootCmd) publickey() *serpent.Command { func (r *RootCmd) publickey() *serpent.Command {
var reset bool var reset bool
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "publickey", Use: "publickey",
Aliases: []string{"pubkey"}, Aliases: []string{"pubkey"},
Short: "Output your Coder public key used for Git operations", Short: "Output your Coder public key used for Git operations",
Middleware: r.InitClient(client),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
if reset { if reset {
// Confirm prompt if using --reset. We don't want to accidentally // Confirm prompt if using --reset. We don't want to accidentally
// reset our public key. // reset our public key.
+6 -4
View File
@@ -13,18 +13,20 @@ import (
) )
func (r *RootCmd) rename() *serpent.Command { func (r *RootCmd) rename() *serpent.Command {
var appearanceConfig codersdk.AppearanceConfig
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "rename <workspace> <new name>", Use: "rename <workspace> <new name>",
Short: "Rename a workspace", Short: "Rename a workspace",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(2), serpent.RequireNArgs(2),
r.InitClient(client),
initAppearance(client, &appearanceConfig),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
appearanceConfig := initAppearance(inv.Context(), client)
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0]) workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
return xerrors.Errorf("get workspace: %w", err) return xerrors.Errorf("get workspace: %w", err)
+5 -2
View File
@@ -19,17 +19,20 @@ func (r *RootCmd) restart() *serpent.Command {
bflags buildFlags bflags buildFlags
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "restart <workspace>", Use: "restart <workspace>",
Short: "Restart a workspace", Short: "Restart a workspace",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Options: serpent.OptionSet{cliui.SkipPromptOption()}, Options: serpent.OptionSet{cliui.SkipPromptOption()},
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
out := inv.Stdout out := inv.Stdout
+92 -100
View File
@@ -494,110 +494,106 @@ type RootCmd struct {
noFeatureWarning bool noFeatureWarning bool
} }
// InitClient authenticates the client with files from disk // InitClient creates and configures a new client with authentication, telemetry,
// and injects header middlewares for telemetry, authentication,
// and version checks. // and version checks.
func (r *RootCmd) InitClient(client *codersdk.Client) serpent.MiddlewareFunc { func (r *RootCmd) InitClient(inv *serpent.Invocation) (*codersdk.Client, error) {
return func(next serpent.HandlerFunc) serpent.HandlerFunc { conf := r.createConfig()
return func(inv *serpent.Invocation) error { var err error
conf := r.createConfig() // Read the client URL stored on disk.
var err error if r.clientURL == nil || r.clientURL.String() == "" {
// Read the client URL stored on disk. rawURL, err := conf.URL().Read()
if r.clientURL == nil || r.clientURL.String() == "" { // If the configuration files are absent, the user is logged out
rawURL, err := conf.URL().Read() if os.IsNotExist(err) {
// If the configuration files are absent, the user is logged out binPath, err := os.Executable()
if os.IsNotExist(err) {
binPath, err := os.Executable()
if err != nil {
binPath = "coder"
}
return xerrors.Errorf(notLoggedInMessage, binPath)
}
if err != nil {
return err
}
r.clientURL, err = url.Parse(strings.TrimSpace(rawURL))
if err != nil {
return err
}
}
// Read the token stored on disk.
if r.token == "" {
r.token, err = conf.Session().Read()
// Even if there isn't a token, we don't care.
// Some API routes can be unauthenticated.
if err != nil && !os.IsNotExist(err) {
return err
}
}
err = r.configureClient(inv.Context(), client, r.clientURL, inv)
if err != nil { if err != nil {
return err binPath = "coder"
} }
client.SetSessionToken(r.token) return nil, xerrors.Errorf(notLoggedInMessage, binPath)
}
if err != nil {
return nil, err
}
if r.debugHTTP { r.clientURL, err = url.Parse(strings.TrimSpace(rawURL))
client.PlainLogger = os.Stderr if err != nil {
client.SetLogBodies(true) return nil, err
}
client.DisableDirectConnections = r.disableDirect
return next(inv)
} }
} }
// Read the token stored on disk.
if r.token == "" {
r.token, err = conf.Session().Read()
// Even if there isn't a token, we don't care.
// Some API routes can be unauthenticated.
if err != nil && !os.IsNotExist(err) {
return nil, err
}
}
client := &codersdk.Client{}
err = r.configureClient(inv.Context(), client, r.clientURL, inv)
if err != nil {
return nil, err
}
client.SetSessionToken(r.token)
if r.debugHTTP {
client.PlainLogger = os.Stderr
client.SetLogBodies(true)
}
client.DisableDirectConnections = r.disableDirect
return client, nil
} }
// TryInitClient is similar to InitClient but doesn't error when credentials are missing. // TryInitClient is similar to InitClient but doesn't error when credentials are missing.
// This allows commands to run without requiring authentication, but still use auth if available. // This allows commands to run without requiring authentication, but still use auth if available.
func (r *RootCmd) TryInitClient(client *codersdk.Client) serpent.MiddlewareFunc { func (r *RootCmd) TryInitClient(inv *serpent.Invocation) (*codersdk.Client, error) {
return func(next serpent.HandlerFunc) serpent.HandlerFunc { conf := r.createConfig()
return func(inv *serpent.Invocation) error { var err error
conf := r.createConfig() // Read the client URL stored on disk.
var err error if r.clientURL == nil || r.clientURL.String() == "" {
// Read the client URL stored on disk. rawURL, err := conf.URL().Read()
if r.clientURL == nil || r.clientURL.String() == "" { // If the configuration files are absent, just continue without URL
rawURL, err := conf.URL().Read() if err != nil {
// If the configuration files are absent, just continue without URL // Continue with a nil or empty URL
if err != nil { if !os.IsNotExist(err) {
// Continue with a nil or empty URL return nil, err
if !os.IsNotExist(err) {
return err
}
} else {
r.clientURL, err = url.Parse(strings.TrimSpace(rawURL))
if err != nil {
return err
}
}
} }
// Read the token stored on disk. } else {
if r.token == "" { r.clientURL, err = url.Parse(strings.TrimSpace(rawURL))
r.token, err = conf.Session().Read() if err != nil {
// Even if there isn't a token, we don't care. return nil, err
// Some API routes can be unauthenticated.
if err != nil && !os.IsNotExist(err) {
return err
}
} }
// Only configure the client if we have a URL
if r.clientURL != nil && r.clientURL.String() != "" {
err = r.configureClient(inv.Context(), client, r.clientURL, inv)
if err != nil {
return err
}
client.SetSessionToken(r.token)
if r.debugHTTP {
client.PlainLogger = os.Stderr
client.SetLogBodies(true)
}
client.DisableDirectConnections = r.disableDirect
}
return next(inv)
} }
} }
// Read the token stored on disk.
if r.token == "" {
r.token, err = conf.Session().Read()
// Even if there isn't a token, we don't care.
// Some API routes can be unauthenticated.
if err != nil && !os.IsNotExist(err) {
return nil, err
}
}
// Only configure the client if we have a URL
if r.clientURL != nil && r.clientURL.String() != "" {
client := &codersdk.Client{}
err = r.configureClient(inv.Context(), client, r.clientURL, inv)
if err != nil {
return nil, err
}
client.SetSessionToken(r.token)
if r.debugHTTP {
client.PlainLogger = os.Stderr
client.SetLogBodies(true)
}
client.DisableDirectConnections = r.disableDirect
return client, nil
}
// Return a minimal client if no URL is available
return &codersdk.Client{}, nil
} }
// HeaderTransport creates a new transport that executes `--header-command` // HeaderTransport creates a new transport that executes `--header-command`
@@ -817,17 +813,13 @@ func namedWorkspace(ctx context.Context, client *codersdk.Client, identifier str
return client.WorkspaceByOwnerAndName(ctx, owner, name, codersdk.WorkspaceOptions{}) return client.WorkspaceByOwnerAndName(ctx, owner, name, codersdk.WorkspaceOptions{})
} }
func initAppearance(client *codersdk.Client, outConfig *codersdk.AppearanceConfig) serpent.MiddlewareFunc { func initAppearance(ctx context.Context, client *codersdk.Client) codersdk.AppearanceConfig {
return func(next serpent.HandlerFunc) serpent.HandlerFunc { // best effort
return func(inv *serpent.Invocation) error { cfg, _ := client.Appearance(ctx)
cfg, _ := client.Appearance(inv.Context()) if cfg.DocsURL == "" {
if cfg.DocsURL == "" { cfg.DocsURL = codersdk.DefaultDocsURL()
cfg.DocsURL = codersdk.DefaultDocsURL()
}
*outConfig = cfg
return next(inv)
}
} }
return cfg
} }
// createConfig consumes the global configuration flag to produce a config root. // createConfig consumes the global configuration flag to produce a config root.
+16 -8
View File
@@ -90,16 +90,18 @@ func (r *RootCmd) scheduleShow() *serpent.Command {
cliui.JSONFormat(), cliui.JSONFormat(),
) )
) )
client := new(codersdk.Client)
showCmd := &serpent.Command{ showCmd := &serpent.Command{
Use: "show <workspace | --search <query> | --all>", Use: "show <workspace | --search <query> | --all>",
Short: "Show workspace schedules", Short: "Show workspace schedules",
Long: scheduleShowDescriptionLong, Long: scheduleShowDescriptionLong,
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1), serpent.RequireRangeArgs(0, 1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
// To preserve existing behavior, if an argument is passed we will // To preserve existing behavior, if an argument is passed we will
// only show the schedule for that workspace. // only show the schedule for that workspace.
// This will clobber the search query if one is passed. // This will clobber the search query if one is passed.
@@ -137,7 +139,6 @@ func (r *RootCmd) scheduleShow() *serpent.Command {
} }
func (r *RootCmd) scheduleStart() *serpent.Command { func (r *RootCmd) scheduleStart() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "start <workspace-name> { <start-time> [day-of-week] [location] | manual }", Use: "start <workspace-name> { <start-time> [day-of-week] [location] | manual }",
Long: scheduleStartDescriptionLong + "\n" + FormatExamples( Long: scheduleStartDescriptionLong + "\n" + FormatExamples(
@@ -149,9 +150,12 @@ func (r *RootCmd) scheduleStart() *serpent.Command {
Short: "Edit workspace start schedule", Short: "Edit workspace start schedule",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(2, 4), serpent.RequireRangeArgs(2, 4),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0]) workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
return err return err
@@ -193,7 +197,6 @@ func (r *RootCmd) scheduleStart() *serpent.Command {
} }
func (r *RootCmd) scheduleStop() *serpent.Command { func (r *RootCmd) scheduleStop() *serpent.Command {
client := new(codersdk.Client)
return &serpent.Command{ return &serpent.Command{
Use: "stop <workspace-name> { <duration> | manual }", Use: "stop <workspace-name> { <duration> | manual }",
Long: scheduleStopDescriptionLong + "\n" + FormatExamples( Long: scheduleStopDescriptionLong + "\n" + FormatExamples(
@@ -204,9 +207,12 @@ func (r *RootCmd) scheduleStop() *serpent.Command {
Short: "Edit workspace stop schedule", Short: "Edit workspace stop schedule",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(2), serpent.RequireNArgs(2),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0]) workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
return err return err
@@ -244,7 +250,6 @@ func (r *RootCmd) scheduleStop() *serpent.Command {
} }
func (r *RootCmd) scheduleExtend() *serpent.Command { func (r *RootCmd) scheduleExtend() *serpent.Command {
client := new(codersdk.Client)
extendCmd := &serpent.Command{ extendCmd := &serpent.Command{
Use: "extend <workspace-name> <duration from now>", Use: "extend <workspace-name> <duration from now>",
Aliases: []string{"override-stop"}, Aliases: []string{"override-stop"},
@@ -256,9 +261,12 @@ func (r *RootCmd) scheduleExtend() *serpent.Command {
), ),
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(2), serpent.RequireNArgs(2),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
extendDuration, err := parseDuration(inv.Args[1]) extendDuration, err := parseDuration(inv.Args[1])
if err != nil { if err != nil {
return err return err
+14 -7
View File
@@ -36,17 +36,19 @@ func (r *RootCmd) sharing() *serpent.Command {
} }
func (r *RootCmd) statusWorkspaceSharing() *serpent.Command { func (r *RootCmd) statusWorkspaceSharing() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "status <workspace>", Use: "status <workspace>",
Short: "List all users and groups the given Workspace is shared with.", Short: "List all users and groups the given Workspace is shared with.",
Aliases: []string{"list"}, Aliases: []string{"list"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0]) workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
return xerrors.Errorf("unable to fetch Workspace %s: %w", inv.Args[0], err) return xerrors.Errorf("unable to fetch Workspace %s: %w", inv.Args[0], err)
@@ -72,7 +74,6 @@ func (r *RootCmd) statusWorkspaceSharing() *serpent.Command {
func (r *RootCmd) shareWorkspace() *serpent.Command { func (r *RootCmd) shareWorkspace() *serpent.Command {
var ( var (
client = new(codersdk.Client)
users []string users []string
groups []string groups []string
@@ -98,10 +99,14 @@ func (r *RootCmd) shareWorkspace() *serpent.Command {
}, },
}, },
Middleware: serpent.Chain( Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
if len(users) == 0 && len(groups) == 0 { if len(users) == 0 && len(groups) == 0 {
return xerrors.New("at least one user or group must be provided") return xerrors.New("at least one user or group must be provided")
} }
@@ -171,7 +176,6 @@ func (r *RootCmd) shareWorkspace() *serpent.Command {
func (r *RootCmd) unshareWorkspace() *serpent.Command { func (r *RootCmd) unshareWorkspace() *serpent.Command {
var ( var (
client = new(codersdk.Client)
users []string users []string
groups []string groups []string
) )
@@ -194,13 +198,16 @@ func (r *RootCmd) unshareWorkspace() *serpent.Command {
}, },
}, },
Middleware: serpent.Chain( Middleware: serpent.Chain(
r.InitClient(client),
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
if len(users) == 0 && len(groups) == 0 { if len(users) == 0 && len(groups) == 0 {
return xerrors.New("at least one user or group must be provided") return xerrors.New("at least one user or group must be provided")
} }
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0]) workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
+5 -2
View File
@@ -15,7 +15,6 @@ import (
) )
func (r *RootCmd) show() *serpent.Command { func (r *RootCmd) show() *serpent.Command {
client := new(codersdk.Client)
var details bool var details bool
return &serpent.Command{ return &serpent.Command{
Use: "show <workspace>", Use: "show <workspace>",
@@ -30,9 +29,13 @@ func (r *RootCmd) show() *serpent.Command {
}, },
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
buildInfo, err := client.BuildInfo(inv.Context()) buildInfo, err := client.BuildInfo(inv.Context())
if err != nil { if err != nil {
return xerrors.Errorf("get server version: %w", err) return xerrors.Errorf("get server version: %w", err)
+10 -10
View File
@@ -13,7 +13,6 @@ import (
"cdr.dev/slog" "cdr.dev/slog"
"cdr.dev/slog/sloggers/sloghuman" "cdr.dev/slog/sloggers/sloghuman"
"github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/workspacesdk" "github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/serpent" "github.com/coder/serpent"
) )
@@ -36,12 +35,11 @@ type speedtestTableItem struct {
func (r *RootCmd) speedtest() *serpent.Command { func (r *RootCmd) speedtest() *serpent.Command {
var ( var (
direct bool direct bool
duration time.Duration duration time.Duration
direction string direction string
pcapFile string pcapFile string
appearanceConfig codersdk.AppearanceConfig formatter = cliui.NewOutputFormatter(
formatter = cliui.NewOutputFormatter(
cliui.ChangeFormatterData(cliui.TableFormat([]speedtestTableItem{}, []string{"Interval", "Throughput"}), func(data any) (any, error) { cliui.ChangeFormatterData(cliui.TableFormat([]speedtestTableItem{}, []string{"Interval", "Throughput"}), func(data any) (any, error) {
res, ok := data.(SpeedtestResult) res, ok := data.(SpeedtestResult)
if !ok { if !ok {
@@ -65,19 +63,21 @@ func (r *RootCmd) speedtest() *serpent.Command {
cliui.JSONFormat(), cliui.JSONFormat(),
) )
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "speedtest <workspace>", Use: "speedtest <workspace>",
Short: "Run upload and download tests from your machine to a workspace", Short: "Run upload and download tests from your machine to a workspace",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
initAppearance(client, &appearanceConfig),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx, cancel := context.WithCancel(inv.Context()) ctx, cancel := context.WithCancel(inv.Context())
defer cancel() defer cancel()
client, err := r.InitClient(inv)
if err != nil {
return err
}
appearanceConfig := initAppearance(ctx, client)
if direct && r.disableDirect { if direct && r.disableDirect {
return xerrors.Errorf("--direct (-d) is incompatible with --%s", varDisableDirect) return xerrors.Errorf("--direct (-d) is incompatible with --%s", varDisableDirect)
+7 -5
View File
@@ -79,15 +79,12 @@ func (r *RootCmd) ssh() *serpent.Command {
env []string env []string
usageApp string usageApp string
disableAutostart bool disableAutostart bool
appearanceConfig codersdk.AppearanceConfig
networkInfoDir string networkInfoDir string
networkInfoInterval time.Duration networkInfoInterval time.Duration
containerName string containerName string
containerUser string containerUser string
) )
client := new(codersdk.Client)
wsClient := workspacesdk.New(client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "ssh <workspace> [command]", Use: "ssh <workspace> [command]",
@@ -111,10 +108,15 @@ func (r *RootCmd) ssh() *serpent.Command {
return next(i) return next(i)
} }
}, },
r.InitClient(client),
initAppearance(client, &appearanceConfig),
), ),
Handler: func(inv *serpent.Invocation) (retErr error) { Handler: func(inv *serpent.Invocation) (retErr error) {
client, err := r.InitClient(inv)
if err != nil {
return err
}
appearanceConfig := initAppearance(inv.Context(), client)
wsClient := workspacesdk.New(client)
command := strings.Join(inv.Args[1:], " ") command := strings.Join(inv.Args[1:], " ")
// Before dialing the SSH server over TCP, capture Interrupt signals // Before dialing the SSH server over TCP, capture Interrupt signals
+5 -2
View File
@@ -21,14 +21,12 @@ func (r *RootCmd) start() *serpent.Command {
noWait bool noWait bool
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "start <workspace>", Use: "start <workspace>",
Short: "Start a workspace", Short: "Start a workspace",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
{ {
@@ -40,6 +38,11 @@ func (r *RootCmd) start() *serpent.Command {
cliui.SkipPromptOption(), cliui.SkipPromptOption(),
}, },
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0]) workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
return err return err
+8 -5
View File
@@ -28,16 +28,17 @@ func (r *RootCmd) state() *serpent.Command {
func (r *RootCmd) statePull() *serpent.Command { func (r *RootCmd) statePull() *serpent.Command {
var buildNumber int64 var buildNumber int64
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "pull <workspace> [file]", Use: "pull <workspace> [file]",
Short: "Pull a Terraform state file from a workspace.", Short: "Pull a Terraform state file from a workspace.",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(1, 2), serpent.RequireRangeArgs(1, 2),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var err error client, err := r.InitClient(inv)
if err != nil {
return err
}
var build codersdk.WorkspaceBuild var build codersdk.WorkspaceBuild
if buildNumber == 0 { if buildNumber == 0 {
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0]) workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
@@ -86,15 +87,17 @@ func buildNumberOption(n *int64) serpent.Option {
func (r *RootCmd) statePush() *serpent.Command { func (r *RootCmd) statePush() *serpent.Command {
var buildNumber int64 var buildNumber int64
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "push <workspace> <file>", Use: "push <workspace> <file>",
Short: "Push a Terraform state file to a workspace.", Short: "Push a Terraform state file to a workspace.",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(2), serpent.RequireNArgs(2),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0]) workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
return err return err
+6 -3
View File
@@ -12,20 +12,23 @@ import (
func (r *RootCmd) stop() *serpent.Command { func (r *RootCmd) stop() *serpent.Command {
var bflags buildFlags var bflags buildFlags
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "stop <workspace>", Use: "stop <workspace>",
Short: "Stop a workspace", Short: "Stop a workspace",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
cliui.SkipPromptOption(), cliui.SkipPromptOption(),
}, },
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
_, err := cliui.Prompt(inv, cliui.PromptOptions{ client, err := r.InitClient(inv)
if err != nil {
return err
}
_, err = cliui.Prompt(inv, cliui.PromptOptions{
Text: "Confirm stop workspace?", Text: "Confirm stop workspace?",
IsConfirm: true, IsConfirm: true,
}) })
+4 -2
View File
@@ -62,16 +62,18 @@ var supportBundleBlurb = cliui.Bold("This will collect the following information
func (r *RootCmd) supportBundle() *serpent.Command { func (r *RootCmd) supportBundle() *serpent.Command {
var outputPath string var outputPath string
var coderURLOverride string var coderURLOverride string
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "bundle <workspace> [<agent>]", Use: "bundle <workspace> [<agent>]",
Short: "Generate a support bundle to troubleshoot issues connecting to a workspace.", Short: "Generate a support bundle to troubleshoot issues connecting to a workspace.",
Long: `This command generates a file containing detailed troubleshooting information about the Coder deployment and workspace connections. You must specify a single workspace (and optionally an agent name).`, Long: `This command generates a file containing detailed troubleshooting information about the Coder deployment and workspace connections. You must specify a single workspace (and optionally an agent name).`,
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 2), serpent.RequireRangeArgs(0, 2),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
var cliLogBuf bytes.Buffer var cliLogBuf bytes.Buffer
cliLogW := sloghuman.Sink(&cliLogBuf) cliLogW := sloghuman.Sink(&cliLogBuf)
cliLog := slog.Make(cliLogW).Leveled(slog.LevelDebug) cliLog := slog.Make(cliLogW).Leveled(slog.LevelDebug)
+4 -2
View File
@@ -33,7 +33,6 @@ func (r *RootCmd) templateCreate() *serpent.Command {
uploadFlags templateUploadFlags uploadFlags templateUploadFlags
orgContext = NewOrganizationContext() orgContext = NewOrganizationContext()
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "create [name]", Use: "create [name]",
Short: "DEPRECATED: Create a template from the current directory or as specified by flag", Short: "DEPRECATED: Create a template from the current directory or as specified by flag",
@@ -43,9 +42,12 @@ func (r *RootCmd) templateCreate() *serpent.Command {
"Use `coder templates push` command for creating and updating templates. \n"+ "Use `coder templates push` command for creating and updating templates. \n"+
"Use `coder templates edit` command for editing template settings. ", "Use `coder templates edit` command for editing template settings. ",
), ),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
isTemplateSchedulingOptionsSet := failureTTL != 0 || dormancyThreshold != 0 || dormancyAutoDeletion != 0 isTemplateSchedulingOptionsSet := failureTTL != 0 || dormancyThreshold != 0 || dormancyAutoDeletion != 0
if isTemplateSchedulingOptionsSet || requireActiveVersion { if isTemplateSchedulingOptionsSet || requireActiveVersion {
+4 -5
View File
@@ -16,13 +16,9 @@ import (
func (r *RootCmd) templateDelete() *serpent.Command { func (r *RootCmd) templateDelete() *serpent.Command {
orgContext := NewOrganizationContext() orgContext := NewOrganizationContext()
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "delete [name...]", Use: "delete [name...]",
Short: "Delete templates", Short: "Delete templates",
Middleware: serpent.Chain(
r.InitClient(client),
),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
cliui.SkipPromptOption(), cliui.SkipPromptOption(),
}, },
@@ -32,7 +28,10 @@ func (r *RootCmd) templateDelete() *serpent.Command {
templateNames = []string{} templateNames = []string{}
templates = []codersdk.Template{} templates = []codersdk.Template{}
) )
client, err := r.InitClient(inv)
if err != nil {
return err
}
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return err return err
+4 -2
View File
@@ -37,16 +37,18 @@ func (r *RootCmd) templateEdit() *serpent.Command {
disableEveryone bool disableEveryone bool
orgContext = NewOrganizationContext() orgContext = NewOrganizationContext()
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "edit <template>", Use: "edit <template>",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Short: "Edit the metadata of a template by name.", Short: "Edit the metadata of a template by name.",
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
unsetAutostopRequirementDaysOfWeek := len(autostopRequirementDaysOfWeek) == 1 && autostopRequirementDaysOfWeek[0] == "none" unsetAutostopRequirementDaysOfWeek := len(autostopRequirementDaysOfWeek) == 1 && autostopRequirementDaysOfWeek[0] == "none"
requiresScheduling := (len(autostopRequirementDaysOfWeek) > 0 && !unsetAutostopRequirementDaysOfWeek) || requiresScheduling := (len(autostopRequirementDaysOfWeek) > 0 && !unsetAutostopRequirementDaysOfWeek) ||
autostopRequirementWeeks > 0 || autostopRequirementWeeks > 0 ||
+4 -4
View File
@@ -16,15 +16,15 @@ func (r *RootCmd) templateList() *serpent.Command {
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "list", Use: "list",
Short: "List all the templates available for the organization", Short: "List all the templates available for the organization",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
templates, err := client.Templates(inv.Context(), codersdk.TemplateFilter{}) templates, err := client.Templates(inv.Context(), codersdk.TemplateFilter{})
if err != nil { if err != nil {
return err return err
+4 -2
View File
@@ -50,7 +50,6 @@ func (r *RootCmd) templatePresetsList() *serpent.Command {
cliui.TableFormat([]TemplatePresetRow{}, defaultColumns), cliui.TableFormat([]TemplatePresetRow{}, defaultColumns),
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client)
orgContext := NewOrganizationContext() orgContext := NewOrganizationContext()
var templateVersion string var templateVersion string
@@ -59,7 +58,6 @@ func (r *RootCmd) templatePresetsList() *serpent.Command {
Use: "list <template>", Use: "list <template>",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Short: "List all presets of the specified template. Defaults to the active template version.", Short: "List all presets of the specified template. Defaults to the active template version.",
Options: serpent.OptionSet{ Options: serpent.OptionSet{
@@ -71,6 +69,10 @@ func (r *RootCmd) templatePresetsList() *serpent.Command {
}, },
}, },
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return xerrors.Errorf("get current organization: %w", err) return xerrors.Errorf("get current organization: %w", err)
+4 -2
View File
@@ -23,13 +23,11 @@ func (r *RootCmd) templatePull() *serpent.Command {
orgContext = NewOrganizationContext() orgContext = NewOrganizationContext()
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "pull <name> [destination]", Use: "pull <name> [destination]",
Short: "Download the active, latest, or specified version of a template to a path.", Short: "Download the active, latest, or specified version of a template to a path.",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(1, 2), serpent.RequireRangeArgs(1, 2),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var ( var (
@@ -37,6 +35,10 @@ func (r *RootCmd) templatePull() *serpent.Command {
templateName = inv.Args[0] templateName = inv.Args[0]
dest string dest string
) )
client, err := r.InitClient(inv)
if err != nil {
return err
}
if len(inv.Args) > 1 { if len(inv.Args) > 1 {
dest = inv.Args[1] dest = inv.Args[1]
+4 -2
View File
@@ -37,15 +37,17 @@ func (r *RootCmd) templatePush() *serpent.Command {
activate bool activate bool
orgContext = NewOrganizationContext() orgContext = NewOrganizationContext()
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "push [template]", Use: "push [template]",
Short: "Create or update a template from the current directory or as specified by flag", Short: "Create or update a template from the current directory or as specified by flag",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1), serpent.RequireRangeArgs(0, 1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
uploadFlags.setWorkdir(workdir) uploadFlags.setWorkdir(workdir)
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
+9 -9
View File
@@ -32,13 +32,9 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Command {
} }
orgContext := NewOrganizationContext() orgContext := NewOrganizationContext()
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: presentVerb + " <template-name> [template-version-names...] ", Use: presentVerb + " <template-name> [template-version-names...] ",
Short: strings.ToUpper(string(presentVerb[0])) + presentVerb[1:] + " a template version(s).", Short: strings.ToUpper(string(presentVerb[0])) + presentVerb[1:] + " a template version(s).",
Middleware: serpent.Chain(
r.InitClient(client),
),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
cliui.SkipPromptOption(), cliui.SkipPromptOption(),
}, },
@@ -48,6 +44,11 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Command {
versions []codersdk.TemplateVersion versions []codersdk.TemplateVersion
) )
client, err := r.InitClient(inv)
if err != nil {
return err
}
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return err return err
@@ -100,14 +101,10 @@ func (r *RootCmd) setArchiveTemplateVersion(archive bool) *serpent.Command {
func (r *RootCmd) archiveTemplateVersions() *serpent.Command { func (r *RootCmd) archiveTemplateVersions() *serpent.Command {
var all serpent.Bool var all serpent.Bool
client := new(codersdk.Client)
orgContext := NewOrganizationContext() orgContext := NewOrganizationContext()
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "archive [template-name...] ", Use: "archive [template-name...] ",
Short: "Archive unused or failed template versions from a given template(s)", Short: "Archive unused or failed template versions from a given template(s)",
Middleware: serpent.Chain(
r.InitClient(client),
),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
cliui.SkipPromptOption(), cliui.SkipPromptOption(),
serpent.Option{ serpent.Option{
@@ -123,7 +120,10 @@ func (r *RootCmd) archiveTemplateVersions() *serpent.Command {
templateNames = []string{} templateNames = []string{}
templates = []codersdk.Template{} templates = []codersdk.Template{}
) )
client, err := r.InitClient(inv)
if err != nil {
return err
}
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return err return err
+8 -6
View File
@@ -51,7 +51,6 @@ func (r *RootCmd) templateVersionsList() *serpent.Command {
cliui.TableFormat([]templateVersionRow{}, defaultColumns), cliui.TableFormat([]templateVersionRow{}, defaultColumns),
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client)
orgContext := NewOrganizationContext() orgContext := NewOrganizationContext()
var includeArchived serpent.Bool var includeArchived serpent.Bool
@@ -60,7 +59,6 @@ func (r *RootCmd) templateVersionsList() *serpent.Command {
Use: "list <template>", Use: "list <template>",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
func(next serpent.HandlerFunc) serpent.HandlerFunc { func(next serpent.HandlerFunc) serpent.HandlerFunc {
return func(i *serpent.Invocation) error { return func(i *serpent.Invocation) error {
// This is the only way to dynamically add the "archived" // This is the only way to dynamically add the "archived"
@@ -95,6 +93,10 @@ func (r *RootCmd) templateVersionsList() *serpent.Command {
}, },
}, },
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return xerrors.Errorf("get current organization: %w", err) return xerrors.Errorf("get current organization: %w", err)
@@ -177,15 +179,15 @@ func (r *RootCmd) templateVersionsPromote() *serpent.Command {
templateVersionName string templateVersionName string
orgContext = NewOrganizationContext() orgContext = NewOrganizationContext()
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "promote --template=<template_name> --template-version=<template_version_name>", Use: "promote --template=<template_name> --template-version=<template_version_name>",
Short: "Promote a template version to active.", Short: "Promote a template version to active.",
Long: "Promote an existing template version to be the active version for the specified template.", Long: "Promote an existing template version to be the active version for the specified template.",
Middleware: serpent.Chain(
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return err return err
+15 -7
View File
@@ -51,22 +51,24 @@ func (r *RootCmd) createToken() *serpent.Command {
name string name string
user string user string
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "create", Use: "create",
Short: "Create a token", Short: "Create a token",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
userID := codersdk.Me userID := codersdk.Me
if user != "" { if user != "" {
userID = user userID = user
} }
var parsedLifetime time.Duration var parsedLifetime time.Duration
var err error
tokenConfig, err := client.GetTokenConfig(inv.Context(), userID) tokenConfig, err := client.GetTokenConfig(inv.Context(), userID)
if err != nil { if err != nil {
@@ -168,16 +170,19 @@ func (r *RootCmd) listTokens() *serpent.Command {
) )
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "list", Use: "list",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Short: "List tokens", Short: "List tokens",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
tokens, err := client.Tokens(inv.Context(), codersdk.Me, codersdk.TokensFilter{ tokens, err := client.Tokens(inv.Context(), codersdk.Me, codersdk.TokensFilter{
IncludeAll: all, IncludeAll: all,
}) })
@@ -222,16 +227,19 @@ func (r *RootCmd) listTokens() *serpent.Command {
} }
func (r *RootCmd) removeToken() *serpent.Command { func (r *RootCmd) removeToken() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "remove <name|id|token>", Use: "remove <name|id|token>",
Aliases: []string{"delete"}, Aliases: []string{"delete"},
Short: "Delete a token", Short: "Delete a token",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
token, err := client.APIKeyByName(inv.Context(), codersdk.Me, inv.Args[0]) token, err := client.APIKeyByName(inv.Context(), codersdk.Me, inv.Args[0])
if err != nil { if err != nil {
// If it's a token, we need to extract the ID // If it's a token, we need to extract the ID
+5 -2
View File
@@ -15,7 +15,6 @@ func (r *RootCmd) update() *serpent.Command {
parameterFlags workspaceParameterFlags parameterFlags workspaceParameterFlags
bflags buildFlags bflags buildFlags
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "update <workspace>", Use: "update <workspace>",
@@ -23,9 +22,13 @@ func (r *RootCmd) update() *serpent.Command {
Long: "Use --always-prompt to change the parameter values of the workspace.", Long: "Use --always-prompt to change the parameter values of the workspace.",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0]) workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
if err != nil { if err != nil {
return err return err
+5 -2
View File
@@ -26,15 +26,18 @@ func (r *RootCmd) userCreate() *serpent.Command {
loginType string loginType string
orgContext = NewOrganizationContext() orgContext = NewOrganizationContext()
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "create", Use: "create",
Short: "Create a new user.", Short: "Create a new user.",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
organization, err := orgContext.Selected(inv, client) organization, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
return err return err
+4 -3
View File
@@ -6,22 +6,23 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/pretty" "github.com/coder/pretty"
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) userDelete() *serpent.Command { func (r *RootCmd) userDelete() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "delete <username|user_id>", Use: "delete <username|user_id>",
Short: "Delete a user by username or user_id.", Short: "Delete a user by username or user_id.",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
user, err := client.User(ctx, inv.Args[0]) user, err := client.User(ctx, inv.Args[0])
if err != nil { if err != nil {
return xerrors.Errorf("fetch user: %w", err) return xerrors.Errorf("fetch user: %w", err)
+6 -2
View File
@@ -12,7 +12,6 @@ import (
) )
func (r *RootCmd) userEditRoles() *serpent.Command { func (r *RootCmd) userEditRoles() *serpent.Command {
client := new(codersdk.Client)
var givenRoles []string var givenRoles []string
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "edit-roles <username|user_id>", Use: "edit-roles <username|user_id>",
@@ -26,8 +25,13 @@ func (r *RootCmd) userEditRoles() *serpent.Command {
Value: serpent.StringArrayOf(&givenRoles), Value: serpent.StringArrayOf(&givenRoles),
}, },
}, },
Middleware: serpent.Chain(serpent.RequireNArgs(1), r.InitClient(client)), Middleware: serpent.Chain(serpent.RequireNArgs(1)),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
user, err := client.User(ctx, inv.Args[0]) user, err := client.User(ctx, inv.Args[0])
+10 -4
View File
@@ -18,7 +18,6 @@ func (r *RootCmd) userList() *serpent.Command {
cliui.TableFormat([]codersdk.User{}, []string{"username", "email", "created at", "status"}), cliui.TableFormat([]codersdk.User{}, []string{"username", "email", "created at", "status"}),
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client)
var githubUserID int64 var githubUserID int64
cmd := &serpent.Command{ cmd := &serpent.Command{
@@ -27,7 +26,6 @@ func (r *RootCmd) userList() *serpent.Command {
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Options: serpent.OptionSet{ Options: serpent.OptionSet{
{ {
@@ -40,6 +38,11 @@ func (r *RootCmd) userList() *serpent.Command {
}, },
}, },
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
req := codersdk.UsersRequest{} req := codersdk.UsersRequest{}
if githubUserID != 0 { if githubUserID != 0 {
req.Search = fmt.Sprintf("github_com_user_id:%d", githubUserID) req.Search = fmt.Sprintf("github_com_user_id:%d", githubUserID)
@@ -69,7 +72,6 @@ func (r *RootCmd) userSingle() *serpent.Command {
&userShowFormat{}, &userShowFormat{},
cliui.JSONFormat(), cliui.JSONFormat(),
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "show <username|user_id|'me'>", Use: "show <username|user_id|'me'>",
@@ -81,9 +83,13 @@ func (r *RootCmd) userSingle() *serpent.Command {
), ),
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
user, err := client.User(inv.Context(), inv.Args[0]) user, err := client.User(inv.Context(), inv.Args[0])
if err != nil { if err != nil {
return err return err
+4 -3
View File
@@ -33,8 +33,6 @@ func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *serpen
panic(fmt.Sprintf("%s is not supported", sdkStatus)) panic(fmt.Sprintf("%s is not supported", sdkStatus))
} }
client := new(codersdk.Client)
var columns []string var columns []string
allColumns := []string{"username", "email", "created at", "status"} allColumns := []string{"username", "email", "created at", "status"}
cmd := &serpent.Command{ cmd := &serpent.Command{
@@ -48,9 +46,12 @@ func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *serpen
), ),
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
identifier := inv.Args[0] identifier := inv.Args[0]
if identifier == "" { if identifier == "" {
return xerrors.Errorf("user identifier cannot be an empty string") return xerrors.Errorf("user identifier cannot be an empty string")
+5 -2
View File
@@ -10,16 +10,19 @@ import (
) )
func (r *RootCmd) whoami() *serpent.Command { func (r *RootCmd) whoami() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: workspaceCommand, Annotations: workspaceCommand,
Use: "whoami", Use: "whoami",
Short: "Fetch authenticated user info for Coder deployment", Short: "Fetch authenticated user info for Coder deployment",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
ctx := inv.Context() ctx := inv.Context()
// Fetch the user info // Fetch the user info
resp, err := client.User(ctx, codersdk.Me) resp, err := client.User(ctx, codersdk.Me)
+17 -8
View File
@@ -77,10 +77,12 @@ func (r *RootCmd) externalWorkspaceCreate() *serpent.Command {
cmd := r.Create(opts) cmd := r.Create(opts)
cmd.Use = "create [workspace]" cmd.Use = "create [workspace]"
cmd.Short = "Create a new external workspace" cmd.Short = "Create a new external workspace"
cmd.Middleware = serpent.Chain( newMiddlewares := []serpent.MiddlewareFunc{}
cmd.Middleware, if cmd.Middleware != nil {
serpent.RequireNArgs(1), newMiddlewares = append(newMiddlewares, cmd.Middleware)
) }
newMiddlewares = append(newMiddlewares, serpent.RequireNArgs(1))
cmd.Middleware = serpent.Chain(newMiddlewares...)
for i := range cmd.Options { for i := range cmd.Options {
if cmd.Options[i].Flag == "template" { if cmd.Options[i].Flag == "template" {
@@ -93,7 +95,6 @@ func (r *RootCmd) externalWorkspaceCreate() *serpent.Command {
// externalWorkspaceAgentInstructions prints the instructions for an external agent. // externalWorkspaceAgentInstructions prints the instructions for an external agent.
func (r *RootCmd) externalWorkspaceAgentInstructions() *serpent.Command { func (r *RootCmd) externalWorkspaceAgentInstructions() *serpent.Command {
client := new(codersdk.Client)
formatter := cliui.NewOutputFormatter( formatter := cliui.NewOutputFormatter(
cliui.ChangeFormatterData(cliui.TextFormat(), func(data any) (any, error) { cliui.ChangeFormatterData(cliui.TextFormat(), func(data any) (any, error) {
agent, ok := data.(externalAgent) agent, ok := data.(externalAgent)
@@ -109,8 +110,13 @@ func (r *RootCmd) externalWorkspaceAgentInstructions() *serpent.Command {
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "agent-instructions [user/]workspace[.agent]", Use: "agent-instructions [user/]workspace[.agent]",
Short: "Get the instructions for an external agent", Short: "Get the instructions for an external agent",
Middleware: serpent.Chain(r.InitClient(client), serpent.RequireNArgs(1)), Middleware: serpent.Chain(serpent.RequireNArgs(1)),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
workspace, workspaceAgent, _, err := agpl.GetWorkspaceAndAgent(inv.Context(), inv, client, false, inv.Args[0]) workspace, workspaceAgent, _, err := agpl.GetWorkspaceAndAgent(inv.Context(), inv, client, false, inv.Args[0])
if err != nil { if err != nil {
return xerrors.Errorf("find workspace and agent: %w", err) return xerrors.Errorf("find workspace and agent: %w", err)
@@ -162,7 +168,6 @@ func (r *RootCmd) externalWorkspaceList() *serpent.Command {
cliui.JSONFormat(), cliui.JSONFormat(),
) )
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Annotations: map[string]string{ Annotations: map[string]string{
"workspaces": "", "workspaces": "",
@@ -172,9 +177,13 @@ func (r *RootCmd) externalWorkspaceList() *serpent.Command {
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
baseFilter := filter.Filter() baseFilter := filter.Filter()
if baseFilter.FilterQuery == "" { if baseFilter.FilterQuery == "" {
+7 -6
View File
@@ -36,15 +36,16 @@ func (r *RootCmd) featuresList() *serpent.Command {
columns []string columns []string
outputFormat string outputFormat string
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "list", Use: "list",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
entitlements, err := client.Entitlements(inv.Context()) entitlements, err := client.Entitlements(inv.Context())
var apiError *codersdk.Error var apiError *codersdk.Error
if errors.As(err, &apiError) && apiError.StatusCode() == http.StatusNotFound { if errors.As(err, &apiError) && apiError.StatusCode() == http.StatusNotFound {
+4 -2
View File
@@ -19,16 +19,18 @@ func (r *RootCmd) groupCreate() *serpent.Command {
orgContext = agpl.NewOrganizationContext() orgContext = agpl.NewOrganizationContext()
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "create <name>", Use: "create <name>",
Short: "Create a user group", Short: "Create a user group",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
+4 -3
View File
@@ -7,26 +7,27 @@ import (
agpl "github.com/coder/coder/v2/cli" agpl "github.com/coder/coder/v2/cli"
"github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/pretty" "github.com/coder/pretty"
"github.com/coder/serpent" "github.com/coder/serpent"
) )
func (r *RootCmd) groupDelete() *serpent.Command { func (r *RootCmd) groupDelete() *serpent.Command {
orgContext := agpl.NewOrganizationContext() orgContext := agpl.NewOrganizationContext()
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "delete <name>", Use: "delete <name>",
Short: "Delete a user group", Short: "Delete a user group",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var ( var (
ctx = inv.Context() ctx = inv.Context()
groupName = inv.Args[0] groupName = inv.Args[0]
) )
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
+4 -2
View File
@@ -24,19 +24,21 @@ func (r *RootCmd) groupEdit() *serpent.Command {
rmUsers []string rmUsers []string
orgContext = agpl.NewOrganizationContext() orgContext = agpl.NewOrganizationContext()
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "edit <name>", Use: "edit <name>",
Short: "Edit a user group", Short: "Edit a user group",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var ( var (
ctx = inv.Context() ctx = inv.Context()
groupName = inv.Args[0] groupName = inv.Args[0]
) )
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
+4 -2
View File
@@ -20,16 +20,18 @@ func (r *RootCmd) groupList() *serpent.Command {
) )
orgContext := agpl.NewOrganizationContext() orgContext := agpl.NewOrganizationContext()
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "list", Use: "list",
Short: "List user groups", Short: "List user groups",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
+15 -7
View File
@@ -42,16 +42,18 @@ func (r *RootCmd) licenseAdd() *serpent.Command {
license string license string
debug bool debug bool
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "add [-f file | -l license]", Use: "add [-f file | -l license]",
Short: "Add license to Coder deployment", Short: "Add license to Coder deployment",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
var err error client, err := r.InitClient(inv)
if err != nil {
return err
}
switch { switch {
case filename != "" && license != "": case filename != "" && license != "":
return xerrors.New("only one of (--file, --license) may be specified") return xerrors.New("only one of (--file, --license) may be specified")
@@ -137,16 +139,19 @@ func validJWT(s string) error {
func (r *RootCmd) licensesList() *serpent.Command { func (r *RootCmd) licensesList() *serpent.Command {
formatter := cliutil.NewLicenseFormatter() formatter := cliutil.NewLicenseFormatter()
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "list", Use: "list",
Short: "List licenses (including expired)", Short: "List licenses (including expired)",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
licenses, err := client.Licenses(inv.Context()) licenses, err := client.Licenses(inv.Context())
if err != nil { if err != nil {
return err return err
@@ -170,16 +175,19 @@ func (r *RootCmd) licensesList() *serpent.Command {
} }
func (r *RootCmd) licenseDelete() *serpent.Command { func (r *RootCmd) licenseDelete() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "delete <id>", Use: "delete <id>",
Short: "Delete license by ID", Short: "Delete license by ID",
Aliases: []string{"del"}, Aliases: []string{"del"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
id, err := strconv.ParseInt(inv.Args[0], 10, 32) id, err := strconv.ParseInt(inv.Args[0], 10, 32)
if err != nil { if err != nil {
return xerrors.Errorf("license ID must be an integer: %s", inv.Args[0]) return xerrors.Errorf("license ID must be an integer: %s", inv.Args[0])
+12 -6
View File
@@ -38,16 +38,19 @@ func (r *RootCmd) prebuilds() *serpent.Command {
} }
func (r *RootCmd) pausePrebuilds() *serpent.Command { func (r *RootCmd) pausePrebuilds() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "pause", Use: "pause",
Short: "Pause prebuilds", Short: "Pause prebuilds",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
err := client.PutPrebuildsSettings(inv.Context(), codersdk.PrebuildsSettings{ client, err := r.InitClient(inv)
if err != nil {
return err
}
err = client.PutPrebuildsSettings(inv.Context(), codersdk.PrebuildsSettings{
ReconciliationPaused: true, ReconciliationPaused: true,
}) })
if err != nil { if err != nil {
@@ -62,16 +65,19 @@ func (r *RootCmd) pausePrebuilds() *serpent.Command {
} }
func (r *RootCmd) resumePrebuilds() *serpent.Command { func (r *RootCmd) resumePrebuilds() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "resume", Use: "resume",
Short: "Resume prebuilds", Short: "Resume prebuilds",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
err := client.PutPrebuildsSettings(inv.Context(), codersdk.PrebuildsSettings{ client, err := r.InitClient(inv)
if err != nil {
return err
}
err = client.PutPrebuildsSettings(inv.Context(), codersdk.PrebuildsSettings{
ReconciliationPaused: false, ReconciliationPaused: false,
}) })
if err != nil { if err != nil {
+4 -7
View File
@@ -53,19 +53,16 @@ func (r *RootCmd) provisionerDaemonStart() *serpent.Command {
prometheusAddress string prometheusAddress string
) )
orgContext := agpl.NewOrganizationContext() orgContext := agpl.NewOrganizationContext()
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "start", Use: "start",
Short: "Run a provisioner daemon", Short: "Run a provisioner daemon",
Middleware: serpent.Chain(
// disable checks and warnings because this command starts a daemon; it is
// not meant for humans typing commands. Furthermore, the checks are
// incompatible with PSK auth that this command uses
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx, cancel := context.WithCancel(inv.Context()) ctx, cancel := context.WithCancel(inv.Context())
defer cancel() defer cancel()
client, err := r.InitClient(inv)
if err != nil {
return err
}
stopCtx, stopCancel := inv.SignalNotifyContext(ctx, agpl.StopSignalsNoInterrupt...) stopCtx, stopCancel := inv.SignalNotifyContext(ctx, agpl.StopSignalsNoInterrupt...)
defer stopCancel() defer stopCancel()
+12 -6
View File
@@ -37,16 +37,18 @@ func (r *RootCmd) provisionerKeysCreate() *serpent.Command {
rawTags []string rawTags []string
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "create <name>", Use: "create <name>",
Short: "Create a new provisioner key", Short: "Create a new provisioner key",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
@@ -100,17 +102,19 @@ func (r *RootCmd) provisionerKeysList() *serpent.Command {
) )
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "list", Use: "list",
Short: "List provisioner keys in an organization", Short: "List provisioner keys in an organization",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
@@ -147,16 +151,18 @@ func (r *RootCmd) provisionerKeysList() *serpent.Command {
func (r *RootCmd) provisionerKeysDelete() *serpent.Command { func (r *RootCmd) provisionerKeysDelete() *serpent.Command {
orgContext := agpl.NewOrganizationContext() orgContext := agpl.NewOrganizationContext()
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "delete <name>", Use: "delete <name>",
Short: "Delete a provisioner key", Short: "Delete a provisioner key",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client) org, err := orgContext.Selected(inv, client)
if err != nil { if err != nil {
+20 -11
View File
@@ -42,17 +42,19 @@ func (r *RootCmd) workspaceProxy() *serpent.Command {
func (r *RootCmd) regenerateProxyToken() *serpent.Command { func (r *RootCmd) regenerateProxyToken() *serpent.Command {
formatter := newUpdateProxyResponseFormatter() formatter := newUpdateProxyResponseFormatter()
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "regenerate-token <name|id>", Use: "regenerate-token <name|id>",
Short: "Regenerate a workspace proxy authentication token. " + Short: "Regenerate a workspace proxy authentication token. " +
"This will invalidate the existing authentication token.", "This will invalidate the existing authentication token.",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
formatter.primaryAccessURL = client.URL.String() formatter.primaryAccessURL = client.URL.String()
// This is cheeky, but you can also use a uuid string in // This is cheeky, but you can also use a uuid string in
// 'DeleteWorkspaceProxyByName' and it will work. // 'DeleteWorkspaceProxyByName' and it will work.
@@ -112,16 +114,18 @@ func (r *RootCmd) patchProxy() *serpent.Command {
}), }),
) )
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "edit <name|id>", Use: "edit <name|id>",
Short: "Edit a workspace proxy", Short: "Edit a workspace proxy",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
if proxyIcon == "" && displayName == "" && proxyName == "" { if proxyIcon == "" && displayName == "" && proxyName == "" {
_ = inv.Command.HelpHandler(inv) _ = inv.Command.HelpHandler(inv)
return xerrors.Errorf("specify at least one field to update") return xerrors.Errorf("specify at least one field to update")
@@ -187,7 +191,6 @@ func (r *RootCmd) patchProxy() *serpent.Command {
} }
func (r *RootCmd) deleteProxy() *serpent.Command { func (r *RootCmd) deleteProxy() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "delete <name|id>", Use: "delete <name|id>",
Short: "Delete a workspace proxy", Short: "Delete a workspace proxy",
@@ -196,10 +199,13 @@ func (r *RootCmd) deleteProxy() *serpent.Command {
}, },
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(1), serpent.RequireNArgs(1),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
wsproxy, err := client.WorkspaceProxyByName(ctx, inv.Args[0]) wsproxy, err := client.WorkspaceProxyByName(ctx, inv.Args[0])
if err != nil { if err != nil {
@@ -244,18 +250,19 @@ func (r *RootCmd) createProxy() *serpent.Command {
return nil return nil
} }
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "create", Use: "create",
Short: "Create a workspace proxy", Short: "Create a workspace proxy",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
formatter.primaryAccessURL = client.URL.String() formatter.primaryAccessURL = client.URL.String()
var err error
if proxyName == "" && !noPrompts { if proxyName == "" && !noPrompts {
proxyName, err = cliui.Prompt(inv, cliui.PromptOptions{ proxyName, err = cliui.Prompt(inv, cliui.PromptOptions{
Text: "Proxy Name:", Text: "Proxy Name:",
@@ -362,17 +369,19 @@ func (r *RootCmd) listProxies() *serpent.Command {
}), }),
) )
client := new(codersdk.Client)
cmd := &serpent.Command{ cmd := &serpent.Command{
Use: "ls", Use: "ls",
Aliases: []string{"list"}, Aliases: []string{"list"},
Short: "List all workspace proxies", Short: "List all workspace proxies",
Middleware: serpent.Chain( Middleware: serpent.Chain(
serpent.RequireNArgs(0), serpent.RequireNArgs(0),
r.InitClient(client),
), ),
Handler: func(inv *serpent.Invocation) error { Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context() ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
proxies, err := client.WorkspaceProxies(ctx) proxies, err := client.WorkspaceProxies(ctx)
if err != nil { if err != nil {
return xerrors.Errorf("list workspace proxies: %w", err) return xerrors.Errorf("list workspace proxies: %w", err)