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

Commit 6b95234

Browse files
author
Russtopia
committed
Add subcommands 'create' and 'del' to manage devurls
1 parent 6450687 commit 6b95234

File tree

6 files changed

+185
-8
lines changed

6 files changed

+185
-8
lines changed

cmd/coder/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ func (r *rootCmd) Subcommands() []cli.Command {
3737
&shellCmd{},
3838
&syncCmd{},
3939
&urlsCmd{},
40+
&createURLCmd{},
41+
&delURLCmd{},
4042
&versionCmd{},
4143
&configSSHCmd{},
4244
}

cmd/coder/sync.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ func (cmd *syncCmd) RegisterFlags(fl *pflag.FlagSet) {
3232
}
3333

3434
// See https://lxadm.com/Rsync_exit_codes#List_of_standard_rsync_exit_codes.
35-
var IncompatRsync = errors.New("rsync: exit status 2")
36-
var StreamErrRsync = errors.New("rsync: exit status 12")
35+
var errIncompatRsync = errors.New("rsync: exit status 2")
36+
var errStreamErrRsync = errors.New("rsync: exit status 12")
3737

3838
func (cmd *syncCmd) Run(fl *pflag.FlagSet) {
3939
var (
@@ -81,9 +81,9 @@ func (cmd *syncCmd) Run(fl *pflag.FlagSet) {
8181
err = s.Run()
8282
}
8383

84-
if fmt.Sprintf("%v", err) == fmt.Sprintf("%v", IncompatRsync) {
84+
if fmt.Sprintf("%v", err) == fmt.Sprintf("%v", errIncompatRsync) {
8585
flog.Fatal("no compatible rsync present on remote machine")
86-
} else if fmt.Sprintf("%v", err) == fmt.Sprintf("%v", StreamErrRsync) {
86+
} else if fmt.Sprintf("%v", err) == fmt.Sprintf("%v", errStreamErrRsync) {
8787
flog.Fatal("error in rsync protocol datastream (no installed remote rsync?)")
8888
} else {
8989
flog.Fatal("sync: %v", err)

cmd/coder/urls.go

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"net/http"
77
"os"
8+
"strings"
89
"text/tabwriter"
910

1011
"github.com/spf13/pflag"
@@ -14,13 +15,124 @@ import (
1415
)
1516

1617
type urlsCmd struct{}
18+
type createURLCmd struct{}
19+
type delURLCmd struct{}
1720

21+
// DevURL is the parsed json response record for a devURL from cemanager
1822
type DevURL struct {
23+
ID string `json:"id"`
1924
URL string `json:"url"`
2025
Port string `json:"port"`
2126
Access string `json:"access"`
2227
}
2328

29+
var urlAccessLevel = map[string]string{
30+
"PRIVATE": "Only you can access",
31+
"ORG": "All members of your organization can access",
32+
"AUTHED": "Authenticated users can access",
33+
"PUBLIC": "Anyone on the internet can access this link",
34+
}
35+
36+
func isAccessLevelValid(level string) bool {
37+
_, ok := urlAccessLevel[level]
38+
return ok
39+
}
40+
41+
func (cmd createURLCmd) Spec() cli.CommandSpec {
42+
return cli.CommandSpec{
43+
Name: "create",
44+
Usage: "<env name> <port> [<access>]",
45+
Desc: `create/update a devurl for external access
46+
<access> is one of [PRIVATE | ORG | AUTHED | PUBLIC]`,
47+
}
48+
}
49+
50+
// Run() creates or updates a devURL, specified by env ID and port
51+
// (fl.Arg(0) and fl.Arg(1)), with access level (fl.Arg(2)) on
52+
// the cemanager.
53+
func (cmd createURLCmd) Run(fl *pflag.FlagSet) {
54+
var envName = fl.Arg(0)
55+
var port = fl.Arg(1)
56+
var access = fl.Arg(2)
57+
58+
if envName == "" {
59+
exitUsage(fl)
60+
}
61+
62+
if access == "" {
63+
access = "PRIVATE"
64+
}
65+
66+
access = strings.ToUpper(access)
67+
68+
if !isAccessLevelValid(access) {
69+
fmt.Printf("Invalid access level '%v'\n", access)
70+
exitUsage(fl)
71+
}
72+
73+
entClient := requireAuth()
74+
75+
env := findEnv(entClient, envName)
76+
77+
_, found := devURLID(port, urlList(fl))
78+
if found {
79+
fmt.Printf("Updating devurl for port %v\n", port)
80+
} else {
81+
fmt.Printf("Adding devurl for port %v\n", port)
82+
}
83+
84+
err := entClient.UpsertDevURL(env.ID, port, access)
85+
if err != nil {
86+
flog.Error("upsert devurl: %s", err.Error())
87+
}
88+
}
89+
90+
func (cmd delURLCmd) Spec() cli.CommandSpec {
91+
return cli.CommandSpec{
92+
Name: "del",
93+
Usage: "<env name> <port>",
94+
Desc: "delete a devurl",
95+
}
96+
}
97+
98+
// Return the ID of a devURL, given the env name and port.
99+
// ("", false) is returned if no match is found.
100+
101+
func devURLID(port string, urls []DevURL) (string, bool) {
102+
for _, url := range urls {
103+
if url.Port == port {
104+
return url.ID, true
105+
}
106+
}
107+
return "", false
108+
}
109+
110+
// Run() deletes a devURL, specified by env ID and port, from the cemanager.
111+
func (cmd delURLCmd) Run(fl *pflag.FlagSet) {
112+
var envName = fl.Arg(0)
113+
var port = fl.Arg(1)
114+
115+
if envName == "" {
116+
exitUsage(fl)
117+
}
118+
119+
entClient := requireAuth()
120+
121+
env := findEnv(entClient, envName)
122+
123+
urlID, found := devURLID(port, urlList(fl))
124+
if found {
125+
fmt.Printf("Deleting devurl for port %v\n", port)
126+
} else {
127+
flog.Fatal("No devurl found for port %v", port)
128+
}
129+
130+
err := entClient.DelDevURL(env.ID, urlID)
131+
if err != nil {
132+
flog.Error("delete devurl: %s", err.Error())
133+
}
134+
}
135+
24136
func (cmd urlsCmd) Spec() cli.CommandSpec {
25137
return cli.CommandSpec{
26138
Name: "urls",
@@ -29,7 +141,9 @@ func (cmd urlsCmd) Spec() cli.CommandSpec {
29141
}
30142
}
31143

32-
func (cmd urlsCmd) Run(fl *pflag.FlagSet) {
144+
// urlList() returns the list of active devURLs from the cemanager server.
145+
146+
func urlList(fl *pflag.FlagSet) []DevURL {
33147
var envName = fl.Arg(0)
34148

35149
if envName == "" {
@@ -41,9 +155,9 @@ func (cmd urlsCmd) Run(fl *pflag.FlagSet) {
41155
env := findEnv(entClient, envName)
42156

43157
reqString := "%s/api/environments/%s/devurls?session_token=%s"
44-
reqUrl := fmt.Sprintf(reqString, entClient.BaseURL, env.ID, entClient.Token)
158+
reqURL := fmt.Sprintf(reqString, entClient.BaseURL, env.ID, entClient.Token)
45159

46-
resp, err := http.Get(reqUrl)
160+
resp, err := http.Get(reqURL)
47161
if err != nil {
48162
flog.Fatal("%v", err)
49163
}
@@ -65,6 +179,14 @@ func (cmd urlsCmd) Run(fl *pflag.FlagSet) {
65179
fmt.Printf("no dev urls were found for environment: %s\n", envName)
66180
}
67181

182+
return devURLs
183+
}
184+
185+
// Run() gets the list of active devURLs from the cemanager for the
186+
// specified environment and outputs info to stdout.
187+
func (cmd urlsCmd) Run(fl *pflag.FlagSet) {
188+
devURLs := urlList(fl)
189+
68190
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.TabIndent)
69191
for _, devURL := range devURLs {
70192
fmt.Fprintf(w, "%s\t%s\t%s\n", devURL.URL, devURL.Port, devURL.Access)

internal/entclient/activity.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package entclient
22

3-
import "net/http"
3+
import (
4+
"net/http"
5+
)
46

57
func (c Client) PushActivity(source string, envID string) error {
68
res, err := c.request("POST", "/api/metrics/usage/push", map[string]string{

internal/entclient/request.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import (
88
"golang.org/x/xerrors"
99
)
1010

11+
// request() makes an HTTP request of the specified method, returning the
12+
// *http.Response and any error.
13+
1114
func (c Client) request(
1215
method string, path string,
1316
request interface{},
@@ -30,6 +33,9 @@ func (c Client) request(
3033
return client.Do(req)
3134
}
3235

36+
// requestBody() makes an HTTP request of the specified method, storing any
37+
// returned body in the supplied response.
38+
3339
func (c Client) requestBody(
3440
method string, path string, request interface{}, response interface{},
3541
) error {

internal/entclient/url.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package entclient
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
)
7+
8+
func (c Client) DelDevURL(envID, urlID string) error {
9+
reqString := "/api/environments/%s/devurls/%s"
10+
reqUrl := fmt.Sprintf(reqString, envID, urlID)
11+
12+
res, err := c.request("DELETE", reqUrl, map[string]string{
13+
"environment_id": envID,
14+
"url_id": urlID,
15+
})
16+
if err != nil {
17+
return err
18+
}
19+
20+
if res.StatusCode != http.StatusOK {
21+
return bodyError(res)
22+
}
23+
24+
return nil
25+
}
26+
27+
func (c Client) UpsertDevURL(envID, port, access string) error {
28+
reqString := "/api/environments/%s/devurls"
29+
reqUrl := fmt.Sprintf(reqString, envID)
30+
31+
res, err := c.request("POST", reqUrl, map[string]string{
32+
"environment_id": envID,
33+
"port": port,
34+
"access": access,
35+
})
36+
if err != nil {
37+
return err
38+
}
39+
40+
if res.StatusCode != http.StatusOK {
41+
return bodyError(res)
42+
}
43+
44+
return nil
45+
}

0 commit comments

Comments
 (0)