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

Commit 366e9db

Browse files
committed
Migrate urls command
1 parent 3237d1f commit 366e9db

File tree

2 files changed

+124
-133
lines changed

2 files changed

+124
-133
lines changed

cmd/coder/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func main() {
5050
makeSecretsCmd(),
5151
makeEnvsCommand(),
5252
makeSyncCmd(),
53+
makeURLCmd(),
5354
}
5455
err = app.Run(os.Args)
5556
if err != nil {

cmd/coder/urls.go

Lines changed: 123 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,49 @@ import (
1010
"strings"
1111
"text/tabwriter"
1212

13-
"github.com/spf13/pflag"
13+
"github.com/urfave/cli"
1414

15-
"go.coder.com/cli"
1615
"go.coder.com/flog"
1716
)
1817

19-
type urlsCmd struct{}
20-
21-
func (cmd *urlsCmd) Subcommands() []cli.Command {
22-
return []cli.Command{
23-
&listSubCmd{},
24-
&createSubCmd{},
25-
&delSubCmd{},
26-
}
27-
}
28-
29-
func (cmd urlsCmd) Spec() cli.CommandSpec {
30-
return cli.CommandSpec{
31-
Name: "urls",
32-
Usage: "[subcommand] <flags>",
33-
Desc: "interact with environment devurls",
18+
func makeURLCmd() cli.Command {
19+
var outputFmt string
20+
return cli.Command{
21+
Name: "urls",
22+
Usage: "Interact with environment DevURLs",
23+
ArgsUsage: "",
24+
Before: nil,
25+
After: nil,
26+
OnUsageError: nil,
27+
Subcommands: []cli.Command{
28+
makeCreateDevURL(),
29+
{
30+
Name: "ls",
31+
Usage: "List all DevURLs for an environment",
32+
ArgsUsage: "[env_name]",
33+
Before: nil,
34+
Action: makeListDevURLs(&outputFmt),
35+
Flags: []cli.Flag{
36+
cli.StringFlag{
37+
Name: "output",
38+
Usage: "human | json",
39+
Value: "human",
40+
Destination: &outputFmt,
41+
},
42+
},
43+
},
44+
{
45+
Name: "rm",
46+
Usage: "Remove a dev url",
47+
ArgsUsage: "",
48+
Before: nil,
49+
Action: removeDevURL,
50+
Flags: nil,
51+
},
52+
},
3453
}
3554
}
3655

37-
func (cmd urlsCmd) Run(fl *pflag.FlagSet) {
38-
exitUsage(fl)
39-
}
40-
4156
// DevURL is the parsed json response record for a devURL from cemanager
4257
type DevURL struct {
4358
ID string `json:"id"`
@@ -78,64 +93,97 @@ func accessLevelIsValid(level string) bool {
7893
return ok
7994
}
8095

81-
type listSubCmd struct {
82-
outputFmt string
83-
}
84-
8596
// Run gets the list of active devURLs from the cemanager for the
8697
// specified environment and outputs info to stdout.
87-
func (sub listSubCmd) Run(fl *pflag.FlagSet) {
88-
envName := fl.Arg(0)
89-
devURLs := urlList(envName)
90-
91-
if len(devURLs) == 0 {
92-
return
93-
}
98+
func makeListDevURLs(outputFmt *string) func(c *cli.Context) {
99+
return func(c *cli.Context) {
100+
envName := c.Args().First()
101+
devURLs := urlList(envName)
94102

95-
switch sub.outputFmt {
96-
case "human":
97-
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.TabIndent)
98-
for _, devURL := range devURLs {
99-
fmt.Fprintf(w, "%s\t%d\t%s\n", devURL.URL, devURL.Port, devURL.Access)
103+
if len(devURLs) == 0 {
104+
return
100105
}
101-
err := w.Flush()
102-
requireSuccess(err, "failed to flush writer: %v", err)
103-
case "json":
104-
err := json.NewEncoder(os.Stdout).Encode(devURLs)
105-
requireSuccess(err, "failed to encode devurls to json: %v", err)
106-
default:
107-
exitUsage(fl)
108-
}
109-
}
110106

111-
func (sub *listSubCmd) RegisterFlags(fl *pflag.FlagSet) {
112-
fl.StringVarP(&sub.outputFmt, "output", "o", "human", "output format (human | json)")
113-
}
114-
115-
func (sub *listSubCmd) Spec() cli.CommandSpec {
116-
return cli.CommandSpec{
117-
Name: "ls",
118-
Usage: "<env> <flags>",
119-
Desc: "list all devurls for a given environment",
107+
switch *outputFmt {
108+
case "human":
109+
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.TabIndent)
110+
for _, devURL := range devURLs {
111+
fmt.Fprintf(w, "%s\t%d\t%s\n", devURL.URL, devURL.Port, devURL.Access)
112+
}
113+
err := w.Flush()
114+
requireSuccess(err, "failed to flush writer: %v", err)
115+
case "json":
116+
err := json.NewEncoder(os.Stdout).Encode(devURLs)
117+
requireSuccess(err, "failed to encode devurls to json: %v", err)
118+
default:
119+
flog.Fatal("unknown --output value %q", *outputFmt)
120+
}
120121
}
121122
}
122123

123-
type createSubCmd struct {
124-
access string
125-
urlname string
126-
}
127-
128-
func (sub *createSubCmd) RegisterFlags(fl *pflag.FlagSet) {
129-
fl.StringVarP(&sub.access, "access", "a", "private", "[private | org | authed | public] set devurl access")
130-
fl.StringVarP(&sub.urlname, "name", "n", "", "devurl name")
131-
}
132-
133-
func (sub createSubCmd) Spec() cli.CommandSpec {
134-
return cli.CommandSpec{
124+
func makeCreateDevURL() cli.Command {
125+
var (
126+
access string
127+
urlname string
128+
)
129+
return cli.Command{
135130
Name: "create",
136-
Usage: "<env name> <port> [--access <level>] [--name <name>]",
131+
Usage: "[env_name] [port] [--access <level>] [--name <name>]",
137132
Aliases: []string{"edit"},
138-
Desc: "create or update a devurl for external access",
133+
Before: nil,
134+
Flags: []cli.Flag{
135+
cli.StringFlag{
136+
Name: "access",
137+
Usage: "Set DevURL access to [private | org | authed | public]",
138+
Value: "private",
139+
Destination: &access,
140+
},
141+
cli.StringFlag{
142+
Name: "name",
143+
Usage: "DevURL name",
144+
Destination: &urlname,
145+
},
146+
},
147+
// Run creates or updates a devURL
148+
Action: func(c *cli.Context) {
149+
var (
150+
envName = c.Args().First()
151+
port = c.Args().Get(1)
152+
)
153+
154+
if envName == "" {
155+
cli.ShowCommandHelpAndExit(c, c.Command.FullName(), 1)
156+
}
157+
158+
portNum, err := validatePort(port)
159+
if err != nil {
160+
cli.ShowCommandHelpAndExit(c, c.Command.FullName(), 1)
161+
}
162+
163+
access = strings.ToUpper(access)
164+
if !accessLevelIsValid(access) {
165+
cli.ShowCommandHelpAndExit(c, c.Command.FullName(), 1)
166+
}
167+
168+
if urlname != "" && !devURLNameValidRx.MatchString(urlname) {
169+
flog.Fatal("update devurl: name must be < 64 chars in length, begin with a letter and only contain letters or digits.")
170+
return
171+
}
172+
entClient := requireAuth()
173+
174+
env := findEnv(entClient, envName)
175+
176+
urlID, found := devURLID(portNum, urlList(envName))
177+
if found {
178+
flog.Info("Updating devurl for port %v", port)
179+
err := entClient.UpdateDevURL(env.ID, urlID, portNum, urlname, access)
180+
requireSuccess(err, "update devurl: %s", err)
181+
} else {
182+
flog.Info("Adding devurl for port %v", port)
183+
err := entClient.InsertDevURL(env.ID, portNum, urlname, access)
184+
requireSuccess(err, "insert devurl: %s", err)
185+
}
186+
},
139187
}
140188
}
141189

@@ -144,60 +192,6 @@ func (sub createSubCmd) Spec() cli.CommandSpec {
144192
// consist solely of letters and digits, with a max length of 64 chars.
145193
var devURLNameValidRx = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9]{0,63}$")
146194

147-
// Run creates or updates a devURL, specified by env ID and port
148-
// (fl.Arg(0) and fl.Arg(1)), with access level (fl.Arg(2)) on
149-
// the cemanager.
150-
func (sub createSubCmd) Run(fl *pflag.FlagSet) {
151-
envName := fl.Arg(0)
152-
port := fl.Arg(1)
153-
name := fl.Arg(2)
154-
access := fl.Arg(3)
155-
156-
if envName == "" {
157-
exitUsage(fl)
158-
}
159-
160-
portNum, err := validatePort(port)
161-
if err != nil {
162-
exitUsage(fl)
163-
}
164-
165-
access = strings.ToUpper(sub.access)
166-
if !accessLevelIsValid(access) {
167-
exitUsage(fl)
168-
}
169-
170-
name = sub.urlname
171-
if name != "" && !devURLNameValidRx.MatchString(name) {
172-
flog.Fatal("update devurl: name must be < 64 chars in length, begin with a letter and only contain letters or digits.")
173-
return
174-
}
175-
entClient := requireAuth()
176-
177-
env := findEnv(entClient, envName)
178-
179-
urlID, found := devURLID(portNum, urlList(envName))
180-
if found {
181-
flog.Info("Updating devurl for port %v", port)
182-
err := entClient.UpdateDevURL(env.ID, urlID, portNum, name, access)
183-
requireSuccess(err, "update devurl: %s", err)
184-
} else {
185-
flog.Info("Adding devurl for port %v", port)
186-
err := entClient.InsertDevURL(env.ID, portNum, name, access)
187-
requireSuccess(err, "insert devurl: %s", err)
188-
}
189-
}
190-
191-
type delSubCmd struct{}
192-
193-
func (sub delSubCmd) Spec() cli.CommandSpec {
194-
return cli.CommandSpec{
195-
Name: "rm",
196-
Usage: "<env name> <port>",
197-
Desc: "remove a devurl",
198-
}
199-
}
200-
201195
// devURLID returns the ID of a devURL, given the env name and port
202196
// from a list of DevURL records.
203197
// ("", false) is returned if no match is found.
@@ -211,18 +205,14 @@ func devURLID(port int, urls []DevURL) (string, bool) {
211205
}
212206

213207
// Run deletes a devURL, specified by env ID and port, from the cemanager.
214-
func (sub delSubCmd) Run(fl *pflag.FlagSet) {
215-
envName := fl.Arg(0)
216-
port := fl.Arg(1)
217-
218-
if envName == "" {
219-
exitUsage(fl)
220-
}
208+
func removeDevURL(c *cli.Context) {
209+
var (
210+
envName = c.Args().First()
211+
port = c.Args().Get(1)
212+
)
221213

222214
portNum, err := validatePort(port)
223-
if err != nil {
224-
exitUsage(fl)
225-
}
215+
requireSuccess(err, "failed to validate port: %v", err)
226216

227217
entClient := requireAuth()
228218
env := findEnv(entClient, envName)

0 commit comments

Comments
 (0)