From 3308b25506fae68b0aa0d6456a233d8d61ac996c Mon Sep 17 00:00:00 2001 From: Pierre G Date: Tue, 19 Dec 2023 11:02:39 +0100 Subject: [PATCH] feat: add helm redeploy and stop cmd (#221) --- cmd/helm_redeploy.go | 82 ++++++++++++++++++++++++ cmd/helm_stop.go | 148 +++++++++++++++++++++++++++++++++++++++++++ utils/qovery.go | 66 ++++++++++++++++++- 3 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 cmd/helm_redeploy.go create mode 100644 cmd/helm_stop.go diff --git a/cmd/helm_redeploy.go b/cmd/helm_redeploy.go new file mode 100644 index 00000000..05446b56 --- /dev/null +++ b/cmd/helm_redeploy.go @@ -0,0 +1,82 @@ +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/pterm/pterm" + "github.com/qovery/qovery-cli/utils" + "github.com/spf13/cobra" +) + +var helmRedeployCmd = &cobra.Command{ + Use: "redeploy", + Short: "Redeploy a helm", + Run: func(cmd *cobra.Command, args []string) { + utils.Capture(cmd) + + tokenType, token, err := utils.GetAccessToken() + if err != nil { + utils.PrintlnError(err) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + client := utils.GetQoveryClient(tokenType, token) + _, _, envId, err := getOrganizationProjectEnvironmentContextResourcesIds(client) + + if err != nil { + utils.PrintlnError(err) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + helms, _, err := client.HelmsAPI.ListHelms(context.Background(), envId).Execute() + + if err != nil { + utils.PrintlnError(err) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + helm := utils.FindByHelmName(helms.GetResults(), helmName) + + if helm == nil { + utils.PrintlnError(fmt.Errorf("helm %s not found", helmName)) + utils.PrintlnInfo("You can list all helms with: qovery helm list") + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + msg, err := utils.RedeployService(client, envId, helm.Id, utils.HelmType, watchFlag) + + if err != nil { + utils.PrintlnError(err) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + if msg != "" { + utils.PrintlnInfo(msg) + return + } + + if watchFlag { + utils.Println(fmt.Sprintf("Helm %s redeployed!", pterm.FgBlue.Sprintf(helmName))) + } else { + utils.Println(fmt.Sprintf("Redeploying helm %s in progress..", pterm.FgBlue.Sprintf(helmName))) + } + }, +} + +func init() { + helmCmd.AddCommand(helmRedeployCmd) + helmRedeployCmd.Flags().StringVarP(&organizationName, "organization", "", "", "Organization Name") + helmRedeployCmd.Flags().StringVarP(&projectName, "project", "", "", "Project Name") + helmRedeployCmd.Flags().StringVarP(&environmentName, "environment", "", "", "Environment Name") + helmRedeployCmd.Flags().StringVarP(&helmName, "helm", "n", "", "Helm Name") + helmRedeployCmd.Flags().BoolVarP(&watchFlag, "watch", "w", false, "Watch helm status until it's ready or an error occurs") + + _ = helmRedeployCmd.MarkFlagRequired("helm") +} diff --git a/cmd/helm_stop.go b/cmd/helm_stop.go new file mode 100644 index 00000000..4103f01f --- /dev/null +++ b/cmd/helm_stop.go @@ -0,0 +1,148 @@ +package cmd + +import ( + "context" + "fmt" + "github.com/qovery/qovery-client-go" + "os" + "strings" + "time" + + "github.com/pterm/pterm" + "github.com/spf13/cobra" + + "github.com/qovery/qovery-cli/utils" +) + +var helmStopCmd = &cobra.Command{ + Use: "stop", + Short: "Stop a helm", + Run: func(cmd *cobra.Command, args []string) { + utils.Capture(cmd) + + tokenType, token, err := utils.GetAccessToken() + if err != nil { + utils.PrintlnError(err) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + if helmName == "" && helmNames == "" { + utils.PrintlnError(fmt.Errorf("use either --helm \"\" or --helms \", \" but not both at the same time")) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + if helmName != "" && helmNames != "" { + utils.PrintlnError(fmt.Errorf("you can't use --helm and --helms at the same time")) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + client := utils.GetQoveryClient(tokenType, token) + _, _, envId, err := getOrganizationProjectEnvironmentContextResourcesIds(client) + + if err != nil { + utils.PrintlnError(err) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + if helmNames != "" { + // wait until service is ready + for { + if utils.IsEnvironmentInATerminalState(envId, client) { + break + } + + utils.Println(fmt.Sprintf("Waiting for environment %s to be ready..", pterm.FgBlue.Sprintf(envId))) + time.Sleep(5 * time.Second) + } + + helms, _, err := client.HelmsAPI.ListHelms(context.Background(), envId).Execute() + + if err != nil { + utils.PrintlnError(err) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + var serviceIds []string + for _, helmName := range strings.Split(helmNames, ",") { + trimmedHelmName := strings.TrimSpace(helmName) + + helm := utils.FindByHelmName(helms.GetResults(), trimmedHelmName) + if helm == nil { + utils.PrintlnError(fmt.Errorf("helm %s not found", trimmedHelmName)) + utils.PrintlnInfo("You can list all helms with: qovery helm list") + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + serviceIds = append(serviceIds, helm.Id) + } + + // stop multiple services + _, err = utils.StopServices(client, envId, serviceIds, utils.HelmType) + + if watchFlag { + utils.WatchEnvironment(envId, qovery.STATEENUM_STOPPED, client) + } else { + utils.Println(fmt.Sprintf("Stopping helms %s in progress..", pterm.FgBlue.Sprintf(helmNames))) + } + + if err != nil { + utils.PrintlnError(err) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + return + } + + helms, _, err := client.HelmsAPI.ListHelms(context.Background(), envId).Execute() + + if err != nil { + utils.PrintlnError(err) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + helm := utils.FindByHelmName(helms.GetResults(), helmName) + + if helm == nil { + utils.PrintlnError(fmt.Errorf("helm %s not found", helmName)) + utils.PrintlnInfo("You can list all helms with: qovery helm list") + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + msg, err := utils.StopService(client, envId, helm.Id, utils.HelmType, watchFlag) + + if err != nil { + utils.PrintlnError(err) + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + if msg != "" { + utils.PrintlnInfo(msg) + return + } + + if watchFlag { + utils.Println(fmt.Sprintf("Helm %s stopped!", pterm.FgBlue.Sprintf(helmName))) + } else { + utils.Println(fmt.Sprintf("Stopping helm %s in progress..", pterm.FgBlue.Sprintf(helmName))) + } + }, +} + +func init() { + helmCmd.AddCommand(helmStopCmd) + helmStopCmd.Flags().StringVarP(&organizationName, "organization", "", "", "Organization Name") + helmStopCmd.Flags().StringVarP(&projectName, "project", "", "", "Project Name") + helmStopCmd.Flags().StringVarP(&environmentName, "environment", "", "", "Environment Name") + helmStopCmd.Flags().StringVarP(&helmName, "helm", "n", "", "Helm Name") + helmStopCmd.Flags().StringVarP(&helmNames, "helms", "", "", "Helm Names (comma separated) (ex: --helms \"helm1,helm2\")") + helmStopCmd.Flags().BoolVarP(&watchFlag, "watch", "w", false, "Watch helm status until it's ready or an error occurs") +} diff --git a/utils/qovery.go b/utils/qovery.go index 77da5334..10ab3264 100644 --- a/utils/qovery.go +++ b/utils/qovery.go @@ -1058,9 +1058,10 @@ func WatchEnvironmentWithOptions(envId string, finalServiceState qovery.StateEnu countStatuses := countStatus(statuses.Applications, finalServiceState) + countStatus(statuses.Databases, finalServiceState) + countStatus(statuses.Jobs, finalServiceState) + - countStatus(statuses.Containers, finalServiceState) + countStatus(statuses.Containers, finalServiceState) + + countStatus(statuses.Helms, finalServiceState) - totalStatuses := len(statuses.Applications) + len(statuses.Databases) + len(statuses.Jobs) + len(statuses.Containers) + totalStatuses := len(statuses.Applications) + len(statuses.Databases) + len(statuses.Jobs) + len(statuses.Containers) + len(statuses.Helms) icon := "⏳" if countStatuses > 0 { @@ -1196,6 +1197,33 @@ out: WatchEnvironmentWithOptions(envId, "unused", client, true) } +func WatchHelm(helmId string, envId string, client *qovery.APIClient) { +out: + for { + status, _, err := client.HelmMainCallsAPI.GetHelmStatus(context.Background(), helmId).Execute() + + if err != nil { + break + } + + switch WatchStatus(status) { + case Continue: + case Stop: + break out + case Err: + os.Exit(1) + panic("unreachable") // staticcheck false positive: https://staticcheck.io/docs/checks#SA5011 + } + + time.Sleep(3 * time.Second) + } + + log.Println("Check environment status..") + + // check status of environment + WatchEnvironmentWithOptions(envId, "unused", client, true) +} + type Status int8 const ( @@ -2078,6 +2106,21 @@ func StopService(client *qovery.APIClient, envId string, serviceIds string, serv WatchJob(serviceIds, envId, client) } + return "", nil + } + } + case HelmType: + for _, helm := range statuses.GetHelms() { + if helm.Id == serviceIds && IsTerminalState(helm.State) { + _, _, err := client.HelmActionsAPI.StopHelm(context.Background(), serviceIds).Execute() + if err != nil { + return "", err + } + + if watchFlag { + WatchHelm(serviceIds, envId, client) + } + return "", nil } } @@ -2181,6 +2224,25 @@ func StopServices(client *qovery.APIClient, envId string, serviceIds []string, s return "", err } + return "", nil + } + case HelmType: + for _, helm := range statuses.GetHelms() { + if _, ok := serviceIdsSet[helm.Id]; ok && !IsTerminalState(helm.State) { + cannotStop = true + } + } + if !cannotStop { + _, err := client.EnvironmentActionsAPI. + StopSelectedServices(context.Background(), envId). + EnvironmentServiceIdsAllRequest(qovery.EnvironmentServiceIdsAllRequest{ + HelmIds: serviceIds, + }). + Execute() + if err != nil { + return "", err + } + return "", nil } }