Skip to content

Commit e01974b

Browse files
Shu Kutsuzawatk3fftk
authored andcommitted
feat: Add Secret Command(set only) (#30)
* add secret cmd * change func name
1 parent c83cf56 commit e01974b

File tree

6 files changed

+460
-1
lines changed

6 files changed

+460
-1
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,9 @@ Successfully DELETE a banner ID 28
105105
$ sdctl banner get
106106
ID IsActive Message
107107
```
108+
109+
- write a secret
110+
```bash
111+
$ sdctl secret set -p 1111 -k FOO -v bar
112+
setting secret FOO is succuseed!
113+
```

command/cmd.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func NewCmd(config sdctl_context.SdctlConfig, api sdapi.SDAPI) *cobra.Command {
3636
NewCmdGet(config, api),
3737
NewCmdSet(config, api),
3838
NewCmdValidate(api),
39-
NewCmdValidateTemplate(api))
39+
NewCmdValidateTemplate(api),
40+
NewCmdSecret(api))
4041
return cmd
4142
}

command/secret.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package command
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
"github.com/tk3fftk/sdctl/pkg/sdapi"
6+
)
7+
8+
func NewCmdSecret(api sdapi.SDAPI) *cobra.Command {
9+
cmd := &cobra.Command{
10+
Use: "secret",
11+
Short: "handle screwdriver secrets (write only)",
12+
Run: func(cmd *cobra.Command, args []string) {
13+
cmd.Help()
14+
},
15+
Aliases: []string{"sec"},
16+
}
17+
18+
cmd.AddCommand(
19+
NewCmdSecretSet(api),
20+
)
21+
return cmd
22+
}

command/secret_set.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package command
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/spf13/cobra"
10+
"github.com/tk3fftk/sdctl/pkg/sdapi"
11+
)
12+
13+
type SecretSetOption struct {
14+
API sdapi.SDAPI
15+
PipelineID string
16+
SecretKey string
17+
SecretValue string
18+
AllowInPR bool
19+
}
20+
21+
func NewCmdSecretSet(api sdapi.SDAPI) *cobra.Command {
22+
o := &SecretSetOption{
23+
API: api,
24+
}
25+
cmd := &cobra.Command{
26+
Use: "set",
27+
Short: "set secret to pipeline",
28+
RunE: func(cmd *cobra.Command, args []string) error {
29+
return o.Run(cmd, args)
30+
},
31+
}
32+
33+
cmd.Flags().StringVarP(&o.PipelineID, "pipeline", "p", "", "specify pipeline id")
34+
_ = cmd.MarkFlagRequired("pipeline")
35+
cmd.Flags().StringVarP(&o.SecretKey, "key", "k", "", "SECRET_KEY")
36+
_ = cmd.MarkFlagRequired("key")
37+
cmd.Flags().StringVarP(&o.SecretValue, "value", "v", "", "SECRET_VALUE")
38+
_ = cmd.MarkFlagRequired("value")
39+
cmd.Flags().BoolVarP(&o.AllowInPR, "allow-in-pr", "", false, "ALLOW_IN_PR")
40+
41+
return cmd
42+
}
43+
44+
func (o *SecretSetOption) Run(cmd *cobra.Command, args []string) error {
45+
pipelineIDNum, err := strconv.Atoi(o.PipelineID)
46+
if err != nil {
47+
return fmt.Errorf("failed to convert %s to int: %v", o.PipelineID, err)
48+
}
49+
50+
// Screwdriver allow only "/^[A-Z_][A-Z0-9_]*$/]" as secret key
51+
uppperKey := strings.ToUpper(o.SecretKey)
52+
if err := o.API.SetSecret(pipelineIDNum, uppperKey, o.SecretValue, o.AllowInPR); err != nil {
53+
return fmt.Errorf("failed to set secret: %v", err)
54+
}
55+
56+
fmt.Fprintf(os.Stdout, "setting secret %s is succeed!\n", uppperKey)
57+
return nil
58+
}

pkg/sdapi/sdapi.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,3 +369,97 @@ func (sd *SDAPI) getEvents(eventID int) (*eventResponse, error) {
369369

370370
return eventResponse, err
371371
}
372+
373+
type Secret struct {
374+
ID int `json:"id"`
375+
PipelineID int `json:"pipelineId"`
376+
Name string `json:"name"`
377+
AllowInPR bool `json:"allowInPR"`
378+
}
379+
380+
func (sd *SDAPI) SetSecret(pipelineID int, key, value string, allowInPR bool) error {
381+
382+
secrets, err := sd.getPipelineSecrets(pipelineID)
383+
if err != nil {
384+
return err
385+
}
386+
387+
duplicatedKeyID, exist := sd.checkKey(secrets, key)
388+
389+
if !exist {
390+
return sd.createSecret(pipelineID, key, value, allowInPR)
391+
}
392+
393+
return sd.updateSecret(duplicatedKeyID, value, allowInPR)
394+
}
395+
396+
func (sd *SDAPI) getPipelineSecrets(pipelineID int) ([]Secret, error) {
397+
path := fmt.Sprintf("/v4/pipelines/%d/secrets?token=%s", pipelineID, sd.sdctx.SDJWT)
398+
res, err := sd.request(context.TODO(), http.MethodGet, path, nil)
399+
if err != nil {
400+
return nil, err
401+
}
402+
defer res.Body.Close()
403+
if res.StatusCode != http.StatusOK {
404+
return nil, fmt.Errorf("GET %s status code is not %d: %d", path, http.StatusOK, res.StatusCode)
405+
}
406+
var secrets []Secret
407+
if err := json.NewDecoder(res.Body).Decode(&secrets); err != nil {
408+
return nil, err
409+
}
410+
411+
return secrets, nil
412+
}
413+
414+
func (sd *SDAPI) checkKey(secrets []Secret, key string) (int, bool) {
415+
for _, s := range secrets {
416+
if s.Name == key {
417+
return s.ID, true
418+
}
419+
}
420+
return 0, false
421+
}
422+
423+
func (sd *SDAPI) createSecret(pipelineID int, key, value string, allowInPR bool) error {
424+
path := "/v4/secrets"
425+
body := make(map[string]interface{})
426+
body["pipelineId"] = pipelineID
427+
body["name"] = key
428+
body["value"] = value
429+
body["allowInPR"] = allowInPR
430+
bodyJSON, err := json.Marshal(&body)
431+
if err != nil {
432+
return err
433+
}
434+
res, err := sd.request(context.TODO(), http.MethodPost, path, bytes.NewBuffer(bodyJSON))
435+
if err != nil {
436+
return err
437+
}
438+
defer res.Body.Close()
439+
440+
if res.StatusCode != http.StatusCreated {
441+
return fmt.Errorf("POST %s status code is not %d: %d", path, http.StatusCreated, res.StatusCode)
442+
}
443+
return nil
444+
}
445+
446+
func (sd *SDAPI) updateSecret(secretID int, value string, allowInPR bool) error {
447+
path := fmt.Sprintf("/v4/secrets/%d", secretID)
448+
body := make(map[string]interface{})
449+
body["value"] = value
450+
body["allowInPR"] = allowInPR
451+
bodyJSON, err := json.Marshal(&body)
452+
if err != nil {
453+
return err
454+
}
455+
res, err := sd.request(context.TODO(), http.MethodPut, path, bytes.NewBuffer(bodyJSON))
456+
if err != nil {
457+
return err
458+
}
459+
defer res.Body.Close()
460+
461+
if res.StatusCode != http.StatusOK {
462+
return fmt.Errorf("PUT %s status code is not %d: %d", path, http.StatusOK, res.StatusCode)
463+
}
464+
return nil
465+
}

0 commit comments

Comments
 (0)