From 3afbaada9ebec8775abf5719e48ea4f1b0b55cb6 Mon Sep 17 00:00:00 2001 From: Richard Hagen Date: Thu, 26 Sep 2024 11:10:51 +0200 Subject: [PATCH] Autocomplete flags (#107) * Add Autocomplete to flags * Generic cache * Add Application autocompletion to all commands * Fix Application autocompletion to all commands, Add Component autocomplete * Add Component autocomplete * remove appName pointer to cleanup code * Add autocomplete of Deployments * Add autocomplete of Variables and Secrets * better failsafe * Set n to shorthand for component everywhere * Add Jobs autocompletion * revert "n" as shortcut for component * remove RADIX_ from available variables * use slice.FindAll() instead of slices.Filter() --- Makefile | 4 ++ cmd/createApplication.go | 9 +-- cmd/createApplyConfigPipelineJob.go | 11 ++-- cmd/createBuildDeployPipelineJob.go | 11 ++-- cmd/createDeployPipelineJob.go | 15 +++-- cmd/createEnvironment.go | 10 ++- cmd/createPromotePipelineJob.go | 16 +++-- cmd/deleteApplication.go | 11 ++-- cmd/deleteEnvironment.go | 10 ++- cmd/getApplication.go | 16 +++-- cmd/getBranchEnvironment.go | 7 ++- cmd/getDeployment.go | 18 ++++-- cmd/logsEnvironment.go | 13 ++-- cmd/logsEnvironmentComponent.go | 14 +++-- cmd/logsPipelineJob.go | 11 +++- cmd/restartApplication.go | 10 ++- cmd/restartComponent.go | 12 +++- cmd/restartEnvironment.go | 11 +++- cmd/restartPipelineJob.go | 11 +++- cmd/root.go | 83 +----------------------- cmd/scale.go | 5 ++ cmd/scaleComponent.go | 9 +-- cmd/setEnvironmentSecret.go | 15 +++-- cmd/setEnvironmentVariable.go | 15 +++-- cmd/setExternalDnsTls.go | 23 ++++--- cmd/startApplication.go | 9 ++- cmd/startComponent.go | 13 ++-- cmd/startEnvironment.go | 10 ++- cmd/stopApplication.go | 9 ++- cmd/stopComponent.go | 11 +++- cmd/stopEnvironment.go | 11 +++- cmd/version.go | 34 ++++++++++ pkg/client/auth/token_cache.go | 4 +- pkg/config/radix_config.go | 59 ++++++++++++++++- pkg/config/utils.go | 87 ++++++++++++++++++++++++++ pkg/utils/completion/alias.go | 55 ++++++++++++++++ pkg/utils/completion/application.go | 44 +++++++++++++ pkg/utils/completion/component.go | 47 ++++++++++++++ pkg/utils/completion/config-context.go | 16 +++++ pkg/utils/completion/deployments.go | 71 +++++++++++++++++++++ pkg/utils/completion/environment.go | 41 ++++++++++++ pkg/utils/completion/job.go | 41 ++++++++++++ pkg/utils/completion/secrets.go | 49 +++++++++++++++ pkg/utils/completion/variable.go | 52 +++++++++++++++ 44 files changed, 843 insertions(+), 190 deletions(-) create mode 100644 cmd/version.go create mode 100644 pkg/config/utils.go create mode 100644 pkg/utils/completion/alias.go create mode 100644 pkg/utils/completion/application.go create mode 100644 pkg/utils/completion/component.go create mode 100644 pkg/utils/completion/config-context.go create mode 100644 pkg/utils/completion/deployments.go create mode 100644 pkg/utils/completion/environment.go create mode 100644 pkg/utils/completion/job.go create mode 100644 pkg/utils/completion/secrets.go create mode 100644 pkg/utils/completion/variable.go diff --git a/Makefile b/Makefile index a63b2a7..6de7d3b 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,10 @@ push: lint: bootstrap golangci-lint run --max-same-issues 0 +install: + go build ./cli/rx/ + mv rx $$(go env GOPATH)/bin/rx + HAS_SWAGGER := $(shell command -v swagger;) HAS_GOLANGCI_LINT := $(shell command -v golangci-lint;) HAS_GORELEASER := $(shell command -v goreleaser;) diff --git a/cmd/createApplication.go b/cmd/createApplication.go index db705c3..6a69f02 100644 --- a/cmd/createApplication.go +++ b/cmd/createApplication.go @@ -24,6 +24,7 @@ import ( "github.com/equinor/radix-cli/generated-client/client/platform" "github.com/equinor/radix-cli/generated-client/models" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" "github.com/spf13/cobra" ) @@ -35,7 +36,7 @@ var createApplicationCmd = &cobra.Command{ Long: "Creates a Radix application in the cluster", Example: `rx create application --application your-application-name --repository https://github.com/your-repository --config-branch main --ad-groups abcdef-1234-5678-9aaa-abcdefgf --reader-ad-groups=23456789--9123-4567-8901-23456701 --shared-secret someSecretPhrase12345 --acknowledge-warnings --configuration-item "YOUR PROJECT CONFIG ITEM" --context playground`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } @@ -51,7 +52,7 @@ var createApplicationCmd = &cobra.Command{ return err } - if appName == nil || *appName == "" || repository == "" || configBranch == "" || configurationItem == "" { + if appName == "" || repository == "" || configBranch == "" || configurationItem == "" { return errors.New("application name, repository, configuration item and config branch are required fields") } @@ -67,7 +68,7 @@ var createApplicationCmd = &cobra.Command{ AdGroups: adGroups, ConfigBranch: &configBranch, ConfigurationItem: configurationItem, - Name: appName, + Name: &appName, RadixConfigFullName: configFile, ReaderAdGroups: readerAdGroups, Repository: &repository, @@ -98,7 +99,7 @@ var createApplicationCmd = &cobra.Command{ } deployKeyAndSecretParams := application.NewGetDeployKeyAndSecretParams() - deployKeyAndSecretParams.SetAppName(*appName) + deployKeyAndSecretParams.SetAppName(appName) getRadixRegistrationNoAccessErrorCount := 3 getRadixRegistrationNoAccessErrorPause := 2 * time.Second for { diff --git a/cmd/createApplyConfigPipelineJob.go b/cmd/createApplyConfigPipelineJob.go index b0dc914..2f6af91 100644 --- a/cmd/createApplyConfigPipelineJob.go +++ b/cmd/createApplyConfigPipelineJob.go @@ -20,7 +20,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/application" "github.com/equinor/radix-cli/generated-client/models" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -37,7 +39,7 @@ Currently applied changes in properties DNS alias, build secrets, create new or rx create job apply-config -a radix-test`, RunE: func(cmd *cobra.Command, args []string) error { var errs []error - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { errs = append(errs, err) } @@ -52,7 +54,7 @@ Currently applied changes in properties DNS alias, build secrets, create new or if len(errs) > 0 { return errors.Join(errs...) } - if appName == nil || *appName == "" { + if appName == "" { return errors.New("application name is required") } @@ -64,7 +66,7 @@ Currently applied changes in properties DNS alias, build secrets, create new or } triggerPipelineParams := application.NewTriggerPipelineApplyConfigParams() - triggerPipelineParams.SetAppName(*appName) + triggerPipelineParams.SetAppName(appName) parametersApplyConfig := models.PipelineParametersApplyConfig{ TriggeredBy: triggeredByUser, } @@ -80,7 +82,7 @@ Currently applied changes in properties DNS alias, build secrets, create new or if !follow { return nil } - return getLogsJob(cmd, apiClient, *appName, jobName) + return getLogsJob(cmd, apiClient, appName, jobName) }, } @@ -89,5 +91,6 @@ func init() { createApplyConfigPipelineJobCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application to apply-config") createApplyConfigPipelineJobCmd.Flags().StringP(flagnames.User, "u", "", "The user who triggered the apply-config") createApplyConfigPipelineJobCmd.Flags().BoolP(flagnames.Follow, "f", false, "Follow applyConfig") + _ = createApplyConfigPipelineJobCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) setContextSpecificPersistentFlags(createApplyConfigPipelineJobCmd) } diff --git a/cmd/createBuildDeployPipelineJob.go b/cmd/createBuildDeployPipelineJob.go index c01ac4e..e1e7b3c 100644 --- a/cmd/createBuildDeployPipelineJob.go +++ b/cmd/createBuildDeployPipelineJob.go @@ -17,7 +17,9 @@ package cmd import ( "errors" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/model" + "github.com/equinor/radix-cli/pkg/utils/completion" log "github.com/sirupsen/logrus" "github.com/equinor/radix-cli/generated-client/client/application" @@ -35,7 +37,7 @@ var createBuildDeployApplicationCmd = &cobra.Command{ Short: "Will trigger build-deploy of a Radix application", Long: `Triggers build-deploy of Radix application, if branch to environment map exists for the branch in the Radix config`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } @@ -44,7 +46,7 @@ var createBuildDeployApplicationCmd = &cobra.Command{ commitID, _ := cmd.Flags().GetString(flagnames.CommitID) follow, _ := cmd.Flags().GetBool(flagnames.Follow) - if appName == nil || *appName == "" || branch == "" { + if appName == "" || branch == "" { return errors.New("application name and branch are required") } cmd.SilenceUsage = true @@ -55,7 +57,7 @@ var createBuildDeployApplicationCmd = &cobra.Command{ } triggerPipelineParams := application.NewTriggerPipelineBuildDeployParams() - triggerPipelineParams.SetAppName(*appName) + triggerPipelineParams.SetAppName(appName) triggerPipelineParams.SetPipelineParametersBuild(&models.PipelineParametersBuild{ Branch: branch, CommitID: commitID, @@ -72,7 +74,7 @@ var createBuildDeployApplicationCmd = &cobra.Command{ if !follow { return nil } - return getLogsJob(cmd, apiClient, *appName, jobName) + return getLogsJob(cmd, apiClient, appName, jobName) }, } @@ -84,5 +86,6 @@ func init() { createBuildDeployApplicationCmd.Flags().BoolP(flagnames.Follow, "f", false, "Follow build-deploy") createBuildDeployApplicationCmd.Flags().Var(&overrideUseBuildCache, flagnames.UseBuildCache, "Optional. Overrides configured or default useBuildCache option. It is applicable when the useBuildKit option is set as true.") + _ = createBuildDeployApplicationCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) setContextSpecificPersistentFlags(createBuildDeployApplicationCmd) } diff --git a/cmd/createDeployPipelineJob.go b/cmd/createDeployPipelineJob.go index 4e07f48..d0bd50b 100644 --- a/cmd/createDeployPipelineJob.go +++ b/cmd/createDeployPipelineJob.go @@ -19,6 +19,8 @@ import ( "fmt" "regexp" + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-cli/pkg/utils/completion" log "github.com/sirupsen/logrus" "github.com/equinor/radix-cli/generated-client/client/application" @@ -48,7 +50,7 @@ var createDeployPipelineJobCmd = &cobra.Command{ rx create job deploy -a radix-test -e dev --component web-app --component api-server`, RunE: func(cmd *cobra.Command, args []string) error { var errs []error - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { errs = append(errs, err) } @@ -71,7 +73,7 @@ var createDeployPipelineJobCmd = &cobra.Command{ if len(errs) > 0 { return errors.Join(errs...) } - if appName == nil || *appName == "" || targetEnvironment == "" { + if appName == "" || targetEnvironment == "" { return errors.New("application name and target environment are required") } commitID, _ := cmd.Flags().GetString(flagnames.CommitID) @@ -92,7 +94,7 @@ var createDeployPipelineJobCmd = &cobra.Command{ } triggerPipelineParams := application.NewTriggerPipelineDeployParams() - triggerPipelineParams.SetAppName(*appName) + triggerPipelineParams.SetAppName(appName) parametersDeploy := models.PipelineParametersDeploy{ ToEnvironment: targetEnvironment, ImageTagNames: imageTagNames, @@ -112,7 +114,7 @@ var createDeployPipelineJobCmd = &cobra.Command{ if !follow { return nil } - return getLogsJob(cmd, apiClient, *appName, jobName) + return getLogsJob(cmd, apiClient, appName, jobName) }, } @@ -137,7 +139,10 @@ func init() { createDeployPipelineJobCmd.Flags().StringP(flagnames.User, "u", "", "The user who triggered the deploy") createDeployPipelineJobCmd.Flags().StringToStringP(flagnames.ImageTagName, "t", map[string]string{}, "Image tag name for a component: component-name=tag-name. Multiple pairs can be specified.") createDeployPipelineJobCmd.Flags().StringP(flagnames.CommitID, "i", "", "An optional 40 character commit id to tag the new pipeline job") - createDeployPipelineJobCmd.Flags().StringSlice(flagnames.Component, []string{}, "Optional component to deploy, when only specific component need to be deployed. Multiple components can be specified.") + createDeployPipelineJobCmd.Flags().StringSliceP(flagnames.Component, "n", []string{}, "Optional component to deploy, when only specific component need to be deployed. Multiple components can be specified.") createDeployPipelineJobCmd.Flags().BoolP(flagnames.Follow, "f", false, "Follow deploy") + _ = createDeployPipelineJobCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = createDeployPipelineJobCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = createDeployPipelineJobCmd.RegisterFlagCompletionFunc(flagnames.Component, completion.ComponentCompletion) setContextSpecificPersistentFlags(createDeployPipelineJobCmd) } diff --git a/cmd/createEnvironment.go b/cmd/createEnvironment.go index 5c723be..3ecb911 100644 --- a/cmd/createEnvironment.go +++ b/cmd/createEnvironment.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/environment" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -29,21 +31,21 @@ var createEnvironmentCmd = &cobra.Command{ Short: "Create environment", Long: `Creates a Radix environment for the application`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } envName, err := cmd.Flags().GetString(flagnames.Environment) - if err != nil || appName == nil || *appName == "" { + if err != nil || appName == "" { return errors.New("environment name and application name are required fields") } cmd.SilenceUsage = true parameters := environment.NewCreateEnvironmentParams(). - WithAppName(*appName). + WithAppName(appName). WithEnvName(envName) apiClient, err := client.GetForCommand(cmd) @@ -60,5 +62,7 @@ func init() { createCmd.AddCommand(createEnvironmentCmd) createEnvironmentCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application namespace") createEnvironmentCmd.Flags().StringP(flagnames.Environment, "e", "", "Name of the environment to create") + _ = createEnvironmentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = createEnvironmentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) setContextSpecificPersistentFlags(createEnvironmentCmd) } diff --git a/cmd/createPromotePipelineJob.go b/cmd/createPromotePipelineJob.go index a758b12..756fd90 100644 --- a/cmd/createPromotePipelineJob.go +++ b/cmd/createPromotePipelineJob.go @@ -20,6 +20,8 @@ import ( apiclient "github.com/equinor/radix-cli/generated-client/client" "github.com/equinor/radix-cli/generated-client/client/environment" + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-cli/pkg/utils/completion" log "github.com/sirupsen/logrus" @@ -36,7 +38,7 @@ var createPromotePipelineJobCmd = &cobra.Command{ Short: "Will trigger promote of a Radix application", Long: `Triggers promote of a Radix application deployment`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } @@ -55,7 +57,7 @@ var createPromotePipelineJobCmd = &cobra.Command{ return errors.New("you cannot set use-active-deployment and specify deployment name at the same time") } - if appName == nil || *appName == "" || fromEnvironment == "" || toEnvironment == "" { + if appName == "" || fromEnvironment == "" || toEnvironment == "" { return errors.New("application name, from and to environments are required") } @@ -67,7 +69,7 @@ var createPromotePipelineJobCmd = &cobra.Command{ } if useActiveDeployment { - d, err := getActiveDeploymentName(apiClient, *appName, fromEnvironment) + d, err := getActiveDeploymentName(apiClient, appName, fromEnvironment) if err != nil { return err } @@ -76,7 +78,7 @@ var createPromotePipelineJobCmd = &cobra.Command{ } triggerPipelineParams := application.NewTriggerPipelinePromoteParams() - triggerPipelineParams.SetAppName(*appName) + triggerPipelineParams.SetAppName(appName) triggerPipelineParams.SetPipelineParametersPromote(&models.PipelineParametersPromote{ DeploymentName: deploymentName, FromEnvironment: fromEnvironment, @@ -95,7 +97,7 @@ var createPromotePipelineJobCmd = &cobra.Command{ return nil } - return getLogsJob(cmd, apiClient, *appName, jobName) + return getLogsJob(cmd, apiClient, appName, jobName) }, } @@ -125,5 +127,9 @@ func init() { createPromotePipelineJobCmd.Flags().StringP(flagnames.User, "u", "", "The user who triggered the promote pipeline job") createPromotePipelineJobCmd.Flags().BoolP(flagnames.Follow, "f", false, "Follow the promote pipeline job log") createPromotePipelineJobCmd.Flags().BoolP(flagnames.UseActiveDeployment, "", false, "Promote the active deployment") + _ = createPromotePipelineJobCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = createPromotePipelineJobCmd.RegisterFlagCompletionFunc(flagnames.FromEnvironment, completion.EnvironmentCompletion) + _ = createPromotePipelineJobCmd.RegisterFlagCompletionFunc(flagnames.ToEnvironment, completion.EnvironmentCompletion) + _ = createPromotePipelineJobCmd.RegisterFlagCompletionFunc(flagnames.Deployment, completion.CreateDeploymentCompletion(flagnames.FromEnvironment, true)) setContextSpecificPersistentFlags(createPromotePipelineJobCmd) } diff --git a/cmd/deleteApplication.go b/cmd/deleteApplication.go index 4a78646..1178a8f 100644 --- a/cmd/deleteApplication.go +++ b/cmd/deleteApplication.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/application" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -29,19 +31,19 @@ var deleteApplicationCmd = &cobra.Command{ Short: "Delete application", Long: `Will delete an application from the cluster`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if appName == nil || *appName == "" { + if appName == "" { return errors.New("application name is a required field") } cmd.SilenceUsage = true deleteApplicationParams := application.NewDeleteApplicationParams() - deleteApplicationParams.SetAppName(*appName) + deleteApplicationParams.SetAppName(appName) apiClient, err := client.GetForCommand(cmd) if err != nil { @@ -55,6 +57,7 @@ var deleteApplicationCmd = &cobra.Command{ func init() { deleteCmd.AddCommand(deleteApplicationCmd) - deleteApplicationCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application to create") + deleteApplicationCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application to delete") + _ = deleteApplicationCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) setContextSpecificPersistentFlags(deleteApplicationCmd) } diff --git a/cmd/deleteEnvironment.go b/cmd/deleteEnvironment.go index 7f2cd8e..e727b0d 100644 --- a/cmd/deleteEnvironment.go +++ b/cmd/deleteEnvironment.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/environment" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -29,21 +31,21 @@ var deleteEnvironmentCmd = &cobra.Command{ Short: "delete environment", Long: `deletes an orphaned Radix environment`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } envName, err := cmd.Flags().GetString(flagnames.Environment) - if err != nil || appName == nil || *appName == "" { + if err != nil || appName == "" { return errors.New("environment name and application name are required fields") } cmd.SilenceUsage = true parameters := environment.NewDeleteEnvironmentParams(). - WithAppName(*appName). + WithAppName(appName). WithEnvName(envName) apiClient, err := client.GetForCommand(cmd) @@ -60,5 +62,7 @@ func init() { deleteCmd.AddCommand(deleteEnvironmentCmd) deleteEnvironmentCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application") deleteEnvironmentCmd.Flags().StringP(flagnames.Environment, "e", "", "Name of the environment to delete") + _ = deleteEnvironmentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = deleteEnvironmentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) setContextSpecificPersistentFlags(deleteEnvironmentCmd) } diff --git a/cmd/getApplication.go b/cmd/getApplication.go index c132e9a..859506d 100644 --- a/cmd/getApplication.go +++ b/cmd/getApplication.go @@ -16,12 +16,13 @@ package cmd import ( "fmt" - "strings" "github.com/equinor/radix-cli/generated-client/client/application" "github.com/equinor/radix-cli/generated-client/client/platform" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/equinor/radix-cli/pkg/utils/json" "github.com/spf13/cobra" ) @@ -32,7 +33,7 @@ var getApplicationCmd = &cobra.Command{ Short: "Gets Radix application", Long: `Gets a list of Radix applications or a single application if provided`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } @@ -44,21 +45,27 @@ var getApplicationCmd = &cobra.Command{ return err } - if appName == nil || strings.EqualFold(*appName, "") { + if appName == "" { // List applications showApplicationParams := platform.NewShowApplicationsParams() resp, err := apiClient.Platform.ShowApplications(showApplicationParams, nil) + var appNames []string + if err == nil { for _, application := range resp.Payload { fmt.Println(application.Name) + appNames = append(appNames, application.Name) } + completion.UpdateAppNamesCache(appNames) + return nil } + return err } getApplicationParams := application.NewGetApplicationParams() - getApplicationParams.SetAppName(*appName) + getApplicationParams.SetAppName(appName) resp, err := apiClient.Application.GetApplication(getApplicationParams, nil) if err != nil { return err @@ -75,5 +82,6 @@ var getApplicationCmd = &cobra.Command{ func init() { getCmd.AddCommand(getApplicationCmd) getApplicationCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application") + _ = getApplicationCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) setContextSpecificPersistentFlags(getApplicationCmd) } diff --git a/cmd/getBranchEnvironment.go b/cmd/getBranchEnvironment.go index 2f1bc80..b93787c 100644 --- a/cmd/getBranchEnvironment.go +++ b/cmd/getBranchEnvironment.go @@ -18,6 +18,7 @@ import ( "errors" "fmt" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" "github.com/spf13/cobra" ) @@ -33,7 +34,7 @@ var getBranchEnvironmentCmd = &cobra.Command{ return errors.New("config can only come from radixconfig file in current folder") } - _, err := getRadixApplicationFromFile() + _, err := config.GetRadixApplicationFromFile() if err != nil { return err } @@ -44,12 +45,12 @@ var getBranchEnvironmentCmd = &cobra.Command{ return errors.New("`branch` is required") } - environment, err := getEnvironmentFromConfig(cmd, branch) + environment, err := config.GetEnvironmentFromConfig(cmd, branch) if err != nil { return err } - fmt.Print(*environment) + fmt.Print(environment) return nil }, } diff --git a/cmd/getDeployment.go b/cmd/getDeployment.go index 7947ddb..9435c6b 100644 --- a/cmd/getDeployment.go +++ b/cmd/getDeployment.go @@ -21,7 +21,9 @@ import ( apiclient "github.com/equinor/radix-cli/generated-client/client" "github.com/equinor/radix-cli/generated-client/client/application" "github.com/equinor/radix-cli/generated-client/client/environment" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/equinor/radix-cli/pkg/utils/json" "github.com/equinor/radix-cli/generated-client/client/deployment" @@ -46,11 +48,11 @@ Examples: rx get deployment --application radix-test --environment test `, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if appName == nil || *appName == "" { + if appName == "" { return errors.New("application name is required field") } @@ -74,12 +76,12 @@ Examples: } if deploymentName == "" && envName == "" { - return getDeploymentForAllEnvironments(apiClient, *appName) + return getDeploymentForAllEnvironments(apiClient, appName) } if deploymentName != "" { - return getDeployment(apiClient, *appName, deploymentName) + return getDeployment(apiClient, appName, deploymentName) } - return getDeploymentForEnvironment(apiClient, *appName, envName) + return getDeploymentForEnvironment(apiClient, appName, envName) }, } @@ -137,5 +139,11 @@ func init() { getDeploymentCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application") getDeploymentCmd.Flags().StringP(flagnames.Deployment, "d", "", "Optional, name of a deployment. It cannot be used together with an option 'environment'.") getDeploymentCmd.Flags().StringP(flagnames.Environment, "e", "", "Optional, name of the environment. It cannot be used together with an option 'deployment'.") + getDeploymentCmd.MarkFlagsOneRequired(flagnames.Environment, flagnames.Deployment) + getDeploymentCmd.MarkFlagsMutuallyExclusive(flagnames.Environment, flagnames.Deployment) + + _ = getDeploymentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = getDeploymentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = getDeploymentCmd.RegisterFlagCompletionFunc(flagnames.Deployment, completion.CreateDeploymentCompletion(flagnames.Environment, false)) setContextSpecificPersistentFlags(getDeploymentCmd) } diff --git a/cmd/logsEnvironment.go b/cmd/logsEnvironment.go index d0e2f88..0b1fdef 100644 --- a/cmd/logsEnvironment.go +++ b/cmd/logsEnvironment.go @@ -21,8 +21,10 @@ import ( "github.com/equinor/radix-cli/generated-client/client/environment" "github.com/equinor/radix-cli/generated-client/models" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" "github.com/equinor/radix-cli/pkg/settings" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/equinor/radix-common/utils/slice" "github.com/spf13/cobra" ) @@ -38,12 +40,12 @@ It may take few seconds to get the log. Example: `# Get logs for all components in an environment. Log lines from different components have different colors rx get logs environment --application radix-test --environment dev`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if appName == nil || *appName == "" { + if appName == "" { return errors.New("application name is required") } @@ -62,12 +64,12 @@ rx get logs environment --application radix-test --environment dev`, return err } - componentReplicas, err := getComponentReplicasForEnvironment(apiClient, *appName, environmentName) + componentReplicas, err := getComponentReplicasForEnvironment(apiClient, appName, environmentName) if err != nil { return err } - return logForComponentReplicas(cmd, apiClient, *appName, environmentName, since, componentReplicas, previousLog) + return logForComponentReplicas(cmd, apiClient, appName, environmentName, since, componentReplicas, previousLog) }, } @@ -104,5 +106,8 @@ func init() { logsEnvironmentCmd.Flags().StringP(flagnames.Environment, "e", "", "Environment the component runs in") logsEnvironmentCmd.Flags().BoolP(flagnames.Previous, "p", false, "If set, print the logs for the previous instances of containers in environment component pods, if they exist") logsEnvironmentCmd.Flags().DurationP(flagnames.Since, "s", settings.DeltaRefreshApplication, "If set, start get logs from the specified time, eg. 5m or 12h") + + _ = logsEnvironmentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = logsEnvironmentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) setContextSpecificPersistentFlags(logsEnvironmentCmd) } diff --git a/cmd/logsEnvironmentComponent.go b/cmd/logsEnvironmentComponent.go index 0028f85..09e8d58 100644 --- a/cmd/logsEnvironmentComponent.go +++ b/cmd/logsEnvironmentComponent.go @@ -25,8 +25,10 @@ import ( "github.com/equinor/radix-cli/generated-client/client/environment" "github.com/equinor/radix-cli/generated-client/models" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" "github.com/equinor/radix-cli/pkg/settings" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/equinor/radix-cli/pkg/utils/log" "github.com/equinor/radix-common/utils/slice" "github.com/go-openapi/strfmt" @@ -52,12 +54,12 @@ Examples: rx get logs component -a radix-test -e dev --component web-app -p `, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if appName == nil || *appName == "" { + if appName == "" { return errors.New("application name is required") } @@ -77,7 +79,7 @@ Examples: return err } - _, replicas, err := getReplicasForComponent(apiClient, *appName, environmentName, componentName) + _, replicas, err := getReplicasForComponent(apiClient, appName, environmentName, componentName) if err != nil { return err } @@ -85,7 +87,7 @@ Examples: componentReplicas := make(map[string][]string) componentReplicas[componentName] = replicas - return logForComponentReplicas(cmd, apiClient, *appName, environmentName, since, componentReplicas, previousLog) + return logForComponentReplicas(cmd, apiClient, appName, environmentName, since, componentReplicas, previousLog) }, } @@ -180,5 +182,9 @@ func init() { logsEnvironmentComponentCmd.Flags().String(flagnames.Component, "", "The component to follow") logsEnvironmentComponentCmd.Flags().BoolP(flagnames.Previous, "p", false, "If set, print the logs for the previous instance of the container in a component pod, if it exists") logsEnvironmentComponentCmd.Flags().DurationP(flagnames.Since, "s", settings.DeltaRefreshApplication, "If set, start get logs from the specified time, eg. 5m or 12h") + + _ = logsEnvironmentComponentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = logsEnvironmentComponentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = logsEnvironmentComponentCmd.RegisterFlagCompletionFunc(flagnames.Component, completion.ComponentCompletion) setContextSpecificPersistentFlags(logsEnvironmentComponentCmd) } diff --git a/cmd/logsPipelineJob.go b/cmd/logsPipelineJob.go index f5f84ed..fbdfba8 100644 --- a/cmd/logsPipelineJob.go +++ b/cmd/logsPipelineJob.go @@ -24,8 +24,10 @@ import ( apiclient "github.com/equinor/radix-cli/generated-client/client" "github.com/equinor/radix-cli/generated-client/client/pipeline_job" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" "github.com/equinor/radix-cli/pkg/settings" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/equinor/radix-cli/pkg/utils/log" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -54,12 +56,12 @@ It may take few seconds to get the log.`, rx get logs pipeline-job --application radix-test --job radix-pipeline-20230323185013-ehvnz`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if appName == nil || *appName == "" { + if appName == "" { return errors.New("application name is required") } @@ -76,7 +78,7 @@ rx get logs pipeline-job --application radix-test --job radix-pipeline-202303231 return err } - return getLogsJob(cmd, apiClient, *appName, jobName) + return getLogsJob(cmd, apiClient, appName, jobName) }, } @@ -181,5 +183,8 @@ func init() { logsJobCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application for the job") logsJobCmd.Flags().StringP(flagnames.Job, "j", "", "The job to get logs for") + + _ = logsJobCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = logsJobCmd.RegisterFlagCompletionFunc(flagnames.Job, completion.JobCompletion) setContextSpecificPersistentFlags(logsJobCmd) } diff --git a/cmd/restartApplication.go b/cmd/restartApplication.go index 73edc5d..4007a0a 100644 --- a/cmd/restartApplication.go +++ b/cmd/restartApplication.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/application" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -31,19 +33,19 @@ var restartApplicationCmd = &cobra.Command{ - Starts the application's containers, using up to date images - Stops the application's old containers`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if err != nil || appName == nil || *appName == "" { + if err != nil || appName == "" { return errors.New("application name is required fields") } cmd.SilenceUsage = true parameters := application.NewRestartApplicationParams(). - WithAppName(*appName) + WithAppName(appName) apiClient, err := client.GetForCommand(cmd) if err != nil { @@ -58,5 +60,7 @@ var restartApplicationCmd = &cobra.Command{ func init() { restartCmd.AddCommand(restartApplicationCmd) restartApplicationCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application namespace") + + _ = restartApplicationCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) setContextSpecificPersistentFlags(restartApplicationCmd) } diff --git a/cmd/restartComponent.go b/cmd/restartComponent.go index dd69108..a7d16b4 100644 --- a/cmd/restartComponent.go +++ b/cmd/restartComponent.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/component" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -31,14 +33,14 @@ var restartComponentCmd = &cobra.Command{ - Starts the component's container, using up to date image - Stops the application component's old containers`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } envName, err := cmd.Flags().GetString(flagnames.Environment) - if err != nil || appName == nil || *appName == "" { + if err != nil || appName == "" { return errors.New("environment name and application name are required fields") } @@ -50,7 +52,7 @@ var restartComponentCmd = &cobra.Command{ cmd.SilenceUsage = true parameters := component.NewRestartComponentParams(). - WithAppName(*appName). + WithAppName(appName). WithEnvName(envName). WithComponentName(cmpName) @@ -69,5 +71,9 @@ func init() { restartComponentCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application namespace") restartComponentCmd.Flags().StringP(flagnames.Environment, "e", "", "Name of the environment of the application") restartComponentCmd.Flags().StringP(flagnames.Component, "n", "", "Name of the component to restart") + + _ = restartComponentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = restartComponentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = restartComponentCmd.RegisterFlagCompletionFunc(flagnames.Component, completion.ComponentCompletion) setContextSpecificPersistentFlags(restartComponentCmd) } diff --git a/cmd/restartEnvironment.go b/cmd/restartEnvironment.go index 808c11d..0365eac 100644 --- a/cmd/restartEnvironment.go +++ b/cmd/restartEnvironment.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/environment" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -31,21 +33,21 @@ var restartEnvironmentCmd = &cobra.Command{ - Starts the environment's containers, using up to date images - Stops the application environment's old containers`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } envName, err := cmd.Flags().GetString(flagnames.Environment) - if err != nil || appName == nil || *appName == "" || envName == "" { + if err != nil || appName == "" || envName == "" { return errors.New("environment name and application name are required fields") } cmd.SilenceUsage = true parameters := environment.NewRestartEnvironmentParams(). - WithAppName(*appName). + WithAppName(appName). WithEnvName(envName) apiClient, err := client.GetForCommand(cmd) @@ -62,5 +64,8 @@ func init() { restartCmd.AddCommand(restartEnvironmentCmd) restartEnvironmentCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application namespace") restartEnvironmentCmd.Flags().StringP(flagnames.Environment, "e", "", "Name of the environment of the application") + + _ = restartComponentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = restartComponentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) setContextSpecificPersistentFlags(restartEnvironmentCmd) } diff --git a/cmd/restartPipelineJob.go b/cmd/restartPipelineJob.go index 240f357..5b6eb11 100644 --- a/cmd/restartPipelineJob.go +++ b/cmd/restartPipelineJob.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/pipeline_job" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -32,12 +34,12 @@ var restartPipelineJobCmd = &cobra.Command{ Example: `rx restart pipeline-job --application radix-test --job radix-pipeline-20230323185013-ehvnz`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if appName == nil || *appName == "" { + if appName == "" { return errors.New("application name is required") } @@ -55,7 +57,7 @@ var restartPipelineJobCmd = &cobra.Command{ } params := pipeline_job.NewRerunApplicationJobParams() - params.AppName = *appName + params.AppName = appName params.JobName = jobName _, err = apiClient.PipelineJob.RerunApplicationJob(params, nil) @@ -67,5 +69,8 @@ func init() { restartCmd.AddCommand(restartPipelineJobCmd) restartPipelineJobCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application for the job") restartPipelineJobCmd.Flags().StringP(flagnames.Job, "j", "", "The job to restart") + + _ = restartPipelineJobCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = restartPipelineJobCmd.RegisterFlagCompletionFunc(flagnames.Job, completion.JobCompletion) setContextSpecificPersistentFlags(restartPipelineJobCmd) } diff --git a/cmd/root.go b/cmd/root.go index 4e01cca..b50d090 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,10 +1,8 @@ package cmd import ( - "errors" "fmt" "os" - "path/filepath" "runtime/debug" "strings" "time" @@ -13,8 +11,7 @@ import ( radixconfig "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" "github.com/equinor/radix-cli/pkg/settings" - v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" - "github.com/equinor/radix-operator/pkg/apis/utils" + "github.com/equinor/radix-cli/pkg/utils/completion" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -63,8 +60,8 @@ func setContextSpecificPersistentFlags(cmd *cobra.Command) { } func setContextPersistentFlags(cmd *cobra.Command) { - cmd.PersistentFlags().StringP(flagnames.Context, "c", "", fmt.Sprintf("Use context %s|%s|%s|%s regardless of current context (default production) ", - radixconfig.ContextPlatform, radixconfig.ContextPlatform2, radixconfig.ContextPlayground, radixconfig.ContextDevelopment)) + cmd.PersistentFlags().StringP(flagnames.Context, "c", "", fmt.Sprintf("Use context %s regardless of current context (default production) ", strings.Join(radixconfig.ValidContexts, "|"))) + _ = cmd.RegisterFlagCompletionFunc(flagnames.Context, completion.ConfigContext) } // CheckFnNew The prototype function for any check function @@ -90,77 +87,3 @@ func awaitReconciliation(checkFunc checkFn) bool { } } } - -func getAppNameFromConfigOrFromParameter(cmd *cobra.Command, appNameField string) (*string, error) { - var appName string - var err error - - fromConfig, _ := cmd.Flags().GetBool(flagnames.FromConfig) - if fromConfig { - radicConfig, err := getRadixApplicationFromFile() - if err != nil { - return nil, err - } - - appName = radicConfig.GetName() - } else { - appName, err = cmd.Flags().GetString(appNameField) - if err != nil { - return nil, err - } - } - - return &appName, nil -} - -func getEnvironmentFromConfig(cmd *cobra.Command, branchName string) (*string, error) { - var err error - - fromConfig, _ := cmd.Flags().GetBool(flagnames.FromConfig) - if !fromConfig { - return nil, errors.New("--from-config is required when getting environment from branch") - } - - cmd.SilenceUsage = true - - radicConfig, err := getRadixApplicationFromFile() - if err != nil { - return nil, err - } - - for _, environment := range radicConfig.Spec.Environments { - if environment.Build.From != "" && environment.Build.From == branchName { - return &environment.Name, nil - } - } - - return nil, fmt.Errorf("no environment found which maps to branch name `%s`", branchName) -} - -func getRadixApplicationFromFile() (*v1.RadixApplication, error) { - filePath, _ := filepath.Abs("./radixconfig.yaml") - return loadConfigFromFile(filePath) -} - -// LoadConfigFromFile loads radix config from appFileName -func loadConfigFromFile(appFileName string) (*v1.RadixApplication, error) { - radixApplication, err := utils.GetRadixApplicationFromFile(appFileName) - if err != nil { - return nil, fmt.Errorf("failed to get ra from file (%s) for app Error: %v", appFileName, err) - } - - return radixApplication, nil -} - -func getStringFromFlagValueOrFlagFile(cmd *cobra.Command, valueFlag, fileNameFlag string) (string, error) { - fileName, err := cmd.Flags().GetString(fileNameFlag) - if err != nil { - return "", err - } - if len(fileName) > 0 { - fileContent, err := os.ReadFile(fileName) - return string(fileContent), err - } - - return cmd.Flags().GetString(valueFlag) -} diff --git a/cmd/scale.go b/cmd/scale.go index a9e1876..43bcaa7 100644 --- a/cmd/scale.go +++ b/cmd/scale.go @@ -16,6 +16,7 @@ package cmd import ( "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -39,5 +40,9 @@ func init() { scaleCmd.PersistentFlags().Bool(flagnames.Reset, false, "Reset manualy scaled component to use replica count from RadixConfig or managed by horizontal autoscaling") scaleCmd.MarkFlagsOneRequired(flagnames.Replicas, flagnames.Reset) scaleCmd.MarkFlagsMutuallyExclusive(flagnames.Replicas, flagnames.Reset) + + _ = scaleCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = scaleCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = scaleCmd.RegisterFlagCompletionFunc(flagnames.Component, completion.ComponentCompletion) setContextSpecificPersistentFlags(scaleComponentCmd) } diff --git a/cmd/scaleComponent.go b/cmd/scaleComponent.go index fcc9d8d..904ca8f 100644 --- a/cmd/scaleComponent.go +++ b/cmd/scaleComponent.go @@ -21,6 +21,7 @@ import ( apiclient "github.com/equinor/radix-cli/generated-client/client" "github.com/equinor/radix-cli/generated-client/client/component" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -44,7 +45,7 @@ rx scale component -a radix-test -e dev -n component-abc -r 2 rx scale component --application radix-test --environment dev --component component-abc --reset `, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } @@ -64,7 +65,7 @@ rx scale component --application radix-test --environment dev --component compon if err != nil { return err } - if appName == nil || *appName == "" || envName == "" || cmpName == "" { + if appName == "" || envName == "" || cmpName == "" { return errors.New("application name, environment name and component name are required fields") } if !reset && (replicas < 0 || replicas > 20) { @@ -79,9 +80,9 @@ rx scale component --application radix-test --environment dev --component compon cmd.SilenceUsage = true if reset { - return resetScaledComponent(apiClient, *appName, envName, cmpName) + return resetScaledComponent(apiClient, appName, envName, cmpName) } - return scaleComponent(apiClient, *appName, envName, cmpName, strconv.Itoa(replicas)) + return scaleComponent(apiClient, appName, envName, cmpName, strconv.Itoa(replicas)) }, } diff --git a/cmd/setEnvironmentSecret.go b/cmd/setEnvironmentSecret.go index f04f142..0761d3e 100644 --- a/cmd/setEnvironmentSecret.go +++ b/cmd/setEnvironmentSecret.go @@ -23,7 +23,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/environment" "github.com/equinor/radix-cli/generated-client/models" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -33,12 +35,12 @@ var setEnvironmentSecretCmd = &cobra.Command{ Short: "Will set an environment secret", Long: `Will set an environment secret`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if appName == nil || *appName == "" { + if appName == "" { return errors.New("application name is required") } @@ -75,7 +77,7 @@ var setEnvironmentSecretCmd = &cobra.Command{ if awaitReconcile { reconciledOk := awaitReconciliation(func() bool { - return isComponentSecretReconciled(apiClient, *appName, environmentName, component, secretName) + return isComponentSecretReconciled(apiClient, appName, environmentName, component, secretName) }) if !reconciledOk { @@ -88,7 +90,7 @@ var setEnvironmentSecretCmd = &cobra.Command{ componentSecret.SecretValue = &secretValue changeComponentSecretParameters := environment.NewChangeComponentSecretParams() - changeComponentSecretParameters.SetAppName(*appName) + changeComponentSecretParameters.SetAppName(appName) changeComponentSecretParameters.SetEnvName(environmentName) changeComponentSecretParameters.SetComponentName(component) changeComponentSecretParameters.SetSecretName(secretName) @@ -136,5 +138,10 @@ func init() { setEnvironmentSecretCmd.Flags().StringP(flagnames.Secret, "s", "", "Name of the secret to set") setEnvironmentSecretCmd.Flags().StringP(flagnames.Value, "v", "", "Value of the secret to set") setEnvironmentSecretCmd.Flags().Bool(flagnames.AwaitReconcile, true, "Await reconciliation in Radix. Default is true") + + _ = setEnvironmentSecretCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = setEnvironmentSecretCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = setEnvironmentSecretCmd.RegisterFlagCompletionFunc(flagnames.Component, completion.ComponentCompletion) + _ = setEnvironmentSecretCmd.RegisterFlagCompletionFunc(flagnames.Secret, completion.SecretCompletion) setContextSpecificPersistentFlags(setEnvironmentSecretCmd) } diff --git a/cmd/setEnvironmentVariable.go b/cmd/setEnvironmentVariable.go index f164ac9..1a74105 100644 --- a/cmd/setEnvironmentVariable.go +++ b/cmd/setEnvironmentVariable.go @@ -24,7 +24,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/environment" "github.com/equinor/radix-cli/generated-client/models" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -35,12 +37,12 @@ var setEnvironmentVariableCmd = &cobra.Command{ Long: "Will set an environment variable", Example: `rx set environment-variable --application your-application-name --environment test --component component-abc --variable LOG_LEVEL --value INFO`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if appName == nil || *appName == "" { + if appName == "" { return errors.New("application name is required") } @@ -77,7 +79,7 @@ var setEnvironmentVariableCmd = &cobra.Command{ if awaitReconcile { reconciledOk := awaitReconciliation(func() bool { - return isComponentVariableReconciled(apiClient, *appName, environmentName, componentName, variableName) + return isComponentVariableReconciled(apiClient, appName, environmentName, componentName, variableName) }) if !reconciledOk { @@ -91,7 +93,7 @@ var setEnvironmentVariableCmd = &cobra.Command{ componentVariable.Value = &variableValue changeComponentVariableParameters := component.NewChangeEnvVarParams() - changeComponentVariableParameters.SetAppName(*appName) + changeComponentVariableParameters.SetAppName(appName) changeComponentVariableParameters.SetEnvName(environmentName) changeComponentVariableParameters.SetComponentName(componentName) changeComponentVariableParameters.SetEnvVarParameter([]*models.EnvVarParameter{&componentVariable}) @@ -138,5 +140,10 @@ func init() { setEnvironmentVariableCmd.Flags().StringP(flagnames.Variable, "", "", "Name of the variable to set") setEnvironmentVariableCmd.Flags().StringP(flagnames.Value, "v", "", "Value of the variable to set") setEnvironmentVariableCmd.Flags().Bool(flagnames.AwaitReconcile, true, "Await reconciliation in Radix. Default is true") + + _ = setEnvironmentVariableCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = setEnvironmentVariableCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = setEnvironmentVariableCmd.RegisterFlagCompletionFunc(flagnames.Component, completion.ComponentCompletion) + _ = setEnvironmentVariableCmd.RegisterFlagCompletionFunc(flagnames.Variable, completion.VariableCompletion) setContextSpecificPersistentFlags(setEnvironmentVariableCmd) } diff --git a/cmd/setExternalDnsTls.go b/cmd/setExternalDnsTls.go index eaa895e..7f16c87 100644 --- a/cmd/setExternalDnsTls.go +++ b/cmd/setExternalDnsTls.go @@ -24,7 +24,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/environment" "github.com/equinor/radix-cli/generated-client/models" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -36,11 +38,14 @@ var setExternalDnsTlsCmd = &cobra.Command{ Example: `# Read certificate and private key from file rx set external-dns-tls --application myapp --environment prod --component web --alias myapp.example.com --certificate-from-file "cert.crt" --private-key-from-file "cert.key" `, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + + completion.AliasCompletion(cmd, []string{}, "") + + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if appName == nil || *appName == "" { + if appName == "" { return errors.New("application name is required") } @@ -59,7 +64,7 @@ rx set external-dns-tls --application myapp --environment prod --component web - return errors.New("`alias` is required") } - certificate, err := getStringFromFlagValueOrFlagFile(cmd, flagnames.Certificate, flagnames.CertificateFromFile) + certificate, err := config.GetStringFromFlagValueOrFlagFile(cmd, flagnames.Certificate, flagnames.CertificateFromFile) if err != nil { return err } @@ -67,7 +72,7 @@ rx set external-dns-tls --application myapp --environment prod --component web - return errors.New("certificate value cannot be empty") } - privateKey, err := getStringFromFlagValueOrFlagFile(cmd, flagnames.PrivateKey, flagnames.PrivateKeyFromFile) + privateKey, err := config.GetStringFromFlagValueOrFlagFile(cmd, flagnames.PrivateKey, flagnames.PrivateKeyFromFile) if err != nil { return err } @@ -86,7 +91,7 @@ rx set external-dns-tls --application myapp --environment prod --component web - if awaitReconcile { reconciledOk := awaitReconciliation(func() bool { - return isComponentExternalDNSReconciled(apiClient, *appName, environmentName, componentName, fqdn) + return isComponentExternalDNSReconciled(apiClient, appName, environmentName, componentName, fqdn) }) if !reconciledOk { @@ -95,7 +100,7 @@ rx set external-dns-tls --application myapp --environment prod --component web - } } updateExternalDNSTLS := component.NewUpdateComponentExternalDNSTLSParams(). - WithAppName(*appName). + WithAppName(appName). WithEnvName(environmentName). WithComponentName(componentName). WithFqdn(fqdn). @@ -155,7 +160,11 @@ func init() { setExternalDnsTlsCmd.MarkFlagsOneRequired(flagnames.PrivateKey, flagnames.PrivateKeyFromFile) setExternalDnsTlsCmd.MarkFlagsMutuallyExclusive(flagnames.PrivateKey, flagnames.PrivateKeyFromFile) - setContextSpecificPersistentFlags(setExternalDnsTlsCmd) + _ = setExternalDnsTlsCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = setExternalDnsTlsCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = setExternalDnsTlsCmd.RegisterFlagCompletionFunc(flagnames.Component, completion.ComponentCompletion) + _ = setExternalDnsTlsCmd.RegisterFlagCompletionFunc(flagnames.Alias, completion.AliasCompletion) + setContextSpecificPersistentFlags(setExternalDnsTlsCmd) setCmd.AddCommand(setExternalDnsTlsCmd) } diff --git a/cmd/startApplication.go b/cmd/startApplication.go index 92d7974..b8e14f2 100644 --- a/cmd/startApplication.go +++ b/cmd/startApplication.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/application" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -31,19 +33,19 @@ var startApplicationCmd = &cobra.Command{ - Pulls new images from image hub in radix configuration - Starts the application containers using up to date images`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if err != nil || appName == nil || *appName == "" { + if err != nil || appName == "" { return errors.New("application name is required fields") } cmd.SilenceUsage = true parameters := application.NewStartApplicationParams(). - WithAppName(*appName) + WithAppName(appName) apiClient, err := client.GetForCommand(cmd) if err != nil { @@ -58,5 +60,6 @@ var startApplicationCmd = &cobra.Command{ func init() { startCmd.AddCommand(startApplicationCmd) startApplicationCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application namespace") + _ = startApplicationCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) setContextSpecificPersistentFlags(startApplicationCmd) } diff --git a/cmd/startComponent.go b/cmd/startComponent.go index 4de66a6..1a85ac1 100644 --- a/cmd/startComponent.go +++ b/cmd/startComponent.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/component" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -35,14 +37,14 @@ Deprecated: Use 'rx scale component --reset' instead Resets a manully scaled component to resume normal operations again.`, Deprecated: " Use 'rx scale component --reset' instead. Will be removed after September 2025", RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } envName, err := cmd.Flags().GetString(flagnames.Environment) - if err != nil || appName == nil || *appName == "" || envName == "" { + if err != nil || appName == "" || envName == "" { return errors.New("environment name and application name are required fields") } @@ -54,7 +56,7 @@ Resets a manully scaled component to resume normal operations again.`, cmd.SilenceUsage = true parameters := component.NewStartComponentParams(). - WithAppName(*appName). + WithAppName(appName). WithEnvName(envName). WithComponentName(cmpName) @@ -77,6 +79,9 @@ func init() { startCmd.AddCommand(startComponentCmd) startComponentCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application namespace") startComponentCmd.Flags().StringP(flagnames.Environment, "e", "", "Name of the environment of the application") - startComponentCmd.Flags().StringP(flagnames.Component, "n", "", "Name of the component to start") + startComponentCmd.Flags().String(flagnames.Component, "", "Name of the component to start") + _ = startComponentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = startComponentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = startComponentCmd.RegisterFlagCompletionFunc(flagnames.Component, completion.ComponentCompletion) setContextSpecificPersistentFlags(startComponentCmd) } diff --git a/cmd/startEnvironment.go b/cmd/startEnvironment.go index 925d8d9..9e97331 100644 --- a/cmd/startEnvironment.go +++ b/cmd/startEnvironment.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/environment" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -31,21 +33,21 @@ var startEnvironmentCmd = &cobra.Command{ - Pulls new images from image hub in radix configuration - Starts the environment containers using up to date images`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } envName, err := cmd.Flags().GetString(flagnames.Environment) - if err != nil || appName == nil || *appName == "" || envName == "" { + if err != nil || appName == "" || envName == "" { return errors.New("environment name and application name are required fields") } cmd.SilenceUsage = true parameters := environment.NewStartEnvironmentParams(). - WithAppName(*appName). + WithAppName(appName). WithEnvName(envName) apiClient, err := client.GetForCommand(cmd) @@ -62,5 +64,7 @@ func init() { startCmd.AddCommand(startEnvironmentCmd) startEnvironmentCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application namespace") startEnvironmentCmd.Flags().StringP(flagnames.Environment, "e", "", "Name of the environment of the application") + _ = startEnvironmentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = startEnvironmentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) setContextSpecificPersistentFlags(startEnvironmentCmd) } diff --git a/cmd/stopApplication.go b/cmd/stopApplication.go index 0266417..79de6c4 100644 --- a/cmd/stopApplication.go +++ b/cmd/stopApplication.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/application" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -30,19 +32,19 @@ var stopApplicationCmd = &cobra.Command{ Long: `Stop an application - Stops the application components running containers`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } - if err != nil || appName == nil || *appName == "" { + if err != nil || appName == "" { return errors.New("application name is required fields") } cmd.SilenceUsage = true parameters := application.NewStopApplicationParams(). - WithAppName(*appName) + WithAppName(appName) apiClient, err := client.GetForCommand(cmd) if err != nil { @@ -57,5 +59,6 @@ var stopApplicationCmd = &cobra.Command{ func init() { stopCmd.AddCommand(stopApplicationCmd) stopApplicationCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application namespace") + _ = stopApplicationCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) setContextSpecificPersistentFlags(stopApplicationCmd) } diff --git a/cmd/stopComponent.go b/cmd/stopComponent.go index f8b7fb6..1877400 100644 --- a/cmd/stopComponent.go +++ b/cmd/stopComponent.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/component" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -30,14 +32,14 @@ var stopComponentCmd = &cobra.Command{ Long: `Stop a component - Stops the component running container`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } envName, err := cmd.Flags().GetString(flagnames.Environment) - if err != nil || appName == nil || *appName == "" || envName == "" { + if err != nil || appName == "" || envName == "" { return errors.New("environment name and application name are required fields") } @@ -49,7 +51,7 @@ var stopComponentCmd = &cobra.Command{ cmd.SilenceUsage = true parameters := component.NewStopComponentParams(). - WithAppName(*appName). + WithAppName(appName). WithEnvName(envName). WithComponentName(cmpName) @@ -68,5 +70,8 @@ func init() { stopComponentCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application namespace") stopComponentCmd.Flags().StringP(flagnames.Environment, "e", "", "Name of the environment of the application") stopComponentCmd.Flags().StringP(flagnames.Component, "n", "", "Name of the component to stop") + _ = stopComponentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = stopComponentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) + _ = stopComponentCmd.RegisterFlagCompletionFunc(flagnames.Component, completion.ComponentCompletion) setContextSpecificPersistentFlags(stopComponentCmd) } diff --git a/cmd/stopEnvironment.go b/cmd/stopEnvironment.go index e3030e6..7d4a81c 100644 --- a/cmd/stopEnvironment.go +++ b/cmd/stopEnvironment.go @@ -19,7 +19,9 @@ import ( "github.com/equinor/radix-cli/generated-client/client/environment" "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-cli/pkg/utils/completion" "github.com/spf13/cobra" ) @@ -30,21 +32,21 @@ var stopEnvironmentCmd = &cobra.Command{ Long: `Stop an environment - Stops the environment components running containers`, RunE: func(cmd *cobra.Command, args []string) error { - appName, err := getAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) if err != nil { return err } envName, err := cmd.Flags().GetString(flagnames.Environment) - if err != nil || appName == nil || *appName == "" || envName == "" { + if err != nil || appName == "" || envName == "" { return errors.New("environment name and application name are required fields") } cmd.SilenceUsage = true parameters := environment.NewStopEnvironmentParams(). - WithAppName(*appName). + WithAppName(appName). WithEnvName(envName) apiClient, err := client.GetForCommand(cmd) @@ -61,5 +63,8 @@ func init() { stopCmd.AddCommand(stopEnvironmentCmd) stopEnvironmentCmd.Flags().StringP(flagnames.Application, "a", "", "Name of the application namespace") stopEnvironmentCmd.Flags().StringP(flagnames.Environment, "e", "", "Name of the environment of the application") + + _ = stopEnvironmentCmd.RegisterFlagCompletionFunc(flagnames.Application, completion.ApplicationCompletion) + _ = stopEnvironmentCmd.RegisterFlagCompletionFunc(flagnames.Environment, completion.EnvironmentCompletion) setContextSpecificPersistentFlags(stopEnvironmentCmd) } diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 0000000..ba6d6ca --- /dev/null +++ b/cmd/version.go @@ -0,0 +1,34 @@ +// Copyright © 2023 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// validateCmd represents the validate command +var versionCmd = &cobra.Command{ + Use: "version", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Printf("Current version is %s", rootCmd.Version) + return nil + }, +} + +func init() { + rootCmd.AddCommand(versionCmd) +} diff --git a/pkg/client/auth/token_cache.go b/pkg/client/auth/token_cache.go index acd4e1b..fb40208 100644 --- a/pkg/client/auth/token_cache.go +++ b/pkg/client/auth/token_cache.go @@ -21,7 +21,7 @@ func NewTokenCache(radixConfig *radixconfig.RadixConfig) *TokenCache { } // Replace replaces the cache with what is in external storage. Implementors should honor -// Context cancellations and return context.Canceled or context.DeadlineExceeded in those cases. +// RadixCluster cancellations and return context.Canceled or context.DeadlineExceeded in those cases. func (t *TokenCache) Replace(ctx context.Context, cache cache.Unmarshaler, hints cache.ReplaceHints) error { var ( data []byte @@ -38,7 +38,7 @@ func (t *TokenCache) Replace(ctx context.Context, cache cache.Unmarshaler, hints } // Export writes the binary representation of the cache (cache.Marshal()) to external storage. -// This is considered opaque. Context cancellations should be honored as in Replace. +// This is considered opaque. RadixCluster cancellations should be honored as in Replace. func (t *TokenCache) Export(ctx context.Context, cache cache.Marshaler, hints cache.ExportHints) error { data, err := cache.Marshal() if err != nil { diff --git a/pkg/config/radix_config.go b/pkg/config/radix_config.go index 6f40513..b336dc7 100644 --- a/pkg/config/radix_config.go +++ b/pkg/config/radix_config.go @@ -1,8 +1,10 @@ package config import ( + "encoding/json" "os" "path" + "time" jsonutils "github.com/equinor/radix-cli/pkg/utils/json" ) @@ -17,7 +19,8 @@ const ( radixConfigDir = ".radix" radixConfigFileName = "config" - defaultContext = ContextPlatform + defaultContext = ContextPlatform + DefaultCacheDuration = 7 * 24 * time.Hour ) var ( @@ -34,9 +37,15 @@ func getUserHomeDir() string { return homeDir } +type CacheItem struct { + UpdatedAt time.Time `json:"updated_at"` + ExpiresAt time.Time `json:"expires_at"` + Content string `json:"content"` +} type RadixConfig struct { // CustomConfig is the custom environment config - CustomConfig *CustomConfig `json:"customConfig"` + CustomConfig *CustomConfig `json:"customConfig"` + Cache map[string]map[string]CacheItem `json:"cache"` // MSAL is the internal cache structure used by the MSAL module. The content is base64 encoded MSAL string `json:"msal,omitempty"` @@ -74,11 +83,55 @@ func GetRadixConfig() (*RadixConfig, error) { func GetDefaultRadixConfig() *RadixConfig { return &RadixConfig{ CustomConfig: &CustomConfig{ - Context: defaultContext, + Context: string(defaultContext), }, } } +func GetCache[T any](key string) (content T, ok bool) { + config, err := GetRadixConfig() + if err != nil { + return content, false + } + item, ok := config.Cache[config.CustomConfig.Context][key] + if !ok { + return content, false + } + + if time.Now().After(item.ExpiresAt) { + return content, false + } + + err = json.Unmarshal([]byte(item.Content), &content) + if err != nil { + return content, false + } + return content, ok +} +func SetCache[T any](key string, content T, ttl time.Duration) { + config, _ := GetRadixConfig() + + if config.Cache == nil { + config.Cache = make(map[string]map[string]CacheItem) + } + if _, ok := config.Cache[config.CustomConfig.Context]; !ok { + config.Cache[config.CustomConfig.Context] = make(map[string]CacheItem) + } + + jsonContent, err := json.Marshal(content) + if err != nil { + return + } + + config.Cache[config.CustomConfig.Context][key] = CacheItem{ + UpdatedAt: time.Now(), + ExpiresAt: time.Now().Add(ttl), + Content: string(jsonContent), + } + + _ = Save(config) +} + // Save Saves RadixConfig func Save(radixConfig *RadixConfig) error { return jsonutils.Save(RadixConfigFileFullName, *radixConfig) diff --git a/pkg/config/utils.go b/pkg/config/utils.go new file mode 100644 index 0000000..8b2248a --- /dev/null +++ b/pkg/config/utils.go @@ -0,0 +1,87 @@ +package config + +import ( + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/equinor/radix-cli/pkg/flagnames" + v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" + "github.com/equinor/radix-operator/pkg/apis/utils" + "github.com/spf13/cobra" +) + +func GetAppNameFromConfigOrFromParameter(cmd *cobra.Command, appNameField string) (string, error) { + var appName string + var err error + + fromConfig, _ := cmd.Flags().GetBool(flagnames.FromConfig) + if fromConfig { + radicConfig, err := GetRadixApplicationFromFile() + if err != nil { + return "", err + } + + appName = radicConfig.GetName() + } else { + appName, err = cmd.Flags().GetString(appNameField) + if err != nil { + return "", err + } + } + + return appName, nil +} + +func GetEnvironmentFromConfig(cmd *cobra.Command, branchName string) (string, error) { + var err error + + fromConfig, _ := cmd.Flags().GetBool(flagnames.FromConfig) + if !fromConfig { + return "", errors.New("--from-config is required when getting environment from branch") + } + + cmd.SilenceUsage = true + + radicConfig, err := GetRadixApplicationFromFile() + if err != nil { + return "", err + } + + for _, environment := range radicConfig.Spec.Environments { + if environment.Build.From != "" && environment.Build.From == branchName { + return environment.Name, nil + } + } + + return "", fmt.Errorf("no environment found which maps to branch name `%s`", branchName) +} + +func GetRadixApplicationFromFile() (*v1.RadixApplication, error) { + filePath, _ := filepath.Abs("./radixconfig.yaml") + return loadConfigFromFile(filePath) +} + +// LoadConfigFromFile loads radix config from appFileName +func loadConfigFromFile(appFileName string) (*v1.RadixApplication, error) { + radixApplication, err := utils.GetRadixApplicationFromFile(appFileName) + if err != nil { + return nil, fmt.Errorf("failed to get ra from file (%s) for app Error: %v", appFileName, err) + } + + return radixApplication, nil +} + +func GetStringFromFlagValueOrFlagFile(cmd *cobra.Command, valueFlag, fileNameFlag string) (string, error) { + fileName, err := cmd.Flags().GetString(fileNameFlag) + if err != nil { + return "", err + } + if len(fileName) > 0 { + fileContent, err := os.ReadFile(fileName) + return string(fileContent), err + } + + return cmd.Flags().GetString(valueFlag) +} diff --git a/pkg/utils/completion/alias.go b/pkg/utils/completion/alias.go new file mode 100644 index 0000000..401b683 --- /dev/null +++ b/pkg/utils/completion/alias.go @@ -0,0 +1,55 @@ +package completion + +import ( + "strings" + + "github.com/equinor/radix-cli/generated-client/client/environment" + "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-common/utils/pointers" + "github.com/equinor/radix-common/utils/slice" + "github.com/spf13/cobra" +) + +func AliasCompletion(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + if err != nil || appName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + envName, err := cmd.Flags().GetString(flagnames.Environment) + if err != nil || envName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + componentName, err := cmd.Flags().GetString(flagnames.Component) + if err != nil || componentName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + apiClient, err := client.GetForCommand(cmd) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + params := environment.NewGetEnvironmentParams().WithEnvName(envName).WithAppName(appName) + resp, err := apiClient.Environment.GetEnvironment(params, nil) + if err != nil || resp.Payload == nil || resp.Payload.ActiveDeployment == nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + var names []string + for _, component := range resp.Payload.ActiveDeployment.Components { + if component.Name != nil && *component.Name == componentName { + for _, externalDns := range component.ExternalDNS { + if externalDns.FQDN != nil { + names = append(names, pointers.Val(externalDns.FQDN)) + } + } + } + } + + filteredNames := slice.FindAll(names, func(name string) bool { + return strings.HasPrefix(name, toComplete) + }) + + return filteredNames, cobra.ShellCompDirectiveNoFileComp +} diff --git a/pkg/utils/completion/application.go b/pkg/utils/completion/application.go new file mode 100644 index 0000000..1c443a2 --- /dev/null +++ b/pkg/utils/completion/application.go @@ -0,0 +1,44 @@ +package completion + +import ( + "strings" + + "github.com/equinor/radix-cli/generated-client/client/platform" + "github.com/equinor/radix-cli/generated-client/models" + "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-common/utils/slice" + "github.com/spf13/cobra" +) + +const knownApps = "known_apps" + +func ApplicationCompletion(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if content, ok := config.GetCache[[]string]("known_apps"); ok { + return content, cobra.ShellCompDirectiveNoFileComp + } + + apiClient, err := client.GetForCommand(cmd) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + showApplicationParams := platform.NewShowApplicationsParams() + resp, err := apiClient.Platform.ShowApplications(showApplicationParams, nil) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + appNames := slice.Map(resp.Payload, func(app *models.ApplicationSummary) string { + return app.Name + }) + applications := slice.FindAll(appNames, func(appName string) bool { + return strings.HasPrefix(appName, toComplete) + }) + config.SetCache(knownApps, applications, config.DefaultCacheDuration) + + return applications, cobra.ShellCompDirectiveNoFileComp +} + +func UpdateAppNamesCache(appNames []string) { + config.SetCache(knownApps, appNames, config.DefaultCacheDuration) +} diff --git a/pkg/utils/completion/component.go b/pkg/utils/completion/component.go new file mode 100644 index 0000000..db80bf7 --- /dev/null +++ b/pkg/utils/completion/component.go @@ -0,0 +1,47 @@ +package completion + +import ( + "strings" + + "github.com/equinor/radix-cli/generated-client/client/environment" + "github.com/equinor/radix-cli/generated-client/models" + "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-common/utils/pointers" + "github.com/equinor/radix-common/utils/slice" + "github.com/spf13/cobra" +) + +func ComponentCompletion(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + if err != nil || appName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + envName, err := cmd.Flags().GetString(flagnames.Environment) + if err != nil || envName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + apiClient, err := client.GetForCommand(cmd) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + GetEnvironmentParams := environment.NewGetEnvironmentParams().WithEnvName(envName).WithAppName(appName) + resp, err := apiClient.Environment.GetEnvironment(GetEnvironmentParams, nil) + if err != nil || resp.Payload == nil || resp.Payload.ActiveDeployment == nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + names := slice.Map(resp.Payload.ActiveDeployment.Components, func(component *models.Component) string { + return pointers.Val(component.Name) + }) + + filteredNames := slice.FindAll(names, func(appName string) bool { + return strings.HasPrefix(appName, toComplete) + }) + + return filteredNames, cobra.ShellCompDirectiveNoFileComp +} diff --git a/pkg/utils/completion/config-context.go b/pkg/utils/completion/config-context.go new file mode 100644 index 0000000..a9bfea7 --- /dev/null +++ b/pkg/utils/completion/config-context.go @@ -0,0 +1,16 @@ +package completion + +import ( + "strings" + + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-common/utils/slice" + "github.com/spf13/cobra" +) + +func ConfigContext(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + contexts := slice.FindAll(config.ValidContexts, func(appName string) bool { + return strings.HasPrefix(appName, toComplete) + }) + return contexts, cobra.ShellCompDirectiveNoFileComp +} diff --git a/pkg/utils/completion/deployments.go b/pkg/utils/completion/deployments.go new file mode 100644 index 0000000..c3b4e2f --- /dev/null +++ b/pkg/utils/completion/deployments.go @@ -0,0 +1,71 @@ +package completion + +import ( + "strings" + + apiclient "github.com/equinor/radix-cli/generated-client/client" + "github.com/equinor/radix-cli/generated-client/client/application" + "github.com/equinor/radix-cli/generated-client/client/environment" + "github.com/equinor/radix-cli/generated-client/models" + "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-common/utils/pointers" + "github.com/equinor/radix-common/utils/slice" + "github.com/spf13/cobra" +) + +func CreateDeploymentCompletion(environmentFlagName string, envRequired bool) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + if err != nil || appName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + envName, err := cmd.Flags().GetString(environmentFlagName) + if err != nil || (envRequired && envName == "") { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + apiClient, err := client.GetForCommand(cmd) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + var names []string + if envName == "" { + names = getAllDeployments(appName, apiClient) + } else { + names = getEnvironmentDeployments(appName, envName, apiClient) + } + + filteredNames := slice.FindAll(names, func(name string) bool { + return strings.HasPrefix(name, toComplete) + }) + + return filteredNames, cobra.ShellCompDirectiveNoFileComp + } +} + +func getEnvironmentDeployments(appName, envName string, apiClient *apiclient.Radixapi) []string { + params := environment.NewGetEnvironmentParams().WithEnvName(envName).WithAppName(appName) + resp, err := apiClient.Environment.GetEnvironment(params, nil) + if err != nil || resp.Payload == nil { + return nil + } + + return slice.Map(resp.Payload.Deployments, func(item *models.DeploymentSummary) string { + return pointers.Val(item.Name) + }) +} + +func getAllDeployments(appName string, apiClient *apiclient.Radixapi) []string { + params := application.NewGetDeploymentsParams().WithAppName(appName) + resp, err := apiClient.Application.GetDeployments(params, nil) + if err != nil { + return nil + } + + return slice.Map(resp.Payload, func(item *models.DeploymentSummary) string { + return pointers.Val(item.Name) + }) +} diff --git a/pkg/utils/completion/environment.go b/pkg/utils/completion/environment.go new file mode 100644 index 0000000..3e13f12 --- /dev/null +++ b/pkg/utils/completion/environment.go @@ -0,0 +1,41 @@ +package completion + +import ( + "strings" + + "github.com/equinor/radix-cli/generated-client/client/application" + "github.com/equinor/radix-cli/generated-client/models" + "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-common/utils/slice" + "github.com/spf13/cobra" +) + +func EnvironmentCompletion(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + if err != nil || appName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + apiClient, err := client.GetForCommand(cmd) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + getApplicationParams := application.NewGetApplicationParams() + getApplicationParams.SetAppName(appName) + resp, err := apiClient.Application.GetApplication(getApplicationParams, nil) + if err != nil || resp.Payload == nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + names := slice.Map(resp.Payload.Environments, func(item *models.EnvironmentSummary) string { + return item.Name + }) + filteredNames := slice.FindAll(names, func(name string) bool { + return strings.HasPrefix(name, toComplete) + }) + + return filteredNames, cobra.ShellCompDirectiveNoFileComp +} diff --git a/pkg/utils/completion/job.go b/pkg/utils/completion/job.go new file mode 100644 index 0000000..ad0a177 --- /dev/null +++ b/pkg/utils/completion/job.go @@ -0,0 +1,41 @@ +package completion + +import ( + "strings" + + "github.com/equinor/radix-cli/generated-client/client/application" + "github.com/equinor/radix-cli/generated-client/models" + "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-common/utils/slice" + "github.com/spf13/cobra" +) + +func JobCompletion(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + if err != nil || appName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + apiClient, err := client.GetForCommand(cmd) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + params := application.NewGetApplicationParams().WithAppName(appName) + resp, err := apiClient.Application.GetApplication(params, nil) + if err != nil || resp.Payload == nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + names := slice.Map(resp.Payload.Jobs, func(component *models.JobSummary) string { + return component.Name + }) + + filteredNames := slice.FindAll(names, func(appName string) bool { + return strings.HasPrefix(appName, toComplete) + }) + + return filteredNames, cobra.ShellCompDirectiveNoFileComp +} diff --git a/pkg/utils/completion/secrets.go b/pkg/utils/completion/secrets.go new file mode 100644 index 0000000..68cd608 --- /dev/null +++ b/pkg/utils/completion/secrets.go @@ -0,0 +1,49 @@ +package completion + +import ( + "strings" + + "github.com/equinor/radix-cli/generated-client/client/environment" + "github.com/equinor/radix-cli/generated-client/models" + "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-common/utils/pointers" + "github.com/equinor/radix-common/utils/slice" + "github.com/spf13/cobra" +) + +func SecretCompletion(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + if err != nil || appName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + envName, err := cmd.Flags().GetString(flagnames.Environment) + if err != nil || envName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + componentName, err := cmd.Flags().GetString(flagnames.Component) + if err != nil || componentName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + apiClient, err := client.GetForCommand(cmd) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + params := environment.NewGetEnvironmentParams().WithEnvName(envName).WithAppName(appName) + resp, err := apiClient.Environment.GetEnvironment(params, nil) + if err != nil || resp.Payload == nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + names := slice.Map(resp.Payload.Secrets, func(item *models.Secret) string { + return pointers.Val(item.Name) + }) + + filteredNames := slice.FindAll(names, func(name string) bool { + return strings.HasPrefix(name, toComplete) + }) + + return filteredNames, cobra.ShellCompDirectiveNoFileComp +} diff --git a/pkg/utils/completion/variable.go b/pkg/utils/completion/variable.go new file mode 100644 index 0000000..13ce168 --- /dev/null +++ b/pkg/utils/completion/variable.go @@ -0,0 +1,52 @@ +package completion + +import ( + "strings" + + "github.com/equinor/radix-cli/generated-client/client/environment" + "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/config" + "github.com/equinor/radix-cli/pkg/flagnames" + "github.com/equinor/radix-common/utils/slice" + "github.com/spf13/cobra" +) + +func VariableCompletion(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + appName, err := config.GetAppNameFromConfigOrFromParameter(cmd, flagnames.Application) + if err != nil || appName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + envName, err := cmd.Flags().GetString(flagnames.Environment) + if err != nil || envName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + componentName, err := cmd.Flags().GetString(flagnames.Component) + if err != nil || componentName == "" { + return nil, cobra.ShellCompDirectiveNoFileComp + } + apiClient, err := client.GetForCommand(cmd) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + params := environment.NewGetEnvironmentParams().WithEnvName(envName).WithAppName(appName) + resp, err := apiClient.Environment.GetEnvironment(params, nil) + if err != nil || resp.Payload == nil || resp.Payload.ActiveDeployment == nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + var names []string + for _, comp := range resp.Payload.ActiveDeployment.Components { + if comp.Name != nil && *comp.Name == componentName { + for variable := range comp.Variables { + names = append(names, variable) + } + } + } + + filteredNames := slice.FindAll(names, func(name string) bool { + return strings.HasPrefix(name, toComplete) && !strings.HasPrefix(name, "RADIX_") + }) + + return filteredNames, cobra.ShellCompDirectiveNoFileComp +}