Skip to content

Commit

Permalink
Merge pull request #146 from semaphoreci/create-secrets-with-env-vars
Browse files Browse the repository at this point in the history
Pass list of env vars when creating secrets
  • Loading branch information
shiroyasha committed Apr 17, 2019
2 parents 74a010d + fd7ba9f commit 18c9049
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 113 deletions.
3 changes: 2 additions & 1 deletion api/models/secret_v1_beta.go
Expand Up @@ -37,11 +37,12 @@ type SecretV1BetaMetadata struct {
UpdateTime json.Number `json:"update_time,omitempty,string" yaml:"update_time,omitempty"`
}

func NewSecretV1Beta(name string, files []SecretV1BetaFile) SecretV1Beta {
func NewSecretV1Beta(name string, envVars []SecretV1BetaEnvVar, files []SecretV1BetaFile) SecretV1Beta {
s := SecretV1Beta{}

s.setApiVersionAndKind()
s.Metadata.Name = name
s.Data.EnvVars = envVars
s.Data.Files = files

return s
Expand Down
52 changes: 2 additions & 50 deletions cmd/create.go
Expand Up @@ -5,8 +5,6 @@ import (
"fmt"
"io/ioutil"
"log"
"regexp"
"strings"

"github.com/semaphoreci/cli/cmd/utils"
"github.com/semaphoreci/cli/cmd/workflows"
Expand Down Expand Up @@ -123,48 +121,6 @@ var CreateDashboardCmd = &cobra.Command{
},
}

var CreateSecretCmd = &cobra.Command{
Use: "secret [NAME]",
Short: "Create a secret.",
Long: ``,
Aliases: []string{"secrets"},
Args: cobra.ExactArgs(1),

Run: func(cmd *cobra.Command, args []string) {
name := args[0]

fileFlags, err := cmd.Flags().GetStringArray("file")
utils.Check(err)

var files []models.SecretV1BetaFile
for _, fileFlag := range fileFlags {
matchFormat, err := regexp.MatchString(`^[^: ]+:[^: ]+$`, fileFlag)
utils.Check(err)

if matchFormat == true {
flagPaths := strings.Split(fileFlag, ":")

file := models.SecretV1BetaFile{}
file.Path = flagPaths[1]
file.Content = encodeFromFileAt(flagPaths[0])
files = append(files, file)
} else {
utils.Fail("The format of --file flag must be: <local-path>:<semaphore-path>")
}
}

secret := models.NewSecretV1Beta(name, files)

c := client.NewSecretV1BetaApi()

_, err = c.CreateSecret(&secret)

utils.Check(err)

fmt.Printf("Secret '%s' created.\n", secret.Metadata.Name)
},
}

var CreateWorkflowCmd = &cobra.Command{
Use: "workflow [NAME]",
Short: "Create a workflow from snapshot.",
Expand Down Expand Up @@ -221,9 +177,10 @@ func encodeFromFileAt(path string) string {
func init() {
createJobCmd := NewCreateJobCmd().Cmd
createNotificationCmd := NewCreateNotificationCmd()
createSecretCmd := NewCreateSecretCmd()

RootCmd.AddCommand(createCmd)
createCmd.AddCommand(CreateSecretCmd)
createCmd.AddCommand(createSecretCmd)
createCmd.AddCommand(CreateDashboardCmd)
createCmd.AddCommand(createJobCmd)
createCmd.AddCommand(CreateWorkflowCmd)
Expand All @@ -234,11 +191,6 @@ func init() {
desc := "Filename, directory, or URL to files to use to create the resource"
createCmd.Flags().StringP("file", "f", "", desc)

// Secret Create Flags

desc = "File mapping <local-path>:<mount-path>, used to create a secret with file"
CreateSecretCmd.Flags().StringArrayP("file", "f", []string{}, desc)

CreateWorkflowCmd.Flags().StringP("project-name", "p", "", "project name; if not specified will be inferred wrom git origin")
CreateWorkflowCmd.Flags().StringP("label", "l", "", "workflow label")
CreateWorkflowCmd.Flags().StringP("archive", "a", "", "snapshot archive; if not specified current dir will be compressed into .tgz file")
Expand Down
92 changes: 92 additions & 0 deletions cmd/create_secret.go
@@ -0,0 +1,92 @@
package cmd

import (
"fmt"
"regexp"
"strings"

client "github.com/semaphoreci/cli/api/client"
models "github.com/semaphoreci/cli/api/models"
"github.com/semaphoreci/cli/cmd/utils"
"github.com/spf13/cobra"
)

func NewCreateSecretCmd() *cobra.Command {
cmd := &cobra.Command{}

cmd.Use = "secret [NAME]"
cmd.Short = "Create a secret."
cmd.Long = ``
cmd.Aliases = []string{"secrets"}
cmd.Args = cobra.ExactArgs(1)

cmd.Flags().StringArrayP(
"file",
"f",
[]string{},
"File mapping <local-path>:<mount-path>, used to create a secret with file",
)

cmd.Flags().StringArrayP(
"env",
"e",
[]string{},
"Environment Variables",
)

cmd.Run = func(cmd *cobra.Command, args []string) {
name := args[0]

fileFlags, err := cmd.Flags().GetStringArray("file")
utils.Check(err)

var files []models.SecretV1BetaFile
for _, fileFlag := range fileFlags {
matchFormat, err := regexp.MatchString(`^[^: ]+:[^: ]+$`, fileFlag)
utils.Check(err)

if !matchFormat {
utils.Fail("The format of --file flag must be: <local-path>:<semaphore-path>")
}

flagPaths := strings.Split(fileFlag, ":")

file := models.SecretV1BetaFile{}
file.Path = flagPaths[1]
file.Content = encodeFromFileAt(flagPaths[0])
files = append(files, file)
}

envFlags, err := cmd.Flags().GetStringArray("env")
utils.Check(err)

var envVars []models.SecretV1BetaEnvVar
for _, envFlag := range envFlags {
matchFormat, err := regexp.MatchString(`^.+=.+$`, envFlag)
utils.Check(err)

if !matchFormat {
utils.Fail("The format of -e flag must be: <NAME>=<VALUE>")
}

parts := strings.SplitN(envFlag, "=", 2)

envVars = append(envVars, models.SecretV1BetaEnvVar{
Name: parts[0],
Value: parts[1],
})
}

secret := models.NewSecretV1Beta(name, envVars, files)

c := client.NewSecretV1BetaApi()

_, err = c.CreateSecret(&secret)

utils.Check(err)

fmt.Printf("Secret '%s' created.\n", secret.Metadata.Name)
}

return cmd
}
54 changes: 54 additions & 0 deletions cmd/create_secret_test.go
@@ -0,0 +1,54 @@
package cmd

import (
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"testing"

assert "github.com/stretchr/testify/assert"
httpmock "gopkg.in/jarcoal/httpmock.v1"
)

func Test__CreateSecret__WithSubcommand__Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

content1 := "This is some docker config"
content2 := "This is some gcloud config"

ioutil.WriteFile("/tmp/docker", []byte(content1), 0644)
ioutil.WriteFile("/tmp/gcloud", []byte(content2), 0644)

received := ""

httpmock.RegisterResponder("POST", "https://org.semaphoretext.xyz/api/v1beta/secrets",
func(req *http.Request) (*http.Response, error) {
body, _ := ioutil.ReadAll(req.Body)

received = string(body)

return httpmock.NewStringResponse(200, received), nil
},
)

RootCmd.SetArgs([]string{
"create",
"secret",
"-e", "FOO=BAR",
"-e", "ZEZ=Hello World",
"--file", "/tmp/docker:.config/docker",
"--file", "/tmp/gcloud:.config/gcloud",
"abc",
})

RootCmd.Execute()

file1 := base64.StdEncoding.EncodeToString([]byte(content1))
file2 := base64.StdEncoding.EncodeToString([]byte(content2))

expected := fmt.Sprintf(`{"apiVersion":"v1beta","kind":"Secret","metadata":{"name":"abc"},"data":{"env_vars":[{"name":"FOO","value":"BAR"},{"name":"ZEZ","value":"Hello World"}],"files":[{"path":".config/docker","content":"%s"},{"path":".config/gcloud","content":"%s"}]}}`, file1, file2)

assert.Equal(t, received, expected)
}
62 changes: 0 additions & 62 deletions cmd/create_test.go
@@ -1,8 +1,6 @@
package cmd

import (
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"testing"
Expand Down Expand Up @@ -130,66 +128,6 @@ data:
}
}

func Test__CreateSecret__WithSubcommand__Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

received := ""

httpmock.RegisterResponder("POST", "https://org.semaphoretext.xyz/api/v1beta/secrets",
func(req *http.Request) (*http.Response, error) {
body, _ := ioutil.ReadAll(req.Body)

received = string(body)

return httpmock.NewStringResponse(200, received), nil
},
)

RootCmd.SetArgs([]string{"create", "secret", "abc"})
RootCmd.Execute()

expected := `{"apiVersion":"v1beta","kind":"Secret","metadata":{"name":"abc"},"data":{"env_vars":null,"files":null}}`

if received != expected {
t.Errorf("Expected the API to receive POST secret with: %s, got: %s", expected, received)
}
}

func Test__CreateSecret__WithSubcommand_WithFileFlags__Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

content1 := "This is some docker config"
content2 := "This is some gcloud config"
ioutil.WriteFile("/tmp/docker", []byte(content1), 0644)
ioutil.WriteFile("/tmp/gcloud", []byte(content2), 0644)

received := ""

httpmock.RegisterResponder("POST", "https://org.semaphoretext.xyz/api/v1beta/secrets",
func(req *http.Request) (*http.Response, error) {
body, _ := ioutil.ReadAll(req.Body)

received = string(body)

return httpmock.NewStringResponse(200, received), nil
},
)

RootCmd.SetArgs([]string{"create", "secret", "--file", "/tmp/docker:.config/docker", "--file", "/tmp/gcloud:.config/gcloud", "abc"})
RootCmd.Execute()

file1 := base64.StdEncoding.EncodeToString([]byte(content1))
file2 := base64.StdEncoding.EncodeToString([]byte(content2))

expected := fmt.Sprintf(`{"apiVersion":"v1beta","kind":"Secret","metadata":{"name":"abc"},"data":{"env_vars":null,"files":[{"path":".config/docker","content":"%s"},{"path":".config/gcloud","content":"%s"}]}}`, file1, file2)

if received != expected {
t.Errorf("Expected the API to receive POST secret with: %s, got: %s", expected, received)
}
}

func Test__CreateDashboard__FromYaml__Response200(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
Expand Down

0 comments on commit 18c9049

Please sign in to comment.