Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Add commands for workspace providers #238

Merged
merged 1 commit into from
Feb 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ linters:
- structcheck
- stylecheck
- typecheck
- noctx
- nolintlint
- rowserrcheck
- scopelint
Expand Down
65 changes: 0 additions & 65 deletions coder-sdk/resourcepools.go

This file was deleted.

80 changes: 80 additions & 0 deletions coder-sdk/workspace_providers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package coder

import (
"context"
"net/http"
)

// WorkspaceProvider defines an entity capable of deploying and acting as an ingress for Coder environments.
type WorkspaceProvider struct {
ID string `json:"id" table:"-"`
Name string `json:"name" table:"Name"`
Status WorkspaceProviderStatus `json:"status" table:"Status"`
Local bool `json:"local" table:"-"`
ClusterAddress string `json:"cluster_address" table:"Cluster Address"`
DefaultNamespace string `json:"default_namespace" table:"Namespace"`
StorageClass string `json:"storage_class" table:"Storage Class"`
ClusterDomainSuffix string `json:"cluster_domain_suffix" table:"Cluster Domain Suffix"`
EnvproxyAccessURL string `json:"envproxy_access_url" validate:"required" table:"Access URL"`
DevurlHost string `json:"devurl_host" table:"Devurl Host"`
SSHEnabled bool `json:"ssh_enabled" table:"SSH Enabled"`
NamespaceWhitelist []string `json:"namespace_whitelist" table:"Namespace Allowlist"`
OrgWhitelist []string `json:"org_whitelist" table:"-"`
}

// WorkspaceProviderStatus represents the configuration state of a workspace provider.
type WorkspaceProviderStatus string

// Workspace Provider statuses.
const (
WorkspaceProviderPending WorkspaceProviderStatus = "pending"
WorkspaceProviderReady WorkspaceProviderStatus = "ready"
)

// WorkspaceProviderByID fetches a workspace provider entity by its unique ID.
func (c *Client) WorkspaceProviderByID(ctx context.Context, id string) (*WorkspaceProvider, error) {
var wp WorkspaceProvider
err := c.requestBody(ctx, http.MethodGet, "/api/private/resource-pools/"+id, nil, &wp)
if err != nil {
return nil, err
}
return &wp, nil
}

// WorkspaceProviders fetches all workspace providers known to the Coder control plane.
func (c *Client) WorkspaceProviders(ctx context.Context) ([]WorkspaceProvider, error) {
var providers []WorkspaceProvider
err := c.requestBody(ctx, http.MethodGet, "/api/private/resource-pools", nil, &providers)
if err != nil {
return nil, err
}
return providers, nil
}

// CreateWorkspaceProviderReq defines the request parameters for creating a new workspace provider entity.
type CreateWorkspaceProviderReq struct {
Name string `json:"name"`
}

// CreateWorkspaceProviderRes defines the response from creating a new workspace provider entity.
type CreateWorkspaceProviderRes struct {
ID string `json:"id" table:"ID"`
Name string `json:"name" table:"Name"`
Status WorkspaceProviderStatus `json:"status" table:"Status"`
EnvproxyToken string `json:"envproxy_token" table:"Envproxy Token"`
}

// CreateWorkspaceProvider creates a new WorkspaceProvider entity.
func (c *Client) CreateWorkspaceProvider(ctx context.Context, req CreateWorkspaceProviderReq) (*CreateWorkspaceProviderRes, error) {
var res CreateWorkspaceProviderRes
err := c.requestBody(ctx, http.MethodPost, "/api/private/resource-pools", req, &res)
if err != nil {
return nil, err
}
return &res, nil
}

// DeleteWorkspaceProviderByID deletes a workspace provider entity from the Coder control plane.
func (c *Client) DeleteWorkspaceProviderByID(ctx context.Context, id string) error {
return c.requestBody(ctx, http.MethodDelete, "/api/private/resource-pools/"+id, nil, nil)
}
1 change: 1 addition & 0 deletions internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func Make() *cobra.Command {
resourceCmd(),
completionCmd(),
imgsCmd(),
providersCmd(),
genDocsCmd(app),
)
app.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "show verbose output")
Expand Down
24 changes: 12 additions & 12 deletions internal/cmd/configssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,16 @@ func configSSH(configpath *string, remove *bool) func(cmd *cobra.Command, _ []st
return xerrors.New("no environments found")
}

envsWithPools, err := coderutil.EnvsWithPool(ctx, client, envs)
envsWithProviders, err := coderutil.EnvsWithProvider(ctx, client, envs)
if err != nil {
return xerrors.Errorf("resolve env pools: %w", err)
return xerrors.Errorf("resolve env workspace providers: %w", err)
}

