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

Commit b039b93

Browse files
authored
Add commands for workspace providers (#238)
1 parent 82f5615 commit b039b93

File tree

7 files changed

+268
-99
lines changed

7 files changed

+268
-99
lines changed

.golangci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ linters:
3939
- structcheck
4040
- stylecheck
4141
- typecheck
42-
- noctx
4342
- nolintlint
4443
- rowserrcheck
4544
- scopelint

coder-sdk/resourcepools.go

Lines changed: 0 additions & 65 deletions
This file was deleted.

coder-sdk/workspace_providers.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package coder
2+
3+
import (
4+
"context"
5+
"net/http"
6+
)
7+
8+
// WorkspaceProvider defines an entity capable of deploying and acting as an ingress for Coder environments.
9+
type WorkspaceProvider struct {
10+
ID string `json:"id" table:"-"`
11+
Name string `json:"name" table:"Name"`
12+
Status WorkspaceProviderStatus `json:"status" table:"Status"`
13+
Local bool `json:"local" table:"-"`
14+
ClusterAddress string `json:"cluster_address" table:"Cluster Address"`
15+
DefaultNamespace string `json:"default_namespace" table:"Namespace"`
16+
StorageClass string `json:"storage_class" table:"Storage Class"`
17+
ClusterDomainSuffix string `json:"cluster_domain_suffix" table:"Cluster Domain Suffix"`
18+
EnvproxyAccessURL string `json:"envproxy_access_url" validate:"required" table:"Access URL"`
19+
DevurlHost string `json:"devurl_host" table:"Devurl Host"`
20+
SSHEnabled bool `json:"ssh_enabled" table:"SSH Enabled"`
21+
NamespaceWhitelist []string `json:"namespace_whitelist" table:"Namespace Allowlist"`
22+
OrgWhitelist []string `json:"org_whitelist" table:"-"`
23+
}
24+
25+
// WorkspaceProviderStatus represents the configuration state of a workspace provider.
26+
type WorkspaceProviderStatus string
27+
28+
// Workspace Provider statuses.
29+
const (
30+
WorkspaceProviderPending WorkspaceProviderStatus = "pending"
31+
WorkspaceProviderReady WorkspaceProviderStatus = "ready"
32+
)
33+
34+
// WorkspaceProviderByID fetches a workspace provider entity by its unique ID.
35+
func (c *Client) WorkspaceProviderByID(ctx context.Context, id string) (*WorkspaceProvider, error) {
36+
var wp WorkspaceProvider
37+
err := c.requestBody(ctx, http.MethodGet, "/api/private/resource-pools/"+id, nil, &wp)
38+
if err != nil {
39+
return nil, err
40+
}
41+
return &wp, nil
42+
}
43+
44+
// WorkspaceProviders fetches all workspace providers known to the Coder control plane.
45+
func (c *Client) WorkspaceProviders(ctx context.Context) ([]WorkspaceProvider, error) {
46+
var providers []WorkspaceProvider
47+
err := c.requestBody(ctx, http.MethodGet, "/api/private/resource-pools", nil, &providers)
48+
if err != nil {
49+
return nil, err
50+
}
51+
return providers, nil
52+
}
53+
54+
// CreateWorkspaceProviderReq defines the request parameters for creating a new workspace provider entity.
55+
type CreateWorkspaceProviderReq struct {
56+
Name string `json:"name"`
57+
}
58+
59+
// CreateWorkspaceProviderRes defines the response from creating a new workspace provider entity.
60+
type CreateWorkspaceProviderRes struct {
61+
ID string `json:"id" table:"ID"`
62+
Name string `json:"name" table:"Name"`
63+
Status WorkspaceProviderStatus `json:"status" table:"Status"`
64+
EnvproxyToken string `json:"envproxy_token" table:"Envproxy Token"`
65+
}
66+
67+
// CreateWorkspaceProvider creates a new WorkspaceProvider entity.
68+
func (c *Client) CreateWorkspaceProvider(ctx context.Context, req CreateWorkspaceProviderReq) (*CreateWorkspaceProviderRes, error) {
69+
var res CreateWorkspaceProviderRes
70+
err := c.requestBody(ctx, http.MethodPost, "/api/private/resource-pools", req, &res)
71+
if err != nil {
72+
return nil, err
73+
}
74+
return &res, nil
75+
}
76+
77+
// DeleteWorkspaceProviderByID deletes a workspace provider entity from the Coder control plane.
78+
func (c *Client) DeleteWorkspaceProviderByID(ctx context.Context, id string) error {
79+
return c.requestBody(ctx, http.MethodDelete, "/api/private/resource-pools/"+id, nil, nil)
80+
}

internal/cmd/cmd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func Make() *cobra.Command {
3737
resourceCmd(),
3838
completionCmd(),
3939
imgsCmd(),
40+
providersCmd(),
4041
genDocsCmd(app),
4142
)
4243
app.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "show verbose output")

internal/cmd/configssh.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,16 @@ func configSSH(configpath *string, remove *bool) func(cmd *cobra.Command, _ []st
104104
return xerrors.New("no environments found")
105105
}
106106

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

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

116-
newConfig := makeNewConfigs(user.Username, envsWithPools, privateKeyFilepath)
116+
newConfig := makeNewConfigs(user.Username, envsWithProviders, privateKeyFilepath)
117117

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

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

177-
func makeNewConfigs(userName string, envs []coderutil.EnvWithPool, privateKeyFilepath string) string {
177+
func makeNewConfigs(userName string, envs []coderutil.EnvWithWorkspaceProvider, privateKeyFilepath string) string {
178178
newConfig := fmt.Sprintf("\n%s\n%s\n\n", sshStartToken, sshStartMessage)
179179
for _, env := range envs {
180-
if !env.Pool.SSHEnabled {
181-
clog.LogWarn(fmt.Sprintf("SSH is not enabled for pool %q", env.Pool.Name),
180+
if !env.WorkspaceProvider.SSHEnabled {
181+
clog.LogWarn(fmt.Sprintf("SSH is not enabled for workspace provider %q", env.WorkspaceProvider.Name),
182182
clog.BlankLine,
183-
clog.Tipf("ask an infrastructure administrator to enable SSH for this resource pool"),
183+
clog.Tipf("ask an infrastructure administrator to enable SSH for this workspace provider"),
184184
)
185185
continue
186186
}
187-
u, err := url.Parse(env.Pool.AccessURL)
187+
u, err := url.Parse(env.WorkspaceProvider.EnvproxyAccessURL)
188188
if err != nil {
189-
clog.LogWarn("invalid access url", clog.Causef("malformed url: %q", env.Pool.AccessURL))
189+
clog.LogWarn("invalid access url", clog.Causef("malformed url: %q", env.WorkspaceProvider.EnvproxyAccessURL))
190190
continue
191191
}
192192
newConfig += makeSSHConfig(u.Host, userName, env.Env.Name, privateKeyFilepath)

internal/cmd/providers.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
"golang.org/x/xerrors"
8+
9+
"cdr.dev/coder-cli/coder-sdk"
10+
"cdr.dev/coder-cli/internal/x/xcobra"
11+
"cdr.dev/coder-cli/pkg/clog"
12+
"cdr.dev/coder-cli/pkg/tablewriter"
13+
)
14+
15+
func providersCmd() *cobra.Command {
16+
cmd := &cobra.Command{
17+
Use: "providers",
18+
Short: "Interact with Coder workspace providers",
19+
Long: "Perform operations on the Coder Workspace Providers for the platform.",
20+
Hidden: true,
21+
}
22+
23+
cmd.AddCommand(
24+
createProviderCmd(),
25+
listProviderCmd(),
26+
deleteProviderCmd(),
27+
)
28+
return cmd
29+
}
30+
31+
func createProviderCmd() *cobra.Command {
32+
cmd := &cobra.Command{
33+
Use: "create [workspace_provider_name]",
34+
Short: "create a new workspace provider.",
35+
Args: xcobra.ExactArgs(1),
36+
Long: "Create a new Coder workspace provider.",
37+
Example: `# create a new workspace provider in a pending state
38+
coder providers create my-new-workspace-provider`,
39+
RunE: func(cmd *cobra.Command, args []string) error {
40+
ctx := cmd.Context()
41+
42+
client, err := newClient(ctx)
43+
if err != nil {
44+
return err
45+
}
46+
47+
// ExactArgs(1) ensures our name value can't panic on an out of bounds.
48+
createReq := &coder.CreateWorkspaceProviderReq{
49+
Name: args[0],
50+
}
51+
52+
wp, err := client.CreateWorkspaceProvider(ctx, *createReq)
53+
if err != nil {
54+
return xerrors.Errorf("create workspace provider: %w", err)
55+
}
56+
57+
err = tablewriter.WriteTable(1, func(i int) interface{} {
58+
return *wp
59+
})
60+
if err != nil {
61+
return xerrors.Errorf("write table: %w", err)
62+
}
63+
return nil
64+
},
65+
}
66+
return cmd
67+
}
68+
69+
func listProviderCmd() *cobra.Command {
70+
cmd := &cobra.Command{
71+
Use: "ls",
72+
Short: "list workspace providers.",
73+
Long: "List all Coder workspace providers.",
74+
Example: `# list workspace providers
75+
coder providers ls`,
76+
RunE: func(cmd *cobra.Command, args []string) error {
77+
ctx := cmd.Context()
78+
79+
client, err := newClient(ctx)
80+
if err != nil {
81+
return err
82+
}
83+
84+
wps, err := client.WorkspaceProviders(ctx)
85+
if err != nil {
86+
return xerrors.Errorf("list workspace providers: %w", err)
87+
}
88+
89+
err = tablewriter.WriteTable(len(wps), func(i int) interface{} {
90+
return wps[i]
91+
})
92+
if err != nil {
93+
return xerrors.Errorf("write table: %w", err)
94+
}
95+
return nil
96+
},
97+
}
98+
return cmd
99+
}
100+
101+
func deleteProviderCmd() *cobra.Command {
102+
cmd := &cobra.Command{
103+
Use: "rm [workspace_provider_name]",
104+
Short: "remove a workspace provider.",
105+
Long: "Remove an existing Coder workspace provider by name.",
106+
Example: `# remove an existing workspace provider by name
107+
coder providers rm my-workspace-provider`,
108+
RunE: func(cmd *cobra.Command, args []string) error {
109+
ctx := cmd.Context()
110+
client, err := newClient(ctx)
111+
if err != nil {
112+
return err
113+
}
114+
115+
wps, err := client.WorkspaceProviders(ctx)
116+
if err != nil {
117+
return xerrors.Errorf("listing workspace providers: %w", err)
118+
}
119+
120+
egroup := clog.LoggedErrGroup()
121+
for _, wpName := range args {
122+
name := wpName
123+
egroup.Go(func() error {
124+
var id string
125+
for _, wp := range wps {
126+
if wp.Name == name {
127+
id = wp.ID
128+
}
129+
}
130+
if id == "" {
131+
return clog.Error(
132+
fmt.Sprintf(`failed to remove workspace provider "%s"`, name),
133+
clog.Causef(`no workspace provider found by name "%s"`, name),
134+
)
135+
}
136+
137+
err = client.DeleteWorkspaceProviderByID(ctx, id)
138+
if err != nil {
139+
return clog.Error(
140+
fmt.Sprintf(`failed to remove workspace provider "%s"`, name),
141+
clog.Causef(err.Error()),
142+
)
143+
}
144+
145+
clog.LogSuccess(fmt.Sprintf(`removed workspace provider with name "%s"`, name))
146+
147+
return nil
148+
})
149+
}
150+
return egroup.Wait()
151+
},
152+
}
153+
return cmd
154+
}

0 commit comments

Comments
 (0)
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