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

Add custom prefix devurl #74

Merged
merged 7 commits into from
Aug 4, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
85 changes: 52 additions & 33 deletions cmd/coder/urls.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"text/tabwriter"
Expand All @@ -21,7 +22,7 @@ type urlsCmd struct{}
type DevURL struct {
ID string `json:"id"`
URL string `json:"url"`
Port string `json:"port"`
Port int `json:"port"`
Access string `json:"access"`
}

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

func portIsValid(port string) bool {
func validatePort(port string) (int, error) {
p, err := strconv.ParseUint(port, 10, 16)
if err != nil {
flog.Error("Invalid port")
return 0, err
}
if p < 1 {
// port 0 means 'any free port', which we don't support
err = strconv.ErrRange
flog.Error("Port must be > 0")
return 0, err
}
if err != nil {
fmt.Println("Invalid port")
}
return err == nil
return int(p), nil
}

func accessLevelIsValid(level string) bool {
_, ok := urlAccessLevel[level]
if !ok {
fmt.Println("Invalid access level")
flog.Error("Invalid access level")
}
return ok
}

type createSubCmd struct {
access string
access string
urlname string
}

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

func (sub createSubCmd) Spec() cli.CommandSpec {
return cli.CommandSpec{
Name: "create",
Usage: "<env name> <port> [--access <level>]",
Desc: "create/update a devurl for external access",
Name: "create",
Usage: "<env name> <port> [--access <level>] [--name <name>]",
Aliases: []string{"edit"},
Desc: "create or update a devurl for external access",
}
}

// devURLNameValidRx is the regex used to validate devurl names specified
// via the --name subcommand. Named devurls must begin with a letter, and
// consist solely of letters and digits, with a max length of 64 chars.
var devURLNameValidRx = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9]{0,63}$")

// Run creates or updates a devURL, specified by env ID and port
// (fl.Arg(0) and fl.Arg(1)), with access level (fl.Arg(2)) on
// the cemanager.
func (sub createSubCmd) Run(fl *pflag.FlagSet) {
envName := fl.Arg(0)
port := fl.Arg(1)
access := fl.Arg(2)
name := fl.Arg(2)
access := fl.Arg(3)

if envName == "" {
exitUsage(fl)
}

if !portIsValid(port) {
portNum, err := validatePort(port)
if err != nil {
exitUsage(fl)
}

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

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

env := findEnv(entClient, envName)

_, found := devURLID(port, urlList(envName))
urlID, found := devURLID(portNum, urlList(envName))
if found {
fmt.Printf("Updating devurl for port %v\n", port)
flog.Info("Updating devurl for port %v", port)
err := entClient.UpdateDevURL(env.ID, urlID, portNum, name, access)
if err != nil {
flog.Error("update devurl: %s", err.Error())
}
} else {
fmt.Printf("Adding devurl for port %v\n", port)
}

err := entClient.UpsertDevURL(env.ID, port, access)
if err != nil {
flog.Error("upsert devurl: %s", err.Error())
flog.Info("Adding devurl for port %v", port)
err := entClient.InsertDevURL(env.ID, portNum, name, access)
if err != nil {
flog.Error("insert devurl: %s", err.Error())
}
}
}

Expand All @@ -117,9 +139,10 @@ func (sub delSubCmd) Spec() cli.CommandSpec {
}
}

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

if !portIsValid(port) {
portNum, err := validatePort(port)
if err != nil {
exitUsage(fl)
}
Copy link
Author

@Russtopia Russtopia Jul 31, 2020

Choose a reason for hiding this comment

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

What's your preference on exit/fatal from 'helper' funcs?

Current way here prints out usage and exits with nonzero status which is what we'd want, I guess. If I tucked in the if err != nil { exitUsage(f1) } right into those two helpers it would simplify the flow here. However, to call exitUsage(fl) within, I'd have to pass fl to both helpers which seems icky so perhaps I'll just leave these alone.


entClient := requireAuth()

env := findEnv(entClient, envName)

urlID, found := devURLID(port, urlList(envName))
urlID, found := devURLID(portNum, urlList(envName))
if found {
fmt.Printf("Deleting devurl for port %v\n", port)
flog.Info("Deleting devurl for port %v", port)
} else {
flog.Fatal("No devurl found for port %v", port)
}

err := entClient.DelDevURL(env.ID, urlID)
err = entClient.DelDevURL(env.ID, urlID)
if err != nil {
flog.Error("delete devurl: %s", err.Error())
}
Expand Down Expand Up @@ -192,10 +215,6 @@ func urlList(envName string) []DevURL {
flog.Fatal("%v", err)
}

if len(devURLs) == 0 {
fmt.Printf("no dev urls were found for environment: %s\n", envName)
}

return devURLs
}

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

w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.TabIndent)
for _, devURL := range devURLs {
fmt.Fprintf(w, "%s\t%s\t%s\n", devURL.URL, devURL.Port, devURL.Access)
fmt.Fprintf(w, "%s\t%d\t%s\n", devURL.URL, devURL.Port, devURL.Access)
}
w.Flush()
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
github.com/rjeczalik/notify v0.9.2
github.com/spf13/pflag v1.0.5
go.coder.com/cli v0.4.0
go.coder.com/cli v0.5.0
go.coder.com/flog v0.0.0-20190906214207-47dd47ea0512
golang.org/x/crypto v0.0.0-20200422194213-44a606286825
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
Expand All @@ -171,6 +173,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
go.coder.com/cli v0.4.0 h1:PruDGwm/CPFndyK/eMowZG3vzg5CgohRWeXWCTr3zi8=
go.coder.com/cli v0.4.0/go.mod h1:hRTOURCR3LJF1FRW9arecgrzX+AHG7mfYMwThPIgq+w=
go.coder.com/cli v0.5.0 h1:7W9ECtZdVKaAc0Oe2uk5J/c0LCtsWufQz4NeX6YwP0g=
go.coder.com/cli v0.5.0/go.mod h1:h6091Eox0VdgJw2CDBvTyx7SnhduTm8qYM2bR2pewls=
go.coder.com/flog v0.0.0-20190906214207-47dd47ea0512 h1:DjCS6dRQh+1PlfiBmnabxfdrzenb0tAwJqFxDEH/s9g=
go.coder.com/flog v0.0.0-20190906214207-47dd47ea0512/go.mod h1:83JsYgXYv0EOaXjIMnaZ1Fl6ddNB3fJnDZ/8845mUJ8=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
Expand Down Expand Up @@ -300,6 +304,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
64 changes: 52 additions & 12 deletions internal/entclient/devurl.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,23 @@ package entclient
import (
"fmt"
"net/http"

"golang.org/x/xerrors"
)

type delDevURLRequest struct {
EnvID string `json:"environment_id"`
DevURLID string `json:"url_id"`
}

// DelDevURL deletes the specified devurl
func (c Client) DelDevURL(envID, urlID string) error {
reqString := "/api/environments/%s/devurls/%s"
reqURL := fmt.Sprintf(reqString, envID, urlID)

res, err := c.request("DELETE", reqURL, map[string]string{
"environment_id": envID,
"url_id": urlID,
res, err := c.request("DELETE", reqURL, delDevURLRequest{
EnvID: envID,
DevURLID: urlID,
})
if err != nil {
return err
Expand All @@ -25,23 +32,56 @@ func (c Client) DelDevURL(envID, urlID string) error {
return nil
}

// UpsertDevURL upserts the specified devurl for the authenticated user
func (c Client) UpsertDevURL(envID, port, access string) error {
type createDevURLRequest struct {
EnvID string `json:"environment_id"`
Port int `json:"port"`
Access string `json:"access"`
Name string `json:"name"`
}

// InsertDevURL inserts a new devurl for the authenticated user
func (c Client) InsertDevURL(envID string, port int, name, access string) error {
reqString := "/api/environments/%s/devurls"
reqURL := fmt.Sprintf(reqString, envID)

res, err := c.request("POST", reqURL, map[string]string{
"environment_id": envID,
"port": port,
"access": access,
res, err := c.request("POST", reqURL, createDevURLRequest{
EnvID: envID,
Port: port,
Access: access,
Name: name,
})
if res != nil && res.StatusCode == http.StatusConflict {
return xerrors.Errorf("Failed to create devurl. Check that the port and name are unique.")
}
if err != nil {
return err
}
return nil
}

if res.StatusCode != http.StatusOK {
return bodyError(res)
}
type updateDevURLRequest struct {
EnvID string `json:"environment_id"`
Port int `json:"port"`
Access string `json:"access"`
Name string `json:"name"`
}

// UpdateDevURL updates an existing devurl for the authenticated user
func (c Client) UpdateDevURL(envID, urlID string, port int, name, access string) error {
reqString := "/api/environments/%s/devurls/%s"
reqURL := fmt.Sprintf(reqString, envID, urlID)

res, err := c.request("PUT", reqURL, updateDevURLRequest{
EnvID: envID,
Port: port,
Access: access,
Name: name,
})
if res != nil && res.StatusCode == http.StatusConflict {
return xerrors.Errorf("Failed to update devurl. Check that the port and name are unique.")
}
if err != nil {
return err
}
return nil
}