From 9274b3f57040bc443ece92d6bc63d14220e1cf0e Mon Sep 17 00:00:00 2001 From: Chris Goodwin <40746380+CGoodwin90@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:54:58 +1100 Subject: [PATCH 1/6] Change: Refactored `get project` command, adds `deploymentsDisabled` & updated to use `bools` instead of `int bools` (#297) --- cmd/get.go | 90 ++++++++++++++++++++++++++++++++++++++++++------------ go.mod | 2 +- 2 files changed, 72 insertions(+), 20 deletions(-) diff --git a/cmd/get.go b/cmd/get.go index 193aef2c..1a603ecd 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -1,14 +1,18 @@ package cmd import ( + "context" "encoding/json" "fmt" - "os" - "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/uselagoon/lagoon-cli/pkg/api" "github.com/uselagoon/lagoon-cli/pkg/output" + "os" + "strconv" + + l "github.com/uselagoon/machinery/api/lagoon" + lclient "github.com/uselagoon/machinery/api/lagoon/client" ) // GetFlags . @@ -44,30 +48,78 @@ var getProjectCmd = &cobra.Command{ Use: "project", Aliases: []string{"p"}, Short: "Get details about a project", - Run: func(cmd *cobra.Command, args []string) { - getProjectFlags := parseGetFlags(*cmd.Flags()) - if getProjectFlags.Project == "" { + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + if cmdProjectName == "" { fmt.Println("Missing arguments: Project name is not defined") - cmd.Help() - os.Exit(1) + return nil } - returnedJSON, err := pClient.GetProjectInfo(getProjectFlags.Project) + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + project, err := l.GetProjectByName(context.TODO(), cmdProjectName, lc) if err != nil { - output.RenderError(err.Error(), outputOptions) - os.Exit(1) + return err } - var dataMain output.Table - err = json.Unmarshal([]byte(returnedJSON), &dataMain) - if err != nil { - output.RenderError(err.Error(), outputOptions) - os.Exit(1) + + if project == nil { + output.RenderInfo(fmt.Sprintf("No details for project '%s'", cmdProjectName), outputOptions) + return nil } - if len(dataMain.Data) == 0 { - output.RenderInfo(fmt.Sprintf("No details for project '%s'", getProjectFlags.Project), outputOptions) - os.Exit(0) + + DevEnvironments := 0 + productionRoute := "none" + deploymentsDisabled, err := strconv.ParseBool(strconv.Itoa(int(project.DeploymentsDisabled))) + handleError(err) + autoIdle, err := strconv.ParseBool(strconv.Itoa(int(project.AutoIdle))) + handleError(err) + factsUI, err := strconv.ParseBool(strconv.Itoa(int(project.FactsUI))) + handleError(err) + problemsUI, err := strconv.ParseBool(strconv.Itoa(int(project.ProblemsUI))) + handleError(err) + for _, environment := range project.Environments { + if environment.EnvironmentType == "development" { + DevEnvironments++ + } + if environment.EnvironmentType == "production" { + productionRoute = environment.Route + } } - output.RenderOutput(dataMain, outputOptions) + data := []output.Data{} + data = append(data, []string{ + returnNonEmptyString(fmt.Sprintf("%d", project.ID)), + returnNonEmptyString(fmt.Sprintf("%v", project.Name)), + returnNonEmptyString(fmt.Sprintf("%v", project.GitURL)), + returnNonEmptyString(fmt.Sprintf("%v", project.Branches)), + returnNonEmptyString(fmt.Sprintf("%v", project.PullRequests)), + returnNonEmptyString(fmt.Sprintf("%v", productionRoute)), + returnNonEmptyString(fmt.Sprintf("%v/%v", DevEnvironments, project.DevelopmentEnvironmentsLimit)), + returnNonEmptyString(fmt.Sprintf("%v", project.DevelopmentEnvironmentsLimit)), + returnNonEmptyString(fmt.Sprintf("%v", project.ProductionEnvironment)), + returnNonEmptyString(fmt.Sprintf("%v", project.RouterPattern)), + returnNonEmptyString(fmt.Sprintf("%v", autoIdle)), + returnNonEmptyString(fmt.Sprintf("%v", factsUI)), + returnNonEmptyString(fmt.Sprintf("%v", problemsUI)), + returnNonEmptyString(fmt.Sprintf("%v", deploymentsDisabled)), + }) + dataMain := output.Table{ + Header: []string{"ID", "ProjectName", "GitURL", "Branches", "PullRequests", "ProductionRoute", "DevEnvironments", "DevEnvLimit", "ProductionEnv", "RouterPattern", "AutoIdle", "FactsUI", "ProblemsUI", "DeploymentsDisabled"}, + Data: data, + } + output.RenderOutput(dataMain, outputOptions) + return nil }, } diff --git a/go.mod b/go.mod index 2d25de43..3bcebbdb 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,6 @@ require ( // use this version for fixes to formatting of end header replace github.com/olekukonko/tablewriter => github.com/shreddedbacon/tablewriter v0.0.2-0.20200114082015-d810c4a558bf -// replace github.com/machinebox/graphql => ../../shreddedbacon/graphql +//replace github.com/uselagoon/machinery => ../machinery // replace github.com/olekukonko/tablewriter => ../../shreddedbacon/tablewriter From fb8a37ab6386748463400306eca65a63137a34b1 Mon Sep 17 00:00:00 2001 From: Chris Goodwin <40746380+CGoodwin90@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:58:31 +1100 Subject: [PATCH 2/6] Feature: List Project Groups functionality (#288) Co-authored-by: Ben Jackson --- cmd/list.go | 56 +++++++++++++++++++ docs/commands/lagoon.md | 2 +- docs/commands/lagoon_list.md | 5 +- docs/commands/lagoon_list_backups.md | 2 +- docs/commands/lagoon_list_deployments.md | 2 +- .../lagoon_list_deploytarget-configs.md | 2 +- docs/commands/lagoon_list_deploytargets.md | 2 +- docs/commands/lagoon_list_environments.md | 2 +- docs/commands/lagoon_list_group-projects.md | 2 +- docs/commands/lagoon_list_groups.md | 2 +- docs/commands/lagoon_list_invokable-tasks.md | 2 +- docs/commands/lagoon_list_notification.md | 2 +- docs/commands/lagoon_list_project-groups.md | 39 +++++++++++++ .../lagoon_list_projects-by-metadata.md | 2 +- docs/commands/lagoon_list_projects.md | 2 +- docs/commands/lagoon_list_tasks.md | 2 +- docs/commands/lagoon_list_users.md | 2 +- docs/commands/lagoon_list_variables.md | 2 +- internal/lagoon/client/lgraphql/lgraphql.go | 12 ++-- 19 files changed, 120 insertions(+), 22 deletions(-) create mode 100644 docs/commands/lagoon_list_project-groups.md diff --git a/cmd/list.go b/cmd/list.go index 6c2a23ef..d77f7188 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "os" + "strconv" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -436,11 +437,66 @@ var listNotificationCmd = &cobra.Command{ }, } +var listProjectGroupsCmd = &cobra.Command{ + Use: "project-groups", + Aliases: []string{"pg"}, + Short: "List groups in a project (alias: pg)", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + if cmdProjectName == "" { + fmt.Println("Missing arguments: Project is not defined") + cmd.Help() + os.Exit(1) + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + projectGroups, err := l.GetProjectGroups(context.TODO(), cmdProjectName, lc) + handleError(err) + + if len(projectGroups.Groups) == 0 { + output.RenderInfo(fmt.Sprintf("There are no projects in group '%s'", groupName), outputOptions) + os.Exit(0) + } + + data := []output.Data{} + for _, group := range projectGroups.Groups { + var organization = "null" + if group.Organization != 0 { + organization = strconv.Itoa(group.Organization) + } + data = append(data, []string{ + returnNonEmptyString(fmt.Sprintf("%v", group.ID)), + returnNonEmptyString(fmt.Sprintf("%v", group.Name)), + returnNonEmptyString(fmt.Sprintf("%v", organization)), + }) + } + dataMain := output.Table{ + Header: []string{"Group ID", "Group Name", "Organization"}, + Data: data, + } + output.RenderOutput(dataMain, outputOptions) + return nil + }, +} + func init() { listCmd.AddCommand(listDeployTargetsCmd) listCmd.AddCommand(listDeploymentsCmd) listCmd.AddCommand(listGroupsCmd) listCmd.AddCommand(listGroupProjectsCmd) + listCmd.AddCommand(listProjectGroupsCmd) listCmd.AddCommand(listEnvironmentsCmd) listCmd.AddCommand(listProjectsCmd) listCmd.AddCommand(listNotificationCmd) diff --git a/docs/commands/lagoon.md b/docs/commands/lagoon.md index 6cfb47c0..7ab9ad66 100644 --- a/docs/commands/lagoon.md +++ b/docs/commands/lagoon.md @@ -39,7 +39,7 @@ lagoon [flags] * [lagoon get](lagoon_get.md) - Get info on a resource * [lagoon import](lagoon_import.md) - Import a config from a yaml file * [lagoon kibana](lagoon_kibana.md) - Launch the kibana interface -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications * [lagoon login](lagoon_login.md) - Log into a Lagoon instance * [lagoon retrieve](lagoon_retrieve.md) - Trigger a retrieval operation on backups * [lagoon run](lagoon_run.md) - Run a task against an environment diff --git a/docs/commands/lagoon_list.md b/docs/commands/lagoon_list.md index 2b48e078..31bdd6ed 100644 --- a/docs/commands/lagoon_list.md +++ b/docs/commands/lagoon_list.md @@ -1,10 +1,10 @@ ## lagoon list -List projects, deployments, variables or notifications +List projects, environments, deployments, variables or notifications ### Synopsis -List projects, deployments, variables or notifications +List projects, environments, deployments, variables or notifications ### Options @@ -42,6 +42,7 @@ List projects, deployments, variables or notifications * [lagoon list groups](lagoon_list_groups.md) - List groups you have access to (alias: g) * [lagoon list invokable-tasks](lagoon_list_invokable-tasks.md) - Print a list of invokable tasks * [lagoon list notification](lagoon_list_notification.md) - List all notifications or notifications on projects +* [lagoon list project-groups](lagoon_list_project-groups.md) - List groups in a project (alias: pg) * [lagoon list projects](lagoon_list_projects.md) - List all projects you have access to (alias: p) * [lagoon list projects-by-metadata](lagoon_list_projects-by-metadata.md) - List projects by a given metadata key or key:value * [lagoon list tasks](lagoon_list_tasks.md) - List tasks for an environment (alias: t) diff --git a/docs/commands/lagoon_list_backups.md b/docs/commands/lagoon_list_backups.md index 962e9c92..6a209b04 100644 --- a/docs/commands/lagoon_list_backups.md +++ b/docs/commands/lagoon_list_backups.md @@ -35,5 +35,5 @@ lagoon list backups [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_deployments.md b/docs/commands/lagoon_list_deployments.md index 6175372a..25a09aff 100644 --- a/docs/commands/lagoon_list_deployments.md +++ b/docs/commands/lagoon_list_deployments.md @@ -35,5 +35,5 @@ lagoon list deployments [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_deploytarget-configs.md b/docs/commands/lagoon_list_deploytarget-configs.md index d3806da4..723d714e 100644 --- a/docs/commands/lagoon_list_deploytarget-configs.md +++ b/docs/commands/lagoon_list_deploytarget-configs.md @@ -35,5 +35,5 @@ lagoon list deploytarget-configs [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_deploytargets.md b/docs/commands/lagoon_list_deploytargets.md index eb899903..3791e812 100644 --- a/docs/commands/lagoon_list_deploytargets.md +++ b/docs/commands/lagoon_list_deploytargets.md @@ -35,5 +35,5 @@ lagoon list deploytargets [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_environments.md b/docs/commands/lagoon_list_environments.md index 504151f2..5186199d 100644 --- a/docs/commands/lagoon_list_environments.md +++ b/docs/commands/lagoon_list_environments.md @@ -35,5 +35,5 @@ lagoon list environments [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_group-projects.md b/docs/commands/lagoon_list_group-projects.md index a1213b98..803d32db 100644 --- a/docs/commands/lagoon_list_group-projects.md +++ b/docs/commands/lagoon_list_group-projects.md @@ -36,5 +36,5 @@ lagoon list group-projects [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_groups.md b/docs/commands/lagoon_list_groups.md index 40f5cf53..cad64322 100644 --- a/docs/commands/lagoon_list_groups.md +++ b/docs/commands/lagoon_list_groups.md @@ -35,5 +35,5 @@ lagoon list groups [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_invokable-tasks.md b/docs/commands/lagoon_list_invokable-tasks.md index 0c27bef6..77e92550 100644 --- a/docs/commands/lagoon_list_invokable-tasks.md +++ b/docs/commands/lagoon_list_invokable-tasks.md @@ -35,5 +35,5 @@ lagoon list invokable-tasks [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_notification.md b/docs/commands/lagoon_list_notification.md index 5b4f86b5..f89a4300 100644 --- a/docs/commands/lagoon_list_notification.md +++ b/docs/commands/lagoon_list_notification.md @@ -31,7 +31,7 @@ List all notifications or notifications on projects ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications * [lagoon list notification email](lagoon_list_notification_email.md) - List all email notification details (alias: e) * [lagoon list notification microsoftteams](lagoon_list_notification_microsoftteams.md) - List all Microsoft Teams notification details (alias: m) * [lagoon list notification project-email](lagoon_list_notification_project-email.md) - List email details about a project (alias: pe) diff --git a/docs/commands/lagoon_list_project-groups.md b/docs/commands/lagoon_list_project-groups.md new file mode 100644 index 00000000..9bfb2d13 --- /dev/null +++ b/docs/commands/lagoon_list_project-groups.md @@ -0,0 +1,39 @@ +## lagoon list project-groups + +List groups in a project (alias: pg) + +### Synopsis + +List groups in a project (alias: pg) + +``` +lagoon list project-groups [flags] +``` + +### Options + +``` + -h, --help help for project-groups +``` + +### 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 list](lagoon_list.md) - List projects, environments, deployments, variables or notifications + diff --git a/docs/commands/lagoon_list_projects-by-metadata.md b/docs/commands/lagoon_list_projects-by-metadata.md index ca245d5e..981142ac 100644 --- a/docs/commands/lagoon_list_projects-by-metadata.md +++ b/docs/commands/lagoon_list_projects-by-metadata.md @@ -38,5 +38,5 @@ lagoon list projects-by-metadata [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_projects.md b/docs/commands/lagoon_list_projects.md index 7b9fa690..ba3b8559 100644 --- a/docs/commands/lagoon_list_projects.md +++ b/docs/commands/lagoon_list_projects.md @@ -35,5 +35,5 @@ lagoon list projects [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_tasks.md b/docs/commands/lagoon_list_tasks.md index 6d3aac8c..11e2fe80 100644 --- a/docs/commands/lagoon_list_tasks.md +++ b/docs/commands/lagoon_list_tasks.md @@ -35,5 +35,5 @@ lagoon list tasks [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_users.md b/docs/commands/lagoon_list_users.md index 7a22d8d7..7988ae11 100644 --- a/docs/commands/lagoon_list_users.md +++ b/docs/commands/lagoon_list_users.md @@ -36,5 +36,5 @@ lagoon list users [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/docs/commands/lagoon_list_variables.md b/docs/commands/lagoon_list_variables.md index a4c01425..59f6eed4 100644 --- a/docs/commands/lagoon_list_variables.md +++ b/docs/commands/lagoon_list_variables.md @@ -36,5 +36,5 @@ lagoon list variables [flags] ### SEE ALSO -* [lagoon list](lagoon_list.md) - List projects, deployments, variables or notifications +* [lagoon list](lagoon_list.md) - List projects, environments, deployments, variables or notifications diff --git a/internal/lagoon/client/lgraphql/lgraphql.go b/internal/lagoon/client/lgraphql/lgraphql.go index f246b0f1..5c965f9e 100644 --- a/internal/lagoon/client/lgraphql/lgraphql.go +++ b/internal/lagoon/client/lgraphql/lgraphql.go @@ -1432,11 +1432,13 @@ 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("notexist") would return an error From eeb83b5b54f8d56bbf342e604e6b18fdab25d28e Mon Sep 17 00:00:00 2001 From: Chris Goodwin <40746380+CGoodwin90@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:03:03 +1100 Subject: [PATCH 3/6] Fix: Fixes & refactors `user-sshkey` commands (#303) --- cmd/users.go | 159 ++++++++++++++++----- docs/commands/lagoon_delete_user-sshkey.md | 4 +- docs/commands/lagoon_get_user-sshkeys.md | 1 - pkg/lagoon/users/main.go | 2 - pkg/lagoon/users/users.go | 70 --------- 5 files changed, 128 insertions(+), 108 deletions(-) diff --git a/cmd/users.go b/cmd/users.go index 1178213b..72b306e2 100644 --- a/cmd/users.go +++ b/cmd/users.go @@ -1,11 +1,16 @@ package cmd import ( + "context" "encoding/json" "errors" "fmt" + l "github.com/uselagoon/machinery/api/lagoon" + lclient "github.com/uselagoon/machinery/api/lagoon/client" + s "github.com/uselagoon/machinery/api/schema" "io/ioutil" "os" + "strconv" "strings" "github.com/spf13/cobra" @@ -147,22 +152,36 @@ var deleteSSHKeyCmd = &cobra.Command{ Use: "user-sshkey", Aliases: []string{"u"}, Short: "Delete an SSH key from Lagoon", - Run: func(cmd *cobra.Command, args []string) { - if sshKeyName == "" { - fmt.Println("Missing arguments: SSH key name is not defined") - cmd.Help() - os.Exit(1) + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(cmdLagoon) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + sshKeyID, err := cmd.Flags().GetUint("id") + if err != nil { + return err } - var customReqResult []byte - var err error - if yesNo(fmt.Sprintf("You are attempting to delete SSH key named '%s', are you sure?", sshKeyName)) { - customReqResult, err = uClient.DeleteSSHKey(sshKeyName) + if sshKeyID == 0 { + fmt.Println("Missing arguments: SSH key ID is not defined") + return nil + } + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + if yesNo(fmt.Sprintf("You are attempting to delete SSH key ID:'%d', are you sure?", sshKeyID)) { + _, err := l.RemoveSSHKey(context.TODO(), sshKeyID, lc) handleError(err) resultData := output.Result{ - Result: string(customReqResult), + Result: "success", } output.RenderResult(resultData, outputOptions) } + return nil }, } @@ -226,23 +245,55 @@ var getUserKeysCmd = &cobra.Command{ Aliases: []string{"us"}, Short: "Get a user's SSH keys", Long: `Get a user's SSH keys. This will only work for users that are part of a group`, - Run: func(cmd *cobra.Command, args []string) { + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(cmdLagoon) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + userEmail, err := cmd.Flags().GetString("email") + if err != nil { + return err + } if userEmail == "" { fmt.Println("Missing arguments: Email address is not defined") - cmd.Help() - os.Exit(1) + return nil } - returnedJSON, err := uClient.ListUserSSHKeys(groupName, strings.ToLower(userEmail), false) - handleError(err) - var dataMain output.Table - err = json.Unmarshal([]byte(returnedJSON), &dataMain) + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + userKeys, err := l.GetUserSSHKeysByEmail(context.TODO(), userEmail, lc) handleError(err) - if len(dataMain.Data) == 0 { + if len(userKeys.SSHKeys) == 0 { output.RenderInfo(fmt.Sprintf("No SSH keys for user '%s'", strings.ToLower(userEmail)), outputOptions) - os.Exit(0) + return nil } - output.RenderOutput(dataMain, outputOptions) + data := []output.Data{} + for _, userkey := range userKeys.SSHKeys { + data = append(data, []string{ + strconv.Itoa(int(userkey.ID)), + userKeys.Email, + userkey.Name, + string(userkey.KeyType), + userkey.KeyValue, + }) + } + + dataMain := output.Table{ + Header: []string{"ID", "Email", "Name", "Type", "Value"}, + Data: data, + } + + output.RenderOutput(dataMain, outputOptions) + return nil }, } @@ -252,18 +303,61 @@ var getAllUserKeysCmd = &cobra.Command{ Aliases: []string{"aus"}, Short: "Get all user SSH keys", Long: `Get all user SSH keys. This will only work for users that are part of a group`, - Run: func(cmd *cobra.Command, args []string) { - returnedJSON, err := uClient.ListUserSSHKeys(groupName, strings.ToLower(userEmail), true) - handleError(err) - var dataMain output.Table - err = json.Unmarshal([]byte(returnedJSON), &dataMain) + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(cmdLagoon) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + groupName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + groupMembers, err := l.ListAllGroupMembersWithKeys(context.TODO(), groupName, lc) handleError(err) - if len(dataMain.Data) == 0 { - output.RenderInfo("No SSH keys for any users", outputOptions) - os.Exit(0) + + var userGroups []s.AddSSHKeyInput + for _, group := range *groupMembers { + for _, member := range group.Members { + for _, key := range member.User.SSHKeys { + userGroups = append(userGroups, s.AddSSHKeyInput{SSHKey: key, UserEmail: member.User.Email}) + } + } } - output.RenderOutput(dataMain, outputOptions) + var data []output.Data + for _, userData := range userGroups { + keyID := strconv.Itoa(int(userData.SSHKey.ID)) + userEmail := returnNonEmptyString(strings.Replace(userData.UserEmail, " ", "_", -1)) + keyName := returnNonEmptyString(strings.Replace(userData.SSHKey.Name, " ", "_", -1)) + keyValue := returnNonEmptyString(strings.Replace(userData.SSHKey.KeyValue, " ", "_", -1)) + keyType := returnNonEmptyString(strings.Replace(string(userData.SSHKey.KeyType), " ", "_", -1)) + data = append(data, []string{ + keyID, + userEmail, + keyName, + keyType, + keyValue, + }) + } + + dataMain := output.Table{ + Header: []string{"ID", "Email", "Name", "Type", "Value"}, + Data: data, + } + + output.RenderOutput(dataMain, outputOptions) + return nil }, } @@ -281,12 +375,11 @@ func init() { addUserSSHKeyCmd.Flags().StringVarP(&pubKeyFile, "pubkey", "K", "", "Specify path to the public key to add") addUserSSHKeyCmd.Flags().StringVarP(&pubKeyValue, "keyvalue", "V", "", "Value of the public key to add (ssh-ed25519 AAA..)") deleteUserCmd.Flags().StringVarP(&userEmail, "email", "E", "", "Email address of the user") - deleteSSHKeyCmd.Flags().StringVarP(&sshKeyName, "keyname", "N", "", "Name of the SSH key") + deleteSSHKeyCmd.Flags().Uint("id", 0, "ID of the SSH key") updateUserCmd.Flags().StringVarP(&userFirstName, "firstName", "F", "", "New first name of the user") updateUserCmd.Flags().StringVarP(&userLastName, "lastName", "L", "", "New last name of the user") updateUserCmd.Flags().StringVarP(&userEmail, "email", "E", "", "New email address of the user") updateUserCmd.Flags().StringVarP(¤tUserEmail, "current-email", "C", "", "Current email address of the user") - getUserKeysCmd.Flags().StringVarP(&userEmail, "email", "E", "", "New email address of the user") - getUserKeysCmd.Flags().StringVarP(&groupName, "name", "N", "", "Name of the group to check users in (if not specified, will default to all groups)") - getAllUserKeysCmd.Flags().StringVarP(&groupName, "name", "N", "", "Name of the group to list users in (if not specified, will default to all groups)") + getUserKeysCmd.Flags().StringP("email", "E", "", "New email address of the user") + getAllUserKeysCmd.Flags().StringP("name", "N", "", "Name of the group to list users in (if not specified, will default to all groups)") } diff --git a/docs/commands/lagoon_delete_user-sshkey.md b/docs/commands/lagoon_delete_user-sshkey.md index cb7184e9..3c08eaaf 100644 --- a/docs/commands/lagoon_delete_user-sshkey.md +++ b/docs/commands/lagoon_delete_user-sshkey.md @@ -13,8 +13,8 @@ lagoon delete user-sshkey [flags] ### Options ``` - -h, --help help for user-sshkey - -N, --keyname string Name of the SSH key + -h, --help help for user-sshkey + --id uint ID of the SSH key ``` ### Options inherited from parent commands diff --git a/docs/commands/lagoon_get_user-sshkeys.md b/docs/commands/lagoon_get_user-sshkeys.md index dd396691..c38f2c3a 100644 --- a/docs/commands/lagoon_get_user-sshkeys.md +++ b/docs/commands/lagoon_get_user-sshkeys.md @@ -15,7 +15,6 @@ lagoon get user-sshkeys [flags] ``` -E, --email string New email address of the user -h, --help help for user-sshkeys - -N, --name string Name of the group to check users in (if not specified, will default to all groups) ``` ### Options inherited from parent commands diff --git a/pkg/lagoon/users/main.go b/pkg/lagoon/users/main.go index 4afa8fb6..39d40039 100644 --- a/pkg/lagoon/users/main.go +++ b/pkg/lagoon/users/main.go @@ -23,10 +23,8 @@ type Client interface { ListUsers(string) ([]byte, error) AddUser(api.User) ([]byte, error) AddSSHKeyToUser(api.User, api.SSHKey) ([]byte, error) - DeleteSSHKey(string) ([]byte, error) DeleteUser(api.User) ([]byte, error) ModifyUser(api.User, api.User) ([]byte, error) - ListUserSSHKeys(string, string, bool) ([]byte, error) ListGroups(string) ([]byte, error) ListGroupProjects(string, bool) ([]byte, error) } diff --git a/pkg/lagoon/users/users.go b/pkg/lagoon/users/users.go index c4c15897..1cf3ea5a 100644 --- a/pkg/lagoon/users/users.go +++ b/pkg/lagoon/users/users.go @@ -61,24 +61,6 @@ func (u *Users) AddSSHKeyToUser(user api.User, sshKey api.SSHKey) ([]byte, error return returnResult, nil } -// DeleteSSHKey function -func (u *Users) DeleteSSHKey(keyName string) ([]byte, error) { - customReq := api.CustomRequest{ - Query: `mutation deleteSshKey ($keyname: String!) { - deleteSshKey(input:{name: $keyname}) - }`, - Variables: map[string]interface{}{ - "keyname": keyName, - }, - MappedResult: "deleteSshKey", - } - returnResult, err := u.api.Request(customReq) - if err != nil { - return []byte(""), err - } - return returnResult, nil -} - // DeleteUser function func (u *Users) DeleteUser(user api.User) ([]byte, error) { customReq := api.CustomRequest{ @@ -212,58 +194,6 @@ func processUserList(listUsers []byte) ([]byte, error) { return json.Marshal(dataMain) } -// ListUserSSHKeys function -func (u *Users) ListUserSSHKeys(groupName string, email string, allUsers bool) ([]byte, error) { - //@TODO: once individual user interaction comes in, this will need to be adjusted - customReq := api.CustomRequest{ - Query: `query allGroups ($name: String) { - allGroups (name: $name) { - name - id - members{ - user{ - id - email - firstName - lastName - sshKeys{ - name - keyType - keyValue - } - } - role - } - } - }`, - Variables: map[string]interface{}{ - "name": groupName, - }, - MappedResult: "allGroups", - } - listUsers, err := u.api.Request(customReq) - if err != nil { - return []byte(""), err - } - returnedKeys, err := processReturnedUserKeysList(listUsers) - if err != nil { - return []byte(""), err - } - var returnResult []byte - if allUsers { - returnResult, err = processAllUserKeysList(returnedKeys) - if err != nil { - return []byte(""), err - } - } else { - returnResult, err = processUserKeysList(returnedKeys, email) - if err != nil { - return []byte(""), err - } - } - return returnResult, nil -} - func processReturnedUserKeysList(listUsers []byte) ([]ExtendedSSHKey, error) { var groupMembers GroupMembers userDataStep1 := []ExtendedSSHKey{} From 472d8ce7022713f03790de70909770121119b28b Mon Sep 17 00:00:00 2001 From: Chris Goodwin <40746380+CGoodwin90@users.noreply.github.com> Date: Wed, 6 Dec 2023 07:20:45 +1100 Subject: [PATCH 4/6] Feature: Organizations support (#292) --- cmd/add.go | 10 + cmd/delete.go | 10 + cmd/deploytarget.go | 126 +++++++- cmd/environment.go | 16 +- cmd/get.go | 63 +++- cmd/groups.go | 68 +++++ cmd/list.go | 273 ++++++++++++++++++ cmd/organization.go | 250 ++++++++++++++++ cmd/project.go | 252 ++++++++++++++++ cmd/shared.go | 32 +- cmd/update.go | 1 + cmd/users.go | 134 +++++++++ docs/commands/lagoon_add.md | 1 + docs/commands/lagoon_add_organization.md | 40 +++ .../lagoon_add_organization_deploytarget.md | 41 +++ .../commands/lagoon_add_organization_group.md | 42 +++ .../lagoon_add_organization_organization.md | 47 +++ .../lagoon_add_organization_project.md | 61 ++++ docs/commands/lagoon_add_organization_user.md | 42 +++ docs/commands/lagoon_delete.md | 1 + docs/commands/lagoon_delete_organization.md | 39 +++ ...lagoon_delete_organization_deploytarget.md | 41 +++ ...lagoon_delete_organization_organization.md | 40 +++ .../lagoon_delete_organization_project.md | 40 +++ .../lagoon_delete_organization_user.md | 42 +++ docs/commands/lagoon_get.md | 1 + docs/commands/lagoon_get_organization.md | 40 +++ docs/commands/lagoon_list.md | 1 + docs/commands/lagoon_list_organization.md | 40 +++ .../lagoon_list_organization_deploytargets.md | 41 +++ .../lagoon_list_organization_groups.md | 40 +++ .../lagoon_list_organization_organizations.md | 39 +++ .../lagoon_list_organization_projects.md | 40 +++ .../lagoon_list_organization_users.md | 40 +++ docs/commands/lagoon_update.md | 1 + docs/commands/lagoon_update_organization.md | 47 +++ 36 files changed, 2021 insertions(+), 21 deletions(-) create mode 100644 cmd/organization.go create mode 100644 docs/commands/lagoon_add_organization.md create mode 100644 docs/commands/lagoon_add_organization_deploytarget.md create mode 100644 docs/commands/lagoon_add_organization_group.md create mode 100644 docs/commands/lagoon_add_organization_organization.md create mode 100644 docs/commands/lagoon_add_organization_project.md create mode 100644 docs/commands/lagoon_add_organization_user.md create mode 100644 docs/commands/lagoon_delete_organization.md create mode 100644 docs/commands/lagoon_delete_organization_deploytarget.md create mode 100644 docs/commands/lagoon_delete_organization_organization.md create mode 100644 docs/commands/lagoon_delete_organization_project.md create mode 100644 docs/commands/lagoon_delete_organization_user.md create mode 100644 docs/commands/lagoon_get_organization.md create mode 100644 docs/commands/lagoon_list_organization.md create mode 100644 docs/commands/lagoon_list_organization_deploytargets.md create mode 100644 docs/commands/lagoon_list_organization_groups.md create mode 100644 docs/commands/lagoon_list_organization_organizations.md create mode 100644 docs/commands/lagoon_list_organization_projects.md create mode 100644 docs/commands/lagoon_list_organization_users.md create mode 100644 docs/commands/lagoon_update_organization.md diff --git a/cmd/add.go b/cmd/add.go index eeb2951e..6ea38fef 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -22,6 +22,15 @@ var addNotificationCmd = &cobra.Command{ }, } +var addOrganizationCmd = &cobra.Command{ + Use: "organization", + Aliases: []string{"o"}, + Short: "Add an organization, or add a group/project to an organization", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + validateToken(lagoonCLIConfig.Current) // get a new token if the current one is invalid + }, +} + func init() { addCmd.AddCommand(addDeployTargetCmd) addCmd.AddCommand(addGroupCmd) @@ -29,6 +38,7 @@ func init() { addCmd.AddCommand(addProjectToGroupCmd) addCmd.AddCommand(addNotificationCmd) addCmd.AddCommand(addUserCmd) + addCmd.AddCommand(addOrganizationCmd) addCmd.AddCommand(addUserToGroupCmd) addCmd.AddCommand(addUserSSHKeyCmd) addCmd.AddCommand(addVariableCmd) diff --git a/cmd/delete.go b/cmd/delete.go index aa6288af..d2e07788 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -22,6 +22,15 @@ var deleteNotificationCmd = &cobra.Command{ }, } +var deleteOrganizationCmd = &cobra.Command{ + Use: "organization", + Aliases: []string{"o"}, + Short: "Add an organization, or add a group/project to an organization", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + validateToken(lagoonCLIConfig.Current) // get a new token if the current one is invalid + }, +} + func init() { deleteCmd.AddCommand(deleteEnvCmd) deleteCmd.AddCommand(deleteGroupCmd) @@ -34,4 +43,5 @@ func init() { deleteCmd.AddCommand(deleteUserFromGroupCmd) deleteCmd.AddCommand(deleteVariableCmd) deleteCmd.AddCommand(deleteDeployTargetConfigCmd) + deleteCmd.AddCommand(deleteOrganizationCmd) } diff --git a/cmd/deploytarget.go b/cmd/deploytarget.go index 59fe8785..e1025b33 100644 --- a/cmd/deploytarget.go +++ b/cmd/deploytarget.go @@ -3,12 +3,15 @@ package cmd import ( "context" "fmt" - "github.com/spf13/cobra" "github.com/uselagoon/lagoon-cli/internal/lagoon" "github.com/uselagoon/lagoon-cli/internal/lagoon/client" "github.com/uselagoon/lagoon-cli/internal/schema" "github.com/uselagoon/lagoon-cli/pkg/output" + l "github.com/uselagoon/machinery/api/lagoon" + lclient "github.com/uselagoon/machinery/api/lagoon/client" + s "github.com/uselagoon/machinery/api/schema" + "strconv" ) var addDeployTargetCmd = &cobra.Command{ @@ -319,6 +322,121 @@ var deleteDeployTargetCmd = &cobra.Command{ }, } +var addDeployTargetToOrganizationCmd = &cobra.Command{ + Use: "deploytarget", + Aliases: []string{"dt"}, + Short: "Add a deploy target to an Organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + handleError(err) + + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + deployTarget, err := cmd.Flags().GetUint("deploy-target") + if err != nil { + return err + } + if err := requiredInputCheck("Deploy Target", strconv.Itoa(int(deployTarget))); err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + organization, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + handleError(err) + + deployTargetInput := s.AddDeployTargetToOrganizationInput{ + DeployTarget: deployTarget, + Organization: organization.ID, + } + + deployTargetResponse, err := l.AddDeployTargetToOrganization(context.TODO(), &deployTargetInput, lc) + handleError(err) + + resultData := output.Result{ + Result: "success", + ResultData: map[string]interface{}{ + "Deploy Target": deployTargetResponse.Name, + "Organization Name": organizationName, + }, + } + output.RenderResult(resultData, outputOptions) + return nil + }, +} + +var RemoveDeployTargetFromOrganizationCmd = &cobra.Command{ + Use: "deploytarget", + Aliases: []string{"dt"}, + Short: "Remove a deploy target from an Organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + handleError(err) + + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + deployTarget, err := cmd.Flags().GetUint("deploy-target") + if err != nil { + return err + } + if err := requiredInputCheck("Deploy Target", strconv.Itoa(int(deployTarget))); err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + organization, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + handleError(err) + + deployTargetInput := s.RemoveDeployTargetFromOrganizationInput{ + DeployTarget: deployTarget, + Organization: organization.ID, + } + + if yesNo(fmt.Sprintf("You are attempting to remove deploy target '%d' from organization '%s', are you sure?", deployTarget, organization.Name)) { + _, err := l.RemoveDeployTargetFromOrganization(context.TODO(), &deployTargetInput, lc) + handleError(err) + resultData := output.Result{ + Result: "success", + ResultData: map[string]interface{}{ + "Deploy Target": deployTarget, + "Organization Name": organizationName, + }, + } + output.RenderResult(resultData, outputOptions) + } + return nil + }, +} + func init() { addDeployTargetCmd.Flags().UintP("id", "", 0, "ID of the DeployTarget") addDeployTargetCmd.Flags().StringP("name", "", "", "Name of DeployTarget") @@ -332,9 +450,15 @@ func init() { addDeployTargetCmd.Flags().StringP("ssh-port", "", "", "DeployTarget ssh port") addDeployTargetCmd.Flags().StringP("build-image", "", "", "DeployTarget build image to use (if different to the default)") + addDeployTargetToOrganizationCmd.Flags().StringP("name", "O", "", "Name of Organization") + addDeployTargetToOrganizationCmd.Flags().UintP("deploy-target", "D", 0, "ID of DeployTarget") + deleteDeployTargetCmd.Flags().UintP("id", "", 0, "ID of the DeployTarget") deleteDeployTargetCmd.Flags().StringP("name", "", "", "Name of DeployTarget") + RemoveDeployTargetFromOrganizationCmd.Flags().StringP("name", "O", "", "Name of Organization") + RemoveDeployTargetFromOrganizationCmd.Flags().UintP("deploy-target", "D", 0, "ID of DeployTarget") + updateDeployTargetCmd.Flags().UintP("id", "", 0, "ID of the DeployTarget") updateDeployTargetCmd.Flags().StringP("console-url", "", "", "DeployTarget console URL") updateDeployTargetCmd.Flags().StringP("token", "", "", "DeployTarget token") diff --git a/cmd/environment.go b/cmd/environment.go index f7dddd4b..75bc481a 100644 --- a/cmd/environment.go +++ b/cmd/environment.go @@ -124,7 +124,7 @@ var updateEnvironmentCmd = &cobra.Command{ Route: nullStrCheck(route), Routes: nullStrCheck(routes), DeployTitle: nullStrCheck(deployTitle), - Openshift: nullIntCheck(openShift), + Openshift: nullUintCheck(openShift), } if environmentAutoIdleProvided { environmentFlags.AutoIdle = &environmentAutoIdle @@ -158,20 +158,6 @@ var updateEnvironmentCmd = &cobra.Command{ }, } -func nullStrCheck(s string) *string { - if s == "" { - return nil - } - return &s -} - -func nullIntCheck(i uint) *uint { - if i == 0 { - return nil - } - return &i -} - func checkFlags(f *pflag.Flag) { if f.Name == "auto-idle" { environmentAutoIdleProvided = true diff --git a/cmd/get.go b/cmd/get.go index 1a603ecd..d5dd10d4 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -4,13 +4,13 @@ import ( "context" "encoding/json" "fmt" + "os" + "strconv" + "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/uselagoon/lagoon-cli/pkg/api" "github.com/uselagoon/lagoon-cli/pkg/output" - "os" - "strconv" - l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" ) @@ -218,10 +218,66 @@ var getToken = &cobra.Command{ }, } +var getOrganizationCmd = &cobra.Command{ + Use: "organization", + Aliases: []string{"o"}, + Short: "Get details about an organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(cmdLagoon) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + organization, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + handleError(err) + + if organization.Name == "" { + output.RenderInfo(fmt.Sprintf("No organization found for '%s'", organizationName), outputOptions) + return nil + } + + data := []output.Data{} + data = append(data, []string{ + strconv.Itoa(int(organization.ID)), + organization.Name, + organization.Description, + strconv.Itoa(int(organization.QuotaProject)), + strconv.Itoa(int(organization.QuotaGroup)), + strconv.Itoa(int(organization.QuotaNotification)), + }) + + dataMain := output.Table{ + Header: []string{"ID", "Name", "Description", "Project Quota", "Group Quota", "Notification Quota"}, + Data: data, + } + + output.RenderOutput(dataMain, outputOptions) + return nil + }, +} + func init() { getCmd.AddCommand(getAllUserKeysCmd) getCmd.AddCommand(getDeploymentCmd) getCmd.AddCommand(getEnvironmentCmd) + getCmd.AddCommand(getOrganizationCmd) getCmd.AddCommand(getProjectCmd) getCmd.AddCommand(getProjectKeyCmd) getCmd.AddCommand(getUserKeysCmd) @@ -231,4 +287,5 @@ func init() { getTaskByID.Flags().BoolP("logs", "L", false, "Show the task logs if available") getProjectKeyCmd.Flags().BoolVarP(&revealValue, "reveal", "", false, "Reveal the variable values") getDeploymentCmd.Flags().StringVarP(&remoteID, "remoteid", "R", "", "The remote ID of the deployment") + getOrganizationCmd.Flags().StringP("name", "O", "", "Name of the organization") } diff --git a/cmd/groups.go b/cmd/groups.go index f3351e6b..1dedda77 100644 --- a/cmd/groups.go +++ b/cmd/groups.go @@ -1,8 +1,12 @@ package cmd import ( + "context" "encoding/json" "fmt" + l "github.com/uselagoon/machinery/api/lagoon" + lclient "github.com/uselagoon/machinery/api/lagoon/client" + s "github.com/uselagoon/machinery/api/schema" "os" "strings" @@ -228,6 +232,67 @@ var deleteGroupCmd = &cobra.Command{ }, } +var addGroupToOrganizationCmd = &cobra.Command{ + Use: "group", + Aliases: []string{"g"}, + Short: "Add a group to an Organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + handleError(err) + orgOwner, err := cmd.Flags().GetBool("org-owner") + if err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + groupName, err := cmd.Flags().GetString("group") + if err != nil { + return err + } + if err := requiredInputCheck("Group name", groupName); err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + organization, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + handleError(err) + + groupInput := s.AddGroupToOrganizationInput{ + Name: groupName, + Organization: organization.ID, + AddOrgOwner: orgOwner, + } + group := s.OrgGroup{} + err = lc.AddGroupToOrganization(context.TODO(), &groupInput, &group) + handleError(err) + + resultData := output.Result{ + Result: "success", + ResultData: map[string]interface{}{ + "Group Name": group.Name, + "Organization Name": organizationName, + }, + } + output.RenderResult(resultData, outputOptions) + return nil + }, +} + func init() { addGroupCmd.Flags().StringVarP(&groupName, "name", "N", "", "Name of the group") addUserToGroupCmd.Flags().StringVarP(&groupName, "name", "N", "", "Name of the group") @@ -238,4 +303,7 @@ func init() { deleteUserFromGroupCmd.Flags().StringVarP(&userEmail, "email", "E", "", "Email address of the user") deleteProjectFromGroupCmd.Flags().StringVarP(&groupName, "name", "N", "", "Name of the group") deleteGroupCmd.Flags().StringVarP(&groupName, "name", "N", "", "Name of the group") + addGroupToOrganizationCmd.Flags().StringP("name", "O", "", "Name of the organization") + addGroupToOrganizationCmd.Flags().StringP("group", "G", "", "Name of the group") + addGroupToOrganizationCmd.Flags().Bool("org-owner", false, "Flag to add the user to the group as an owner") } diff --git a/cmd/list.go b/cmd/list.go index d77f7188..af74ca2f 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -491,6 +491,268 @@ var listProjectGroupsCmd = &cobra.Command{ }, } +var listOrganizationCmd = &cobra.Command{ + Use: "organization", + Aliases: []string{"o"}, + Short: "List all organizations projects, groups, deploy targets or users", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + validateToken(lagoonCLIConfig.Current) + }, +} + +var listOrganizationProjectsCmd = &cobra.Command{ + Use: "projects", + Aliases: []string{"p"}, + Short: "List projects in an organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(cmdLagoon) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + org, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + orgProjects, err := l.ListProjectsByOrganizationID(context.TODO(), org.ID, lc) + handleError(err) + + data := []output.Data{} + for _, project := range *orgProjects { + data = append(data, []string{ + returnNonEmptyString(fmt.Sprintf("%d", project.ID)), + returnNonEmptyString(fmt.Sprintf("%s", project.Name)), + returnNonEmptyString(fmt.Sprintf("%d", project.GroupCount)), + }) + } + dataMain := output.Table{ + Header: []string{"ID", "Name", "Group Count"}, + Data: data, + } + output.RenderOutput(dataMain, outputOptions) + return nil + }, +} + +var listOrganizationGroupsCmd = &cobra.Command{ + Use: "groups", + Aliases: []string{"g"}, + Short: "List groups in an organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(cmdLagoon) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + org, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + orgGroups, err := l.ListGroupsByOrganizationID(context.TODO(), org.ID, lc) + handleError(err) + + data := []output.Data{} + for _, group := range *orgGroups { + data = append(data, []string{ + returnNonEmptyString(fmt.Sprintf("%s", group.ID.String())), + returnNonEmptyString(fmt.Sprintf("%s", group.Name)), + returnNonEmptyString(fmt.Sprintf("%s", group.Type)), + returnNonEmptyString(fmt.Sprintf("%d", group.MemberCount)), + }) + } + dataMain := output.Table{ + Header: []string{"ID", "Name", "Type", "Member Count"}, + Data: data, + } + output.RenderOutput(dataMain, outputOptions) + return nil + }, +} + +var listOrganizationDeployTargetsCmd = &cobra.Command{ + Use: "deploytargets", + Aliases: []string{"d"}, + Short: "List deploy targets in an organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(cmdLagoon) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + organizationID, err := cmd.Flags().GetUint("id") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + if err := requiredInputCheck("Organization ID", strconv.Itoa(int(organizationID))); err != nil { + return err + } + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + deployTargets, err := l.ListDeployTargetsByOrganizationNameOrID(context.TODO(), nullStrCheck(organizationName), nullUintCheck(organizationID), lc) + handleError(err) + + data := []output.Data{} + for _, dt := range *deployTargets { + data = append(data, []string{ + returnNonEmptyString(fmt.Sprintf("%d", dt.ID)), + returnNonEmptyString(fmt.Sprintf("%s", dt.Name)), + returnNonEmptyString(fmt.Sprintf("%s", dt.RouterPattern)), + returnNonEmptyString(fmt.Sprintf("%s", dt.CloudRegion)), + returnNonEmptyString(fmt.Sprintf("%s", dt.CloudProvider)), + returnNonEmptyString(fmt.Sprintf("%s", dt.SSHHost)), + returnNonEmptyString(fmt.Sprintf("%s", dt.SSHPort)), + }) + } + dataMain := output.Table{ + Header: []string{"ID", "Name", "Router Pattern", "Cloud Region", "Cloud Provider", "SSH Host", "SSH Port"}, + Data: data, + } + output.RenderOutput(dataMain, outputOptions) + return nil + }, +} + +var ListOrganizationUsersCmd = &cobra.Command{ + Use: "users", + Aliases: []string{"u"}, + Short: "List users in an organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(cmdLagoon) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + organization, err := l.GetOrganizationByName(context.Background(), organizationName, lc) + handleError(err) + users, err := l.UsersByOrganization(context.TODO(), organization.ID, lc) + handleError(err) + + data := []output.Data{} + for _, user := range *users { + data = append(data, []string{ + returnNonEmptyString(fmt.Sprintf("%s", user.ID)), + returnNonEmptyString(fmt.Sprintf("%s", user.Email)), + returnNonEmptyString(fmt.Sprintf("%s", user.FirstName)), + returnNonEmptyString(fmt.Sprintf("%s", user.LastName)), + returnNonEmptyString(fmt.Sprintf("%s", user.Comment)), + returnNonEmptyString(fmt.Sprintf("%v", user.Owner)), + }) + } + dataMain := output.Table{ + Header: []string{"ID", "Email", "First Name", "LastName", "Comment", "Owner"}, + Data: data, + } + output.RenderOutput(dataMain, outputOptions) + return nil + }, +} + +var listOrganizationsCmd = &cobra.Command{ + Use: "organizations", + Aliases: []string{"o"}, + Short: "List all organizations", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(cmdLagoon) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + organizations, err := l.AllOrganizations(context.TODO(), lc) + + data := []output.Data{} + for _, organization := range *organizations { + data = append(data, []string{ + returnNonEmptyString(fmt.Sprintf("%d", organization.ID)), + returnNonEmptyString(fmt.Sprintf("%s", organization.Name)), + returnNonEmptyString(fmt.Sprintf("%s", organization.Description)), + returnNonEmptyString(fmt.Sprintf("%d", organization.QuotaProject)), + returnNonEmptyString(fmt.Sprintf("%d", organization.QuotaGroup)), + returnNonEmptyString(fmt.Sprintf("%d", organization.QuotaNotification)), + returnNonEmptyString(fmt.Sprintf("%d", organization.QuotaEnvironment)), + returnNonEmptyString(fmt.Sprintf("%d", organization.QuotaRoute)), + }) + } + dataMain := output.Table{ + Header: []string{"ID", "Name", "Description", "Project Quota", "Group Quota", "Notification Quota", "Environment Quota", "Route Quota"}, + Data: data, + } + output.RenderOutput(dataMain, outputOptions) + return nil + }, +} + func init() { listCmd.AddCommand(listDeployTargetsCmd) listCmd.AddCommand(listDeploymentsCmd) @@ -506,8 +768,19 @@ func init() { listCmd.AddCommand(listInvokableTasks) listCmd.AddCommand(listBackupsCmd) listCmd.AddCommand(listDeployTargetConfigsCmd) + listCmd.AddCommand(listOrganizationCmd) + listOrganizationCmd.AddCommand(listOrganizationProjectsCmd) + listOrganizationCmd.AddCommand(ListOrganizationUsersCmd) + listOrganizationCmd.AddCommand(listOrganizationGroupsCmd) + listOrganizationCmd.AddCommand(listOrganizationDeployTargetsCmd) + listOrganizationCmd.AddCommand(listOrganizationsCmd) listCmd.Flags().BoolVarP(&listAllProjects, "all-projects", "", false, "All projects (if supported)") listUsersCmd.Flags().StringVarP(&groupName, "name", "N", "", "Name of the group to list users in (if not specified, will default to all groups)") listGroupProjectsCmd.Flags().StringVarP(&groupName, "name", "N", "", "Name of the group to list projects in") listVariablesCmd.Flags().BoolP("reveal", "", false, "Reveal the variable values") + listOrganizationProjectsCmd.Flags().StringP("name", "O", "", "Name of the organization to list associated projects for") + ListOrganizationUsersCmd.Flags().StringP("name", "O", "", "Name of the organization to list associated users for") + listOrganizationGroupsCmd.Flags().StringP("name", "O", "", "Name of the organization to list associated groups for") + listOrganizationDeployTargetsCmd.Flags().StringP("name", "O", "", "Name of the organization to list associated deploy targets for") + listOrganizationDeployTargetsCmd.Flags().Uint("id", 0, "ID of the organization to list associated deploy targets for") } diff --git a/cmd/organization.go b/cmd/organization.go new file mode 100644 index 00000000..4ca36b13 --- /dev/null +++ b/cmd/organization.go @@ -0,0 +1,250 @@ +package cmd + +import ( + "context" + "fmt" + "github.com/spf13/cobra" + "github.com/uselagoon/lagoon-cli/pkg/output" + l "github.com/uselagoon/machinery/api/lagoon" + lclient "github.com/uselagoon/machinery/api/lagoon/client" + s "github.com/uselagoon/machinery/api/schema" +) + +var addOrgCmd = &cobra.Command{ + Use: "organization", + Aliases: []string{"o"}, + Short: "Add a new organization to Lagoon", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + organizationFriendlyName, err := cmd.Flags().GetString("friendly-name") + if err != nil { + return err + } + organizationDescription, err := cmd.Flags().GetString("description") + if err != nil { + return err + } + organizationQuotaProject, err := cmd.Flags().GetInt("project-quota") + if err != nil { + return err + } + organizationQuotaGroup, err := cmd.Flags().GetInt("group-quota") + if err != nil { + return err + } + organizationQuotaNotification, err := cmd.Flags().GetInt("notification-quota") + if err != nil { + return err + } + organizationQuotaEnvironment, err := cmd.Flags().GetInt("environment-quota") + if err != nil { + return err + } + organizationQuotaRoute, err := cmd.Flags().GetInt("route-quota") + if err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + organizationInput := s.AddOrganizationInput{ + Name: organizationName, + FriendlyName: organizationFriendlyName, + Description: organizationDescription, + QuotaProject: organizationQuotaProject, + QuotaGroup: organizationQuotaGroup, + QuotaNotification: organizationQuotaNotification, + QuotaEnvironment: organizationQuotaEnvironment, + QuotaRoute: organizationQuotaRoute, + } + org := s.Organization{} + err = lc.AddOrganization(context.TODO(), &organizationInput, &org) + handleError(err) + + resultData := output.Result{ + Result: "success", + ResultData: map[string]interface{}{ + "Organization Name": organizationName, + }, + } + output.RenderResult(resultData, outputOptions) + return nil + }, +} + +var deleteOrgCmd = &cobra.Command{ + Use: "organization", + Aliases: []string{"o"}, + Short: "Delete an organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + organization, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + handleError(err) + if yesNo(fmt.Sprintf("You are attempting to delete organization '%s', are you sure?", organization.Name)) { + _, err := l.DeleteOrganization(context.TODO(), organization.ID, lc) + handleError(err) + resultData := output.Result{ + Result: organization.Name, + } + output.RenderResult(resultData, outputOptions) + } + return nil + }, +} + +var updateOrganizationCmd = &cobra.Command{ + Use: "organization", + Aliases: []string{"o"}, + Short: "Update an organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + if err != nil { + return err + } + organizationFriendlyName, err := cmd.Flags().GetString("friendly-name") + if err != nil { + return err + } + organizationDescription, err := cmd.Flags().GetString("description") + if err != nil { + return err + } + organizationQuotaProject, err := cmd.Flags().GetInt("project-quota") + if err != nil { + return err + } + organizationQuotaGroup, err := cmd.Flags().GetInt("group-quota") + if err != nil { + return err + } + organizationQuotaNotification, err := cmd.Flags().GetInt("notification-quota") + if err != nil { + return err + } + organizationQuotaEnvironment, err := cmd.Flags().GetInt("environment-quota") + if err != nil { + return err + } + organizationQuotaRoute, err := cmd.Flags().GetInt("route-quota") + if err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + organization, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + organizationInput := s.UpdateOrganizationPatchInput{ + Description: nullStrCheck(organizationDescription), + FriendlyName: nullStrCheck(organizationFriendlyName), + QuotaProject: nullIntCheck(organizationQuotaProject), + QuotaGroup: nullIntCheck(organizationQuotaGroup), + QuotaNotification: nullIntCheck(organizationQuotaNotification), + QuotaEnvironment: nullIntCheck(organizationQuotaEnvironment), + QuotaRoute: nullIntCheck(organizationQuotaRoute), + } + result, err := l.UpdateOrganization(context.TODO(), organization.ID, organizationInput, lc) + handleError(err) + + resultData := output.Result{ + Result: "success", + ResultData: map[string]interface{}{ + "Organization Name": result.Name, + }, + } + output.RenderResult(resultData, outputOptions) + return nil + }, +} + +func init() { + addOrganizationCmd.AddCommand(addOrgCmd) + addOrganizationCmd.AddCommand(addGroupToOrganizationCmd) + addOrganizationCmd.AddCommand(addProjectToOrganizationCmd) + addOrganizationCmd.AddCommand(addDeployTargetToOrganizationCmd) + addOrganizationCmd.AddCommand(addUserToOrganizationCmd) + + deleteOrganizationCmd.AddCommand(deleteOrgCmd) + deleteOrganizationCmd.AddCommand(RemoveDeployTargetFromOrganizationCmd) + deleteOrganizationCmd.AddCommand(RemoveProjectFromOrganizationCmd) + deleteOrganizationCmd.AddCommand(RemoveUserFromOrganization) + + addOrgCmd.Flags().StringP("name", "O", "", "Name of the organization") + addOrgCmd.Flags().String("friendly-name", "", "Friendly name of the organization") + addOrgCmd.Flags().String("description", "", "Description of the organization") + addOrgCmd.Flags().Int("project-quota", 0, "Project quota for the organization") + addOrgCmd.Flags().Int("group-quota", 0, "Group quota for the organization") + addOrgCmd.Flags().Int("notification-quota", 0, "Notification quota for the organization") + addOrgCmd.Flags().Int("environment-quota", 0, "Environment quota for the organization") + addOrgCmd.Flags().Int("route-quota", 0, "Route quota for the organization") + + updateOrganizationCmd.Flags().StringP("name", "O", "", "Name of the organization to update") + updateOrganizationCmd.Flags().String("friendly-name", "", "Friendly name of the organization") + updateOrganizationCmd.Flags().String("description", "", "Description of the organization") + updateOrganizationCmd.Flags().Int("project-quota", 0, "Project quota for the organization") + updateOrganizationCmd.Flags().Int("group-quota", 0, "Group quota for the organization") + updateOrganizationCmd.Flags().Int("notification-quota", 0, "Notification quota for the organization") + updateOrganizationCmd.Flags().Int("environment-quota", 0, "Environment quota for the organization") + updateOrganizationCmd.Flags().Int("route-quota", 0, "Route quota for the organization") + + deleteOrgCmd.Flags().StringP("name", "O", "", "Name of the organization to delete") +} diff --git a/cmd/project.go b/cmd/project.go index 25863c1e..8a41a5a8 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -6,7 +6,9 @@ import ( "fmt" l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" + s "github.com/uselagoon/machinery/api/schema" "os" + "strconv" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -366,6 +368,229 @@ var deleteProjectMetadataByKey = &cobra.Command{ }, } +var addProjectToOrganizationCmd = &cobra.Command{ + Use: "project", + Aliases: []string{"p"}, + Short: "Add a project to an Organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + handleError(err) + + if err := requiredInputCheck("Project name", cmdProjectName); err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + gitUrl, err := cmd.Flags().GetString("git-url") + if err != nil { + return err + } + if err := requiredInputCheck("gitUrl", gitUrl); err != nil { + return err + } + productionEnvironment, err := cmd.Flags().GetString("production-environment") + if err != nil { + return err + } + if err := requiredInputCheck("Production Environment", productionEnvironment); err != nil { + return err + } + openshift, err := cmd.Flags().GetUint("openshift") + if err != nil { + return err + } + if err := requiredInputCheck("openshift", strconv.Itoa(int(openshift))); err != nil { + return err + } + standbyProductionEnvironment, err := cmd.Flags().GetString("standby-production-environment") + if err != nil { + return err + } + branches, err := cmd.Flags().GetString("branches") + if err != nil { + return err + } + pullrequests, err := cmd.Flags().GetString("pullrequests") + if err != nil { + return err + } + openshiftProjectPattern, err := cmd.Flags().GetString("openshift-project-pattern") + if err != nil { + return err + } + developmentEnvironmentsLimit, err := cmd.Flags().GetUint("development-environments-limit") + if err != nil { + return err + } + storageCalc, err := cmd.Flags().GetUint("storage-calc") + if err != nil { + return err + } + autoIdle, err := cmd.Flags().GetUint("auto-idle") + if err != nil { + return err + } + subfolder, err := cmd.Flags().GetString("subfolder") + if err != nil { + return err + } + privateKey, err := cmd.Flags().GetString("private-key") + if err != nil { + return err + } + orgOwner, err := cmd.Flags().GetBool("org-owner") + if err != nil { + return err + } + buildImage, err := cmd.Flags().GetString("build-image") + if err != nil { + return err + } + availability, err := cmd.Flags().GetString("availability") + if err != nil { + return err + } + factsUi, err := cmd.Flags().GetUint("facts-ui") + if err != nil { + return err + } + problemsUi, err := cmd.Flags().GetUint("problems-ui") + if err != nil { + return err + } + routerPattern, err := cmd.Flags().GetString("router-pattern") + if err != nil { + return err + } + deploymentsDisabled, err := cmd.Flags().GetUint("deployments-disabled") + if err != nil { + return err + } + ProductionBuildPriority, err := cmd.Flags().GetUint("production-build-priority") + if err != nil { + return err + } + DevelopmentBuildPriority, err := cmd.Flags().GetUint("development-build-priority") + if err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + organization, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + handleError(err) + + projectInput := s.AddProjectInput{ + Name: cmdProjectName, + Organization: organization.ID, + AddOrgOwner: orgOwner, + BuildImage: buildImage, + Availability: s.ProjectAvailability(availability), + GitURL: gitUrl, + ProductionEnvironment: productionEnvironment, + StandbyProductionEnvironment: standbyProductionEnvironment, + Branches: branches, + PullRequests: pullrequests, + OpenshiftProjectPattern: openshiftProjectPattern, + Openshift: openshift, + DevelopmentEnvironmentsLimit: developmentEnvironmentsLimit, + StorageCalc: storageCalc, + AutoIdle: autoIdle, + Subfolder: subfolder, + PrivateKey: privateKey, + RouterPattern: routerPattern, + ProblemsUI: problemsUi, + FactsUI: factsUi, + ProductionBuildPriority: ProductionBuildPriority, + DevelopmentBuildPriority: DevelopmentBuildPriority, + DeploymentsDisabled: deploymentsDisabled, + } + project := s.Project{} + err = lc.AddProject(context.TODO(), &projectInput, &project) + handleError(err) + + resultData := output.Result{ + Result: "success", + ResultData: map[string]interface{}{ + "Project Name": project.Name, + "Organization Name": organizationName, + }, + } + output.RenderResult(resultData, outputOptions) + return nil + }, +} + +var RemoveProjectFromOrganizationCmd = &cobra.Command{ + Use: "project", + Aliases: []string{"p"}, + Short: "Remove a project from an Organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + handleError(err) + + if err := requiredInputCheck("Project name", cmdProjectName); err != nil { + return err + } + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + project, err := l.GetMinimalProjectByName(context.TODO(), cmdProjectName, lc) + handleError(err) + organization, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + handleError(err) + + projectInput := s.RemoveProjectFromOrganizationInput{ + Project: project.ID, + Organization: organization.ID, + } + + if yesNo(fmt.Sprintf("You are attempting to remove project '%s' from organization '%s'. This will return the project to a state where it has no groups or notifications associated, are you sure?", cmdProjectName, organization.Name)) { + _, err := l.RemoveProjectFromOrganization(context.TODO(), &projectInput, lc) + handleError(err) + resultData := output.Result{ + Result: "success", + ResultData: map[string]interface{}{ + "Project Name": cmdProjectName, + "Organization Name": organizationName, + }, + } + output.RenderResult(resultData, outputOptions) + } + return nil + }, +} + func init() { updateProjectCmd.Flags().StringVarP(&jsonPatch, "json", "j", "", "JSON string to patch") @@ -420,4 +645,31 @@ func init() { deleteProjectMetadataByKey.Flags().StringP("key", "K", "", "The key name of the metadata value you are querying on") getCmd.AddCommand(getProjectMetadata) + + addProjectToOrganizationCmd.Flags().String("build-image", "", "Build Image for the project") + addProjectToOrganizationCmd.Flags().String("availability", "", "Availability of the project") + addProjectToOrganizationCmd.Flags().String("git-url", "", "GitURL of the project") + addProjectToOrganizationCmd.Flags().String("production-environment", "", "Production Environment for the project") + addProjectToOrganizationCmd.Flags().String("standby-production-environment", "", "Standby Production Environment for the project") + addProjectToOrganizationCmd.Flags().String("subfolder", "", "Set if the .lagoon.yml should be found in a subfolder useful if you have multiple Lagoon projects per Git Repository") + addProjectToOrganizationCmd.Flags().String("private-key", "", "Private key to use for the project") + addProjectToOrganizationCmd.Flags().String("branches", "", "branches") + addProjectToOrganizationCmd.Flags().String("pullrequests", "", "Which Pull Requests should be deployed") + addProjectToOrganizationCmd.Flags().StringP("name", "O", "", "Name of the Organization to add the project to") + addProjectToOrganizationCmd.Flags().String("openshift-project-pattern", "", "Pattern of OpenShift Project/Namespace that should be generated") + addProjectToOrganizationCmd.Flags().String("router-pattern", "", "Router pattern of the project, e.g. '${service}-${environment}-${project}.lagoon.example.com'") + + addProjectToOrganizationCmd.Flags().Uint("openshift", 0, "Reference to OpenShift Object this Project should be deployed to") + addProjectToOrganizationCmd.Flags().Uint("auto-idle", 0, "Auto idle setting of the project") + addProjectToOrganizationCmd.Flags().Uint("storage-calc", 0, "Should storage for this environment be calculated") + addProjectToOrganizationCmd.Flags().Uint("development-environments-limit", 0, "How many environments can be deployed at one time") + addProjectToOrganizationCmd.Flags().Uint("facts-ui", 0, "Enables the Lagoon insights Facts tab in the UI. Set to 1 to enable, 0 to disable") + addProjectToOrganizationCmd.Flags().Uint("problems-ui", 0, "Enables the Lagoon insights Problems tab in the UI. Set to 1 to enable, 0 to disable") + addProjectToOrganizationCmd.Flags().Uint("deployments-disabled", 0, "Admin only flag for disabling deployments on a project, 1 to disable deployments, 0 to enable") + addProjectToOrganizationCmd.Flags().Uint("production-build-priority", 0, "Set the priority of the production build") + addProjectToOrganizationCmd.Flags().Uint("development-build-priority", 0, "Set the priority of the development build") + + addProjectToOrganizationCmd.Flags().Bool("org-owner", false, "Add the user as an owner of the project") + + RemoveProjectFromOrganizationCmd.Flags().StringP("name", "O", "", "Name of the Organization to remove the project from") } diff --git a/cmd/shared.go b/cmd/shared.go index d878be0e..7af150db 100644 --- a/cmd/shared.go +++ b/cmd/shared.go @@ -1,10 +1,10 @@ package cmd import ( + "fmt" + "github.com/uselagoon/lagoon-cli/pkg/output" "os" "strings" - - "github.com/uselagoon/lagoon-cli/pkg/output" ) // config vars @@ -91,3 +91,31 @@ func fileExists(filename string) bool { func stripNewLines(stripString string) string { return strings.TrimSuffix(stripString, "\n") } + +func nullStrCheck(s string) *string { + if s == "" { + return nil + } + return &s +} + +func nullUintCheck(i uint) *uint { + if i == 0 { + return nil + } + return &i +} + +func nullIntCheck(i int) *int { + if i == 0 { + return nil + } + return &i +} + +func requiredInputCheck(field string, value string) error { + if value == "" || value == "0" { + return fmt.Errorf(fmt.Sprintf("Missing argument: %s is not defined", field)) + } + return nil +} diff --git a/cmd/update.go b/cmd/update.go index 68a23ff7..12106614 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -29,4 +29,5 @@ func init() { updateCmd.AddCommand(updateUserCmd) updateCmd.AddCommand(updateDeployTargetConfigCmd) updateCmd.AddCommand(updateDeployTargetCmd) + updateCmd.AddCommand(updateOrganizationCmd) } diff --git a/cmd/users.go b/cmd/users.go index 72b306e2..c493ad5e 100644 --- a/cmd/users.go +++ b/cmd/users.go @@ -361,6 +361,134 @@ var getAllUserKeysCmd = &cobra.Command{ }, } +var addUserToOrganizationCmd = &cobra.Command{ + Use: "user", + Aliases: []string{"u"}, + Short: "Add a user to an Organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + handleError(err) + + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + userEmail, err := cmd.Flags().GetString("email") + if err != nil { + return err + } + if err := requiredInputCheck("User email", userEmail); err != nil { + return err + } + owner, err := cmd.Flags().GetBool("owner") + if err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + organization, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + handleError(err) + + userInput := s.AddUserToOrganizationInput{ + User: s.UserInput{Email: userEmail}, + Organization: organization.ID, + Owner: owner, + } + + orgUser := s.Organization{} + err = lc.AddUserToOrganization(context.TODO(), &userInput, &orgUser) + handleError(err) + + resultData := output.Result{ + Result: "success", + ResultData: map[string]interface{}{ + "User": userEmail, + "Organization Name": organizationName, + }, + } + output.RenderResult(resultData, outputOptions) + return nil + }, +} + +var RemoveUserFromOrganization = &cobra.Command{ + Use: "user", + Aliases: []string{"u"}, + Short: "Remove a user to an Organization", + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + handleError(err) + + organizationName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + if err := requiredInputCheck("Organization name", organizationName); err != nil { + return err + } + userEmail, err := cmd.Flags().GetString("email") + if err != nil { + return err + } + if err := requiredInputCheck("User email", userEmail); err != nil { + return err + } + owner, err := cmd.Flags().GetBool("owner") + if err != nil { + return err + } + + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + + organization, err := l.GetOrganizationByName(context.TODO(), organizationName, lc) + handleError(err) + + userInput := s.AddUserToOrganizationInput{ + User: s.UserInput{Email: userEmail}, + Organization: organization.ID, + Owner: owner, + } + + orgUser := s.Organization{} + + if yesNo(fmt.Sprintf("You are attempting to remove user '%s' from organization '%s'. This removes the users ability to view or manage the organizations groups, projects, & notifications, are you sure?", userEmail, organization.Name)) { + err = lc.RemoveUserFromOrganization(context.TODO(), &userInput, &orgUser) + handleError(err) + resultData := output.Result{ + Result: "success", + ResultData: map[string]interface{}{ + "User": userEmail, + "Organization Name": organizationName, + }, + } + output.RenderResult(resultData, outputOptions) + } + return nil + }, +} + var ( currentUserEmail string pubKeyValue string @@ -382,4 +510,10 @@ func init() { updateUserCmd.Flags().StringVarP(¤tUserEmail, "current-email", "C", "", "Current email address of the user") getUserKeysCmd.Flags().StringP("email", "E", "", "New email address of the user") getAllUserKeysCmd.Flags().StringP("name", "N", "", "Name of the group to list users in (if not specified, will default to all groups)") + addUserToOrganizationCmd.Flags().StringP("name", "O", "", "Name of the organization") + addUserToOrganizationCmd.Flags().StringP("email", "E", "", "Email address of the user") + addUserToOrganizationCmd.Flags().Bool("owner", false, "Set the user as an owner of the organization") + RemoveUserFromOrganization.Flags().StringP("name", "O", "", "Name of the organization") + RemoveUserFromOrganization.Flags().StringP("email", "E", "", "Email address of the user") + RemoveUserFromOrganization.Flags().Bool("owner", false, "Set the user as an owner of the organization") } diff --git a/docs/commands/lagoon_add.md b/docs/commands/lagoon_add.md index e54917c5..5a97546a 100644 --- a/docs/commands/lagoon_add.md +++ b/docs/commands/lagoon_add.md @@ -36,6 +36,7 @@ Add a project, or add notifications and variables to projects or environments * [lagoon add deploytarget-config](lagoon_add_deploytarget-config.md) - Add deploytarget config to a project * [lagoon add group](lagoon_add_group.md) - Add a group to lagoon * [lagoon add notification](lagoon_add_notification.md) - Add notifications or add notifications to projects +* [lagoon add organization](lagoon_add_organization.md) - Add an organization, or add a group/project to an organization * [lagoon add project](lagoon_add_project.md) - Add a new project to Lagoon * [lagoon add project-group](lagoon_add_project-group.md) - Add a project to a group in lagoon * [lagoon add user](lagoon_add_user.md) - Add a user to lagoon diff --git a/docs/commands/lagoon_add_organization.md b/docs/commands/lagoon_add_organization.md new file mode 100644 index 00000000..efcb823e --- /dev/null +++ b/docs/commands/lagoon_add_organization.md @@ -0,0 +1,40 @@ +## lagoon add organization + +Add an organization, or add a group/project to an organization + +### Synopsis + +Add an organization, or add a group/project to an organization + +### Options + +``` + -h, --help help for organization +``` + +### 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 add](lagoon_add.md) - Add a project, or add notifications and variables to projects or environments +* [lagoon add organization deploytarget](lagoon_add_organization_deploytarget.md) - Add a deploy target to an Organization +* [lagoon add organization group](lagoon_add_organization_group.md) - Add a group to an Organization +* [lagoon add organization organization](lagoon_add_organization_organization.md) - Add a new organization to Lagoon +* [lagoon add organization project](lagoon_add_organization_project.md) - Add a project to an Organization +* [lagoon add organization user](lagoon_add_organization_user.md) - Add a user to an Organization + diff --git a/docs/commands/lagoon_add_organization_deploytarget.md b/docs/commands/lagoon_add_organization_deploytarget.md new file mode 100644 index 00000000..d618390f --- /dev/null +++ b/docs/commands/lagoon_add_organization_deploytarget.md @@ -0,0 +1,41 @@ +## lagoon add organization deploytarget + +Add a deploy target to an Organization + +### Synopsis + +Add a deploy target to an Organization + +``` +lagoon add organization deploytarget [flags] +``` + +### Options + +``` + -D, --deploy-target uint ID of DeployTarget + -h, --help help for deploytarget + -O, --name string Name of Organization +``` + +### 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 add organization](lagoon_add_organization.md) - Add an organization, or add a group/project to an organization + diff --git a/docs/commands/lagoon_add_organization_group.md b/docs/commands/lagoon_add_organization_group.md new file mode 100644 index 00000000..2a929f9e --- /dev/null +++ b/docs/commands/lagoon_add_organization_group.md @@ -0,0 +1,42 @@ +## lagoon add organization group + +Add a group to an Organization + +### Synopsis + +Add a group to an Organization + +``` +lagoon add organization group [flags] +``` + +### Options + +``` + -G, --group string Name of the group + -h, --help help for group + -O, --name string Name of the organization + --org-owner Flag to add the user to the group as an owner +``` + +### 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 add organization](lagoon_add_organization.md) - Add an organization, or add a group/project to an organization + diff --git a/docs/commands/lagoon_add_organization_organization.md b/docs/commands/lagoon_add_organization_organization.md new file mode 100644 index 00000000..a1b3af72 --- /dev/null +++ b/docs/commands/lagoon_add_organization_organization.md @@ -0,0 +1,47 @@ +## lagoon add organization organization + +Add a new organization to Lagoon + +### Synopsis + +Add a new organization to Lagoon + +``` +lagoon add organization organization [flags] +``` + +### Options + +``` + --description string Description of the organization + --environment-quota int Environment quota for the organization + --friendly-name string Friendly name of the organization + --group-quota int Group quota for the organization + -h, --help help for organization + -O, --name string Name of the organization + --notification-quota int Notification quota for the organization + --project-quota int Project quota for the organization + --route-quota int Route quota for the organization +``` + +### 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 add organization](lagoon_add_organization.md) - Add an organization, or add a group/project to an organization + diff --git a/docs/commands/lagoon_add_organization_project.md b/docs/commands/lagoon_add_organization_project.md new file mode 100644 index 00000000..0e86ae21 --- /dev/null +++ b/docs/commands/lagoon_add_organization_project.md @@ -0,0 +1,61 @@ +## lagoon add organization project + +Add a project to an Organization + +### Synopsis + +Add a project to an Organization + +``` +lagoon add organization project [flags] +``` + +### Options + +``` + --auto-idle uint Auto idle setting of the project + --availability string Availability of the project + --branches string branches + --build-image string Build Image for the project + --deployments-disabled uint Admin only flag for disabling deployments on a project, 1 to disable deployments, 0 to enable + --development-build-priority uint Set the priority of the development build + --development-environments-limit uint How many environments can be deployed at one time + --facts-ui uint Enables the Lagoon insights Facts tab in the UI. Set to 1 to enable, 0 to disable + --git-url string GitURL of the project + -h, --help help for project + -O, --name string Name of the Organization to add the project to + --openshift uint Reference to OpenShift Object this Project should be deployed to + --openshift-project-pattern string Pattern of OpenShift Project/Namespace that should be generated + --org-owner Add the user as an owner of the project + --private-key string Private key to use for the project + --problems-ui uint Enables the Lagoon insights Problems tab in the UI. Set to 1 to enable, 0 to disable + --production-build-priority uint Set the priority of the production build + --production-environment string Production Environment for the project + --pullrequests string Which Pull Requests should be deployed + --router-pattern string Router pattern of the project, e.g. '${service}-${environment}-${project}.lagoon.example.com' + --standby-production-environment string Standby Production Environment for the project + --storage-calc uint Should storage for this environment be calculated + --subfolder string Set if the .lagoon.yml should be found in a subfolder useful if you have multiple Lagoon projects per Git Repository +``` + +### 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 add organization](lagoon_add_organization.md) - Add an organization, or add a group/project to an organization + diff --git a/docs/commands/lagoon_add_organization_user.md b/docs/commands/lagoon_add_organization_user.md new file mode 100644 index 00000000..411879fb --- /dev/null +++ b/docs/commands/lagoon_add_organization_user.md @@ -0,0 +1,42 @@ +## lagoon add organization user + +Add a user to an Organization + +### Synopsis + +Add a user to an Organization + +``` +lagoon add organization user [flags] +``` + +### Options + +``` + -E, --email string Email address of the user + -h, --help help for user + -O, --name string Name of the organization + --owner Set the user as an owner of the organization +``` + +### 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 add organization](lagoon_add_organization.md) - Add an organization, or add a group/project to an organization + diff --git a/docs/commands/lagoon_delete.md b/docs/commands/lagoon_delete.md index b8b4e8e6..b163a592 100644 --- a/docs/commands/lagoon_delete.md +++ b/docs/commands/lagoon_delete.md @@ -37,6 +37,7 @@ Delete a project, or delete notifications and variables from projects or environ * [lagoon delete environment](lagoon_delete_environment.md) - Delete an environment * [lagoon delete group](lagoon_delete_group.md) - Delete a group from lagoon * [lagoon delete notification](lagoon_delete_notification.md) - Delete notifications or delete notifications from projects +* [lagoon delete organization](lagoon_delete_organization.md) - Add an organization, or add a group/project to an organization * [lagoon delete project](lagoon_delete_project.md) - Delete a project * [lagoon delete project-group](lagoon_delete_project-group.md) - Delete a project from a group in lagoon * [lagoon delete project-metadata](lagoon_delete_project-metadata.md) - Delete a key from a projects metadata diff --git a/docs/commands/lagoon_delete_organization.md b/docs/commands/lagoon_delete_organization.md new file mode 100644 index 00000000..733b9640 --- /dev/null +++ b/docs/commands/lagoon_delete_organization.md @@ -0,0 +1,39 @@ +## lagoon delete organization + +Add an organization, or add a group/project to an organization + +### Synopsis + +Add an organization, or add a group/project to an organization + +### Options + +``` + -h, --help help for organization +``` + +### 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 delete](lagoon_delete.md) - Delete a project, or delete notifications and variables from projects or environments +* [lagoon delete organization deploytarget](lagoon_delete_organization_deploytarget.md) - Remove a deploy target from an Organization +* [lagoon delete organization organization](lagoon_delete_organization_organization.md) - Delete an organization +* [lagoon delete organization project](lagoon_delete_organization_project.md) - Remove a project from an Organization +* [lagoon delete organization user](lagoon_delete_organization_user.md) - Remove a user to an Organization + diff --git a/docs/commands/lagoon_delete_organization_deploytarget.md b/docs/commands/lagoon_delete_organization_deploytarget.md new file mode 100644 index 00000000..173873c6 --- /dev/null +++ b/docs/commands/lagoon_delete_organization_deploytarget.md @@ -0,0 +1,41 @@ +## lagoon delete organization deploytarget + +Remove a deploy target from an Organization + +### Synopsis + +Remove a deploy target from an Organization + +``` +lagoon delete organization deploytarget [flags] +``` + +### Options + +``` + -D, --deploy-target uint ID of DeployTarget + -h, --help help for deploytarget + -O, --name string Name of Organization +``` + +### 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 delete organization](lagoon_delete_organization.md) - Add an organization, or add a group/project to an organization + diff --git a/docs/commands/lagoon_delete_organization_organization.md b/docs/commands/lagoon_delete_organization_organization.md new file mode 100644 index 00000000..2c0cc29f --- /dev/null +++ b/docs/commands/lagoon_delete_organization_organization.md @@ -0,0 +1,40 @@ +## lagoon delete organization organization + +Delete an organization + +### Synopsis + +Delete an organization + +``` +lagoon delete organization organization [flags] +``` + +### Options + +``` + -h, --help help for organization + -O, --name string Name of the organization to delete +``` + +### 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 delete organization](lagoon_delete_organization.md) - Add an organization, or add a group/project to an organization + diff --git a/docs/commands/lagoon_delete_organization_project.md b/docs/commands/lagoon_delete_organization_project.md new file mode 100644 index 00000000..5f814ff1 --- /dev/null +++ b/docs/commands/lagoon_delete_organization_project.md @@ -0,0 +1,40 @@ +## lagoon delete organization project + +Remove a project from an Organization + +### Synopsis + +Remove a project from an Organization + +``` +lagoon delete organization project [flags] +``` + +### Options + +``` + -h, --help help for project + -O, --name string Name of the Organization to remove the project from +``` + +### 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 delete organization](lagoon_delete_organization.md) - Add an organization, or add a group/project to an organization + diff --git a/docs/commands/lagoon_delete_organization_user.md b/docs/commands/lagoon_delete_organization_user.md new file mode 100644 index 00000000..7b542f1d --- /dev/null +++ b/docs/commands/lagoon_delete_organization_user.md @@ -0,0 +1,42 @@ +## lagoon delete organization user + +Remove a user to an Organization + +### Synopsis + +Remove a user to an Organization + +``` +lagoon delete organization user [flags] +``` + +### Options + +``` + -E, --email string Email address of the user + -h, --help help for user + -O, --name string Name of the organization + --owner Set the user as an owner of the organization +``` + +### 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 delete organization](lagoon_delete_organization.md) - Add an organization, or add a group/project to an organization + diff --git a/docs/commands/lagoon_get.md b/docs/commands/lagoon_get.md index f6ceda5b..e86e4800 100644 --- a/docs/commands/lagoon_get.md +++ b/docs/commands/lagoon_get.md @@ -36,6 +36,7 @@ Get info on a resource * [lagoon get backup](lagoon_get_backup.md) - Get a backup download link * [lagoon get deployment](lagoon_get_deployment.md) - Get a build log by remote id * [lagoon get environment](lagoon_get_environment.md) - Get details about an environment +* [lagoon get organization](lagoon_get_organization.md) - Get details about an organization * [lagoon get project](lagoon_get_project.md) - Get details about a project * [lagoon get project-key](lagoon_get_project-key.md) - Get a projects public key * [lagoon get project-metadata](lagoon_get_project-metadata.md) - Get all metadata for a project diff --git a/docs/commands/lagoon_get_organization.md b/docs/commands/lagoon_get_organization.md new file mode 100644 index 00000000..d7e3438f --- /dev/null +++ b/docs/commands/lagoon_get_organization.md @@ -0,0 +1,40 @@ +## lagoon get organization + +Get details about an organization + +### Synopsis + +Get details about an organization + +``` +lagoon get organization [flags] +``` + +### Options + +``` + -h, --help help for organization + -O, --name string Name of the organization +``` + +### 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 get](lagoon_get.md) - Get info on a resource + diff --git a/docs/commands/lagoon_list.md b/docs/commands/lagoon_list.md index 31bdd6ed..774a8467 100644 --- a/docs/commands/lagoon_list.md +++ b/docs/commands/lagoon_list.md @@ -42,6 +42,7 @@ List projects, environments, deployments, variables or notifications * [lagoon list groups](lagoon_list_groups.md) - List groups you have access to (alias: g) * [lagoon list invokable-tasks](lagoon_list_invokable-tasks.md) - Print a list of invokable tasks * [lagoon list notification](lagoon_list_notification.md) - List all notifications or notifications on projects +* [lagoon list organization](lagoon_list_organization.md) - List all organizations projects, groups, deploy targets or users * [lagoon list project-groups](lagoon_list_project-groups.md) - List groups in a project (alias: pg) * [lagoon list projects](lagoon_list_projects.md) - List all projects you have access to (alias: p) * [lagoon list projects-by-metadata](lagoon_list_projects-by-metadata.md) - List projects by a given metadata key or key:value diff --git a/docs/commands/lagoon_list_organization.md b/docs/commands/lagoon_list_organization.md new file mode 100644 index 00000000..0142cc33 --- /dev/null +++ b/docs/commands/lagoon_list_organization.md @@ -0,0 +1,40 @@ +## lagoon list organization + +List all organizations projects, groups, deploy targets or users + +### Synopsis + +List all organizations projects, groups, deploy targets or users + +### Options + +``` + -h, --help help for organization +``` + +### 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 list](lagoon_list.md) - List projects, environments, deployments, variables or notifications +* [lagoon list organization deploytargets](lagoon_list_organization_deploytargets.md) - List deploy targets in an organization +* [lagoon list organization groups](lagoon_list_organization_groups.md) - List groups in an organization +* [lagoon list organization organizations](lagoon_list_organization_organizations.md) - List all organizations +* [lagoon list organization projects](lagoon_list_organization_projects.md) - List projects in an organization +* [lagoon list organization users](lagoon_list_organization_users.md) - List users in an organization + diff --git a/docs/commands/lagoon_list_organization_deploytargets.md b/docs/commands/lagoon_list_organization_deploytargets.md new file mode 100644 index 00000000..80518ab2 --- /dev/null +++ b/docs/commands/lagoon_list_organization_deploytargets.md @@ -0,0 +1,41 @@ +## lagoon list organization deploytargets + +List deploy targets in an organization + +### Synopsis + +List deploy targets in an organization + +``` +lagoon list organization deploytargets [flags] +``` + +### Options + +``` + -h, --help help for deploytargets + --id uint ID of the organization to list associated deploy targets for + -O, --name string Name of the organization to list associated deploy targets for +``` + +### 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 list organization](lagoon_list_organization.md) - List all organizations projects, groups, deploy targets or users + diff --git a/docs/commands/lagoon_list_organization_groups.md b/docs/commands/lagoon_list_organization_groups.md new file mode 100644 index 00000000..af0c6d1e --- /dev/null +++ b/docs/commands/lagoon_list_organization_groups.md @@ -0,0 +1,40 @@ +## lagoon list organization groups + +List groups in an organization + +### Synopsis + +List groups in an organization + +``` +lagoon list organization groups [flags] +``` + +### Options + +``` + -h, --help help for groups + -O, --name string Name of the organization to list associated groups for +``` + +### 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 list organization](lagoon_list_organization.md) - List all organizations projects, groups, deploy targets or users + diff --git a/docs/commands/lagoon_list_organization_organizations.md b/docs/commands/lagoon_list_organization_organizations.md new file mode 100644 index 00000000..09ff1c21 --- /dev/null +++ b/docs/commands/lagoon_list_organization_organizations.md @@ -0,0 +1,39 @@ +## lagoon list organization organizations + +List all organizations + +### Synopsis + +List all organizations + +``` +lagoon list organization organizations [flags] +``` + +### Options + +``` + -h, --help help for organizations +``` + +### 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 list organization](lagoon_list_organization.md) - List all organizations projects, groups, deploy targets or users + diff --git a/docs/commands/lagoon_list_organization_projects.md b/docs/commands/lagoon_list_organization_projects.md new file mode 100644 index 00000000..3127a163 --- /dev/null +++ b/docs/commands/lagoon_list_organization_projects.md @@ -0,0 +1,40 @@ +## lagoon list organization projects + +List projects in an organization + +### Synopsis + +List projects in an organization + +``` +lagoon list organization projects [flags] +``` + +### Options + +``` + -h, --help help for projects + -O, --name string Name of the organization to list associated projects for +``` + +### 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 list organization](lagoon_list_organization.md) - List all organizations projects, groups, deploy targets or users + diff --git a/docs/commands/lagoon_list_organization_users.md b/docs/commands/lagoon_list_organization_users.md new file mode 100644 index 00000000..d3cbfcdf --- /dev/null +++ b/docs/commands/lagoon_list_organization_users.md @@ -0,0 +1,40 @@ +## lagoon list organization users + +List users in an organization + +### Synopsis + +List users in an organization + +``` +lagoon list organization users [flags] +``` + +### Options + +``` + -h, --help help for users + -O, --name string Name of the organization to list associated users for +``` + +### 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 list organization](lagoon_list_organization.md) - List all organizations projects, groups, deploy targets or users + diff --git a/docs/commands/lagoon_update.md b/docs/commands/lagoon_update.md index 0421e622..2fb7cd12 100644 --- a/docs/commands/lagoon_update.md +++ b/docs/commands/lagoon_update.md @@ -36,6 +36,7 @@ Update a resource * [lagoon update deploytarget-config](lagoon_update_deploytarget-config.md) - Update a deploytarget config * [lagoon update environment](lagoon_update_environment.md) - Update an environment * [lagoon update notification](lagoon_update_notification.md) - List all notifications or notifications on projects +* [lagoon update organization](lagoon_update_organization.md) - Update an organization * [lagoon update project](lagoon_update_project.md) - Update a project * [lagoon update project-metadata](lagoon_update_project-metadata.md) - Update a projects metadata with a given key or key:value * [lagoon update user](lagoon_update_user.md) - Update a user in Lagoon diff --git a/docs/commands/lagoon_update_organization.md b/docs/commands/lagoon_update_organization.md new file mode 100644 index 00000000..4c64e34e --- /dev/null +++ b/docs/commands/lagoon_update_organization.md @@ -0,0 +1,47 @@ +## lagoon update organization + +Update an organization + +### Synopsis + +Update an organization + +``` +lagoon update organization [flags] +``` + +### Options + +``` + --description string Description of the organization + --environment-quota int Environment quota for the organization + --friendly-name string Friendly name of the organization + --group-quota int Group quota for the organization + -h, --help help for organization + -O, --name string Name of the organization to update + --notification-quota int Notification quota for the organization + --project-quota int Project quota for the organization + --route-quota int Route quota for the organization +``` + +### 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 update](lagoon_update.md) - Update a resource + From 987c4e82a9dc1c0b96bd7cdc407fb6202d7f0ef7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Dec 2023 07:26:32 +1100 Subject: [PATCH 5/6] feat: task file upload support (#275) --- cmd/root.go | 1 + cmd/tasks.go | 68 ++++++++++++++++++++++- cmd/upload.go | 18 ++++++ docs/commands/lagoon.md | 1 + docs/commands/lagoon_upload.md | 36 ++++++++++++ docs/commands/lagoon_upload_task-files.md | 41 ++++++++++++++ go.mod | 6 +- go.sum | 18 +++--- 8 files changed, 178 insertions(+), 11 deletions(-) create mode 100644 cmd/upload.go create mode 100644 docs/commands/lagoon_upload.md create mode 100644 docs/commands/lagoon_upload_task-files.md diff --git a/cmd/root.go b/cmd/root.go index 78048555..a98b4109 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -194,6 +194,7 @@ Use "{{.CommandPath}} [command] --help" for more information about a command.{{e rootCmd.AddCommand(importCmd) rootCmd.AddCommand(exportCmd) rootCmd.AddCommand(whoamiCmd) + rootCmd.AddCommand(uploadCmd) } // version/build information command diff --git a/cmd/tasks.go b/cmd/tasks.go index a927f91b..4d291c30 100644 --- a/cmd/tasks.go +++ b/cmd/tasks.go @@ -6,13 +6,16 @@ import ( "encoding/json" "errors" "fmt" + "io/ioutil" + "os" + "strings" + "github.com/spf13/cobra" "github.com/uselagoon/lagoon-cli/pkg/api" "github.com/uselagoon/lagoon-cli/pkg/output" + l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" - "io/ioutil" - "os" ) var getTaskByID = &cobra.Command{ @@ -276,6 +279,65 @@ Path: }, } +var uploadFilesToTask = &cobra.Command{ + Use: "task-files", + Short: "Upload files to a task by its ID", + Long: `Upload files to a task by its ID`, + Aliases: []string{"tf"}, + PreRunE: func(_ *cobra.Command, _ []string) error { + return validateTokenE(lagoonCLIConfig.Current) + }, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + + taskID, err := cmd.Flags().GetInt("id") + if err != nil { + return err + } + if taskID == 0 { + return fmt.Errorf("Missing arguments: ID is not defined") + } + files, err := cmd.Flags().GetStringSlice("file") + if err != nil { + return err + } + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + result, err := l.UploadFilesForTask(context.TODO(), taskID, files, lc) + if err != nil { + return err + } + taskFiles := []string{} + for _, f := range result.Files { + taskFiles = append(taskFiles, f.Filename) + } + dataMain := output.Table{ + Header: []string{ + "ID", + "Name", + "Files", + }, + Data: []output.Data{ + { + fmt.Sprintf("%d", result.ID), + returnNonEmptyString(result.Name), + returnNonEmptyString(strings.Join(taskFiles, ",")), + }, + }, + } + output.RenderOutput(dataMain, outputOptions) + return nil + }, +} + var ( taskName string invokedTaskName string @@ -285,6 +347,8 @@ var ( ) func init() { + uploadFilesToTask.Flags().IntP("id", "I", 0, "ID of the task") + uploadFilesToTask.Flags().StringSliceP("file", "F", []string{}, "File to upload (add multiple flags to upload multiple files)") invokeDefinedTask.Flags().StringVarP(&invokedTaskName, "name", "N", "", "Name of the task that will be invoked") 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)") diff --git a/cmd/upload.go b/cmd/upload.go new file mode 100644 index 00000000..33049ae0 --- /dev/null +++ b/cmd/upload.go @@ -0,0 +1,18 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var uploadCmd = &cobra.Command{ + Use: "upload", + Aliases: []string{"u"}, + Short: "Upload files to tasks", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + validateToken(lagoonCLIConfig.Current) // get a new token if the current one is invalid + }, +} + +func init() { + uploadCmd.AddCommand(uploadFilesToTask) +} diff --git a/docs/commands/lagoon.md b/docs/commands/lagoon.md index 7ab9ad66..c1a1cb3f 100644 --- a/docs/commands/lagoon.md +++ b/docs/commands/lagoon.md @@ -45,6 +45,7 @@ lagoon [flags] * [lagoon run](lagoon_run.md) - Run a task against an environment * [lagoon ssh](lagoon_ssh.md) - Display the SSH command to access a specific environment in a project * [lagoon update](lagoon_update.md) - Update a resource +* [lagoon upload](lagoon_upload.md) - Upload files to tasks * [lagoon version](lagoon_version.md) - Version information * [lagoon web](lagoon_web.md) - Launch the web user interface * [lagoon whoami](lagoon_whoami.md) - Whoami will return your user information for lagoon diff --git a/docs/commands/lagoon_upload.md b/docs/commands/lagoon_upload.md new file mode 100644 index 00000000..a2b43843 --- /dev/null +++ b/docs/commands/lagoon_upload.md @@ -0,0 +1,36 @@ +## lagoon upload + +Upload files to tasks + +### Synopsis + +Upload files to tasks + +### Options + +``` + -h, --help help for upload +``` + +### 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](lagoon.md) - Command line integration for Lagoon +* [lagoon upload task-files](lagoon_upload_task-files.md) - Upload files to a task by its ID + diff --git a/docs/commands/lagoon_upload_task-files.md b/docs/commands/lagoon_upload_task-files.md new file mode 100644 index 00000000..453f250f --- /dev/null +++ b/docs/commands/lagoon_upload_task-files.md @@ -0,0 +1,41 @@ +## lagoon upload task-files + +Upload files to a task by its ID + +### Synopsis + +Upload files to a task by its ID + +``` +lagoon upload task-files [flags] +``` + +### Options + +``` + -F, --file strings File to upload (add multiple flags to upload multiple files) + -h, --help help for task-files + -I, --id int ID of the task +``` + +### 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 upload](lagoon_upload.md) - Upload files to tasks + diff --git a/go.mod b/go.mod index 3bcebbdb..386bfb1b 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.3 - github.com/stretchr/testify v1.2.2 + github.com/stretchr/testify v1.8.2 golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b gopkg.in/yaml.v2 v2.2.8 sigs.k8s.io/yaml v1.2.0 @@ -25,7 +25,6 @@ require ( require ( github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect - github.com/go-bindata/go-bindata v3.1.2+incompatible // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/guregu/null v4.0.0+incompatible // workaround for https://github.com/manifoldco/promptui/issues/98 @@ -42,3 +41,6 @@ replace github.com/olekukonko/tablewriter => github.com/shreddedbacon/tablewrite //replace github.com/uselagoon/machinery => ../machinery // replace github.com/olekukonko/tablewriter => ../../shreddedbacon/tablewriter + +// replace github.com/uselagoon/machinery v0.0.8 => ../machinery +replace github.com/uselagoon/machinery v0.0.8 => github.com/uselagoon/machinery v0.0.0-20230518215531-41f1b4bb9b26 diff --git a/go.sum b/go.sum index ce748682..afd7c23b 100644 --- a/go.sum +++ b/go.sum @@ -20,13 +20,12 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE= -github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3 h1:I4BOK3PBMjhWfQM2zPJKK7lOBGsrsvOB7kBELP33hiE= github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= @@ -101,15 +100,17 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tsenart/deadcode v0.0.0-20160724212837-210d2dc333e9 h1:vY5WqiEon0ZSTGM3ayVVi+twaHKHDFUVloaQ/wug9/c= github.com/tsenart/deadcode v0.0.0-20160724212837-210d2dc333e9/go.mod h1:q+QjxYvZ+fpjMXqs+XEriussHjSYqeXVnAdSV1tkMYk= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/uselagoon/machinery v0.0.11 h1:s6EhyU/pj1+C4FdS0EqmR6C0dLsoeCd9n+5xHL1YDag= -github.com/uselagoon/machinery v0.0.11/go.mod h1:IXLxlkahEAEgpCmu9Xa/Wmjo6ja4Aoq7tf8G7VrileE= -github.com/uselagoon/machinery v0.0.12 h1:TJnA+FrL1uEhRTjJ6dExiL4G7SOQ+hUfGuWDmbW2HBA= -github.com/uselagoon/machinery v0.0.12/go.mod h1:h/qeMWQR4Qqu33x+8AulNDeolEwvb/G+aIsn/jyUtwk= github.com/uselagoon/machinery v0.0.13 h1:ZCLBNWJmDr3wikaHs3pWhQ1j8MprhIqRuChgSqmLyZc= github.com/uselagoon/machinery v0.0.13/go.mod h1:h/qeMWQR4Qqu33x+8AulNDeolEwvb/G+aIsn/jyUtwk= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -164,5 +165,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= From d4a8e2efbc2b57e54e553b6697ec9ae6a20ea8f0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Dec 2023 07:52:20 +1100 Subject: [PATCH 6/6] refactor: make get deployment more useful by using build name (#294) --- cmd/get.go | 91 ++++++++++++++++++-------- docs/commands/lagoon_get.md | 2 +- docs/commands/lagoon_get_deployment.md | 10 +-- go.mod | 12 ++-- 4 files changed, 77 insertions(+), 38 deletions(-) diff --git a/cmd/get.go b/cmd/get.go index d5dd10d4..acf45c71 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -9,8 +9,8 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/uselagoon/lagoon-cli/pkg/api" "github.com/uselagoon/lagoon-cli/pkg/output" + l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" ) @@ -123,41 +123,77 @@ var getProjectCmd = &cobra.Command{ }, } -var getDeploymentCmd = &cobra.Command{ +var getDeploymentByNameCmd = &cobra.Command{ Use: "deployment", Aliases: []string{"d"}, - Short: "Get a build log by remote id", - Run: func(cmd *cobra.Command, args []string) { - getProjectFlags := parseGetFlags(*cmd.Flags()) - if getProjectFlags.RemoteID == "" { - fmt.Println("Missing arguments: Remote ID is not defined") - cmd.Help() - os.Exit(1) + Short: "Get a deployment by name", + Long: `Get a deployment by name +This returns information about a deployment, the logs of this build can also be retrieved`, + RunE: func(cmd *cobra.Command, args []string) error { + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err } - returnedJSON, err := eClient.GetDeploymentLog(getProjectFlags.RemoteID) + + buildName, err := cmd.Flags().GetString("name") if err != nil { - output.RenderError(err.Error(), outputOptions) - os.Exit(1) + return err } - if string(returnedJSON) == "null" { - output.RenderInfo(fmt.Sprintf("No deployment for remoteId '%s'", getProjectFlags.RemoteID), outputOptions) - os.Exit(0) + showLogs, err := cmd.Flags().GetBool("logs") + if err != nil { + return err } - var deployment api.Deployment - err = json.Unmarshal([]byte(returnedJSON), &deployment) + current := lagoonCLIConfig.Current + token := lagoonCLIConfig.Lagoons[current].Token + lc := lclient.New( + lagoonCLIConfig.Lagoons[current].GraphQL, + lagoonCLIVersion, + &token, + debug) + deployment, err := l.GetDeploymentByName(context.TODO(), cmdProjectName, cmdProjectEnvironment, buildName, showLogs, lc) if err != nil { - output.RenderError(err.Error(), outputOptions) - os.Exit(1) + return err } - if deployment.BuildLog != "" { - fmt.Println(deployment.BuildLog) - } else { - fmt.Println("Log data is not available") + if showLogs { + dataMain := output.Table{ + Header: []string{ + "Logs", + }, + Data: []output.Data{ + { + returnNonEmptyString(deployment.BuildLog), + }, + }, + } + output.RenderOutput(dataMain, outputOptions) + return nil } - + dataMain := output.Table{ + Header: []string{ + "ID", + "RemoteID", + "Name", + "Status", + "Created", + "Started", + "Completed", + }, + Data: []output.Data{ + { + returnNonEmptyString(fmt.Sprintf("%v", deployment.ID)), + returnNonEmptyString(fmt.Sprintf("%v", deployment.RemoteID)), + returnNonEmptyString(fmt.Sprintf("%v", deployment.Name)), + returnNonEmptyString(fmt.Sprintf("%v", deployment.Status)), + returnNonEmptyString(fmt.Sprintf("%v", deployment.Created)), + returnNonEmptyString(fmt.Sprintf("%v", deployment.Started)), + returnNonEmptyString(fmt.Sprintf("%v", deployment.Completed)), + }, + }, + } + output.RenderOutput(dataMain, outputOptions) + return nil }, } - var getEnvironmentCmd = &cobra.Command{ Use: "environment", Aliases: []string{"e"}, @@ -275,7 +311,6 @@ var getOrganizationCmd = &cobra.Command{ func init() { getCmd.AddCommand(getAllUserKeysCmd) - getCmd.AddCommand(getDeploymentCmd) getCmd.AddCommand(getEnvironmentCmd) getCmd.AddCommand(getOrganizationCmd) getCmd.AddCommand(getProjectCmd) @@ -283,9 +318,11 @@ func init() { getCmd.AddCommand(getUserKeysCmd) getCmd.AddCommand(getTaskByID) getCmd.AddCommand(getToken) + getCmd.AddCommand(getDeploymentByNameCmd) getTaskByID.Flags().IntP("id", "I", 0, "ID of the task") getTaskByID.Flags().BoolP("logs", "L", false, "Show the task logs if available") getProjectKeyCmd.Flags().BoolVarP(&revealValue, "reveal", "", false, "Reveal the variable values") - getDeploymentCmd.Flags().StringVarP(&remoteID, "remoteid", "R", "", "The remote ID of the deployment") + getDeploymentByNameCmd.Flags().StringP("name", "N", "", "The name of the deployment (eg, lagoon-build-abcdef)") + getDeploymentByNameCmd.Flags().BoolP("logs", "L", false, "Show the build logs if available") getOrganizationCmd.Flags().StringP("name", "O", "", "Name of the organization") } diff --git a/docs/commands/lagoon_get.md b/docs/commands/lagoon_get.md index e86e4800..77b6c4e4 100644 --- a/docs/commands/lagoon_get.md +++ b/docs/commands/lagoon_get.md @@ -34,7 +34,7 @@ Get info on a resource * [lagoon](lagoon.md) - Command line integration for Lagoon * [lagoon get all-user-sshkeys](lagoon_get_all-user-sshkeys.md) - Get all user SSH keys * [lagoon get backup](lagoon_get_backup.md) - Get a backup download link -* [lagoon get deployment](lagoon_get_deployment.md) - Get a build log by remote id +* [lagoon get deployment](lagoon_get_deployment.md) - Get a deployment by name * [lagoon get environment](lagoon_get_environment.md) - Get details about an environment * [lagoon get organization](lagoon_get_organization.md) - Get details about an organization * [lagoon get project](lagoon_get_project.md) - Get details about a project diff --git a/docs/commands/lagoon_get_deployment.md b/docs/commands/lagoon_get_deployment.md index e98efa59..5bdbcc47 100644 --- a/docs/commands/lagoon_get_deployment.md +++ b/docs/commands/lagoon_get_deployment.md @@ -1,10 +1,11 @@ ## lagoon get deployment -Get a build log by remote id +Get a deployment by name ### Synopsis -Get a build log by remote id +Get a deployment by name +This returns information about a deployment, the logs of this build can also be retrieved ``` lagoon get deployment [flags] @@ -13,8 +14,9 @@ lagoon get deployment [flags] ### Options ``` - -h, --help help for deployment - -R, --remoteid string The remote ID of the deployment + -h, --help help for deployment + -L, --logs Show the build logs if available + -N, --name string The name of the deployment (eg, lagoon-build-abcdef) ``` ### Options inherited from parent commands diff --git a/go.mod b/go.mod index 386bfb1b..c838de46 100644 --- a/go.mod +++ b/go.mod @@ -4,32 +4,32 @@ go 1.16 require ( github.com/Masterminds/semver v1.4.2 + github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/golang/mock v1.6.0 github.com/google/go-github v0.0.0-20180716180158-c0b63e2f9bb1 + github.com/google/go-querystring v1.0.0 // indirect github.com/google/uuid v1.3.0 + github.com/guregu/null v4.0.0+incompatible github.com/hashicorp/go-version v1.6.0 github.com/integralist/go-findroot v0.0.0-20160518114804-ac90681525dc github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434 github.com/machinebox/graphql v0.2.3-0.20181106130121-3a9253180225 github.com/manifoldco/promptui v0.3.2 + // workaround for https://github.com/manifoldco/promptui/issues/98 + github.com/nicksnyder/go-i18n v1.10.1 // indirect github.com/olekukonko/tablewriter v0.0.4 github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.3 github.com/stretchr/testify v1.8.2 + github.com/uselagoon/machinery v0.0.13 golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b gopkg.in/yaml.v2 v2.2.8 sigs.k8s.io/yaml v1.2.0 ) require ( - github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect - github.com/google/go-querystring v1.0.0 // indirect - github.com/guregu/null v4.0.0+incompatible - // workaround for https://github.com/manifoldco/promptui/issues/98 - github.com/nicksnyder/go-i18n v1.10.1 // indirect - github.com/uselagoon/machinery v0.0.13 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 // indirect golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780 // indirect