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

Commit01e9b77

Browse files
committed
Add secrets ls, add, view, rm commands
1 parent87c708a commit01e9b77

File tree

9 files changed

+405
-46
lines changed

9 files changed

+405
-46
lines changed

‎cmd/coder/auth.go

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,21 @@ package main
33
import (
44
"net/url"
55

6-
"go.coder.com/flog"
6+
"cdr.dev/coder-cli/internal/xcli"
77

88
"cdr.dev/coder-cli/internal/config"
99
"cdr.dev/coder-cli/internal/entclient"
1010
)
1111

1212
funcrequireAuth()*entclient.Client {
1313
sessionToken,err:=config.Session.Read()
14-
iferr!=nil {
15-
flog.Fatal("read session: %v (did you run coder login?)",err)
16-
}
14+
xcli.RequireSuccess(err,"read session: %v (did you run coder login?)",err)
1715

1816
rawURL,err:=config.URL.Read()
19-
iferr!=nil {
20-
flog.Fatal("read url: %v (did you run coder login?)",err)
21-
}
17+
xcli.RequireSuccess(err,"read url: %v (did you run coder login?)",err)
2218

2319
u,err:=url.Parse(rawURL)
24-
iferr!=nil {
25-
flog.Fatal("url misformatted: %v (try runing coder login)",err)
26-
}
20+
xcli.RequireSuccess(err,"url misformatted: %v (try runing coder login)",err)
2721

2822
return&entclient.Client{
2923
BaseURL:u,

‎cmd/coder/ceapi.go

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

33
import (
4+
"cdr.dev/coder-cli/internal/xcli"
5+
46
"go.coder.com/flog"
57

68
"cdr.dev/coder-cli/internal/entclient"
@@ -27,24 +29,19 @@ outer:
2729
// getEnvs returns all environments for the user.
2830
funcgetEnvs(client*entclient.Client) []entclient.Environment {
2931
me,err:=client.Me()
30-
iferr!=nil {
31-
flog.Fatal("get self: %+v",err)
32-
}
32+
xcli.RequireSuccess(err,"get self: %+v",err)
3333

3434
orgs,err:=client.Orgs()
35-
iferr!=nil {
36-
flog.Fatal("get orgs: %+v",err)
37-
}
35+
xcli.RequireSuccess(err,"get orgs: %+v",err)
3836

3937
orgs=userOrgs(me,orgs)
4038

4139
varallEnvs []entclient.Environment
4240

4341
for_,org:=rangeorgs {
4442
envs,err:=client.Envs(me,org)
45-
iferr!=nil {
46-
flog.Fatal("get envs for %v: %+v",org.Name,err)
47-
}
43+
xcli.RequireSuccess(err,"get envs for %v: %+v",org.Name,err)
44+
4845
for_,env:=rangeenvs {
4946
allEnvs=append(allEnvs,env)
5047
}

‎cmd/coder/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func (r *rootCmd) Subcommands() []cli.Command {
4343
&versionCmd{},
4444
&configSSHCmd{},
4545
&usersCmd{},
46+
&secretsCmd{},
4647
}
4748
}
4849

‎cmd/coder/secrets.go

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"cdr.dev/coder-cli/internal/entclient"
8+
"cdr.dev/coder-cli/internal/xcli"
9+
"github.com/spf13/pflag"
10+
"golang.org/x/xerrors"
11+
12+
"go.coder.com/flog"
13+
14+
"go.coder.com/cli"
15+
)
16+
17+
var (
18+
_ cli.FlaggedCommand=secretsCmd{}
19+
_ cli.ParentCommand=secretsCmd{}
20+
21+
_ cli.FlaggedCommand=&listSecretsCmd{}
22+
_ cli.FlaggedCommand=&addSecretCmd{}
23+
)
24+
25+
typesecretsCmdstruct {
26+
}
27+
28+
func (cmdsecretsCmd)Spec() cli.CommandSpec {
29+
return cli.CommandSpec{
30+
Name:"secrets",
31+
Desc:"interact with secrets owned by the authenticated user",
32+
}
33+
}
34+
35+
func (cmdsecretsCmd)Run(fl*pflag.FlagSet) {
36+
exitUsage(fl)
37+
}
38+
39+
func (cmdsecretsCmd)RegisterFlags(fl*pflag.FlagSet) {}
40+
41+
func (cmdsecretsCmd)Subcommands() []cli.Command {
42+
return []cli.Command{
43+
&listSecretsCmd{},
44+
&viewSecretsCmd{},
45+
&addSecretCmd{},
46+
&deleteSecretsCmd{},
47+
}
48+
}
49+
50+
typelistSecretsCmdstruct{}
51+
52+
func (cmdlistSecretsCmd)Spec() cli.CommandSpec {
53+
return cli.CommandSpec{
54+
Name:"ls",
55+
Desc:"list all secrets owned by the authenticated user",
56+
}
57+
}
58+
59+
func (cmdlistSecretsCmd)Run(fl*pflag.FlagSet) {
60+
client:=requireAuth()
61+
62+
secrets,err:=client.Secrets()
63+
xcli.RequireSuccess(err,"failed to get secrets: %v",err)
64+
65+
w:=xcli.HumanReadableWriter()
66+
iflen(secrets)>0 {
67+
_,err:=fmt.Fprintln(w,xcli.TabDelimitedStructHeaders(secrets[0]))
68+
xcli.RequireSuccess(err,"failed to write: %v",err)
69+
}
70+
for_,s:=rangesecrets {
71+
s.Value="******"// value is omitted from bulk responses
72+
73+
_,err=fmt.Fprintln(w,xcli.TabDelimitedStructValues(s))
74+
xcli.RequireSuccess(err,"failed to write: %v",err)
75+
}
76+
err=w.Flush()
77+
xcli.RequireSuccess(err,"failed to flush writer: %v",err)
78+
}
79+
80+
func (cmd*listSecretsCmd)RegisterFlags(fl*pflag.FlagSet) {}
81+
82+
typeviewSecretsCmdstruct{}
83+
84+
func (cmdviewSecretsCmd)Spec() cli.CommandSpec {
85+
return cli.CommandSpec{
86+
Name:"view",
87+
Usage:"[secret_name]",
88+
Desc:"view a secret owned by the authenticated user",
89+
}
90+
}
91+
92+
func (cmdviewSecretsCmd)Run(fl*pflag.FlagSet) {
93+
var (
94+
client=requireAuth()
95+
name=fl.Arg(0)
96+
)
97+
98+
secret,err:=client.SecretByName(name)
99+
xcli.RequireSuccess(err,"failed to get secret by name: %v",err)
100+
101+
_,err=fmt.Fprintln(os.Stdout,secret.Value)
102+
xcli.RequireSuccess(err,"failed to write: %v",err)
103+
}
104+
105+
typeaddSecretCmdstruct {
106+
name,value,descriptionstring
107+
}
108+
109+
func (cmd*addSecretCmd)Validate() (e []error) {
110+
ifcmd.name=="" {
111+
e=append(e,xerrors.New("--name is a required flag"))
112+
}
113+
ifcmd.value=="" {
114+
e=append(e,xerrors.New("--value is a required flag"))
115+
}
116+
returne
117+
}
118+
119+
func (cmd*addSecretCmd)Spec() cli.CommandSpec {
120+
return cli.CommandSpec{
121+
Name:"add",
122+
Usage:`--name MYSQL_KEY --value 123456 --description "MySQL credential for database access"`,
123+
Desc:"insert a new secret",
124+
}
125+
}
126+
127+
func (cmd*addSecretCmd)Run(fl*pflag.FlagSet) {
128+
var (
129+
client=requireAuth()
130+
)
131+
xcli.Validate(cmd)
132+
133+
err:=client.InsertSecret(entclient.InsertSecretReq{
134+
Name:cmd.name,
135+
Value:cmd.value,
136+
Description:cmd.description,
137+
})
138+
xcli.RequireSuccess(err,"failed to insert secret: %v",err)
139+
}
140+
141+
func (cmd*addSecretCmd)RegisterFlags(fl*pflag.FlagSet) {
142+
fl.StringVar(&cmd.name,"name","","the name of the secret")
143+
fl.StringVar(&cmd.value,"value","","the value of the secret")
144+
fl.StringVar(&cmd.description,"description","","a description of the secret")
145+
}
146+
147+
typedeleteSecretsCmdstruct{}
148+
149+
func (cmd*deleteSecretsCmd)Spec() cli.CommandSpec {
150+
return cli.CommandSpec{
151+
Name:"rm",
152+
Usage:"[secret_name]",
153+
Desc:"remove a secret by name",
154+
}
155+
}
156+
157+
func (cmd*deleteSecretsCmd)Run(fl*pflag.FlagSet) {
158+
var (
159+
client=requireAuth()
160+
name=fl.Arg(0)
161+
)
162+
163+
err:=client.DeleteSecretByName(name)
164+
xcli.RequireSuccess(err,"failed to delete secret: %v",err)
165+
166+
flog.Info("Successfully deleted secret %q",name)
167+
}

‎cmd/coder/users.go

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@ import (
44
"encoding/json"
55
"fmt"
66
"os"
7-
"reflect"
8-
"strings"
9-
"text/tabwriter"
107

8+
"cdr.dev/coder-cli/internal/xcli"
119
"github.com/spf13/pflag"
1210

1311
"go.coder.com/cli"
14-
"go.coder.com/flog"
1512
)
1613

1714
typeusersCmdstruct {
@@ -39,41 +36,28 @@ type listCmd struct {
3936
outputFmtstring
4037
}
4138

42-
functabDelimited(datainterface{})string {
43-
v:=reflect.ValueOf(data)
44-
s:=&strings.Builder{}
45-
fori:=0;i<v.NumField();i++ {
46-
s.WriteString(fmt.Sprintf("%s\t",v.Field(i).Interface()))
47-
}
48-
returns.String()
49-
}
50-
5139
func (cmd*listCmd)Run(fl*pflag.FlagSet) {
5240
entClient:=requireAuth()
5341

5442
users,err:=entClient.Users()
55-
iferr!=nil {
56-
flog.Fatal("failed to get users: %v",err)
57-
}
43+
xcli.RequireSuccess(err,"failed to get users: %v",err)
5844

5945
switchcmd.outputFmt {
6046
case"human":
61-
w:=tabwriter.NewWriter(os.Stdout,0,0,3,' ',0)
47+
w:=xcli.HumanReadableWriter()
48+
iflen(users)>0 {
49+
_,err=fmt.Fprintln(w,xcli.TabDelimitedStructHeaders(users[0]))
50+
xcli.RequireSuccess(err,"failed to write: %v",err)
51+
}
6252
for_,u:=rangeusers {
63-
_,err=fmt.Fprintln(w,tabDelimited(u))
64-
iferr!=nil {
65-
flog.Fatal("failed to write: %v",err)
66-
}
53+
_,err=fmt.Fprintln(w,xcli.TabDelimitedStructValues(u))
54+
xcli.RequireSuccess(err,"failed to write: %v",err)
6755
}
6856
err=w.Flush()
69-
iferr!=nil {
70-
flog.Fatal("failed to flush writer: %v",err)
71-
}
57+
xcli.RequireSuccess(err,"failed to flush writer: %v",err)
7258
case"json":
7359
err=json.NewEncoder(os.Stdout).Encode(users)
74-
iferr!=nil {
75-
flog.Fatal("failed to encode users to json: %v",err)
76-
}
60+
xcli.RequireSuccess(err,"failed to encode users to json: %v",err)
7761
default:
7862
exitUsage(fl)
7963
}

‎internal/entclient/error.go

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

10+
// ErrNotFound describes an error case in which the request resource could not be found
11+
varErrNotFound=xerrors.Errorf("resource not found")
12+
1013
funcbodyError(resp*http.Response)error {
1114
byt,err:=httputil.DumpResponse(resp,false)
1215
iferr!=nil {

‎internal/entclient/secrets.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package entclient
2+
3+
import (
4+
"net/http"
5+
"time"
6+
)
7+
8+
// Secret describes a Coder secret
9+
typeSecretstruct {
10+
IDstring`json:"id"`
11+
Namestring`json:"name"`
12+
Valuestring`json:"value,omitempty"`
13+
Descriptionstring`json:"description"`
14+
CreatedAt time.Time`json:"created_at"`
15+
UpdatedAt time.Time`json:"updated_at"`
16+
}
17+
18+
// Secrets gets all secrets owned by the authed user
19+
func (c*Client)Secrets() ([]Secret,error) {
20+
varsecrets []Secret
21+
err:=c.requestBody(http.MethodGet,"/api/users/me/secrets",nil,&secrets)
22+
returnsecrets,err
23+
}
24+
25+
func (c*Client)secretByID(idstring) (*Secret,error) {
26+
varsecretSecret
27+
err:=c.requestBody(http.MethodGet,"/api/users/me/secrets/"+id,nil,&secret)
28+
return&secret,err
29+
}
30+
31+
func (c*Client)secretNameToID(namestring) (idstring,_error) {
32+
secrets,err:=c.Secrets()
33+
iferr!=nil {
34+
return"",err
35+
}
36+
for_,s:=rangesecrets {
37+
ifs.Name==name {
38+
returns.ID,nil
39+
}
40+
}
41+
return"",ErrNotFound
42+
}
43+
44+
// SecretByName gets a secret object by name
45+
func (c*Client)SecretByName(namestring) (*Secret,error) {
46+
id,err:=c.secretNameToID(name)
47+
iferr!=nil {
48+
returnnil,err
49+
}
50+
returnc.secretByID(id)
51+
}
52+
53+
// InsertSecretReq describes the request body for creating a new secret
54+
typeInsertSecretReqstruct {
55+
Namestring`json:"name"`
56+
Valuestring`json:"value"`
57+
Descriptionstring`json:"description"`
58+
}
59+
60+
// InsertSecret adds a new secret for the authed user
61+
func (c*Client)InsertSecret(reqInsertSecretReq)error {
62+
_,err:=c.request(http.MethodPost,"/api/users/me/secrets",req)
63+
returnerr
64+
}
65+
66+
// DeleteSecretByName deletes the authenticated users secret with the given name
67+
func (c*Client)DeleteSecretByName(namestring)error {
68+
id,err:=c.secretNameToID(name)
69+
iferr!=nil {
70+
returnnil
71+
}
72+
_,err=c.request(http.MethodDelete,"/api/users/me/secrets/"+id,nil)
73+
returnerr
74+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp