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

Commit 5d92778

Browse files
author
Russtopia
committed
Add custom prefix devurl
1 parent 87c708a commit 5d92778

File tree

4 files changed

+111
-46
lines changed

4 files changed

+111
-46
lines changed

cmd/coder/urls.go

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"net/http"
77
"os"
8+
"regexp"
89
"strconv"
910
"strings"
1011
"text/tabwriter"
@@ -21,7 +22,7 @@ type urlsCmd struct{}
2122
type DevURL struct {
2223
ID string `json:"id"`
2324
URL string `json:"url"`
24-
Port string `json:"port"`
25+
Port int `json:"port"`
2526
Access string `json:"access"`
2627
}
2728

@@ -33,55 +34,68 @@ var urlAccessLevel = map[string]string{
3334
"PUBLIC": "Anyone on the internet can access this link",
3435
}
3536

36-
func portIsValid(port string) bool {
37+
func validatePort(port string) (int, error) {
3738
p, err := strconv.ParseUint(port, 10, 16)
39+
if err != nil {
40+
flog.Error("Invalid port")
41+
return 0, err
42+
}
3843
if p < 1 {
3944
// port 0 means 'any free port', which we don't support
4045
err = strconv.ErrRange
46+
flog.Error("Port must be > 0")
47+
return 0, err
4148
}
42-
if err != nil {
43-
fmt.Println("Invalid port")
44-
}
45-
return err == nil
49+
return int(p), nil
4650
}
4751

4852
func accessLevelIsValid(level string) bool {
4953
_, ok := urlAccessLevel[level]
5054
if !ok {
51-
fmt.Println("Invalid access level")
55+
flog.Error("Invalid access level")
5256
}
5357
return ok
5458
}
5559

5660
type createSubCmd struct {
57-
access string
61+
access string
62+
urlname string
5863
}
5964

6065
func (sub *createSubCmd) RegisterFlags(fl *pflag.FlagSet) {
6166
fl.StringVarP(&sub.access, "access", "a", "private", "[private | org | authed | public] set devurl access")
67+
fl.StringVarP(&sub.urlname, "name", "n", "", "devurl name")
6268
}
6369

6470
func (sub createSubCmd) Spec() cli.CommandSpec {
6571
return cli.CommandSpec{
66-
Name: "create",
67-
Usage: "<env name> <port> [--access <level>]",
68-
Desc: "create/update a devurl for external access",
72+
Name: "create",
73+
Usage: "<env name> <port> [--access <level>] [--name <name>]",
74+
Aliases: []string{"edit"},
75+
Desc: "create or update a devurl for external access",
6976
}
7077
}
7178

79+
// devURLNameValidRx is the regex used to validate devurl names specified
80+
// via the --name subcommand. Named devurls must begin with a letter, and
81+
// consist solely of letters and digits, with a max length of 64 chars.
82+
var devURLNameValidRx = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9]{0,63}$")
83+
7284
// Run creates or updates a devURL, specified by env ID and port
7385
// (fl.Arg(0) and fl.Arg(1)), with access level (fl.Arg(2)) on
7486
// the cemanager.
7587
func (sub createSubCmd) Run(fl *pflag.FlagSet) {
7688
envName := fl.Arg(0)
7789
port := fl.Arg(1)
78-
access := fl.Arg(2)
90+
name := fl.Arg(2)
91+
access := fl.Arg(3)
7992

8093
if envName == "" {
8194
exitUsage(fl)
8295
}
8396

84-
if !portIsValid(port) {
97+
portNum, err := validatePort(port)
98+
if err != nil {
8599
exitUsage(fl)
86100
}
87101

@@ -90,20 +104,28 @@ func (sub createSubCmd) Run(fl *pflag.FlagSet) {
90104
exitUsage(fl)
91105
}
92106

107+
name = sub.urlname
108+
if name != "" && !devURLNameValidRx.MatchString(name) {
109+
flog.Error("update devurl: name must be < 64 chars in length, begin with a letter and only contain letters or digits.")
110+
return
111+
}
93112
entClient := requireAuth()
94113

95114
env := findEnv(entClient, envName)
96115

97-
_, found := devURLID(port, urlList(envName))
116+
urlID, found := devURLID(portNum, urlList(envName))
98117
if found {
99-
fmt.Printf("Updating devurl for port %v\n", port)
118+
flog.Info("Updating devurl for port %v", port)
119+
err := entClient.UpdateDevURL(env.ID, urlID, portNum, name, access)
120+
if err != nil {
121+
flog.Error("update devurl: %s", err.Error())
122+
}
100123
} else {
101-
fmt.Printf("Adding devurl for port %v\n", port)
102-
}
103-
104-
err := entClient.UpsertDevURL(env.ID, port, access)
105-
if err != nil {
106-
flog.Error("upsert devurl: %s", err.Error())
124+
flog.Info("Adding devurl for port %v", port)
125+
err := entClient.InsertDevURL(env.ID, portNum, name, access)
126+
if err != nil {
127+
flog.Error("insert devurl: %s", err.Error())
128+
}
107129
}
108130
}
109131

@@ -117,9 +139,10 @@ func (sub delSubCmd) Spec() cli.CommandSpec {
117139
}
118140
}
119141

120-
// devURLID returns the ID of a devURL, given the env name and port.
142+
// devURLID returns the ID of a devURL, given the env name and port
143+
// from a list of DevURL records.
121144
// ("", false) is returned if no match is found.
122-
func devURLID(port string, urls []DevURL) (string, bool) {
145+
func devURLID(port int, urls []DevURL) (string, bool) {
123146
for _, url := range urls {
124147
if url.Port == port {
125148
return url.ID, true
@@ -137,22 +160,22 @@ func (sub delSubCmd) Run(fl *pflag.FlagSet) {
137160
exitUsage(fl)
138161
}
139162

140-
if !portIsValid(port) {
163+
portNum, err := validatePort(port)
164+
if err != nil {
141165
exitUsage(fl)
142166
}
143167

144168
entClient := requireAuth()
145-
146169
env := findEnv(entClient, envName)
147170

148-
urlID, found := devURLID(port, urlList(envName))
171+
urlID, found := devURLID(portNum, urlList(envName))
149172
if found {
150-
fmt.Printf("Deleting devurl for port %v\n", port)
173+
flog.Info("Deleting devurl for port %v", port)
151174
} else {
152175
flog.Fatal("No devurl found for port %v", port)
153176
}
154177

155-
err := entClient.DelDevURL(env.ID, urlID)
178+
err = entClient.DelDevURL(env.ID, urlID)
156179
if err != nil {
157180
flog.Error("delete devurl: %s", err.Error())
158181
}
@@ -192,10 +215,6 @@ func urlList(envName string) []DevURL {
192215
flog.Fatal("%v", err)
193216
}
194217

195-
if len(devURLs) == 0 {
196-
fmt.Printf("no dev urls were found for environment: %s\n", envName)
197-
}
198-
199218
return devURLs
200219
}
201220

@@ -207,7 +226,7 @@ func (cmd urlsCmd) Run(fl *pflag.FlagSet) {
207226

208227
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.TabIndent)
209228
for _, devURL := range devURLs {
210-
fmt.Fprintf(w, "%s\t%s\t%s\n", devURL.URL, devURL.Port, devURL.Access)
229+
fmt.Fprintf(w, "%s\t%d\t%s\n", devURL.URL, devURL.Port, devURL.Access)
211230
}
212231
w.Flush()
213232
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ require (
1313
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
1414
github.com/rjeczalik/notify v0.9.2
1515
github.com/spf13/pflag v1.0.5
16-
go.coder.com/cli v0.4.0
16+
go.coder.com/cli v0.5.0
1717
go.coder.com/flog v0.0.0-20190906214207-47dd47ea0512
1818
golang.org/x/crypto v0.0.0-20200422194213-44a606286825
1919
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
163163
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
164164
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
165165
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
166+
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
167+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
166168
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
167169
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
168170
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
@@ -171,6 +173,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
171173
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
172174
go.coder.com/cli v0.4.0 h1:PruDGwm/CPFndyK/eMowZG3vzg5CgohRWeXWCTr3zi8=
173175
go.coder.com/cli v0.4.0/go.mod h1:hRTOURCR3LJF1FRW9arecgrzX+AHG7mfYMwThPIgq+w=
176+
go.coder.com/cli v0.5.0 h1:7W9ECtZdVKaAc0Oe2uk5J/c0LCtsWufQz4NeX6YwP0g=
177+
go.coder.com/cli v0.5.0/go.mod h1:h6091Eox0VdgJw2CDBvTyx7SnhduTm8qYM2bR2pewls=
174178
go.coder.com/flog v0.0.0-20190906214207-47dd47ea0512 h1:DjCS6dRQh+1PlfiBmnabxfdrzenb0tAwJqFxDEH/s9g=
175179
go.coder.com/flog v0.0.0-20190906214207-47dd47ea0512/go.mod h1:83JsYgXYv0EOaXjIMnaZ1Fl6ddNB3fJnDZ/8845mUJ8=
176180
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -300,6 +304,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
300304
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
301305
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
302306
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
307+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
308+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
303309
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
304310
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
305311
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

internal/entclient/devurl.go

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,23 @@ package entclient
33
import (
44
"fmt"
55
"net/http"
6+
7+
"golang.org/x/xerrors"
68
)
79

10+
type delDevURLRequest struct {
11+
EnvID string `json:"environment_id"`
12+
DevURLID string `json:"url_id"`
13+
}
14+
815
// DelDevURL deletes the specified devurl
916
func (c Client) DelDevURL(envID, urlID string) error {
1017
reqString := "/api/environments/%s/devurls/%s"
1118
reqURL := fmt.Sprintf(reqString, envID, urlID)
1219

13-
res, err := c.request("DELETE", reqURL, map[string]string{
14-
"environment_id": envID,
15-
"url_id": urlID,
20+
res, err := c.request("DELETE", reqURL, delDevURLRequest{
21+
EnvID: envID,
22+
DevURLID: urlID,
1623
})
1724
if err != nil {
1825
return err
@@ -25,23 +32,56 @@ func (c Client) DelDevURL(envID, urlID string) error {
2532
return nil
2633
}
2734

28-
// UpsertDevURL upserts the specified devurl for the authenticated user
29-
func (c Client) UpsertDevURL(envID, port, access string) error {
35+
type createDevURLRequest struct {
36+
EnvID string `json:"environment_id"`
37+
Port int `json:"port"`
38+
Access string `json:"access"`
39+
Name string `json:"name"`
40+
}
41+
42+
// InsertDevURL inserts a new devurl for the authenticated user
43+
func (c Client) InsertDevURL(envID string, port int, name, access string) error {
3044
reqString := "/api/environments/%s/devurls"
3145
reqURL := fmt.Sprintf(reqString, envID)
3246

33-
res, err := c.request("POST", reqURL, map[string]string{
34-
"environment_id": envID,
35-
"port": port,
36-
"access": access,
47+
res, err := c.request("POST", reqURL, createDevURLRequest{
48+
EnvID: envID,
49+
Port: port,
50+
Access: access,
51+
Name: name,
3752
})
53+
if res != nil && res.StatusCode == http.StatusConflict {
54+
return xerrors.Errorf("Failed to create devurl. Check that the port and name are unique.")
55+
}
3856
if err != nil {
3957
return err
4058
}
59+
return nil
60+
}
4161

42-
if res.StatusCode != http.StatusOK {
43-
return bodyError(res)
44-
}
62+
type updateDevURLRequest struct {
63+
EnvID string `json:"environment_id"`
64+
Port int `json:"port"`
65+
Access string `json:"access"`
66+
Name string `json:"name"`
67+
}
68+
69+
// UpdateDevURL updates an existing devurl for the authenticated user
70+
func (c Client) UpdateDevURL(envID, urlID string, port int, name, access string) error {
71+
reqString := "/api/environments/%s/devurls/%s"
72+
reqURL := fmt.Sprintf(reqString, envID, urlID)
4573

74+
res, err := c.request("PUT", reqURL, updateDevURLRequest{
75+
EnvID: envID,
76+
Port: port,
77+
Access: access,
78+
Name: name,
79+
})
80+
if res != nil && res.StatusCode == http.StatusConflict {
81+
return xerrors.Errorf("Failed to update devurl. Check that the port and name are unique.")
82+
}
83+
if err != nil {
84+
return err
85+
}
4686
return nil
4787
}

0 commit comments

Comments
 (0)