if !sshAvailable(envsWithPools) {
if !sshAvailable(envsWithProviders) {
return xerrors.New("SSH is disabled or not available for any environments in your Coder Enterprise deployment.")
}

newConfig := makeNewConfigs(user.Username, envsWithPools, privateKeyFilepath)
newConfig := makeNewConfigs(user.Username, envsWithProviders, privateKeyFilepath)

err = os.MkdirAll(filepath.Dir(*configpath), os.ModePerm)
if err != nil {
Expand Down Expand Up @@ -157,9 +157,9 @@ func removeOldConfig(config string) (string, bool) {
}

// sshAvailable returns true if SSH is available for at least one environment.
func sshAvailable(envs []coderutil.EnvWithPool) bool {
func sshAvailable(envs []coderutil.EnvWithWorkspaceProvider) bool {
for _, env := range envs {
if env.Pool.SSHEnabled {
if env.WorkspaceProvider.SSHEnabled {
return true
}
}
Expand All @@ -174,19 +174,19 @@ func writeSSHKey(ctx context.Context, client *coder.Client, privateKeyPath strin
return ioutil.WriteFile(privateKeyPath, []byte(key.PrivateKey), 0600)
}

func makeNewConfigs(userName string, envs []coderutil.EnvWithPool, privateKeyFilepath string) string {
func makeNewConfigs(userName string, envs []coderutil.EnvWithWorkspaceProvider, privateKeyFilepath string) string {
newConfig := fmt.Sprintf("\n%s\n%s\n\n", sshStartToken, sshStartMessage)
for _, env := range envs {
if !env.Pool.SSHEnabled {
clog.LogWarn(fmt.Sprintf("SSH is not enabled for pool %q", env.Pool.Name),
if !env.WorkspaceProvider.SSHEnabled {
clog.LogWarn(fmt.Sprintf("SSH is not enabled for workspace provider %q", env.WorkspaceProvider.Name),
clog.BlankLine,
clog.Tipf("ask an infrastructure administrator to enable SSH for this resource pool"),
clog.Tipf("ask an infrastructure administrator to enable SSH for this workspace provider"),
)
continue
}
u, err := url.Parse(env.Pool.AccessURL)
u, err := url.Parse(env.WorkspaceProvider.EnvproxyAccessURL)
if err != nil {
clog.LogWarn("invalid access url", clog.Causef("malformed url: %q", env.Pool.AccessURL))
clog.LogWarn("invalid access url", clog.Causef("malformed url: %q", env.WorkspaceProvider.EnvproxyAccessURL))
continue
}
newConfig += makeSSHConfig(u.Host, userName, env.Env.Name, privateKeyFilepath)
Expand Down
154 changes: 154 additions & 0 deletions internal/cmd/providers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
"golang.org/x/xerrors"

"cdr.dev/coder-cli/coder-sdk"
"cdr.dev/coder-cli/internal/x/xcobra"
"cdr.dev/coder-cli/pkg/clog"
"cdr.dev/coder-cli/pkg/tablewriter"
)

func providersCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "providers",
Short: "Interact with Coder workspace providers",
Long: "Perform operations on the Coder Workspace Providers for the platform.",
Hidden: true,
}

cmd.AddCommand(
createProviderCmd(),
listProviderCmd(),
deleteProviderCmd(),
)
return cmd
}

func createProviderCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create [workspace_provider_name]",
Short: "create a new workspace provider.",
Args: xcobra.ExactArgs(1),
Long: "Create a new Coder workspace provider.",
Example: `# create a new workspace provider in a pending state
coder providers create my-new-workspace-provider`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()

client, err := newClient(ctx)
if err != nil {
return err
}

// ExactArgs(1) ensures our name value can't panic on an out of bounds.
createReq := &coder.CreateWorkspaceProviderReq{
Name: args[0],
}

wp, err := client.CreateWorkspaceProvider(ctx, *createReq)
if err != nil {
return xerrors.Errorf("create workspace provider: %w", err)
}

err = tablewriter.WriteTable(1, func(i int) interface{} {
return *wp
})
if err != nil {
return xerrors.Errorf("write table: %w", err)
}
return nil
},
}
return cmd
}

func listProviderCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "ls",
Short: "list workspace providers.",
Long: "List all Coder workspace providers.",
Example: `# list workspace providers
coder providers ls`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()

client, err := newClient(ctx)
if err != nil {
return err
}

wps, err := client.WorkspaceProviders(ctx)
if err != nil {
return xerrors.Errorf("list workspace providers: %w", err)
}

err = tablewriter.WriteTable(len(wps), func(i int) interface{} {
return wps[i]
})
if err != nil {
return xerrors.Errorf("write table: %w", err)
}
return nil
},
}
return cmd
}

func deleteProviderCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "rm [workspace_provider_name]",
Short: "remove a workspace provider.",
Long: "Remove an existing Coder workspace provider by name.",
Example: `# remove an existing workspace provider by name
coder providers rm my-workspace-provider`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
client, err := newClient(ctx)
if err != nil {
return err
}

wps, err := client.WorkspaceProviders(ctx)
if err != nil {
return xerrors.Errorf("listing workspace providers: %w", err)
}

egroup := clog.LoggedErrGroup()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

for _, wpName := range args {
name := wpName
egroup.Go(func() error {
var id string
for _, wp := range wps {
if wp.Name == name {
id = wp.ID
}
}
if id == "" {
return clog.Error(
fmt.Sprintf(`failed to remove workspace provider "%s"`, name),
clog.Causef(`no workspace provider found by name "%s"`, name),
)
}

err = client.DeleteWorkspaceProviderByID(ctx, id)
if err != nil {
return clog.Error(
fmt.Sprintf(`failed to remove workspace provider "%s"`, name),
clog.Causef(err.Error()),
)
}

clog.LogSuccess(fmt.Sprintf(`removed workspace provider with name "%s"`, name))

return nil
})
}
return egroup.Wait()
},
}
return cmd
}
Loading