diff --git a/cmd/coder/auth.go b/cmd/coder/auth.go index d15cf914..abd0e8d0 100644 --- a/cmd/coder/auth.go +++ b/cmd/coder/auth.go @@ -35,8 +35,10 @@ func newClient() (*entclient.Client, error) { return nil, xerrors.Errorf("url misformatted: %v (try runing coder login)", err) } - return &entclient.Client{ + client := &entclient.Client{ BaseURL: u, Token: sessionToken, - }, nil + } + + return client, nil } diff --git a/cmd/coder/ceapi.go b/cmd/coder/ceapi.go index aff2bf5e..823f36ff 100644 --- a/cmd/coder/ceapi.go +++ b/cmd/coder/ceapi.go @@ -1,6 +1,8 @@ package main import ( + "context" + "golang.org/x/xerrors" "go.coder.com/flog" @@ -27,23 +29,23 @@ outer: } // getEnvs returns all environments for the user. -func getEnvs(client *entclient.Client) ([]entclient.Environment, error) { - me, err := client.Me() +func getEnvs(ctx context.Context, client *entclient.Client, email string) ([]entclient.Environment, error) { + user, err := client.UserByEmail(ctx, email) if err != nil { - return nil, xerrors.Errorf("get self: %+v", err) + return nil, xerrors.Errorf("get user: %+v", err) } - orgs, err := client.Orgs() + orgs, err := client.Orgs(ctx) if err != nil { return nil, xerrors.Errorf("get orgs: %+v", err) } - orgs = userOrgs(me, orgs) + orgs = userOrgs(user, orgs) var allEnvs []entclient.Environment for _, org := range orgs { - envs, err := client.Envs(me, org) + envs, err := client.Envs(ctx, user, org) if err != nil { return nil, xerrors.Errorf("get envs for %v: %+v", org.Name, err) } @@ -56,8 +58,8 @@ func getEnvs(client *entclient.Client) ([]entclient.Environment, error) { } // findEnv returns a single environment by name (if it exists.) -func findEnv(client *entclient.Client, name string) (*entclient.Environment, error) { - envs, err := getEnvs(client) +func findEnv(ctx context.Context, client *entclient.Client, envName, userEmail string) (*entclient.Environment, error) { + envs, err := getEnvs(ctx, client, userEmail) if err != nil { return nil, xerrors.Errorf("get environments: %w", err) } @@ -66,11 +68,11 @@ func findEnv(client *entclient.Client, name string) (*entclient.Environment, err for _, env := range envs { found = append(found, env.Name) - if env.Name == name { + if env.Name == envName { return &env, nil } } flog.Error("found %q", found) - flog.Error("%q not found", name) + flog.Error("%q not found", envName) return nil, xerrors.New("environment not found") } diff --git a/cmd/coder/configssh.go b/cmd/coder/configssh.go index 135e7c58..50b991d4 100644 --- a/cmd/coder/configssh.go +++ b/cmd/coder/configssh.go @@ -83,19 +83,19 @@ func configSSH(filepath *string, remove *bool) func(cmd *cobra.Command, _ []stri return xerrors.New("SSH is disabled or not available for your Coder Enterprise deployment.") } - me, err := entClient.Me() + user, err := entClient.Me(cmd.Context()) if err != nil { return xerrors.Errorf("fetch username: %w", err) } - envs, err := getEnvs(entClient) + envs, err := getEnvs(cmd.Context(), entClient, entclient.Me) if err != nil { return err } if len(envs) < 1 { return xerrors.New("no environments found") } - newConfig, err := makeNewConfigs(me.Username, envs, startToken, startMessage, endToken) + newConfig, err := makeNewConfigs(user.Username, envs, startToken, startMessage, endToken) if err != nil { return xerrors.Errorf("make new ssh configurations: %w", err) } @@ -127,7 +127,7 @@ var ( ) func writeSSHKey(ctx context.Context, client *entclient.Client) error { - key, err := client.SSHKey() + key, err := client.SSHKey(ctx) if err != nil { return err } diff --git a/cmd/coder/envs.go b/cmd/coder/envs.go index 3442bdea..32d73245 100644 --- a/cmd/coder/envs.go +++ b/cmd/coder/envs.go @@ -4,18 +4,23 @@ import ( "encoding/json" "os" + "cdr.dev/coder-cli/internal/entclient" "cdr.dev/coder-cli/internal/x/xtabwriter" "github.com/spf13/cobra" "golang.org/x/xerrors" + + "go.coder.com/flog" ) func makeEnvsCommand() *cobra.Command { var outputFmt string + var user string cmd := &cobra.Command{ Use: "envs", Short: "Interact with Coder environments", Long: "Perform operations on the Coder environments owned by the active user.", } + cmd.PersistentFlags().StringVar(&user, "user", entclient.Me, "Specify the user whose resources to target") lsCmd := &cobra.Command{ Use: "ls", @@ -23,10 +28,14 @@ func makeEnvsCommand() *cobra.Command { Long: "List all Coder environments owned by the active user.", RunE: func(cmd *cobra.Command, args []string) error { entClient := requireAuth() - envs, err := getEnvs(entClient) + envs, err := getEnvs(cmd.Context(), entClient, user) if err != nil { return err } + if len(envs) < 1 { + flog.Info("no environments found") + return nil + } switch outputFmt { case "human": diff --git a/cmd/coder/main.go b/cmd/coder/main.go index 7346212b..5cf3454b 100644 --- a/cmd/coder/main.go +++ b/cmd/coder/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "log" "net/http" @@ -19,6 +20,9 @@ var ( ) func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + if os.Getenv("PPROF") != "" { go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) @@ -49,7 +53,7 @@ func main() { makeURLCmd(), completionCmd, ) - err = app.Execute() + err = app.ExecuteContext(ctx) if err != nil { os.Exit(1) } diff --git a/cmd/coder/secrets.go b/cmd/coder/secrets.go index 27bade21..9433588c 100644 --- a/cmd/coder/secrets.go +++ b/cmd/coder/secrets.go @@ -15,37 +15,39 @@ import ( ) func makeSecretsCmd() *cobra.Command { + var user string cmd := &cobra.Command{ Use: "secrets", Short: "Interact with Coder Secrets", Long: "Interact with secrets objects owned by the active user.", } + cmd.PersistentFlags().StringVar(&user, "user", entclient.Me, "Specify the user whose resources to target") cmd.AddCommand( &cobra.Command{ Use: "ls", Short: "List all secrets owned by the active user", - RunE: listSecrets, + RunE: listSecrets(&user), }, - makeCreateSecret(), + makeCreateSecret(&user), &cobra.Command{ Use: "rm [...secret_name]", Short: "Remove one or more secrets by name", Args: cobra.MinimumNArgs(1), - RunE: removeSecrets, + RunE: makeRemoveSecrets(&user), Example: "coder secrets rm mysql-password mysql-user", }, &cobra.Command{ Use: "view [secret_name]", Short: "View a secret by name", Args: cobra.ExactArgs(1), - RunE: viewSecret, + RunE: makeViewSecret(&user), Example: "coder secrets view mysql-password", }, ) return cmd } -func makeCreateSecret() *cobra.Command { +func makeCreateSecret(user *string) *cobra.Command { var ( fromFile string fromLiteral string @@ -107,10 +109,12 @@ coder secrets create aws-credentials --from-file ./credentials.json`, } } - err = client.InsertSecret(entclient.InsertSecretReq{ + err = client.InsertSecret(cmd.Context(), entclient.InsertSecretReq{ Name: name, Value: value, Description: description, + }, &entclient.ReqOptions{ + User: *user, }) if err != nil { return xerrors.Errorf("insert secret: %w", err) @@ -127,71 +131,83 @@ coder secrets create aws-credentials --from-file ./credentials.json`, return cmd } -func listSecrets(cmd *cobra.Command, _ []string) error { - client := requireAuth() +func listSecrets(user *string) func(cmd *cobra.Command, _ []string) error { + return func(cmd *cobra.Command, _ []string) error { + client := requireAuth() - secrets, err := client.Secrets() - if err != nil { - return xerrors.Errorf("get secrets: %w", err) - } + secrets, err := client.Secrets(cmd.Context(), &entclient.ReqOptions{ + User: *user, + }) + if err != nil { + return xerrors.Errorf("get secrets: %w", err) + } - if len(secrets) < 1 { - flog.Info("No secrets found") - return nil - } + if len(secrets) < 1 { + flog.Info("No secrets found") + return nil + } - err = xtabwriter.WriteTable(len(secrets), func(i int) interface{} { - s := secrets[i] - s.Value = "******" // value is omitted from bulk responses - return s - }) - if err != nil { - return xerrors.Errorf("write table of secrets: %w", err) + err = xtabwriter.WriteTable(len(secrets), func(i int) interface{} { + s := secrets[i] + s.Value = "******" // value is omitted from bulk responses + return s + }) + if err != nil { + return xerrors.Errorf("write table of secrets: %w", err) + } + return nil } - return nil } -func viewSecret(_ *cobra.Command, args []string) error { - var ( - client = requireAuth() - name = args[0] - ) - if name == "" { - return xerrors.New("[name] is a required argument") - } +func makeViewSecret(user *string) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + var ( + client = requireAuth() + name = args[0] + ) + if name == "" { + return xerrors.New("[name] is a required argument") + } - secret, err := client.SecretByName(name) - if err != nil { - return xerrors.Errorf("get secret by name: %w", err) - } + secret, err := client.SecretByName(cmd.Context(), name, &entclient.ReqOptions{ + User: *user, + }) + if err != nil { + return xerrors.Errorf("get secret by name: %w", err) + } - _, err = fmt.Fprintln(os.Stdout, secret.Value) - if err != nil { - return xerrors.Errorf("write secret value: %w", err) + _, err = fmt.Fprintln(os.Stdout, secret.Value) + if err != nil { + return xerrors.Errorf("write secret value: %w", err) + } + return nil } - return nil } -func removeSecrets(_ *cobra.Command, args []string) error { - var ( - client = requireAuth() - ) - if len(args) < 1 { - return xerrors.New("[...secret_name] is a required argument") - } +func makeRemoveSecrets(user *string) func(c *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + var ( + client = requireAuth() + ) + if len(args) < 1 { + return xerrors.New("[...secret_name] is a required argument") + } - errorSeen := false - for _, n := range args { - err := client.DeleteSecretByName(n) - if err != nil { - flog.Error("failed to delete secret %q: %v", n, err) - errorSeen = true - } else { - flog.Success("successfully deleted secret %q", n) + errorSeen := false + for _, n := range args { + err := client.DeleteSecretByName(cmd.Context(), n, &entclient.ReqOptions{ + User: *user, + }) + if err != nil { + flog.Error("failed to delete secret %q: %v", n, err) + errorSeen = true + } else { + flog.Success("successfully deleted secret %q", n) + } } + if errorSeen { + os.Exit(1) + } + return nil } - if errorSeen { - os.Exit(1) - } - return nil } diff --git a/cmd/coder/shell.go b/cmd/coder/shell.go index 0012c338..eca157e0 100644 --- a/cmd/coder/shell.go +++ b/cmd/coder/shell.go @@ -8,6 +8,7 @@ import ( "time" "cdr.dev/coder-cli/internal/activity" + "cdr.dev/coder-cli/internal/entclient" "cdr.dev/coder-cli/internal/x/xterminal" "cdr.dev/wsep" "github.com/spf13/cobra" @@ -19,23 +20,22 @@ import ( "go.coder.com/flog" ) -func getEnvsForCompletion() []string { - // TODO(@cmoog): Enable this if speed issue can be resolved. Otherwise, all commands will take > 1 second. - return nil - - var envNames []string - client, err := newClient() - if err != nil { - return envNames - } - envs, err := getEnvs(client) - if err != nil { - return envNames - } - for _, e := range envs { - envNames = append(envNames, e.Name) +func getEnvsForCompletion(user string) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var envNames []string + client, err := newClient() + if err != nil { + return envNames, cobra.ShellCompDirectiveDefault + } + envs, err := getEnvs(context.TODO(), client, user) + if err != nil { + return envNames, cobra.ShellCompDirectiveDefault + } + for _, e := range envs { + envNames = append(envNames, e.Name) + } + return envNames, cobra.ShellCompDirectiveDefault } - return envNames } func makeShellCmd() *cobra.Command { @@ -45,7 +45,7 @@ func makeShellCmd() *cobra.Command { Long: "Execute a remote command on the environment\\nIf no command is specified, the default shell is opened.", Args: cobra.MinimumNArgs(1), DisableFlagParsing: true, - ValidArgs: getEnvsForCompletion(), + ValidArgsFunction: getEnvsForCompletion(entclient.Me), RunE: shell, Example: "coder sh backend-env", } @@ -99,7 +99,7 @@ func runCommand(ctx context.Context, envName string, command string, args []stri var ( entClient = requireAuth() ) - env, err := findEnv(entClient, envName) + env, err := findEnv(ctx, entClient, envName, entclient.Me) if err != nil { return err } diff --git a/cmd/coder/sync.go b/cmd/coder/sync.go index 20aed14c..6cab1bc2 100644 --- a/cmd/coder/sync.go +++ b/cmd/coder/sync.go @@ -8,6 +8,7 @@ import ( "path/filepath" "strings" + "cdr.dev/coder-cli/internal/entclient" "cdr.dev/coder-cli/internal/sync" "github.com/spf13/cobra" "golang.org/x/xerrors" @@ -70,7 +71,7 @@ func makeRunSync(init *bool) func(cmd *cobra.Command, args []string) error { remoteDir = remoteTokens[1] ) - env, err := findEnv(entClient, envName) + env, err := findEnv(cmd.Context(), entClient, envName, entclient.Me) if err != nil { return err } diff --git a/cmd/coder/urls.go b/cmd/coder/urls.go index 01a3bd7b..7f29cdb8 100644 --- a/cmd/coder/urls.go +++ b/cmd/coder/urls.go @@ -1,6 +1,7 @@ package main import ( + "context" "encoding/json" "fmt" "net/http" @@ -9,6 +10,7 @@ import ( "strconv" "strings" + "cdr.dev/coder-cli/internal/entclient" "cdr.dev/coder-cli/internal/x/xtabwriter" "github.com/spf13/cobra" "golang.org/x/xerrors" @@ -23,11 +25,11 @@ func makeURLCmd() *cobra.Command { Short: "Interact with environment DevURLs", } lsCmd := &cobra.Command{ - Use: "ls [environment_name]", - Short: "List all DevURLs for an environment", - Args: cobra.ExactArgs(1), - ValidArgs: getEnvsForCompletion(), - RunE: makeListDevURLs(&outputFmt), + Use: "ls [environment_name]", + Short: "List all DevURLs for an environment", + Args: cobra.ExactArgs(1), + ValidArgsFunction: getEnvsForCompletion(entclient.Me), + RunE: makeListDevURLs(&outputFmt), } lsCmd.Flags().StringVarP(&outputFmt, "output", "o", "human", "human|json") @@ -92,13 +94,17 @@ func accessLevelIsValid(level string) bool { func makeListDevURLs(outputFmt *string) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { envName := args[0] - devURLs, err := urlList(envName) + devURLs, err := urlList(cmd.Context(), envName) if err != nil { return err } switch *outputFmt { case "human": + if len(devURLs) < 1 { + flog.Info("No devURLs found for environment %q", envName) + return nil + } err := xtabwriter.WriteTable(len(devURLs), func(i int) interface{} { return devURLs[i] }) @@ -149,12 +155,12 @@ func makeCreateDevURL() *cobra.Command { } entClient := requireAuth() - env, err := findEnv(entClient, envName) + env, err := findEnv(cmd.Context(), entClient, envName, entclient.Me) if err != nil { return err } - urls, err := urlList(envName) + urls, err := urlList(cmd.Context(), envName) if err != nil { return err } @@ -162,13 +168,13 @@ func makeCreateDevURL() *cobra.Command { urlID, found := devURLID(portNum, urls) if found { flog.Info("Updating devurl for port %v", port) - err := entClient.UpdateDevURL(env.ID, urlID, portNum, urlname, access) + err := entClient.UpdateDevURL(cmd.Context(), env.ID, urlID, portNum, urlname, access) if err != nil { return xerrors.Errorf("update DevURL: %w", err) } } else { flog.Info("Adding devurl for port %v", port) - err := entClient.InsertDevURL(env.ID, portNum, urlname, access) + err := entClient.InsertDevURL(cmd.Context(), env.ID, portNum, urlname, access) if err != nil { return xerrors.Errorf("insert DevURL: %w", err) } @@ -214,12 +220,12 @@ func removeDevURL(cmd *cobra.Command, args []string) error { } entClient := requireAuth() - env, err := findEnv(entClient, envName) + env, err := findEnv(cmd.Context(), entClient, envName, entclient.Me) if err != nil { return err } - urls, err := urlList(envName) + urls, err := urlList(cmd.Context(), envName) if err != nil { return err } @@ -231,7 +237,7 @@ func removeDevURL(cmd *cobra.Command, args []string) error { return xerrors.Errorf("No devurl found for port %v", port) } - err = entClient.DelDevURL(env.ID, urlID) + err = entClient.DelDevURL(cmd.Context(), env.ID, urlID) if err != nil { return xerrors.Errorf("delete DevURL: %w", err) } @@ -239,9 +245,9 @@ func removeDevURL(cmd *cobra.Command, args []string) error { } // urlList returns the list of active devURLs from the cemanager. -func urlList(envName string) ([]DevURL, error) { +func urlList(ctx context.Context, envName string) ([]DevURL, error) { entClient := requireAuth() - env, err := findEnv(entClient, envName) + env, err := findEnv(ctx, entClient, envName, entclient.Me) if err != nil { return nil, err } diff --git a/cmd/coder/users.go b/cmd/coder/users.go index bbfb08b8..d43d2f4b 100644 --- a/cmd/coder/users.go +++ b/cmd/coder/users.go @@ -33,7 +33,7 @@ func listUsers(outputFmt *string) func(cmd *cobra.Command, args []string) error return func(cmd *cobra.Command, args []string) error { entClient := requireAuth() - users, err := entClient.Users() + users, err := entClient.Users(cmd.Context()) if err != nil { return xerrors.Errorf("get users: %w", err) } diff --git a/internal/activity/pusher.go b/internal/activity/pusher.go index c2102fc1..d0f5954c 100644 --- a/internal/activity/pusher.go +++ b/internal/activity/pusher.go @@ -1,6 +1,7 @@ package activity import ( + "context" "time" "cdr.dev/coder-cli/internal/entclient" @@ -32,12 +33,12 @@ func NewPusher(c *entclient.Client, envID, source string) *Pusher { } // Push pushes activity, abiding by a rate limit -func (p *Pusher) Push() { +func (p *Pusher) Push(ctx context.Context) { if !p.rate.Allow() { return } - err := p.client.PushActivity(p.source, p.envID) + err := p.client.PushActivity(ctx, p.source, p.envID) if err != nil { flog.Error("push activity: %s", err.Error()) } diff --git a/internal/activity/writer.go b/internal/activity/writer.go index a10c4341..f7358924 100644 --- a/internal/activity/writer.go +++ b/internal/activity/writer.go @@ -1,6 +1,9 @@ package activity -import "io" +import ( + "context" + "io" +) type activityWriter struct { p *Pusher @@ -9,7 +12,7 @@ type activityWriter struct { // Write writes to the underlying writer and tracks activity func (w *activityWriter) Write(p []byte) (n int, err error) { - w.p.Push() + w.p.Push(context.Background()) return w.wr.Write(p) } diff --git a/internal/entclient/activity.go b/internal/entclient/activity.go index 362d7da0..8aba99d3 100644 --- a/internal/entclient/activity.go +++ b/internal/entclient/activity.go @@ -1,12 +1,13 @@ package entclient import ( + "context" "net/http" ) // PushActivity pushes CLI activity to Coder -func (c Client) PushActivity(source string, envID string) error { - res, err := c.request("POST", "/api/metrics/usage/push", map[string]string{ +func (c Client) PushActivity(ctx context.Context, source string, envID string) error { + res, err := c.request(ctx, "POST", "/api/metrics/usage/push", map[string]string{ "source": source, "environment_id": envID, }) diff --git a/internal/entclient/client.go b/internal/entclient/client.go index a306c707..703fcbb1 100644 --- a/internal/entclient/client.go +++ b/internal/entclient/client.go @@ -6,6 +6,9 @@ import ( "net/url" ) +// Me is the route param to access resources of the authenticated user +const Me = "me" + // Client wraps the Coder HTTP API type Client struct { BaseURL *url.URL diff --git a/internal/entclient/devurl.go b/internal/entclient/devurl.go index b2d34025..3ed8187c 100644 --- a/internal/entclient/devurl.go +++ b/internal/entclient/devurl.go @@ -1,6 +1,7 @@ package entclient import ( + "context" "fmt" "net/http" ) @@ -20,11 +21,10 @@ type delDevURLRequest struct { } // DelDevURL deletes the specified devurl -func (c Client) DelDevURL(envID, urlID string) error { - reqString := "/api/environments/%s/devurls/%s" - reqURL := fmt.Sprintf(reqString, envID, urlID) +func (c Client) DelDevURL(ctx context.Context, envID, urlID string) error { + reqURL := fmt.Sprintf("/api/environments/%s/devurls/%s", envID, urlID) - res, err := c.request("DELETE", reqURL, delDevURLRequest{ + res, err := c.request(ctx, "DELETE", reqURL, delDevURLRequest{ EnvID: envID, DevURLID: urlID, }) @@ -48,11 +48,10 @@ type createDevURLRequest struct { } // InsertDevURL inserts a new devurl for the authenticated user -func (c Client) InsertDevURL(envID string, port int, name, access string) error { - reqString := "/api/environments/%s/devurls" - reqURL := fmt.Sprintf(reqString, envID) +func (c Client) InsertDevURL(ctx context.Context, envID string, port int, name, access string) error { + reqURL := fmt.Sprintf("/api/environments/%s/devurls", envID) - res, err := c.request("POST", reqURL, createDevURLRequest{ + res, err := c.request(ctx, "POST", reqURL, createDevURLRequest{ EnvID: envID, Port: port, Access: access, @@ -73,11 +72,10 @@ func (c Client) InsertDevURL(envID string, port int, name, access string) error type updateDevURLRequest createDevURLRequest // UpdateDevURL updates an existing devurl for the authenticated user -func (c Client) UpdateDevURL(envID, urlID string, port int, name, access string) error { - reqString := "/api/environments/%s/devurls/%s" - reqURL := fmt.Sprintf(reqString, envID, urlID) +func (c Client) UpdateDevURL(ctx context.Context, envID, urlID string, port int, name, access string) error { + reqURL := fmt.Sprintf("/api/environments/%s/devurls/%s", envID, urlID) - res, err := c.request("PUT", reqURL, updateDevURLRequest{ + res, err := c.request(ctx, "PUT", reqURL, updateDevURLRequest{ EnvID: envID, Port: port, Access: access, diff --git a/internal/entclient/env.go b/internal/entclient/env.go index 508f438f..93773f43 100644 --- a/internal/entclient/env.go +++ b/internal/entclient/env.go @@ -34,9 +34,10 @@ type Environment struct { } // Envs gets the list of environments owned by the authenticated user -func (c Client) Envs(user *User, org Org) ([]Environment, error) { +func (c Client) Envs(ctx context.Context, user *User, org Org) ([]Environment, error) { var envs []Environment err := c.requestBody( + ctx, "GET", "/api/orgs/"+org.ID+"/members/"+user.ID+"/environments", nil, &envs, diff --git a/internal/entclient/me.go b/internal/entclient/me.go index 2d57d8cc..5a70bd2f 100644 --- a/internal/entclient/me.go +++ b/internal/entclient/me.go @@ -1,6 +1,7 @@ package entclient import ( + "context" "time" ) @@ -14,10 +15,10 @@ type User struct { UpdatedAt time.Time `json:"updated_at" tab:"-"` } -// Me gets the details of the authenticated user -func (c Client) Me() (*User, error) { +// Me gets the details of the authenticated user. +func (c Client) Me(ctx context.Context) (*User, error) { var u User - err := c.requestBody("GET", "/api/users/me", nil, &u) + err := c.requestBody(ctx, "GET", "/api/users/me", nil, &u) if err != nil { return nil, err } @@ -31,9 +32,9 @@ type SSHKey struct { } // SSHKey gets the current SSH kepair of the authenticated user -func (c Client) SSHKey() (*SSHKey, error) { +func (c Client) SSHKey(ctx context.Context) (*SSHKey, error) { var key SSHKey - err := c.requestBody("GET", "/api/users/me/sshkey", nil, &key) + err := c.requestBody(ctx, "GET", "/api/users/me/sshkey", nil, &key) if err != nil { return nil, err } diff --git a/internal/entclient/org.go b/internal/entclient/org.go index 6ac9cdc5..868eeba8 100644 --- a/internal/entclient/org.go +++ b/internal/entclient/org.go @@ -1,5 +1,7 @@ package entclient +import "context" + // Org describes an Organization in Coder type Org struct { ID string `json:"id"` @@ -8,8 +10,8 @@ type Org struct { } // Orgs gets all Organizations -func (c Client) Orgs() ([]Org, error) { +func (c Client) Orgs(ctx context.Context) ([]Org, error) { var os []Org - err := c.requestBody("GET", "/api/orgs", nil, &os) + err := c.requestBody(ctx, "GET", "/api/orgs", nil, &os) return os, err } diff --git a/internal/entclient/request.go b/internal/entclient/request.go index dfd0d6fe..507df424 100644 --- a/internal/entclient/request.go +++ b/internal/entclient/request.go @@ -2,6 +2,7 @@ package entclient import ( "bytes" + "context" "encoding/json" "net/http" @@ -9,6 +10,7 @@ import ( ) func (c Client) request( + ctx context.Context, method string, path string, request interface{}, ) (*http.Response, error) { @@ -23,7 +25,7 @@ func (c Client) request( if err != nil { return nil, xerrors.Errorf("marshal request: %w", err) } - req, err := http.NewRequest(method, c.BaseURL.String()+path, bytes.NewReader(body)) + req, err := http.NewRequestWithContext(ctx, method, c.BaseURL.String()+path, bytes.NewReader(body)) if err != nil { return nil, xerrors.Errorf("create request: %w", err) } @@ -31,9 +33,10 @@ func (c Client) request( } func (c Client) requestBody( + ctx context.Context, method string, path string, request interface{}, response interface{}, ) error { - resp, err := c.request(method, path, request) + resp, err := c.request(ctx, method, path, request) if err != nil { return err } diff --git a/internal/entclient/secrets.go b/internal/entclient/secrets.go index 72a62105..d1ad25bf 100644 --- a/internal/entclient/secrets.go +++ b/internal/entclient/secrets.go @@ -1,6 +1,7 @@ package entclient import ( + "context" "net/http" "time" ) @@ -15,21 +16,47 @@ type Secret struct { UpdatedAt time.Time `json:"updated_at" tab:"-"` } +// ReqOptions define api request configuration options +type ReqOptions struct { + // User defines the users whose resources should be targeted + User string +} + +// DefaultReqOptions define reasonable defaults for an api request +var DefaultReqOptions = &ReqOptions{ + User: Me, +} + // Secrets gets all secrets owned by the authed user -func (c *Client) Secrets() ([]Secret, error) { +func (c *Client) Secrets(ctx context.Context, opts *ReqOptions) ([]Secret, error) { + if opts == nil { + opts = DefaultReqOptions + } + user, err := c.UserByEmail(ctx, opts.User) + if err != nil { + return nil, err + } var secrets []Secret - err := c.requestBody(http.MethodGet, "/api/users/me/secrets", nil, &secrets) + err = c.requestBody(ctx, http.MethodGet, "/api/users/"+user.ID+"/secrets", nil, &secrets) return secrets, err } -func (c *Client) secretByID(id string) (*Secret, error) { +func (c *Client) secretByID(ctx context.Context, id string, opts *ReqOptions) (*Secret, error) { + if opts == nil { + opts = DefaultReqOptions + } + user, err := c.UserByEmail(ctx, opts.User) + if err != nil { + return nil, err + } + var secret Secret - err := c.requestBody(http.MethodGet, "/api/users/me/secrets/"+id, nil, &secret) + err = c.requestBody(ctx, http.MethodGet, "/api/users/"+user.ID+"/secrets/"+id, nil, &secret) return &secret, err } -func (c *Client) secretNameToID(name string) (id string, _ error) { - secrets, err := c.Secrets() +func (c *Client) secretNameToID(ctx context.Context, name string, opts *ReqOptions) (id string, _ error) { + secrets, err := c.Secrets(ctx, opts) if err != nil { return "", err } @@ -42,12 +69,12 @@ func (c *Client) secretNameToID(name string) (id string, _ error) { } // SecretByName gets a secret object by name -func (c *Client) SecretByName(name string) (*Secret, error) { - id, err := c.secretNameToID(name) +func (c *Client) SecretByName(ctx context.Context, name string, opts *ReqOptions) (*Secret, error) { + id, err := c.secretNameToID(ctx, name, opts) if err != nil { return nil, err } - return c.secretByID(id) + return c.secretByID(ctx, id, opts) } // InsertSecretReq describes the request body for creating a new secret @@ -58,18 +85,32 @@ type InsertSecretReq struct { } // InsertSecret adds a new secret for the authed user -func (c *Client) InsertSecret(req InsertSecretReq) error { +func (c *Client) InsertSecret(ctx context.Context, req InsertSecretReq, opts *ReqOptions) error { + if opts == nil { + opts = DefaultReqOptions + } + user, err := c.UserByEmail(ctx, opts.User) + if err != nil { + return err + } var resp interface{} - err := c.requestBody(http.MethodPost, "/api/users/me/secrets", req, &resp) + err = c.requestBody(ctx, http.MethodPost, "/api/users/"+user.ID+"/secrets", req, &resp) return err } // DeleteSecretByName deletes the authenticated users secret with the given name -func (c *Client) DeleteSecretByName(name string) error { - id, err := c.secretNameToID(name) +func (c *Client) DeleteSecretByName(ctx context.Context, name string, opts *ReqOptions) error { + if opts == nil { + opts = DefaultReqOptions + } + user, err := c.UserByEmail(ctx, opts.User) + if err != nil { + return err + } + id, err := c.secretNameToID(ctx, name, opts) if err != nil { return err } - _, err = c.request(http.MethodDelete, "/api/users/me/secrets/"+id, nil) + _, err = c.request(ctx, http.MethodDelete, "/api/users/"+user.ID+"/secrets/"+id, nil) return err } diff --git a/internal/entclient/users.go b/internal/entclient/users.go index 2354e568..a721d3fe 100644 --- a/internal/entclient/users.go +++ b/internal/entclient/users.go @@ -1,11 +1,30 @@ package entclient +import "context" + // Users gets the list of user accounts -func (c Client) Users() ([]User, error) { +func (c Client) Users(ctx context.Context) ([]User, error) { var u []User - err := c.requestBody("GET", "/api/users", nil, &u) + err := c.requestBody(ctx, "GET", "/api/users", nil, &u) if err != nil { return nil, err } return u, nil } + +// UserByEmail gets a user by email +func (c Client) UserByEmail(ctx context.Context, target string) (*User, error) { + if target == Me { + return c.Me(ctx) + } + users, err := c.Users(ctx) + if err != nil { + return nil, err + } + for _, u := range users { + if u.Email == target { + return &u, nil + } + } + return nil, ErrNotFound +} diff --git a/internal/sync/sync.go b/internal/sync/sync.go index b58382a1..70b4d724 100644 --- a/internal/sync/sync.go +++ b/internal/sync/sync.go @@ -322,7 +322,7 @@ func (s Sync) Run() error { s.remoteCmd(ctx, "mkdir", "-p", s.RemoteDir) ap := activity.NewPusher(s.Client, s.Env.ID, activityName) - ap.Push() + ap.Push(ctx) setConsoleTitle("⏳ syncing project") err = s.initSync() @@ -382,7 +382,7 @@ func (s Sync) Run() error { } s.workEventGroup(eventGroup) eventGroup = eventGroup[:0] - ap.Push() + ap.Push(context.TODO()) } } } pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy