diff --git a/cmd/helpers.go b/cmd/helpers.go index 7176566a..f4287392 100644 --- a/cmd/helpers.go +++ b/cmd/helpers.go @@ -2,6 +2,7 @@ package cmd import ( "crypto/sha1" + "errors" "fmt" "regexp" "strings" @@ -53,3 +54,16 @@ func flagStringNullValueOrNil(flags *pflag.FlagSet, flag string) (*null.String, // if not defined, return nil return nil, nil } + +func splitInvokeTaskArguments(invokedTaskArguments []string) (map[string]string, error) { + parsedArgs := map[string]string{} + + for _, v := range invokedTaskArguments { + split := strings.Split(v, "=") + if len(split) != 2 { + return map[string]string{}, errors.New(fmt.Sprintf("Unable to parse `%v`, the form of arguments should be `KEY=VALUE`", v)) + } + parsedArgs[split[0]] = split[1] + } + return parsedArgs, nil +} diff --git a/cmd/helpers_test.go b/cmd/helpers_test.go index 59524a25..f7f9ea75 100644 --- a/cmd/helpers_test.go +++ b/cmd/helpers_test.go @@ -164,3 +164,51 @@ func Test_flagStringNullValueOrNil(t *testing.T) { }) } } + +func Test_splitInvokeTaskArguments(t *testing.T) { + type args struct { + invokedTaskArguments []string + } + tests := []struct { + name string + args args + want map[string]string + wantErr bool + }{ + { + name: "Standard parsing, single argument", + args: args{ + invokedTaskArguments: []string{ + "KEY1=VALUE1", + }, + }, + want: map[string]string{ + "KEY1": "VALUE1", + }, + wantErr: false, + }, + { + name: "Invalid Input, multiple arguments", + args: args{ + invokedTaskArguments: []string{ + "KEY1=VALUE1", + "INVALID_ARGUMENT", + }, + }, + want: map[string]string{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := splitInvokeTaskArguments(tt.args.invokedTaskArguments) + if (err != nil) != tt.wantErr { + t.Errorf("splitInvokeTaskArguments() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("splitInvokeTaskArguments() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/run.go b/cmd/run.go index e22d4538..55aba2e3 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -19,5 +19,5 @@ func init() { runCmd.AddCommand(runDrushCacheClear) runCmd.AddCommand(runDrushSQLDump) runCmd.AddCommand(runActiveStandbySwitch) - runCmd.AddCommand(invokeDefinedTask) + runCmd.AddCommand(runDefinedTask) } diff --git a/cmd/tasks.go b/cmd/tasks.go index 3050c95a..8e54ada7 100644 --- a/cmd/tasks.go +++ b/cmd/tasks.go @@ -187,14 +187,14 @@ var runDrushCacheClear = &cobra.Command{ }, } -var invokeDefinedTask = &cobra.Command{ - Use: "invoke", +var runDefinedTask = &cobra.Command{ + Use: "task", Aliases: []string{"i"}, - Short: "", - Long: `Invoke a task registered against an environment + Short: "Run a custom task registered against an environment", + Long: `Run a custom task registered against an environment The following are supported methods to use Direct: - lagoon run invoke -p example -e main -N "advanced task name" + lagoon run task -p example -e main -N "advanced task name" [--argument=NAME=VALUE|..] `, Run: func(cmd *cobra.Command, args []string) { if cmdProjectName == "" || cmdProjectEnvironment == "" || invokedTaskName == "" { @@ -203,7 +203,13 @@ Direct: os.Exit(1) } - taskResult, err := eClient.InvokeAdvancedTaskDefinition(cmdProjectName, cmdProjectEnvironment, invokedTaskName) + taskArguments, err := splitInvokeTaskArguments(invokedTaskArguments) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + taskResult, err := eClient.InvokeAdvancedTaskDefinition(cmdProjectName, cmdProjectEnvironment, invokedTaskName, taskArguments) handleError(err) var resultMap map[string]interface{} err = json.Unmarshal([]byte(taskResult), &resultMap) @@ -277,15 +283,17 @@ Path: } var ( - taskName string - invokedTaskName string - taskService string - taskCommand string - taskCommandFile string + taskName string + invokedTaskName string + invokedTaskArguments []string + taskService string + taskCommand string + taskCommandFile string ) func init() { - invokeDefinedTask.Flags().StringVarP(&invokedTaskName, "name", "N", "", "Name of the task that will be invoked") + runDefinedTask.Flags().StringVarP(&invokedTaskName, "name", "N", "", "Name of the task that will be run") + runDefinedTask.Flags().StringSliceVar(&invokedTaskArguments, "argument", []string{}, "Arguments to be passed to custom task, of the form NAME=VALUE") runCustomTask.Flags().StringVarP(&taskName, "name", "N", "Custom Task", "Name of the task that will show in the UI (default: Custom Task)") runCustomTask.Flags().StringVarP(&taskService, "service", "S", "cli", "Name of the service (cli, nginx, other) that should run the task (default: cli)") runCustomTask.Flags().StringVarP(&taskCommand, "command", "c", "", "The command to run in the task") diff --git a/docs/commands/lagoon_run.md b/docs/commands/lagoon_run.md index 43cf176c..ff70f4de 100644 --- a/docs/commands/lagoon_run.md +++ b/docs/commands/lagoon_run.md @@ -37,5 +37,5 @@ Run a task against an environment * [lagoon run drush-archivedump](lagoon_run_drush-archivedump.md) - Run a drush archive dump on an environment * [lagoon run drush-cacheclear](lagoon_run_drush-cacheclear.md) - Run a drush cache clear on an environment * [lagoon run drush-sqldump](lagoon_run_drush-sqldump.md) - Run a drush sql dump on an environment -* [lagoon run invoke](lagoon_run_invoke.md) - +* [lagoon run task](lagoon_run_task.md) - Run a custom task registered against an environment diff --git a/docs/commands/lagoon_run_invoke.md b/docs/commands/lagoon_run_invoke.md index c41ced3d..d1cc69e3 100644 --- a/docs/commands/lagoon_run_invoke.md +++ b/docs/commands/lagoon_run_invoke.md @@ -1,13 +1,13 @@ ## lagoon run invoke - +Invoke a custom task registered against an environment ### Synopsis -Invoke a task registered against an environment +Invoke a custom task registered against an environment The following are supported methods to use Direct: - lagoon run invoke -p example -e main -N "advanced task name" + lagoon run invoke -p example -e main -N "advanced task name" [--argument=NAME=VALUE|..] ``` @@ -17,8 +17,9 @@ lagoon run invoke [flags] ### Options ``` - -h, --help help for invoke - -N, --name string Name of the task that will be invoked + --argument strings Arguments to be passed to invoked task, of the form NAME=VALUE + -h, --help help for invoke + -N, --name string Name of the task that will be invoked ``` ### Options inherited from parent commands diff --git a/docs/commands/lagoon_run_task.md b/docs/commands/lagoon_run_task.md new file mode 100644 index 00000000..27f24cee --- /dev/null +++ b/docs/commands/lagoon_run_task.md @@ -0,0 +1,45 @@ +## lagoon run task + +Run a custom task registered against an environment + +### Synopsis + +Run a custom task registered against an environment +The following are supported methods to use +Direct: + lagoon run task -p example -e main -N "advanced task name" [--argument=NAME=VALUE|..] + + +``` +lagoon run task [flags] +``` + +### Options + +``` + --argument strings Arguments to be passed to custom task, of the form NAME=VALUE + -h, --help help for task + -N, --name string Name of the task that will be run +``` + +### Options inherited from parent commands + +``` + --config-file string Path to the config file to use (must be *.yml or *.yaml) + --debug Enable debugging output (if supported) + -e, --environment string Specify an environment to use + --force Force yes on prompts (if supported) + -l, --lagoon string The Lagoon instance to interact with + --no-header No header on table (if supported) + --output-csv Output as CSV (if supported) + --output-json Output as JSON (if supported) + --pretty Make JSON pretty (if supported) + -p, --project string Specify a project to use + --skip-update-check Skip checking for updates + -i, --ssh-key string Specify path to a specific SSH key to use for lagoon authentication +``` + +### SEE ALSO + +* [lagoon run](lagoon_run.md) - Run a task against an environment + diff --git a/internal/lagoon/client/lgraphql/lgraphql.go b/internal/lagoon/client/lgraphql/lgraphql.go index 085f2de6..595da684 100644 --- a/internal/lagoon/client/lgraphql/lgraphql.go +++ b/internal/lagoon/client/lgraphql/lgraphql.go @@ -1,6 +1,5 @@ -// Code generated by go-bindata. (@generated) DO NOT EDIT. - -// Package lgraphql generated by go-bindata.// sources: +// Code generated for package lgraphql by go-bindata DO NOT EDIT. (@generated) +// sources: // _lgraphql/addDeployTarget.graphql // _lgraphql/addDeployTargetConfig.graphql // _lgraphql/addEnvVariable.graphql @@ -60,7 +59,7 @@ import ( func bindataRead(data []byte, name string) ([]byte, error) { gz, err := gzip.NewReader(bytes.NewBuffer(data)) if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) + return nil, fmt.Errorf("Read %q: %v", name, err) } var buf bytes.Buffer @@ -68,7 +67,7 @@ func bindataRead(data []byte, name string) ([]byte, error) { clErr := gz.Close() if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) + return nil, fmt.Errorf("Read %q: %v", name, err) } if clErr != nil { return nil, err @@ -104,7 +103,7 @@ func (fi bindataFileInfo) Mode() os.FileMode { return fi.mode } -// ModTime return file modify time +// Mode return file modify time func (fi bindataFileInfo) ModTime() time.Time { return fi.modTime } @@ -963,8 +962,8 @@ func _lgraphqlVariablesGetenvvariablesbyprojectenvironmentnameGraphql() (*asset, // It returns an error if the asset could not be found or // could not be loaded. func Asset(name string) ([]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { a, err := f() if err != nil { return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) @@ -989,8 +988,8 @@ func MustAsset(name string) []byte { // It returns an error if the asset could not be found or // could not be loaded. func AssetInfo(name string) (os.FileInfo, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { a, err := f() if err != nil { return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) @@ -1059,22 +1058,20 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// -// data/ -// foo.txt -// img/ -// a.png -// b.png -// +// data/ +// foo.txt +// img/ +// a.png +// b.png // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("nonexistent") would return an error +// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("") will return []string{"data"}. func AssetDir(name string) ([]string, error) { node := _bintree if len(name) != 0 { - canonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(canonicalName, "/") + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") for _, p := range pathList { node = node.Children[p] if node == nil { @@ -1189,6 +1186,6 @@ func RestoreAssets(dir, name string) error { } func _filePath(dir, name string) string { - canonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) } diff --git a/pkg/lagoon/environments/main.go b/pkg/lagoon/environments/main.go index 1be98d34..f2e38fce 100644 --- a/pkg/lagoon/environments/main.go +++ b/pkg/lagoon/environments/main.go @@ -33,7 +33,7 @@ type Client interface { AddEnvironmentVariableToEnvironment(string, string, api.EnvVariable) ([]byte, error) DeleteEnvironmentVariableFromEnvironment(string, string, api.EnvVariable) ([]byte, error) PromoteEnvironment(string, string, string) ([]byte, error) - InvokeAdvancedTaskDefinition(string, string, string) ([]byte, error) + InvokeAdvancedTaskDefinition(string, string, string, map[string]string) ([]byte, error) ListInvokableAdvancedTaskDefinitions(string, string) ([]byte, error) } diff --git a/pkg/lagoon/environments/tasks.go b/pkg/lagoon/environments/tasks.go index 2ca2909d..f5e73ce3 100644 --- a/pkg/lagoon/environments/tasks.go +++ b/pkg/lagoon/environments/tasks.go @@ -373,7 +373,7 @@ func (e *Environments) ListInvokableAdvancedTaskDefinitions(projectName string, } // InvokeAdvancedTaskDefinition will attempt to invoke an advanced task definition on an environment -func (e *Environments) InvokeAdvancedTaskDefinition(projectName string, environmentName string, advancedTaskName string) ([]byte, error) { +func (e *Environments) InvokeAdvancedTaskDefinition(projectName string, environmentName string, advancedTaskName string, taskArguments map[string]string) ([]byte, error) { // get project info from lagoon, we need the project ID for later project := api.Project{ Name: projectName, @@ -417,10 +417,24 @@ func (e *Environments) InvokeAdvancedTaskDefinition(projectName string, environm advancedTaskName, projectName, environmentName)) } + //transform any variables to appropriate structre + type AdvancedTaskDefinitionArgumentValueInput struct { + AdvancedTaskDefinitionArgumentName string `json:"advancedTaskDefinitionArgumentName"` + Value string `json:"value"` + } + taskArgumentValues := []AdvancedTaskDefinitionArgumentValueInput{} + for k, v := range taskArguments { + taskArgument := AdvancedTaskDefinitionArgumentValueInput{ + AdvancedTaskDefinitionArgumentName: k, + Value: v, + } + taskArgumentValues = append(taskArgumentValues, taskArgument) + } + // run the query to add the environment variable to lagoon customReq := api.CustomRequest{ - Query: `mutation invokeRegisteredTask ($environment: Int!, $advancedTaskDefinition: Int!) { - invokeRegisteredTask(advancedTaskDefinition: $advancedTaskDefinition, environment: $environment) { + Query: `mutation invokeRegisteredTask ($environment: Int!, $advancedTaskDefinition: Int!, $taskArgumentValues: [AdvancedTaskDefinitionArgumentValueInput]) { + invokeRegisteredTask(advancedTaskDefinition: $advancedTaskDefinition, environment: $environment, argumentValues: $taskArgumentValues) { id name status @@ -429,6 +443,7 @@ func (e *Environments) InvokeAdvancedTaskDefinition(projectName string, environm Variables: map[string]interface{}{ "advancedTaskDefinition": taskId, "environment": environmentInfo.ID, + "taskArgumentValues": taskArgumentValues, }, MappedResult: "invokeRegisteredTask", }