diff --git a/ci/integration/users_test.go b/ci/integration/users_test.go index e3c7e6f3..05d81c6a 100644 --- a/ci/integration/users_test.go +++ b/ci/integration/users_test.go @@ -5,7 +5,7 @@ import ( "testing" "cdr.dev/coder-cli/ci/tcli" - "cdr.dev/coder-cli/internal/entclient" + "cdr.dev/coder-cli/coder-sdk" "cdr.dev/slog/sloggers/slogtest/assert" ) @@ -20,7 +20,7 @@ func TestUsers(t *testing.T) { headlessLogin(ctx, t, c) - var user entclient.User + var user coder.User c.Run(ctx, `coder users ls --output json | jq -c '.[] | select( .username == "charlie")'`).Assert(t, tcli.Success(), tcli.StdoutJSONUnmarshal(&user), diff --git a/internal/entclient/activity.go b/coder-sdk/activity.go similarity index 66% rename from internal/entclient/activity.go rename to coder-sdk/activity.go index 8aba99d3..f5001507 100644 --- a/internal/entclient/activity.go +++ b/coder-sdk/activity.go @@ -1,13 +1,13 @@ -package entclient +package coder import ( "context" "net/http" ) -// PushActivity pushes CLI activity to Coder +// PushActivity pushes CLI activity to Coder. 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{ + res, err := c.request(ctx, http.MethodPost, "/api/metrics/usage/push", map[string]string{ "source": source, "environment_id": envID, }) @@ -18,6 +18,5 @@ func (c Client) PushActivity(ctx context.Context, source string, envID string) e if res.StatusCode != http.StatusOK { return bodyError(res) } - return nil } diff --git a/internal/entclient/client.go b/coder-sdk/client.go similarity index 97% rename from internal/entclient/client.go rename to coder-sdk/client.go index 703fcbb1..a4bcb3f2 100644 --- a/internal/entclient/client.go +++ b/coder-sdk/client.go @@ -1,4 +1,4 @@ -package entclient +package coder import ( "net/http" diff --git a/internal/entclient/devurl.go b/coder-sdk/devurl.go similarity index 88% rename from internal/entclient/devurl.go rename to coder-sdk/devurl.go index 3ed8187c..701ec004 100644 --- a/internal/entclient/devurl.go +++ b/coder-sdk/devurl.go @@ -1,4 +1,4 @@ -package entclient +package coder import ( "context" @@ -24,7 +24,7 @@ type delDevURLRequest struct { 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(ctx, "DELETE", reqURL, delDevURLRequest{ + res, err := c.request(ctx, http.MethodDelete, reqURL, delDevURLRequest{ EnvID: envID, DevURLID: urlID, }) @@ -51,7 +51,7 @@ type createDevURLRequest struct { 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(ctx, "POST", reqURL, createDevURLRequest{ + res, err := c.request(ctx, http.MethodPost, reqURL, createDevURLRequest{ EnvID: envID, Port: port, Access: access, @@ -75,7 +75,7 @@ type updateDevURLRequest createDevURLRequest 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(ctx, "PUT", reqURL, updateDevURLRequest{ + res, err := c.request(ctx, http.MethodPut, reqURL, updateDevURLRequest{ EnvID: envID, Port: port, Access: access, diff --git a/internal/entclient/env.go b/coder-sdk/env.go similarity index 83% rename from internal/entclient/env.go rename to coder-sdk/env.go index 93773f43..7f0a6573 100644 --- a/internal/entclient/env.go +++ b/coder-sdk/env.go @@ -1,7 +1,8 @@ -package entclient +package coder import ( "context" + "net/http" "time" "cdr.dev/coder-cli/internal/x/xjson" @@ -33,12 +34,12 @@ type Environment struct { AutoOffThreshold xjson.Duration `json:"auto_off_threshold" tab:"-"` } -// Envs gets the list of environments owned by the authenticated user -func (c Client) Envs(ctx context.Context, user *User, org Org) ([]Environment, error) { +// EnvironmentsByOrganization gets the list of environments owned by the given user. +func (c Client) EnvironmentsByOrganization(ctx context.Context, userID, orgID string) ([]Environment, error) { var envs []Environment err := c.requestBody( ctx, - "GET", "/api/orgs/"+org.ID+"/members/"+user.ID+"/environments", + http.MethodGet, "/api/orgs/"+orgID+"/members/"+userID+"/environments", nil, &envs, ) @@ -47,7 +48,7 @@ func (c Client) Envs(ctx context.Context, user *User, org Org) ([]Environment, e // DialWsep dials an environments command execution interface // See github.com/cdr/wsep for details -func (c Client) DialWsep(ctx context.Context, env Environment) (*websocket.Conn, error) { +func (c Client) DialWsep(ctx context.Context, env *Environment) (*websocket.Conn, error) { u := c.copyURL() if c.BaseURL.Scheme == "https" { u.Scheme = "wss" diff --git a/internal/entclient/error.go b/coder-sdk/error.go similarity index 97% rename from internal/entclient/error.go rename to coder-sdk/error.go index 9170efa9..aa1adbd2 100644 --- a/internal/entclient/error.go +++ b/coder-sdk/error.go @@ -1,4 +1,4 @@ -package entclient +package coder import ( "encoding/json" diff --git a/internal/entclient/org.go b/coder-sdk/org.go similarity index 70% rename from internal/entclient/org.go rename to coder-sdk/org.go index 868eeba8..260e6321 100644 --- a/internal/entclient/org.go +++ b/coder-sdk/org.go @@ -1,6 +1,9 @@ -package entclient +package coder -import "context" +import ( + "context" + "net/http" +) // Org describes an Organization in Coder type Org struct { @@ -12,6 +15,6 @@ type Org struct { // Orgs gets all Organizations func (c Client) Orgs(ctx context.Context) ([]Org, error) { var os []Org - err := c.requestBody(ctx, "GET", "/api/orgs", nil, &os) + err := c.requestBody(ctx, http.MethodGet, "/api/orgs", nil, &os) return os, err } diff --git a/internal/entclient/request.go b/coder-sdk/request.go similarity index 98% rename from internal/entclient/request.go rename to coder-sdk/request.go index 507df424..fba5f8ab 100644 --- a/internal/entclient/request.go +++ b/coder-sdk/request.go @@ -1,4 +1,4 @@ -package entclient +package coder import ( "bytes" diff --git a/coder-sdk/secrets.go b/coder-sdk/secrets.go new file mode 100644 index 00000000..b13a3060 --- /dev/null +++ b/coder-sdk/secrets.go @@ -0,0 +1,79 @@ +package coder + +import ( + "context" + "net/http" + "time" +) + +// Secret describes a Coder secret +type Secret struct { + ID string `json:"id" tab:"-"` + Name string `json:"name"` + Value string `json:"value,omitempty"` + Description string `json:"description"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at" tab:"-"` +} + +// Secrets gets all secrets for the given user +func (c *Client) Secrets(ctx context.Context, userID string) ([]Secret, error) { + var secrets []Secret + err := c.requestBody(ctx, http.MethodGet, "/api/users/"+userID+"/secrets", nil, &secrets) + return secrets, err +} + +// SecretWithValueByName gets the Coder secret with its value by its name. +func (c *Client) SecretWithValueByName(ctx context.Context, name, userID string) (*Secret, error) { + s, err := c.SecretByName(ctx, name, userID) + if err != nil { + return nil, err + } + var secret Secret + err = c.requestBody(ctx, http.MethodGet, "/api/users/"+userID+"/secrets/"+s.ID, nil, &secret) + return &secret, err +} + +// SecretWithValueByID gets the Coder secret with its value by the secret_id. +func (c *Client) SecretWithValueByID(ctx context.Context, id, userID string) (*Secret, error) { + var secret Secret + err := c.requestBody(ctx, http.MethodGet, "/api/users/"+userID+"/secrets/"+id, nil, &secret) + return &secret, err +} + +// SecretByName gets a secret object by name +func (c *Client) SecretByName(ctx context.Context, name, userID string) (*Secret, error) { + secrets, err := c.Secrets(ctx, userID) + if err != nil { + return nil, err + } + for _, s := range secrets { + if s.Name == name { + return &s, nil + } + } + return nil, ErrNotFound +} + +// InsertSecretReq describes the request body for creating a new secret +type InsertSecretReq struct { + Name string `json:"name"` + Value string `json:"value"` + Description string `json:"description"` +} + +// InsertSecret adds a new secret for the authed user +func (c *Client) InsertSecret(ctx context.Context, user *User, req InsertSecretReq) error { + var resp interface{} + return c.requestBody(ctx, http.MethodPost, "/api/users/"+user.ID+"/secrets", req, &resp) +} + +// DeleteSecretByName deletes the authenticated users secret with the given name +func (c *Client) DeleteSecretByName(ctx context.Context, name, userID string) error { + secret, err := c.SecretByName(ctx, name, userID) + if err != nil { + return err + } + _, err = c.request(ctx, http.MethodDelete, "/api/users/"+userID+"/secrets/"+secret.ID, nil) + return err +} diff --git a/coder-sdk/users.go b/coder-sdk/users.go new file mode 100644 index 00000000..48aa0502 --- /dev/null +++ b/coder-sdk/users.go @@ -0,0 +1,75 @@ +package coder + +import ( + "context" + "net/http" + "time" +) + +// User describes a Coder user account. +type User struct { + ID string `json:"id" tab:"-"` + Email string `json:"email"` + Username string `json:"username"` + Name string `json:"name"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at" tab:"-"` +} + +// Me gets the details of the authenticated user. +func (c Client) Me(ctx context.Context) (*User, error) { + return c.UserByID(ctx, Me) +} + +// UserByID get the details of a user by their id. +func (c Client) UserByID(ctx context.Context, id string) (*User, error) { + var u User + err := c.requestBody(ctx, http.MethodGet, "/api/users/"+id, nil, &u) + if err != nil { + return nil, err + } + return &u, nil +} + +// SSHKey describes an SSH keypair +type SSHKey struct { + PublicKey string `json:"public_key"` + PrivateKey string `json:"private_key"` +} + +// SSHKey gets the current SSH kepair of the authenticated user. +func (c Client) SSHKey(ctx context.Context) (*SSHKey, error) { + var key SSHKey + err := c.requestBody(ctx, http.MethodGet, "/api/users/me/sshkey", nil, &key) + if err != nil { + return nil, err + } + return &key, nil +} + +// Users gets the list of user accounts. +func (c Client) Users(ctx context.Context) ([]User, error) { + var u []User + err := c.requestBody(ctx, http.MethodGet, "/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, email string) (*User, error) { + if email == Me { + return c.Me(ctx) + } + users, err := c.Users(ctx) + if err != nil { + return nil, err + } + for _, u := range users { + if u.Email == email { + return &u, nil + } + } + return nil, ErrNotFound +} diff --git a/internal/activity/pusher.go b/internal/activity/pusher.go index d0f5954c..5efa15d1 100644 --- a/internal/activity/pusher.go +++ b/internal/activity/pusher.go @@ -4,7 +4,7 @@ import ( "context" "time" - "cdr.dev/coder-cli/internal/entclient" + "cdr.dev/coder-cli/coder-sdk" "golang.org/x/time/rate" "go.coder.com/flog" @@ -18,12 +18,12 @@ type Pusher struct { envID string source string - client *entclient.Client + client *coder.Client rate *rate.Limiter } // NewPusher instantiates a new instance of Pusher -func NewPusher(c *entclient.Client, envID, source string) *Pusher { +func NewPusher(c *coder.Client, envID, source string) *Pusher { return &Pusher{ envID: envID, source: source, diff --git a/internal/cmd/auth.go b/internal/cmd/auth.go index 64bcfb23..bfe00a3e 100644 --- a/internal/cmd/auth.go +++ b/internal/cmd/auth.go @@ -3,15 +3,15 @@ package cmd import ( "net/url" + "cdr.dev/coder-cli/coder-sdk" "cdr.dev/coder-cli/internal/config" - "cdr.dev/coder-cli/internal/entclient" "golang.org/x/xerrors" "go.coder.com/flog" ) // requireAuth exits the process with a nonzero exit code if the user is not authenticated to make requests -func requireAuth() *entclient.Client { +func requireAuth() *coder.Client { client, err := newClient() if err != nil { flog.Fatal("%v", err) @@ -19,7 +19,7 @@ func requireAuth() *entclient.Client { return client } -func newClient() (*entclient.Client, error) { +func newClient() (*coder.Client, error) { sessionToken, err := config.Session.Read() if err != nil { return nil, xerrors.Errorf("read session: %v (did you run coder login?)", err) @@ -35,7 +35,7 @@ func newClient() (*entclient.Client, error) { return nil, xerrors.Errorf("url misformatted: %v (try runing coder login)", err) } - client := &entclient.Client{ + client := &coder.Client{ BaseURL: u, Token: sessionToken, } diff --git a/internal/cmd/ceapi.go b/internal/cmd/ceapi.go index b69788fc..d9d8eb7e 100644 --- a/internal/cmd/ceapi.go +++ b/internal/cmd/ceapi.go @@ -3,18 +3,17 @@ package cmd import ( "context" + "cdr.dev/coder-cli/coder-sdk" "golang.org/x/xerrors" "go.coder.com/flog" - - "cdr.dev/coder-cli/internal/entclient" ) // Helpers for working with the Coder Enterprise API. // userOrgs gets a list of orgs the user is apart of. -func userOrgs(user *entclient.User, orgs []entclient.Org) []entclient.Org { - var uo []entclient.Org +func userOrgs(user *coder.User, orgs []coder.Org) []coder.Org { + var uo []coder.Org outer: for _, org := range orgs { for _, member := range org.Members { @@ -29,7 +28,7 @@ outer: } // getEnvs returns all environments for the user. -func getEnvs(ctx context.Context, client *entclient.Client, email string) ([]entclient.Environment, error) { +func getEnvs(ctx context.Context, client *coder.Client, email string) ([]coder.Environment, error) { user, err := client.UserByEmail(ctx, email) if err != nil { return nil, xerrors.Errorf("get user: %+v", err) @@ -42,10 +41,10 @@ func getEnvs(ctx context.Context, client *entclient.Client, email string) ([]ent orgs = userOrgs(user, orgs) - var allEnvs []entclient.Environment + var allEnvs []coder.Environment for _, org := range orgs { - envs, err := client.Envs(ctx, user, org) + envs, err := client.EnvironmentsByOrganization(ctx, user.ID, org.ID) if err != nil { return nil, xerrors.Errorf("get envs for %v: %+v", org.Name, err) } @@ -58,7 +57,7 @@ func getEnvs(ctx context.Context, client *entclient.Client, email string) ([]ent } // findEnv returns a single environment by name (if it exists.) -func findEnv(ctx context.Context, client *entclient.Client, envName, userEmail string) (*entclient.Environment, error) { +func findEnv(ctx context.Context, client *coder.Client, envName, userEmail string) (*coder.Environment, error) { envs, err := getEnvs(ctx, client, userEmail) if err != nil { return nil, xerrors.Errorf("get environments: %w", err) diff --git a/internal/cmd/configssh.go b/internal/cmd/configssh.go index 1d1c3c7b..5023ea7e 100644 --- a/internal/cmd/configssh.go +++ b/internal/cmd/configssh.go @@ -12,8 +12,8 @@ import ( "strings" "time" + "cdr.dev/coder-cli/coder-sdk" "cdr.dev/coder-cli/internal/config" - "cdr.dev/coder-cli/internal/entclient" "github.com/spf13/cobra" "golang.org/x/xerrors" ) @@ -85,19 +85,19 @@ func configSSH(configpath *string, remove *bool) func(cmd *cobra.Command, _ []st return nil } - entClient := requireAuth() + client := requireAuth() sshAvailable := isSSHAvailable(ctx) if !sshAvailable { return xerrors.New("SSH is disabled or not available for your Coder Enterprise deployment.") } - user, err := entClient.Me(cmd.Context()) + user, err := client.Me(cmd.Context()) if err != nil { return xerrors.Errorf("fetch username: %w", err) } - envs, err := getEnvs(cmd.Context(), entClient, entclient.Me) + envs, err := getEnvs(cmd.Context(), client, coder.Me) if err != nil { return err } @@ -122,7 +122,7 @@ func configSSH(configpath *string, remove *bool) func(cmd *cobra.Command, _ []st if err != nil { return xerrors.Errorf("write new configurations to ssh config file %q: %w", *configpath, err) } - err = writeSSHKey(ctx, entClient) + err = writeSSHKey(ctx, client) if err != nil { return xerrors.Errorf("fetch and write ssh key: %w", err) } @@ -139,7 +139,7 @@ var ( privateKeyFilepath = filepath.Join(os.Getenv("HOME"), ".ssh", "coder_enterprise") ) -func writeSSHKey(ctx context.Context, client *entclient.Client) error { +func writeSSHKey(ctx context.Context, client *coder.Client) error { key, err := client.SSHKey(ctx) if err != nil { return err @@ -147,7 +147,7 @@ func writeSSHKey(ctx context.Context, client *entclient.Client) error { return ioutil.WriteFile(privateKeyFilepath, []byte(key.PrivateKey), 0400) } -func makeNewConfigs(userName string, envs []entclient.Environment, startToken, startMsg, endToken string) (string, error) { +func makeNewConfigs(userName string, envs []coder.Environment, startToken, startMsg, endToken string) (string, error) { hostname, err := configuredHostname() if err != nil { return "", nil diff --git a/internal/cmd/envs.go b/internal/cmd/envs.go index 4e3219b7..b5f3eb6d 100644 --- a/internal/cmd/envs.go +++ b/internal/cmd/envs.go @@ -4,7 +4,7 @@ import ( "encoding/json" "os" - "cdr.dev/coder-cli/internal/entclient" + "cdr.dev/coder-cli/coder-sdk" "cdr.dev/coder-cli/internal/x/xtabwriter" "github.com/spf13/cobra" "golang.org/x/xerrors" @@ -20,7 +20,7 @@ func makeEnvsCommand() *cobra.Command { 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") + cmd.PersistentFlags().StringVar(&user, "user", coder.Me, "Specify the user whose resources to target") lsCmd := &cobra.Command{ Use: "ls", diff --git a/internal/cmd/secrets.go b/internal/cmd/secrets.go index 7303d8fa..9dd660e5 100644 --- a/internal/cmd/secrets.go +++ b/internal/cmd/secrets.go @@ -5,7 +5,7 @@ import ( "io/ioutil" "os" - "cdr.dev/coder-cli/internal/entclient" + "cdr.dev/coder-cli/coder-sdk" "cdr.dev/coder-cli/internal/x/xtabwriter" "github.com/manifoldco/promptui" "github.com/spf13/cobra" @@ -21,7 +21,7 @@ func makeSecretsCmd() *cobra.Command { 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.PersistentFlags().StringVar(&user, "user", coder.Me, "Specify the user whose resources to target") cmd.AddCommand( &cobra.Command{ Use: "ls", @@ -47,7 +47,7 @@ func makeSecretsCmd() *cobra.Command { return cmd } -func makeCreateSecret(user *string) *cobra.Command { +func makeCreateSecret(userEmail *string) *cobra.Command { var ( fromFile string fromLiteral string @@ -109,12 +109,14 @@ coder secrets create aws-credentials --from-file ./credentials.json`, } } - err = client.InsertSecret(cmd.Context(), entclient.InsertSecretReq{ + user, err := client.UserByEmail(cmd.Context(), *userEmail) + if err != nil { + return xerrors.Errorf("get user %q by email: %w", *userEmail, err) + } + err = client.InsertSecret(cmd.Context(), user, coder.InsertSecretReq{ Name: name, Value: value, Description: description, - }, &entclient.ReqOptions{ - User: *user, }) if err != nil { return xerrors.Errorf("insert secret: %w", err) @@ -131,13 +133,15 @@ coder secrets create aws-credentials --from-file ./credentials.json`, return cmd } -func listSecrets(user *string) func(cmd *cobra.Command, _ []string) error { +func listSecrets(userEmail *string) func(cmd *cobra.Command, _ []string) error { return func(cmd *cobra.Command, _ []string) error { client := requireAuth() + user, err := client.UserByEmail(cmd.Context(), *userEmail) + if err != nil { + return xerrors.Errorf("get user %q by email: %w", *userEmail, err) + } - secrets, err := client.Secrets(cmd.Context(), &entclient.ReqOptions{ - User: *user, - }) + secrets, err := client.Secrets(cmd.Context(), user.ID) if err != nil { return xerrors.Errorf("get secrets: %w", err) } @@ -159,19 +163,18 @@ func listSecrets(user *string) func(cmd *cobra.Command, _ []string) error { } } -func makeViewSecret(user *string) func(cmd *cobra.Command, args []string) error { +func makeViewSecret(userEmail *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") + user, err := client.UserByEmail(cmd.Context(), *userEmail) + if err != nil { + return xerrors.Errorf("get user %q by email: %w", *userEmail, err) } - secret, err := client.SecretByName(cmd.Context(), name, &entclient.ReqOptions{ - User: *user, - }) + secret, err := client.SecretWithValueByName(cmd.Context(), name, user.ID) if err != nil { return xerrors.Errorf("get secret by name: %w", err) } @@ -184,20 +187,19 @@ func makeViewSecret(user *string) func(cmd *cobra.Command, args []string) error } } -func makeRemoveSecrets(user *string) func(c *cobra.Command, args []string) error { +func makeRemoveSecrets(userEmail *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") + user, err := client.UserByEmail(cmd.Context(), *userEmail) + if err != nil { + return xerrors.Errorf("get user %q by email: %w", *userEmail, err) } errorSeen := false for _, n := range args { - err := client.DeleteSecretByName(cmd.Context(), n, &entclient.ReqOptions{ - User: *user, - }) + err := client.DeleteSecretByName(cmd.Context(), n, user.ID) if err != nil { flog.Error("failed to delete secret %q: %v", n, err) errorSeen = true diff --git a/internal/cmd/shell.go b/internal/cmd/shell.go index 7cfee8b0..62accce2 100644 --- a/internal/cmd/shell.go +++ b/internal/cmd/shell.go @@ -7,8 +7,8 @@ import ( "strings" "time" + "cdr.dev/coder-cli/coder-sdk" "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" @@ -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, - ValidArgsFunction: getEnvsForCompletion(entclient.Me), + ValidArgsFunction: getEnvsForCompletion(coder.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(ctx, entClient, envName, entclient.Me) + env, err := findEnv(ctx, entClient, envName, coder.Me) if err != nil { return err } @@ -118,7 +118,7 @@ func runCommand(ctx context.Context, envName string, command string, args []stri ctx, cancel := context.WithCancel(ctx) defer cancel() - conn, err := entClient.DialWsep(ctx, *env) + conn, err := entClient.DialWsep(ctx, env) if err != nil { return err } diff --git a/internal/cmd/sync.go b/internal/cmd/sync.go index c41d06fd..7507d76a 100644 --- a/internal/cmd/sync.go +++ b/internal/cmd/sync.go @@ -8,7 +8,7 @@ import ( "path/filepath" "strings" - "cdr.dev/coder-cli/internal/entclient" + "cdr.dev/coder-cli/coder-sdk" "cdr.dev/coder-cli/internal/sync" "github.com/spf13/cobra" "golang.org/x/xerrors" @@ -71,7 +71,7 @@ func makeRunSync(init *bool) func(cmd *cobra.Command, args []string) error { remoteDir = remoteTokens[1] ) - env, err := findEnv(cmd.Context(), entClient, envName, entclient.Me) + env, err := findEnv(cmd.Context(), entClient, envName, coder.Me) if err != nil { return err } diff --git a/internal/cmd/urls.go b/internal/cmd/urls.go index ba6e5494..b406c947 100644 --- a/internal/cmd/urls.go +++ b/internal/cmd/urls.go @@ -10,7 +10,7 @@ import ( "strconv" "strings" - "cdr.dev/coder-cli/internal/entclient" + "cdr.dev/coder-cli/coder-sdk" "cdr.dev/coder-cli/internal/x/xtabwriter" "github.com/spf13/cobra" "golang.org/x/xerrors" @@ -28,7 +28,7 @@ func makeURLCmd() *cobra.Command { Use: "ls [environment_name]", Short: "List all DevURLs for an environment", Args: cobra.ExactArgs(1), - ValidArgsFunction: getEnvsForCompletion(entclient.Me), + ValidArgsFunction: getEnvsForCompletion(coder.Me), RunE: makeListDevURLs(&outputFmt), } lsCmd.Flags().StringVarP(&outputFmt, "output", "o", "human", "human|json") @@ -155,7 +155,7 @@ func makeCreateDevURL() *cobra.Command { } entClient := requireAuth() - env, err := findEnv(cmd.Context(), entClient, envName, entclient.Me) + env, err := findEnv(cmd.Context(), entClient, envName, coder.Me) if err != nil { return err } @@ -220,7 +220,7 @@ func removeDevURL(cmd *cobra.Command, args []string) error { } entClient := requireAuth() - env, err := findEnv(cmd.Context(), entClient, envName, entclient.Me) + env, err := findEnv(cmd.Context(), entClient, envName, coder.Me) if err != nil { return err } @@ -247,7 +247,7 @@ func removeDevURL(cmd *cobra.Command, args []string) error { // urlList returns the list of active devURLs from the cemanager. func urlList(ctx context.Context, envName string) ([]DevURL, error) { entClient := requireAuth() - env, err := findEnv(ctx, entClient, envName, entclient.Me) + env, err := findEnv(ctx, entClient, envName, coder.Me) if err != nil { return nil, err } diff --git a/internal/entclient/me.go b/internal/entclient/me.go deleted file mode 100644 index 5a70bd2f..00000000 --- a/internal/entclient/me.go +++ /dev/null @@ -1,42 +0,0 @@ -package entclient - -import ( - "context" - "time" -) - -// User describes a Coder user account -type User struct { - ID string `json:"id" tab:"-"` - Email string `json:"email"` - Username string `json:"username"` - Name string `json:"name"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at" tab:"-"` -} - -// Me gets the details of the authenticated user. -func (c Client) Me(ctx context.Context) (*User, error) { - var u User - err := c.requestBody(ctx, "GET", "/api/users/me", nil, &u) - if err != nil { - return nil, err - } - return &u, nil -} - -// SSHKey describes an SSH keypair -type SSHKey struct { - PublicKey string `json:"public_key"` - PrivateKey string `json:"private_key"` -} - -// SSHKey gets the current SSH kepair of the authenticated user -func (c Client) SSHKey(ctx context.Context) (*SSHKey, error) { - var key SSHKey - err := c.requestBody(ctx, "GET", "/api/users/me/sshkey", nil, &key) - if err != nil { - return nil, err - } - return &key, nil -} diff --git a/internal/entclient/secrets.go b/internal/entclient/secrets.go deleted file mode 100644 index d1ad25bf..00000000 --- a/internal/entclient/secrets.go +++ /dev/null @@ -1,116 +0,0 @@ -package entclient - -import ( - "context" - "net/http" - "time" -) - -// Secret describes a Coder secret -type Secret struct { - ID string `json:"id" tab:"-"` - Name string `json:"name"` - Value string `json:"value,omitempty"` - Description string `json:"description"` - CreatedAt time.Time `json:"created_at"` - 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(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(ctx, http.MethodGet, "/api/users/"+user.ID+"/secrets", nil, &secrets) - return secrets, err -} - -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(ctx, http.MethodGet, "/api/users/"+user.ID+"/secrets/"+id, nil, &secret) - return &secret, err -} - -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 - } - for _, s := range secrets { - if s.Name == name { - return s.ID, nil - } - } - return "", ErrNotFound -} - -// SecretByName gets a secret object by 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(ctx, id, opts) -} - -// InsertSecretReq describes the request body for creating a new secret -type InsertSecretReq struct { - Name string `json:"name"` - Value string `json:"value"` - Description string `json:"description"` -} - -// InsertSecret adds a new secret for the authed user -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(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(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(ctx, http.MethodDelete, "/api/users/"+user.ID+"/secrets/"+id, nil) - return err -} diff --git a/internal/entclient/users.go b/internal/entclient/users.go deleted file mode 100644 index a721d3fe..00000000 --- a/internal/entclient/users.go +++ /dev/null @@ -1,30 +0,0 @@ -package entclient - -import "context" - -// Users gets the list of user accounts -func (c Client) Users(ctx context.Context) ([]User, error) { - var u []User - 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 70b4d724..6e5b6c49 100644 --- a/internal/sync/sync.go +++ b/internal/sync/sync.go @@ -16,6 +16,7 @@ import ( "sync/atomic" "time" + "cdr.dev/coder-cli/coder-sdk" "github.com/gorilla/websocket" "github.com/rjeczalik/notify" "golang.org/x/sync/semaphore" @@ -24,7 +25,6 @@ import ( "go.coder.com/flog" "cdr.dev/coder-cli/internal/activity" - "cdr.dev/coder-cli/internal/entclient" "cdr.dev/wsep" ) @@ -39,8 +39,8 @@ type Sync struct { // DisableMetrics disables activity metric pushing. DisableMetrics bool - Env entclient.Environment - Client *entclient.Client + Env coder.Environment + Client *coder.Client } // See https://lxadm.com/Rsync_exit_codes#List_of_standard_rsync_exit_codes. @@ -89,7 +89,7 @@ func (s Sync) syncPaths(delete bool, local, remote string) error { } func (s Sync) remoteCmd(ctx context.Context, prog string, args ...string) error { - conn, err := s.Client.DialWsep(ctx, s.Env) + conn, err := s.Client.DialWsep(ctx, &s.Env) if err != nil { return err } @@ -270,7 +270,7 @@ func (s Sync) Version() (string, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() - conn, err := s.Client.DialWsep(ctx, s.Env) + conn, err := s.Client.DialWsep(ctx, &s.Env) if err != nil { return "", err }
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: