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..718152f5 100644 --- a/cmd/tasks.go +++ b/cmd/tasks.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/uselagoon/lagoon-cli/internal/lagoon" "github.com/uselagoon/lagoon-cli/internal/lagoon/client" @@ -187,14 +188,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 +204,127 @@ 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) + handleError(err) + resultData := output.Result{ + Result: "success", + ResultData: resultMap, + } + output.RenderResult(resultData, outputOptions) + }, +} + +var invokeInteractiveTask = &cobra.Command{ + Use: "interactive", + Aliases: []string{"i"}, + Short: "Interactively run a custom task against an environment", + Long: `Interactively run a custom task against an environment +Provides prompts for arguments +example: + lagoon run task interactive -p example -e main +`, + Run: func(cmd *cobra.Command, args []string) { + debug, err := cmd.Flags().GetBool("debug") + if cmdProjectName == "" || cmdProjectEnvironment == "" { + fmt.Println("Missing arguments: Project name or environment name are not defined") + cmd.Help() + os.Exit(1) + } + + current := lagoonCLIConfig.Current + lc := client.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Version, + lagoonCLIVersion, + debug) + project, err := lagoon.GetMinimalProjectByName(context.TODO(), cmdProjectName, lc) + if err != nil { + fmt.Println(err.Error()) + return + } + + environment, err := lagoon.TasksForEnvironment(context.TODO(), project.ID, cmdProjectEnvironment, lc) + + if err != nil { + fmt.Println(err.Error()) + return + } + + if len(environment.AdvancedTasks) == 0 { + fmt.Printf("There are no custom tasks registered against %v %v\n", cmdProjectName, environment.Name) + return + } + + prompt := promptui.Select{ + Label: "Select", + Items: environment.AdvancedTasks, + Templates: &promptui.SelectTemplates{ + Active: fmt.Sprintf("%s {{ .Name | underline }} -- {{ .Description }}", promptui.IconSelect), + Inactive: " {{ .Name }} -- {{ .Description }}", + Selected: fmt.Sprintf("Task: {{ .Name }}"), + }, + } + + taskIndex, _, err := prompt.Run() + + if err != nil { + fmt.Printf("Prompt failed %v\n", err) + return + } + + task := environment.AdvancedTasks[taskIndex] + taskArguments := map[string]string{} + for _, v := range task.AdvancedTaskDefinitionArguments { + if len(v.Range) != 0 { //we have a selection + argPrompt := promptui.Select{ + Label: fmt.Sprintf("%v", v.DisplayName), + Items: v.Range, + Templates: &promptui.SelectTemplates{ + Active: fmt.Sprintf("%s {{ . | underline }}", promptui.IconSelect), + Inactive: " {{ . }}", + Selected: fmt.Sprintf("-- %s : {{ . }}", v.DisplayName), + }, + } + _, argumentValue, err := argPrompt.Run() + if err != nil { + fmt.Printf("Prompt failed %v\n", err) + return + } + taskArguments[v.Name] = argumentValue + } else { // standard prompt + prompt := promptui.Prompt{ + Label: fmt.Sprintf("%v", v.DisplayName), + Templates: &promptui.PromptTemplates{ + Valid: fmt.Sprintf("-- {{ . }} : "), + Success: fmt.Sprintf("-- {{ . }} : "), + }, + } + argumentValue, err := prompt.Run() + + if err != nil { + fmt.Printf("Prompt failed %v\n", err) + return + } + taskArguments[v.Name] = argumentValue + } + } + + if !yesNo(fmt.Sprintf("Run above command on %s %s", cmdProjectName, cmdProjectEnvironment)) { + fmt.Println("Exiting") + return + } + + taskResult, err := eClient.InvokeAdvancedTaskDefinition(cmdProjectName, cmdProjectEnvironment, task.Name, taskArguments) handleError(err) var resultMap map[string]interface{} err = json.Unmarshal([]byte(taskResult), &resultMap) @@ -277,15 +398,19 @@ 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") + //register sub tasks + runDefinedTask.AddCommand(invokeInteractiveTask) + 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_task.md b/docs/commands/lagoon_run_task.md new file mode 100644 index 00000000..a4deacd9 --- /dev/null +++ b/docs/commands/lagoon_run_task.md @@ -0,0 +1,46 @@ +## 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 +* [lagoon run task interactive](lagoon_run_task_interactive.md) - Interactively run a custom task against an environment + diff --git a/docs/commands/lagoon_run_invoke.md b/docs/commands/lagoon_run_task_interactive.md similarity index 69% rename from docs/commands/lagoon_run_invoke.md rename to docs/commands/lagoon_run_task_interactive.md index c41ced3d..ca2c4f9a 100644 --- a/docs/commands/lagoon_run_invoke.md +++ b/docs/commands/lagoon_run_task_interactive.md @@ -1,24 +1,23 @@ -## lagoon run invoke - +## lagoon run task interactive +Interactively run a custom task against an environment ### Synopsis -Invoke a task registered against an environment -The following are supported methods to use -Direct: - lagoon run invoke -p example -e main -N "advanced task name" +Interactively run a custom task against an environment +Provides prompts for arguments +example: + lagoon run task interactive -p example -e main ``` -lagoon run invoke [flags] +lagoon run task interactive [flags] ``` ### Options ``` - -h, --help help for invoke - -N, --name string Name of the task that will be invoked + -h, --help help for interactive ``` ### Options inherited from parent commands @@ -40,5 +39,5 @@ lagoon run invoke [flags] ### SEE ALSO -* [lagoon run](lagoon_run.md) - Run a task against an environment +* [lagoon run task](lagoon_run_task.md) - Run a custom task registered against an environment diff --git a/internal/lagoon/client/_lgraphql/environmentAndTasksByEnvironmentName.graphql b/internal/lagoon/client/_lgraphql/environmentAndTasksByEnvironmentName.graphql new file mode 100644 index 00000000..217bdcd8 --- /dev/null +++ b/internal/lagoon/client/_lgraphql/environmentAndTasksByEnvironmentName.graphql @@ -0,0 +1,45 @@ +query ( + $name: String!, + $project: Int!) { + environmentByName( + name: $name, + project: $project) + { + id + name + route + routes + deployType + environmentType + openshiftProjectName + updated + created + deleted + advancedTasks { + ... on AdvancedTaskDefinitionImage { + id + name + description + advancedTaskDefinitionArguments { + id + displayName + name + type + range + } + } + ... on AdvancedTaskDefinitionCommand { + id + name + description + advancedTaskDefinitionArguments { + id + displayName + name + type + range + } + } + } + } + } diff --git a/internal/lagoon/client/lgraphql/lgraphql.go b/internal/lagoon/client/lgraphql/lgraphql.go index 085f2de6..eb6cb04d 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 @@ -25,6 +24,7 @@ // _lgraphql/deployEnvironmentPromote.graphql // _lgraphql/deployEnvironmentPullrequest.graphql // _lgraphql/deployTargetConfigsByProjectId.graphql +// _lgraphql/environmentAndTasksByEnvironmentName.graphql // _lgraphql/environmentByName.graphql // _lgraphql/lagoonSchema.graphql // _lgraphql/lagoonVersion.graphql @@ -60,7 +60,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 +68,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 +104,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 } @@ -599,6 +599,26 @@ func _lgraphqlDeploytargetconfigsbyprojectidGraphql() (*asset, error) { return a, nil } +var __lgraphqlEnvironmentandtasksbyenvironmentnameGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xe4\x50\x4b\x6e\xc3\x20\x10\xdd\xfb\x14\x13\xa9\x8b\x44\x8a\x7c\x80\xec\xd2\x76\x93\x8d\x55\xa9\xb9\xc0\xc8\x4c\x5c\x5a\x33\x50\xc0\x91\x50\x94\xbb\x57\xf8\x17\xfc\x69\x2f\xd0\xd9\xc0\x7b\x30\xf3\xde\x9b\xef\x86\x6c\x80\x6d\x06\xf0\xc4\xa8\xe8\x00\xef\xde\x4a\xae\x36\xfb\xc8\x18\xab\x3f\xa9\xf4\x07\x38\xb1\xdf\xec\xe0\x96\x01\x00\x10\x5f\xa5\xd5\xac\x88\xfd\x73\x28\x50\xd1\xb6\xa5\x01\xba\xfe\x76\xcc\xbe\xa7\xc6\x01\xc3\xa8\x5d\xff\x70\xeb\x4f\x00\x29\xc6\x6b\xec\x1c\x81\xd5\x8d\x9f\x21\x37\x42\x41\xa6\xd6\xe1\x1c\xcc\xe3\x47\x62\x6b\xc2\x6b\x43\xec\x3e\xe4\xc5\xbf\x75\x0e\x8a\x54\xa4\x31\x02\x3d\x3d\x1c\x94\x96\x26\x58\x50\x4d\x29\x46\x71\x45\x2e\x49\x9c\xd1\x7d\xb9\x24\x44\xac\x3c\xcf\x41\x33\x1c\x93\x2f\xaf\x74\x91\x2c\xbd\xd4\x7c\x52\x58\xd1\xac\x21\x49\xbe\x48\xdf\x89\xbb\xd2\x4a\x13\xdb\x27\x3c\xae\x2a\x1c\x6d\xd5\xc4\xf0\x73\x5b\x2b\x4a\xed\x70\xe9\x4c\x8d\xa1\x98\x8b\xae\x3a\x89\xe5\xd3\xa5\x0e\x65\x91\xab\x25\x7b\xcf\x7e\x47\x7f\x6e\xe9\x45\x2b\x85\x2c\xfe\xfb\x9e\x86\x5b\x3c\xef\xd9\x4f\x00\x00\x00\xff\xff\x6d\x2d\x35\x63\xa2\x03\x00\x00") + +func _lgraphqlEnvironmentandtasksbyenvironmentnameGraphqlBytes() ([]byte, error) { + return bindataRead( + __lgraphqlEnvironmentandtasksbyenvironmentnameGraphql, + "_lgraphql/environmentAndTasksByEnvironmentName.graphql", + ) +} + +func _lgraphqlEnvironmentandtasksbyenvironmentnameGraphql() (*asset, error) { + bytes, err := _lgraphqlEnvironmentandtasksbyenvironmentnameGraphqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "_lgraphql/environmentAndTasksByEnvironmentName.graphql", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + var __lgraphqlEnvironmentbynameGraphql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x8e\x4d\x0e\x82\x30\x10\x85\xf7\x9c\xe2\x91\xb8\x80\x84\x13\xb0\x74\xe7\x86\x98\xe8\x05\x08\x1d\xb5\x06\xa6\xb5\x0c\x26\x0d\xf1\xee\x86\x1f\x5b\x70\xd1\xb4\xdf\xd7\xce\xeb\x7b\x0d\xe4\x3c\xb2\x04\x38\x70\xdd\x51\x89\x8b\x38\xcd\xf7\xb4\x98\x8c\x75\xe6\x49\x8d\x94\x38\xb1\xa4\x39\xc6\x04\x00\x88\xdf\xda\x19\xee\x88\xe5\xe8\xab\xba\xa3\x6c\xd6\xc0\x32\x3f\xc7\x14\xab\x0a\x01\xbf\xa8\x7c\xbd\x18\xd7\x1d\xd0\x2a\x1c\xa7\xc9\x00\xce\x0c\xf2\x47\x7d\x40\x45\xb6\x35\xfe\xea\x6d\x7c\xb1\xa9\xb5\xf3\xc6\x12\xf7\x0f\x7d\x93\xf3\xd2\xa0\xda\x7e\x32\x58\x55\x0b\xc5\x06\x8d\xa3\x1d\x2b\x6a\x29\xf2\x27\x99\xd6\x37\x00\x00\xff\xff\xd5\xce\x35\x6e\x32\x01\x00\x00") func _lgraphqlEnvironmentbynameGraphqlBytes() ([]byte, error) { @@ -963,8 +983,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 +1009,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) @@ -1035,6 +1055,7 @@ var _bindata = map[string]func() (*asset, error){ "_lgraphql/deployEnvironmentPromote.graphql": _lgraphqlDeployenvironmentpromoteGraphql, "_lgraphql/deployEnvironmentPullrequest.graphql": _lgraphqlDeployenvironmentpullrequestGraphql, "_lgraphql/deployTargetConfigsByProjectId.graphql": _lgraphqlDeploytargetconfigsbyprojectidGraphql, + "_lgraphql/environmentAndTasksByEnvironmentName.graphql": _lgraphqlEnvironmentandtasksbyenvironmentnameGraphql, "_lgraphql/environmentByName.graphql": _lgraphqlEnvironmentbynameGraphql, "_lgraphql/lagoonSchema.graphql": _lgraphqlLagoonschemaGraphql, "_lgraphql/lagoonVersion.graphql": _lgraphqlLagoonversionGraphql, @@ -1059,22 +1080,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 { @@ -1099,45 +1118,46 @@ type bintree struct { var _bintree = &bintree{nil, map[string]*bintree{ "_lgraphql": &bintree{nil, map[string]*bintree{ - "addDeployTarget.graphql": &bintree{_lgraphqlAdddeploytargetGraphql, map[string]*bintree{}}, - "addDeployTargetConfig.graphql": &bintree{_lgraphqlAdddeploytargetconfigGraphql, map[string]*bintree{}}, - "addEnvVariable.graphql": &bintree{_lgraphqlAddenvvariableGraphql, map[string]*bintree{}}, - "addGroup.graphql": &bintree{_lgraphqlAddgroupGraphql, map[string]*bintree{}}, - "addGroupsToProject.graphql": &bintree{_lgraphqlAddgroupstoprojectGraphql, map[string]*bintree{}}, - "addNotificationEmail.graphql": &bintree{_lgraphqlAddnotificationemailGraphql, map[string]*bintree{}}, - "addNotificationMicrosoftTeams.graphql": &bintree{_lgraphqlAddnotificationmicrosoftteamsGraphql, map[string]*bintree{}}, - "addNotificationRocketChat.graphql": &bintree{_lgraphqlAddnotificationrocketchatGraphql, map[string]*bintree{}}, - "addNotificationSlack.graphql": &bintree{_lgraphqlAddnotificationslackGraphql, map[string]*bintree{}}, - "addNotificationToProject.graphql": &bintree{_lgraphqlAddnotificationtoprojectGraphql, map[string]*bintree{}}, - "addOrUpdateEnvironment.graphql": &bintree{_lgraphqlAddorupdateenvironmentGraphql, map[string]*bintree{}}, - "addProject.graphql": &bintree{_lgraphqlAddprojectGraphql, map[string]*bintree{}}, - "addRestore.graphql": &bintree{_lgraphqlAddrestoreGraphql, map[string]*bintree{}}, - "addSshKey.graphql": &bintree{_lgraphqlAddsshkeyGraphql, map[string]*bintree{}}, - "addUser.graphql": &bintree{_lgraphqlAdduserGraphql, map[string]*bintree{}}, - "addUserToGroup.graphql": &bintree{_lgraphqlAddusertogroupGraphql, map[string]*bintree{}}, - "backupsForEnvironmentByName.graphql": &bintree{_lgraphqlBackupsforenvironmentbynameGraphql, map[string]*bintree{}}, - "deleteDeployTarget.graphql": &bintree{_lgraphqlDeletedeploytargetGraphql, map[string]*bintree{}}, - "deleteDeployTargetConfig.graphql": &bintree{_lgraphqlDeletedeploytargetconfigGraphql, map[string]*bintree{}}, - "deployEnvironmentBranch.graphql": &bintree{_lgraphqlDeployenvironmentbranchGraphql, map[string]*bintree{}}, - "deployEnvironmentLatest.graphql": &bintree{_lgraphqlDeployenvironmentlatestGraphql, map[string]*bintree{}}, - "deployEnvironmentPromote.graphql": &bintree{_lgraphqlDeployenvironmentpromoteGraphql, map[string]*bintree{}}, - "deployEnvironmentPullrequest.graphql": &bintree{_lgraphqlDeployenvironmentpullrequestGraphql, map[string]*bintree{}}, - "deployTargetConfigsByProjectId.graphql": &bintree{_lgraphqlDeploytargetconfigsbyprojectidGraphql, map[string]*bintree{}}, - "environmentByName.graphql": &bintree{_lgraphqlEnvironmentbynameGraphql, map[string]*bintree{}}, - "lagoonSchema.graphql": &bintree{_lgraphqlLagoonschemaGraphql, map[string]*bintree{}}, - "lagoonVersion.graphql": &bintree{_lgraphqlLagoonversionGraphql, map[string]*bintree{}}, - "listDeployTargets.graphql": &bintree{_lgraphqlListdeploytargetsGraphql, map[string]*bintree{}}, - "me.graphql": &bintree{_lgraphqlMeGraphql, map[string]*bintree{}}, - "minimalProjectByName.graphql": &bintree{_lgraphqlMinimalprojectbynameGraphql, map[string]*bintree{}}, - "projectByName.graphql": &bintree{_lgraphqlProjectbynameGraphql, map[string]*bintree{}}, - "projectByNameMetadata.graphql": &bintree{_lgraphqlProjectbynamemetadataGraphql, map[string]*bintree{}}, - "projectsByMetadata.graphql": &bintree{_lgraphqlProjectsbymetadataGraphql, map[string]*bintree{}}, - "removeProjectMetadataByKey.graphql": &bintree{_lgraphqlRemoveprojectmetadatabykeyGraphql, map[string]*bintree{}}, - "switchActiveStandby.graphql": &bintree{_lgraphqlSwitchactivestandbyGraphql, map[string]*bintree{}}, - "taskByID.graphql": &bintree{_lgraphqlTaskbyidGraphql, map[string]*bintree{}}, - "updateDeployTarget.graphql": &bintree{_lgraphqlUpdatedeploytargetGraphql, map[string]*bintree{}}, - "updateDeployTargetConfig.graphql": &bintree{_lgraphqlUpdatedeploytargetconfigGraphql, map[string]*bintree{}}, - "updateProjectMetadata.graphql": &bintree{_lgraphqlUpdateprojectmetadataGraphql, map[string]*bintree{}}, + "addDeployTarget.graphql": &bintree{_lgraphqlAdddeploytargetGraphql, map[string]*bintree{}}, + "addDeployTargetConfig.graphql": &bintree{_lgraphqlAdddeploytargetconfigGraphql, map[string]*bintree{}}, + "addEnvVariable.graphql": &bintree{_lgraphqlAddenvvariableGraphql, map[string]*bintree{}}, + "addGroup.graphql": &bintree{_lgraphqlAddgroupGraphql, map[string]*bintree{}}, + "addGroupsToProject.graphql": &bintree{_lgraphqlAddgroupstoprojectGraphql, map[string]*bintree{}}, + "addNotificationEmail.graphql": &bintree{_lgraphqlAddnotificationemailGraphql, map[string]*bintree{}}, + "addNotificationMicrosoftTeams.graphql": &bintree{_lgraphqlAddnotificationmicrosoftteamsGraphql, map[string]*bintree{}}, + "addNotificationRocketChat.graphql": &bintree{_lgraphqlAddnotificationrocketchatGraphql, map[string]*bintree{}}, + "addNotificationSlack.graphql": &bintree{_lgraphqlAddnotificationslackGraphql, map[string]*bintree{}}, + "addNotificationToProject.graphql": &bintree{_lgraphqlAddnotificationtoprojectGraphql, map[string]*bintree{}}, + "addOrUpdateEnvironment.graphql": &bintree{_lgraphqlAddorupdateenvironmentGraphql, map[string]*bintree{}}, + "addProject.graphql": &bintree{_lgraphqlAddprojectGraphql, map[string]*bintree{}}, + "addRestore.graphql": &bintree{_lgraphqlAddrestoreGraphql, map[string]*bintree{}}, + "addSshKey.graphql": &bintree{_lgraphqlAddsshkeyGraphql, map[string]*bintree{}}, + "addUser.graphql": &bintree{_lgraphqlAdduserGraphql, map[string]*bintree{}}, + "addUserToGroup.graphql": &bintree{_lgraphqlAddusertogroupGraphql, map[string]*bintree{}}, + "backupsForEnvironmentByName.graphql": &bintree{_lgraphqlBackupsforenvironmentbynameGraphql, map[string]*bintree{}}, + "deleteDeployTarget.graphql": &bintree{_lgraphqlDeletedeploytargetGraphql, map[string]*bintree{}}, + "deleteDeployTargetConfig.graphql": &bintree{_lgraphqlDeletedeploytargetconfigGraphql, map[string]*bintree{}}, + "deployEnvironmentBranch.graphql": &bintree{_lgraphqlDeployenvironmentbranchGraphql, map[string]*bintree{}}, + "deployEnvironmentLatest.graphql": &bintree{_lgraphqlDeployenvironmentlatestGraphql, map[string]*bintree{}}, + "deployEnvironmentPromote.graphql": &bintree{_lgraphqlDeployenvironmentpromoteGraphql, map[string]*bintree{}}, + "deployEnvironmentPullrequest.graphql": &bintree{_lgraphqlDeployenvironmentpullrequestGraphql, map[string]*bintree{}}, + "deployTargetConfigsByProjectId.graphql": &bintree{_lgraphqlDeploytargetconfigsbyprojectidGraphql, map[string]*bintree{}}, + "environmentAndTasksByEnvironmentName.graphql": &bintree{_lgraphqlEnvironmentandtasksbyenvironmentnameGraphql, map[string]*bintree{}}, + "environmentByName.graphql": &bintree{_lgraphqlEnvironmentbynameGraphql, map[string]*bintree{}}, + "lagoonSchema.graphql": &bintree{_lgraphqlLagoonschemaGraphql, map[string]*bintree{}}, + "lagoonVersion.graphql": &bintree{_lgraphqlLagoonversionGraphql, map[string]*bintree{}}, + "listDeployTargets.graphql": &bintree{_lgraphqlListdeploytargetsGraphql, map[string]*bintree{}}, + "me.graphql": &bintree{_lgraphqlMeGraphql, map[string]*bintree{}}, + "minimalProjectByName.graphql": &bintree{_lgraphqlMinimalprojectbynameGraphql, map[string]*bintree{}}, + "projectByName.graphql": &bintree{_lgraphqlProjectbynameGraphql, map[string]*bintree{}}, + "projectByNameMetadata.graphql": &bintree{_lgraphqlProjectbynamemetadataGraphql, map[string]*bintree{}}, + "projectsByMetadata.graphql": &bintree{_lgraphqlProjectsbymetadataGraphql, map[string]*bintree{}}, + "removeProjectMetadataByKey.graphql": &bintree{_lgraphqlRemoveprojectmetadatabykeyGraphql, map[string]*bintree{}}, + "switchActiveStandby.graphql": &bintree{_lgraphqlSwitchactivestandbyGraphql, map[string]*bintree{}}, + "taskByID.graphql": &bintree{_lgraphqlTaskbyidGraphql, map[string]*bintree{}}, + "updateDeployTarget.graphql": &bintree{_lgraphqlUpdatedeploytargetGraphql, map[string]*bintree{}}, + "updateDeployTargetConfig.graphql": &bintree{_lgraphqlUpdatedeploytargetconfigGraphql, map[string]*bintree{}}, + "updateProjectMetadata.graphql": &bintree{_lgraphqlUpdateprojectmetadataGraphql, map[string]*bintree{}}, "variables": &bintree{nil, map[string]*bintree{ "addOrUpdateEnvVariableByName.graphql": &bintree{_lgraphqlVariablesAddorupdateenvvariablebynameGraphql, map[string]*bintree{}}, "deleteEnvVariableByName.graphql": &bintree{_lgraphqlVariablesDeleteenvvariablebynameGraphql, map[string]*bintree{}}, @@ -1189,6 +1209,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/internal/lagoon/client/query.go b/internal/lagoon/client/query.go index eced69a2..4b200f4c 100644 --- a/internal/lagoon/client/query.go +++ b/internal/lagoon/client/query.go @@ -65,6 +65,27 @@ func (c *Client) EnvironmentByName(ctx context.Context, name string, }) } +// EnvironmentByName queries the Lagoon API for an environment by its name and +// parent projectID, and unmarshals the response into environment. +func (c *Client) EnvironmentAndTasksByEnvironmentName(ctx context.Context, name string, + projectID uint, environment *schema.Environment) error { + + req, err := c.newRequest("_lgraphql/environmentAndTasksByEnvironmentName.graphql", + map[string]interface{}{ + "name": name, + "project": projectID, + }) + if err != nil { + return err + } + + return c.client.Run(ctx, req, &struct { + Response *schema.Environment `json:"environmentByName"` + }{ + Response: environment, + }) +} + // BackupsForEnvironmentByName queries the Lagoon API for an environment by its name and // parent projectID, and unmarshals the response into environment. func (c *Client) BackupsForEnvironmentByName(ctx context.Context, name string, diff --git a/internal/lagoon/tasks.go b/internal/lagoon/tasks.go index cce1a113..8631e58f 100644 --- a/internal/lagoon/tasks.go +++ b/internal/lagoon/tasks.go @@ -12,6 +12,7 @@ import ( type Tasks interface { RunActiveStandbySwitch(ctx context.Context, project string, result *schema.Task) error GetTaskByID(ctx context.Context, id int, result *schema.Task) error + EnvironmentAndTasksByEnvironmentName(ctx context.Context, name string, projectID uint, environment *schema.Environment) error } // ActiveStandbySwitch runs the activestandby switch. @@ -25,3 +26,8 @@ func TaskByID(ctx context.Context, id int, t Tasks) (*schema.Task, error) { result := schema.Task{} return &result, t.GetTaskByID(ctx, id, &result) } + +func TasksForEnvironment(ctx context.Context, projectId uint, environmentName string, t Tasks) (*schema.Environment, error) { + result := schema.Environment{} + return &result, t.EnvironmentAndTasksByEnvironmentName(ctx, environmentName, projectId, &result) +} diff --git a/internal/schema/environment.go b/internal/schema/environment.go index bc7af0e7..a7609da5 100644 --- a/internal/schema/environment.go +++ b/internal/schema/environment.go @@ -19,17 +19,33 @@ type AddEnvironmentInput struct { // Environment is the Lagoon API Environment object. type Environment struct { AddEnvironmentInput - AutoIdle uint `json:"autoIdle"` - EnvVariables []EnvKeyValue `json:"envVariables,omitempty"` - Route string `json:"route,omitempty"` - Routes string `json:"routes,omitempty"` - Backups []Backup `json:"backups,omitempty"` + AutoIdle uint `json:"autoIdle"` + EnvVariables []EnvKeyValue `json:"envVariables,omitempty"` + Route string `json:"route,omitempty"` + Routes string `json:"routes,omitempty"` + Backups []Backup `json:"backups,omitempty"` + AdvancedTasks []AdvancedTaskDefinition `json:"advancedTasks,omitempty"` // TODO use a unixtime type Updated string `json:"updated,omitempty"` Created string `json:"created,omitempty"` Deleted string `json:"deleted,omitempty"` } +type AdvancedTaskDefinition struct { + Id uint `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + AdvancedTaskDefinitionArguments []AdvancedTaskDefinitionArgument `json:"advancedTaskDefinitionArguments"` +} + +type AdvancedTaskDefinitionArgument struct { + Id uint `json:"id"` + Name string `json:"name"` + DisplayName string `json:"displayName"` + Type string `json:"type"` + Range []string `json:"range"` +} + // EnvironmentConfig contains Environment configuration. type EnvironmentConfig struct { Environment 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", }