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