From 05b0d757320cd672aa0d2e5e6fcd38f50e52e4ba Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Wed, 7 Feb 2024 16:04:21 -0500 Subject: [PATCH] fix(deployments): support commas in values for deploy parameters (#526) Co-authored-by: David May <49894298+wass3rw3rk@users.noreply.github.com> --- action/deployment/add.go | 32 +----- action/deployment/add_test.go | 50 +--------- action/deployment/deployment.go | 4 +- command/deployment/add.go | 77 ++++++++++++++- command/deployment/add_test.go | 97 +++++++++++++++++++ .../testdata/deploy-params-bad-struct.json | 6 ++ .../testdata/deploy-params-bad-type.json | 4 + .../testdata/deploy-params-comma.json | 4 + .../deployment/testdata/deploy-params-env.env | 3 + .../testdata/deploy-params-simple.json | 5 + 10 files changed, 202 insertions(+), 80 deletions(-) create mode 100644 command/deployment/testdata/deploy-params-bad-struct.json create mode 100644 command/deployment/testdata/deploy-params-bad-type.json create mode 100644 command/deployment/testdata/deploy-params-comma.json create mode 100644 command/deployment/testdata/deploy-params-env.env create mode 100644 command/deployment/testdata/deploy-params-simple.json diff --git a/action/deployment/add.go b/action/deployment/add.go index ea9f5e79..1e24e8ca 100644 --- a/action/deployment/add.go +++ b/action/deployment/add.go @@ -3,35 +3,15 @@ package deployment import ( - "fmt" - "strings" - "github.com/go-vela/cli/internal/output" "github.com/go-vela/sdk-go/vela" "github.com/go-vela/types/library" - "github.com/go-vela/types/raw" "github.com/sirupsen/logrus" ) -// parseKeyValue converts the slice of key=value into a map. -func parseKeyValue(input []string) (raw.StringSliceMap, error) { - payload := raw.StringSliceMap{} - - for _, i := range input { - parts := strings.SplitN(i, "=", 2) - if len(parts) != 2 { - return nil, fmt.Errorf("%s is not in key=value format", i) - } - - payload[parts[0]] = parts[1] - } - - return payload, nil -} - // Add creates a deployment based off the provided configuration. func (c *Config) Add(client *vela.Client) error { logrus.Debug("executing add for deployment configuration") @@ -44,17 +24,7 @@ func (c *Config) Add(client *vela.Client) error { Task: &c.Task, Target: &c.Target, Description: &c.Description, - } - - // check if the user provided any parameters - if len(c.Parameters) != 0 { - // convert the parameters into map[string]string format - payload, err := parseKeyValue(c.Parameters) - if err != nil { - return err - } - - d.SetPayload(payload) + Payload: &c.Parameters, } logrus.Tracef("adding deployment for repo %s/%s", c.Org, c.Repo) diff --git a/action/deployment/add_test.go b/action/deployment/add_test.go index b3212d7e..26c42041 100644 --- a/action/deployment/add_test.go +++ b/action/deployment/add_test.go @@ -4,7 +4,6 @@ package deployment import ( "net/http/httptest" - "reflect" "testing" "github.com/go-vela/sdk-go/vela" @@ -90,21 +89,10 @@ func TestDeployment_Config_Add(t *testing.T) { Target: "production", Task: "deploy:vela", Output: "yaml", - Parameters: []string{"foo=test1", "bar=test2"}, - }, - }, - { - failure: true, - config: &Config{ - Action: "add", - Org: "github", - Repo: "octocat", - Description: "Deployment request from Vela", - Ref: "refs/heads/main", - Target: "production", - Task: "deploy:vela", - Output: "yaml", - Parameters: []string{"badinput"}, + Parameters: raw.StringSliceMap{ + "foo": "bar", + "faz": "baz", + }, }, }, } @@ -126,33 +114,3 @@ func TestDeployment_Config_Add(t *testing.T) { } } } - -func Test_parseKeyValue(t *testing.T) { - type args struct { - input []string - } - - tests := []struct { - name string - args args - want raw.StringSliceMap - wantErr bool - }{ - {"valid input", args{input: []string{"foo=test1", "bar=test2"}}, raw.StringSliceMap{"foo": "test1", "bar": "test2"}, false}, - {"invalid input", args{input: []string{"foo=test1", "badinput"}}, nil, true}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := parseKeyValue(tt.args.input) - if (err != nil) != tt.wantErr { - t.Errorf("parseKeyValue() error = %v, wantErr %v", err, tt.wantErr) - return - } - - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("parseKeyValue() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/action/deployment/deployment.go b/action/deployment/deployment.go index 33db5a8e..24203a20 100644 --- a/action/deployment/deployment.go +++ b/action/deployment/deployment.go @@ -2,6 +2,8 @@ package deployment +import "github.com/go-vela/types/raw" + // Config represents the configuration necessary // to perform deployment related quests with Vela. type Config struct { @@ -16,5 +18,5 @@ type Config struct { Page int PerPage int Output string - Parameters []string + Parameters raw.StringSliceMap } diff --git a/command/deployment/add.go b/command/deployment/add.go index 5070977e..0ac5b077 100644 --- a/command/deployment/add.go +++ b/command/deployment/add.go @@ -4,11 +4,15 @@ package deployment import ( "fmt" + "os" + "strings" "github.com/go-vela/cli/action" "github.com/go-vela/cli/action/deployment" "github.com/go-vela/cli/internal" "github.com/go-vela/cli/internal/client" + "github.com/go-vela/types/raw" + "github.com/joho/godotenv" "github.com/urfave/cli/v2" ) @@ -69,6 +73,12 @@ var CommandAdd = &cli.Command{ Aliases: []string{"p"}, Usage: "provide the parameter(s) within `key=value` format for the deployment", }, + &cli.StringFlag{ + EnvVars: []string{"VELA_PARAMETERS_FILE", "DEPLOYMENT_PARAMETERS_FILE"}, + Name: "parameters-file", + Aliases: []string{"pf", "parameter-file"}, + Usage: "provide deployment parameters via a JSON or env file", + }, // Output Flags @@ -95,7 +105,9 @@ EXAMPLES: $ {{.HelpName}} --org MyOrg --repo MyRepo --description 'my custom message' 7. Add a deployment for a repository with two parameters. $ {{.HelpName}} --org MyOrg --repo MyRepo --parameter 'key=value' --parameter 'foo=bar' - 8. Add a deployment for a repository when config or environment variables are set. + 8. Add a deployment for a repository with a parameters JSON file. + $ {{.HelpName}} --org MyOrg --repo MyRepo --parameters-file params.json + 9. Add a deployment for a repository when config or environment variables are set. $ {{.HelpName}} DOCUMENTATION: @@ -114,6 +126,26 @@ func add(c *cli.Context) error { return err } + var parameters raw.StringSliceMap + + pFile := c.String("parameters-file") + pInput := c.StringSlice("parameter") + + if len(pFile) > 0 { + parameters, err = parseParamFile(pFile) + if err != nil { + return err + } + } + + if len(pInput) > 0 { + // convert the parameters into map[string]string format + parameters, err = parseKeyValue(pInput) + if err != nil { + return err + } + } + // parse the Vela client from the context // // https://pkg.go.dev/github.com/go-vela/cli/internal/client?tab=doc#Parse @@ -134,7 +166,7 @@ func add(c *cli.Context) error { Target: c.String("target"), Task: c.String("task"), Output: c.String(internal.FlagOutput), - Parameters: c.StringSlice("parameter"), + Parameters: parameters, } // validate deployment configuration @@ -150,3 +182,44 @@ func add(c *cli.Context) error { // https://pkg.go.dev/github.com/go-vela/cli/action/deployment?tab=doc#Config.Add return d.Add(client) } + +// parseKeyValue converts the slice of key=value into a map. +func parseKeyValue(input []string) (raw.StringSliceMap, error) { + payload := raw.StringSliceMap{} + + for _, i := range input { + parts := strings.SplitN(i, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("%s is not in key=value format", i) + } + + payload[parts[0]] = parts[1] + } + + return payload, nil +} + +// parseParamFile is a helper function that populates a slice map with values from a file. +func parseParamFile(pFile string) (raw.StringSliceMap, error) { + payload := raw.StringSliceMap{} + + data, err := os.ReadFile(pFile) + if err != nil { + return nil, err + } + + switch { + case strings.HasSuffix(pFile, ".json"), strings.HasSuffix(pFile, ".JSON"): + err = payload.UnmarshalJSON(data) + if err != nil { + return nil, err + } + default: + payload, err = godotenv.Read(pFile) + if err != nil { + return nil, err + } + } + + return payload, nil +} diff --git a/command/deployment/add_test.go b/command/deployment/add_test.go index edf6e133..713c160c 100644 --- a/command/deployment/add_test.go +++ b/command/deployment/add_test.go @@ -5,10 +5,12 @@ package deployment import ( "flag" "net/http/httptest" + "reflect" "testing" "github.com/go-vela/cli/test" "github.com/go-vela/server/mock/server" + "github.com/go-vela/types/raw" "github.com/urfave/cli/v2" ) @@ -69,3 +71,98 @@ func TestDeployment_Add(t *testing.T) { } } } + +func Test_parseKeyValue(t *testing.T) { + type args struct { + input []string + } + + tests := []struct { + name string + args args + want raw.StringSliceMap + wantErr bool + }{ + {"valid input", args{input: []string{"foo=test1", "bar=test2"}}, raw.StringSliceMap{"foo": "test1", "bar": "test2"}, false}, + {"invalid input", args{input: []string{"foo=test1", "badinput"}}, nil, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseKeyValue(tt.args.input) + if (err != nil) != tt.wantErr { + t.Errorf("parseKeyValue() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("parseKeyValue() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_parseParamFile(t *testing.T) { + tests := []struct { + name string + file string + want raw.StringSliceMap + wantErr bool + }{ + { + name: "valid simple input JSON", + file: "testdata/deploy-params-simple.json", + want: raw.StringSliceMap{ + "one": "two", + "three": "four", + "five": "six", + }, + }, + { + name: "valid comma input JSON", + file: "testdata/deploy-params-comma.json", + want: raw.StringSliceMap{ + "greeting": "hello, there!", + "farewell": "so long, partner!", + }, + }, + { + name: "valid input ENV", + file: "testdata/deploy-params-env.env", + want: raw.StringSliceMap{ + "USER": "VELA", + "REPO": "CLI", + "ORG": "GO-VELA", + }, + }, + { + name: "invalid input JSON bad type", + file: "testdata/deploy-params-bad-type.json", + wantErr: true, + }, + { + name: "invalid input JSON bad structure", + file: "testdata/deploy-params-bad-struct.json", + wantErr: true, + }, + { + name: "invalid input nonexistent file", + file: "testdata/does-not-exist.json", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseParamFile(tt.file) + if (err != nil) != tt.wantErr { + t.Errorf("parseParamFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("parseParamFile() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/command/deployment/testdata/deploy-params-bad-struct.json b/command/deployment/testdata/deploy-params-bad-struct.json new file mode 100644 index 00000000..7510c407 --- /dev/null +++ b/command/deployment/testdata/deploy-params-bad-struct.json @@ -0,0 +1,6 @@ +{ + "nested": { + "foo": "bar" + }, + "faz": "baz" +} \ No newline at end of file diff --git a/command/deployment/testdata/deploy-params-bad-type.json b/command/deployment/testdata/deploy-params-bad-type.json new file mode 100644 index 00000000..d88d82f4 --- /dev/null +++ b/command/deployment/testdata/deploy-params-bad-type.json @@ -0,0 +1,4 @@ +{ + "integer": 2, + "string": "foo" +} \ No newline at end of file diff --git a/command/deployment/testdata/deploy-params-comma.json b/command/deployment/testdata/deploy-params-comma.json new file mode 100644 index 00000000..797348a2 --- /dev/null +++ b/command/deployment/testdata/deploy-params-comma.json @@ -0,0 +1,4 @@ +{ + "greeting": "hello, there!", + "farewell": "so long, partner!" +} \ No newline at end of file diff --git a/command/deployment/testdata/deploy-params-env.env b/command/deployment/testdata/deploy-params-env.env new file mode 100644 index 00000000..6ad8edf3 --- /dev/null +++ b/command/deployment/testdata/deploy-params-env.env @@ -0,0 +1,3 @@ +USER=VELA +REPO=CLI +ORG=GO-VELA \ No newline at end of file diff --git a/command/deployment/testdata/deploy-params-simple.json b/command/deployment/testdata/deploy-params-simple.json new file mode 100644 index 00000000..e32ceee3 --- /dev/null +++ b/command/deployment/testdata/deploy-params-simple.json @@ -0,0 +1,5 @@ +{ + "one": "two", + "three": "four", + "five": "six" +} \ No newline at end of file