From 8f31ad59be67fa1ee1aae3c0bfa2ca064a1de6a4 Mon Sep 17 00:00:00 2001 From: aiordache Date: Thu, 8 Oct 2020 10:29:45 +0200 Subject: [PATCH] Refactor secrets - create secrets from files - update Secret structure Signed-off-by: aiordache --- api/secrets/api.go | 32 ++++++++--------------- cli/cmd/secrets.go | 48 +++++++++++++++++++++-------------- ecs/sdk.go | 43 +++++++++++++++---------------- tests/ecs-e2e/e2e-ecs_test.go | 11 ++++---- 4 files changed, 66 insertions(+), 68 deletions(-) diff --git a/api/secrets/api.go b/api/secrets/api.go index 681bed62b..bf3315e14 100644 --- a/api/secrets/api.go +++ b/api/secrets/api.go @@ -31,21 +31,17 @@ type Service interface { // Secret hold sensitive data type Secret struct { - ID string `json:"ID"` - Name string `json:"Name"` - Labels map[string]string `json:"Labels"` - Description string `json:"Description"` - username string - password string + ID string `json:"ID"` + Name string `json:"Name"` + Labels map[string]string `json:"Tags"` + content []byte } // NewSecret builds a secret -func NewSecret(name, username, password, description string) Secret { +func NewSecret(name string, content []byte) Secret { return Secret{ - Name: name, - username: username, - password: password, - Description: description, + Name: name, + content: content, } } @@ -58,15 +54,7 @@ func (s Secret) ToJSON() (string, error) { return string(b), nil } -// GetCredString marshall a Secret's sensitive data into JSON string -func (s Secret) GetCredString() (string, error) { - creds := map[string]string{ - "username": s.username, - "password": s.password, - } - b, err := json.Marshal(&creds) - if err != nil { - return "", err - } - return string(b), nil +// GetContent returns a Secret's sensitive data +func (s Secret) GetContent() []byte { + return s.content } diff --git a/cli/cmd/secrets.go b/cli/cmd/secrets.go index 3106e3dc4..0fc14c587 100644 --- a/cli/cmd/secrets.go +++ b/cli/cmd/secrets.go @@ -19,6 +19,7 @@ package cmd import ( "fmt" "io" + "io/ioutil" "os" "github.com/spf13/cobra" @@ -28,13 +29,6 @@ import ( "github.com/docker/compose-cli/formatter" ) -type createSecretOptions struct { - Label string - Username string - Password string - Description string -} - // SecretCommand manage secrets func SecretCommand() *cobra.Command { cmd := &cobra.Command{ @@ -52,18 +46,39 @@ func SecretCommand() *cobra.Command { } func createSecret() *cobra.Command { - opts := createSecretOptions{} cmd := &cobra.Command{ - Use: "create NAME", + Use: "create [OPTIONS] SECRET [file|-]", Short: "Creates a secret.", - Args: cobra.ExactArgs(1), + Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { c, err := client.New(cmd.Context()) if err != nil { return err } + file := "-" + if len(args) == 2 { + file = args[1] + } + if len(file) == 0 { + return fmt.Errorf("secret data source empty: %q", file) + } + var in io.ReadCloser + switch file { + case "-": + in = os.Stdin + default: + in, err = os.Open(file) + if err != nil { + return err + } + defer func() { _ = in.Close() }() + } + content, err := ioutil.ReadAll(in) + if err != nil { + return fmt.Errorf("failed to read content from %q: %v", file, err) + } name := args[0] - secret := secrets.NewSecret(name, opts.Username, opts.Password, opts.Description) + secret := secrets.NewSecret(name, content) id, err := c.SecretsService().CreateSecret(cmd.Context(), secret) if err != nil { return err @@ -72,10 +87,6 @@ func createSecret() *cobra.Command { return nil }, } - - cmd.Flags().StringVarP(&opts.Username, "username", "u", "", "username") - cmd.Flags().StringVarP(&opts.Password, "password", "p", "", "password") - cmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Secret description") return cmd } @@ -135,7 +146,7 @@ func listSecrets() *cobra.Command { for _, secret := range view { _, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", secret.ID, secret.Name, secret.Description) } - }, "ID", "NAME", "DESCRIPTION") + }, "ID", "NAME") }, } cmd.Flags().StringVar(&opts.format, "format", "", "Format the output. Values: [pretty | json]. (Default: pretty)") @@ -153,9 +164,8 @@ func viewFromSecretList(secretList []secrets.Secret) []secretView { retList := make([]secretView, len(secretList)) for i, s := range secretList { retList[i] = secretView{ - ID: s.ID, - Name: s.Name, - Description: s.Description, + ID: s.ID, + Name: s.Name, } } return retList diff --git a/ecs/sdk.go b/ecs/sdk.go index 77ee60d47..6fd8bb183 100644 --- a/ecs/sdk.go +++ b/ecs/sdk.go @@ -551,15 +551,21 @@ func (s sdk) DeleteStack(ctx context.Context, name string) error { func (s sdk) CreateSecret(ctx context.Context, secret secrets.Secret) (string, error) { logrus.Debug("Create secret " + secret.Name) - secretStr, err := secret.GetCredString() - if err != nil { - return "", err + var tags []*secretsmanager.Tag + for k, v := range secret.Labels { + tags = []*secretsmanager.Tag{ + { + Key: aws.String(k), + Value: aws.String(v), + }, + } } - + // store the secret content as string + content := string(secret.GetContent()) response, err := s.SM.CreateSecret(&secretsmanager.CreateSecretInput{ Name: &secret.Name, - SecretString: &secretStr, - Description: &secret.Description, + SecretString: &content, + Tags: tags, }) if err != nil { return "", err @@ -573,17 +579,15 @@ func (s sdk) InspectSecret(ctx context.Context, id string) (secrets.Secret, erro if err != nil { return secrets.Secret{}, err } - labels := map[string]string{} + tags := map[string]string{} for _, tag := range response.Tags { - labels[aws.StringValue(tag.Key)] = aws.StringValue(tag.Value) + tags[aws.StringValue(tag.Key)] = aws.StringValue(tag.Value) } + secret := secrets.Secret{ ID: aws.StringValue(response.ARN), Name: aws.StringValue(response.Name), - Labels: labels, - } - if response.Description != nil { - secret.Description = *response.Description + Labels: tags, } return secret, nil } @@ -598,19 +602,14 @@ func (s sdk) ListSecrets(ctx context.Context) ([]secrets.Secret, error) { var ls []secrets.Secret for _, sec := range response.SecretList { - labels := map[string]string{} + tags := map[string]string{} for _, tag := range sec.Tags { - labels[*tag.Key] = *tag.Value - } - description := "" - if sec.Description != nil { - description = *sec.Description + tags[*tag.Key] = *tag.Value } ls = append(ls, secrets.Secret{ - ID: *sec.ARN, - Name: *sec.Name, - Labels: labels, - Description: description, + ID: *sec.ARN, + Name: *sec.Name, + Labels: tags, }) } return ls, nil diff --git a/tests/ecs-e2e/e2e-ecs_test.go b/tests/ecs-e2e/e2e-ecs_test.go index 18460a926..9acd30be9 100644 --- a/tests/ecs-e2e/e2e-ecs_test.go +++ b/tests/ecs-e2e/e2e-ecs_test.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "net/http" "os" + "path/filepath" "strconv" "strings" "testing" @@ -50,23 +51,23 @@ func TestMain(m *testing.M) { func TestSecrets(t *testing.T) { cmd, testID := setupTest(t) secretName := "secret" + testID - description := "description " + testID t.Run("create secret", func(t *testing.T) { - res := cmd.RunDockerCmd("secret", "create", secretName, "-u", "user1", "-p", "pass1", "-d", description) - assert.Check(t, strings.Contains(res.Stdout(), "secret:"+secretName)) + secretFile := filepath.Join(cmd.BinDir, "secret.txt") + err := ioutil.WriteFile(secretFile, []byte("pass1"), 0644) + assert.Check(t, err == nil) + res := cmd.RunDockerCmd("secret", "create", secretName, secretFile) + assert.Check(t, strings.Contains(res.Stdout(), secretName)) }) t.Run("list secrets", func(t *testing.T) { res := cmd.RunDockerCmd("secret", "list") assert.Check(t, strings.Contains(res.Stdout(), secretName)) - assert.Check(t, strings.Contains(res.Stdout(), description)) }) t.Run("inspect secret", func(t *testing.T) { res := cmd.RunDockerCmd("secret", "inspect", secretName) assert.Check(t, strings.Contains(res.Stdout(), `"Name": "`+secretName+`"`)) - assert.Check(t, strings.Contains(res.Stdout(), `"Description": "`+description+`"`)) }) t.Run("rm secret", func(t *testing.T) {