Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Aug 30, 2024. It is now read-only.
/coder-v1-cliPublic archive

Commit56a76c4

Browse files
authored
Merge pull request#86 from cdr/urfave
Migrate to cobra
2 parentsbae77f0 +be6bc28 commit56a76c4

28 files changed

+1050
-873
lines changed

‎ci/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#ci
2+
3+
##integration tests
4+
5+
###`tcli`
6+
7+
Package`tcli` provides a framework for writing end-to-end CLI tests.
8+
Each test group can have its own container for executing commands in a consistent
9+
and isolated filesystem.
10+
11+
###prerequisites
12+
13+
Assign the following environment variables to run the integration tests
14+
against an existing Enterprise deployment instance.
15+
16+
```bash
17+
export CODER_URL=...
18+
export CODER_EMAIL=...
19+
export CODER_PASSWORD=...
20+
```
21+
22+
Then, simply run the test command from the project root
23+
24+
```sh
25+
gotest -v ./ci/integration
26+
```

‎ci/integration/integration_test.go

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ package integration
22

33
import (
44
"context"
5-
"encoding/json"
65
"math/rand"
76
"testing"
87
"time"
98

109
"cdr.dev/coder-cli/ci/tcli"
11-
"cdr.dev/coder-cli/internal/entclient"
12-
"cdr.dev/slog"
1310
"cdr.dev/slog/sloggers/slogtest/assert"
1411
)
1512

@@ -34,17 +31,15 @@ func TestCoderCLI(t *testing.T) {
3431
tcli.StderrEmpty(),
3532
)
3633

37-
c.Run(ctx,"coder version").Assert(t,
34+
c.Run(ctx,"coder--version").Assert(t,
3835
tcli.StderrEmpty(),
3936
tcli.Success(),
4037
tcli.StdoutMatches("linux"),
4138
)
4239

43-
c.Run(ctx,"coder help").Assert(t,
40+
c.Run(ctx,"coder--help").Assert(t,
4441
tcli.Success(),
45-
tcli.StderrMatches("Commands:"),
46-
tcli.StderrMatches("Usage: coder"),
47-
tcli.StdoutEmpty(),
42+
tcli.StdoutMatches("Available Commands"),
4843
)
4944

5045
headlessLogin(ctx,t,c)
@@ -53,8 +48,12 @@ func TestCoderCLI(t *testing.T) {
5348
tcli.Success(),
5449
)
5550

51+
c.Run(ctx,"coder envs ls").Assert(t,
52+
tcli.Success(),
53+
)
54+
5655
c.Run(ctx,"coder urls").Assert(t,
57-
tcli.Error(),
56+
tcli.Success(),
5857
)
5958

6059
c.Run(ctx,"coder sync").Assert(t,
@@ -65,36 +64,15 @@ func TestCoderCLI(t *testing.T) {
6564
tcli.Error(),
6665
)
6766

68-
varuser entclient.User
69-
c.Run(ctx,`coder users ls -o json | jq -c '.[] | select( .username == "charlie")'`).Assert(t,
70-
tcli.Success(),
71-
stdoutUnmarshalsJSON(&user),
72-
)
73-
assert.Equal(t,"user email is as expected","charlie@coder.com",user.Email)
74-
assert.Equal(t,"username is as expected","Charlie",user.Name)
75-
76-
c.Run(ctx,"coder users ls -o human | grep charlie").Assert(t,
77-
tcli.Success(),
78-
tcli.StdoutMatches("charlie"),
79-
)
80-
8167
c.Run(ctx,"coder logout").Assert(t,
8268
tcli.Success(),
8369
)
8470

85-
c.Run(ctx,"coder envs").Assert(t,
71+
c.Run(ctx,"coder envs ls").Assert(t,
8672
tcli.Error(),
8773
)
8874
}
8975

90-
funcstdoutUnmarshalsJSON(targetinterface{}) tcli.Assertion {
91-
returnfunc(t*testing.T,r*tcli.CommandResult) {
92-
slog.Helper()
93-
err:=json.Unmarshal(r.Stdout,target)
94-
assert.Success(t,"json unmarshals",err)
95-
}
96-
}
97-
9876
varseededRand=rand.New(rand.NewSource(time.Now().UnixNano()))
9977

10078
funcrandString(lengthint)string {

‎ci/integration/secrets_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ func TestSecrets(t *testing.T) {
3636

3737
c.Run(ctx,"coder secrets create").Assert(t,
3838
tcli.Error(),
39-
tcli.StdoutEmpty(),
4039
)
4140

4241
// this tests the "Value:" prompt fallback
@@ -85,9 +84,6 @@ func TestSecrets(t *testing.T) {
8584
c.Run(ctx,fmt.Sprintf("echo %s > ~/secret.json",value)).Assert(t,
8685
tcli.Success(),
8786
)
88-
c.Run(ctx,fmt.Sprintf("coder secrets create %s --from-literal %s --from-file ~/secret.json",name,value)).Assert(t,
89-
tcli.Error(),
90-
)
9187
c.Run(ctx,fmt.Sprintf("coder secrets create %s --from-file ~/secret.json",name)).Assert(t,
9288
tcli.Success(),
9389
)

‎ci/integration/setup_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"golang.org/x/xerrors"
1414
)
1515

16+
// binpath is populated during package initialization with a path to the coder binary
1617
varbinpathstring
1718

1819
// initialize integration tests by building the coder-cli binary
@@ -39,7 +40,7 @@ func build(path string) error {
3940

4041
out,err:=cmd.CombinedOutput()
4142
iferr!=nil {
42-
returnxerrors.Errorf("failed tobuild coder-cli (%v): %w",string(out),err)
43+
returnxerrors.Errorf("build coder-cli (%v): %w",string(out),err)
4344
}
4445
returnnil
4546
}

‎ci/integration/users_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package integration
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"cdr.dev/coder-cli/ci/tcli"
9+
"cdr.dev/coder-cli/internal/entclient"
10+
"cdr.dev/slog/sloggers/slogtest/assert"
11+
)
12+
13+
funcTestUsers(t*testing.T) {
14+
t.Parallel()
15+
ctx,cancel:=context.WithTimeout(context.Background(),time.Minute*5)
16+
defercancel()
17+
18+
c,err:=tcli.NewContainerRunner(ctx,&tcli.ContainerConfig{
19+
Image:"codercom/enterprise-dev",
20+
Name:"users-cli-tests",
21+
BindMounts:map[string]string{
22+
binpath:"/bin/coder",
23+
},
24+
})
25+
assert.Success(t,"new run container",err)
26+
deferc.Close()
27+
28+
c.Run(ctx,"which coder").Assert(t,
29+
tcli.Success(),
30+
tcli.StdoutMatches("/usr/sbin/coder"),
31+
tcli.StderrEmpty(),
32+
)
33+
34+
headlessLogin(ctx,t,c)
35+
36+
varuser entclient.User
37+
c.Run(ctx,`coder users ls --output json | jq -c '.[] | select( .username == "charlie")'`).Assert(t,
38+
tcli.Success(),
39+
tcli.StdoutJSONUnmarshal(&user),
40+
)
41+
assert.Equal(t,"user email is as expected","charlie@coder.com",user.Email)
42+
assert.Equal(t,"username is as expected","Charlie",user.Name)
43+
44+
c.Run(ctx,"coder users ls --output human | grep charlie").Assert(t,
45+
tcli.Success(),
46+
tcli.StdoutMatches("charlie"),
47+
)
48+
49+
c.Run(ctx,"coder logout").Assert(t,
50+
tcli.Success(),
51+
)
52+
53+
c.Run(ctx,"coder users ls").Assert(t,
54+
tcli.Error(),
55+
)
56+
}

‎ci/tcli/tcli.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tcli
33
import (
44
"bytes"
55
"context"
6+
"encoding/json"
67
"fmt"
78
"io"
89
"os/exec"
@@ -76,7 +77,7 @@ func NewContainerRunner(ctx context.Context, config *ContainerConfig) (*Containe
7677
out,err:=cmd.CombinedOutput()
7778
iferr!=nil {
7879
returnnil,xerrors.Errorf(
79-
"failed tostart testing container %q, (%s): %w",
80+
"start testing container %q, (%s): %w",
8081
config.Name,string(out),err)
8182
}
8283

@@ -97,7 +98,7 @@ func (r *ContainerRunner) Close() error {
9798
out,err:=cmd.CombinedOutput()
9899
iferr!=nil {
99100
returnxerrors.Errorf(
100-
"failed tostop testing container %q, (%s): %w",
101+
"stop testing container %q, (%s): %w",
101102
r.name,string(out),err)
102103
}
103104
returnnil
@@ -290,7 +291,7 @@ func matches(t *testing.T, name, pattern string, target []byte) {
290291

291292
ok,err:=regexp.Match(pattern,target)
292293
iferr!=nil {
293-
slogtest.Fatal(t,"failed toattempt regexp match",append(fields,slog.Error(err))...)
294+
slogtest.Fatal(t,"attempt regexp match",append(fields,slog.Error(err))...)
294295
}
295296
if!ok {
296297
slogtest.Fatal(t,"expected to find pattern, no match found",fields...)
@@ -329,3 +330,21 @@ func DurationGreaterThan(dur time.Duration) Assertion {
329330
}
330331
}
331332
}
333+
334+
// StdoutJSONUnmarshal attempts to unmarshal stdout into the given target
335+
funcStdoutJSONUnmarshal(targetinterface{})Assertion {
336+
returnfunc(t*testing.T,r*CommandResult) {
337+
slog.Helper()
338+
err:=json.Unmarshal(r.Stdout,target)
339+
assert.Success(t,"stdout json unmarshals",err)
340+
}
341+
}
342+
343+
// StderrJSONUnmarshal attempts to unmarshal stderr into the given target
344+
funcStderrJSONUnmarshal(targetinterface{})Assertion {
345+
returnfunc(t*testing.T,r*CommandResult) {
346+
slog.Helper()
347+
err:=json.Unmarshal(r.Stdout,target)
348+
assert.Success(t,"stderr json unmarshals",err)
349+
}
350+
}

‎cmd/coder/auth.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,38 @@ import (
55

66
"cdr.dev/coder-cli/internal/config"
77
"cdr.dev/coder-cli/internal/entclient"
8+
"golang.org/x/xerrors"
9+
10+
"go.coder.com/flog"
811
)
912

13+
// requireAuth exits the process with a nonzero exit code if the user is not authenticated to make requests
1014
funcrequireAuth()*entclient.Client {
15+
client,err:=newClient()
16+
iferr!=nil {
17+
flog.Fatal("%v",err)
18+
}
19+
returnclient
20+
}
21+
22+
funcnewClient() (*entclient.Client,error) {
1123
sessionToken,err:=config.Session.Read()
12-
requireSuccess(err,"read session: %v (did you run coder login?)",err)
24+
iferr!=nil {
25+
returnnil,xerrors.Errorf("read session: %v (did you run coder login?)",err)
26+
}
1327

1428
rawURL,err:=config.URL.Read()
15-
requireSuccess(err,"read url: %v (did you run coder login?)",err)
29+
iferr!=nil {
30+
returnnil,xerrors.Errorf("read url: %v (did you run coder login?)",err)
31+
}
1632

1733
u,err:=url.Parse(rawURL)
18-
requireSuccess(err,"url misformatted: %v (try runing coder login)",err)
34+
iferr!=nil {
35+
returnnil,xerrors.Errorf("url misformatted: %v (try runing coder login)",err)
36+
}
1937

2038
return&entclient.Client{
2139
BaseURL:u,
2240
Token:sessionToken,
23-
}
41+
},nil
2442
}

‎cmd/coder/ceapi.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package main
22

33
import (
4+
"golang.org/x/xerrors"
5+
46
"go.coder.com/flog"
57

68
"cdr.dev/coder-cli/internal/entclient"
@@ -25,43 +27,50 @@ outer:
2527
}
2628

2729
// getEnvs returns all environments for the user.
28-
funcgetEnvs(client*entclient.Client) []entclient.Environment {
30+
funcgetEnvs(client*entclient.Client)([]entclient.Environment,error) {
2931
me,err:=client.Me()
30-
requireSuccess(err,"get self: %+v",err)
32+
iferr!=nil {
33+
returnnil,xerrors.Errorf("get self: %+v",err)
34+
}
3135

3236
orgs,err:=client.Orgs()
33-
requireSuccess(err,"get orgs: %+v",err)
37+
iferr!=nil {
38+
returnnil,xerrors.Errorf("get orgs: %+v",err)
39+
}
3440

3541
orgs=userOrgs(me,orgs)
3642

3743
varallEnvs []entclient.Environment
3844

3945
for_,org:=rangeorgs {
4046
envs,err:=client.Envs(me,org)
41-
requireSuccess(err,"get envs for %v: %+v",org.Name,err)
47+
iferr!=nil {
48+
returnnil,xerrors.Errorf("get envs for %v: %+v",org.Name,err)
49+
}
4250

4351
for_,env:=rangeenvs {
4452
allEnvs=append(allEnvs,env)
4553
}
4654
}
47-
48-
returnallEnvs
55+
returnallEnvs,nil
4956
}
5057

5158
// findEnv returns a single environment by name (if it exists.)
52-
funcfindEnv(client*entclient.Client,namestring) entclient.Environment {
53-
envs:=getEnvs(client)
59+
funcfindEnv(client*entclient.Client,namestring) (*entclient.Environment,error) {
60+
envs,err:=getEnvs(client)
61+
iferr!=nil {
62+
returnnil,xerrors.Errorf("get environments: %w",err)
63+
}
5464

5565
varfound []string
5666

5767
for_,env:=rangeenvs {
5868
found=append(found,env.Name)
5969
ifenv.Name==name {
60-
returnenv
70+
return&env,nil
6171
}
6272
}
63-
64-
flog.Info("found %q",found)
65-
flog.Fatal("environment %q not found",name)
66-
panic("unreachable")
73+
flog.Error("found %q",found)
74+
flog.Error("%q not found",name)
75+
returnnil,xerrors.New("environment not found")
6776
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp