diff --git a/autocomplete/helmfile_bash_autocomplete b/autocomplete/helmfile_bash_autocomplete deleted file mode 100644 index 297439f4..00000000 --- a/autocomplete/helmfile_bash_autocomplete +++ /dev/null @@ -1,18 +0,0 @@ -#! /bin/bash - -_helmfile_bash_autocomplete() { - if [[ "${COMP_WORDS[0]}" != "source" ]]; then - local cur opts base - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - if [[ "$cur" == "-"* ]]; then - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) - else - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) - fi - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - fi -} - -complete -o bashdefault -o default -o nospace -F _helmfile_bash_autocomplete helmfile diff --git a/autocomplete/helmfile_zsh_autocomplete b/autocomplete/helmfile_zsh_autocomplete deleted file mode 100644 index 6ebdbd58..00000000 --- a/autocomplete/helmfile_zsh_autocomplete +++ /dev/null @@ -1,23 +0,0 @@ -#compdef helmfile - -_helmfile_zsh_autocomplete() { - - local -a opts - local cur - cur=${words[-1]} - if [[ "$cur" == "-"* ]]; then - opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}") - else - opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}") - fi - - if [[ "${opts[1]}" != "" ]]; then - _describe 'values' opts - else - _files - fi - - return -} - -compdef _helmfile_zsh_autocomplete helmfile diff --git a/cmd/apply.go b/cmd/apply.go index 077301c3..1f781ab0 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -3,113 +3,56 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addApplySubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "apply", - Usage: "apply all resources from state file only when there are changes", - Flags: []cli.Flag{ - cli.StringSliceFlag{ - Name: "set", - Usage: "additional values to be merged into the command", - }, - cli.StringSliceFlag{ - Name: "values", - Usage: "additional value files to be merged into the command", - }, - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent helm processes to run, 0 is unlimited", - }, - cli.BoolFlag{ - Name: "validate", - Usage: "validate your manifests against the Kubernetes cluster you are currently pointing at. Note that this requires access to a Kubernetes cluster to obtain information necessary for validating, like the list of available API versions", - }, - cli.IntFlag{ - Name: "context", - Value: 0, - Usage: "output NUM lines of context around changes", - }, - cli.StringFlag{ - Name: "output", - Value: "", - Usage: "output format for diff plugin", - }, - cli.BoolFlag{ - Name: "detailed-exitcode", - Usage: "return a non-zero exit code 2 instead of 0 when there were changes detected AND the changes are synced successfully", - }, - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm exec", - }, - cli.BoolFlag{ - Name: "retain-values-files", - Usage: "DEPRECATED: Use skip-cleanup instead", - }, - cli.BoolFlag{ - Name: "skip-cleanup", - Usage: "Stop cleaning up temporary values generated by helmfile and helm-secrets. Useful for debugging. Don't use in production for security", - }, - cli.BoolFlag{ - Name: "skip-crds", - Usage: "if set, no CRDs will be installed on sync. By default, CRDs are installed if not already present", - }, - cli.BoolTFlag{ - Name: "skip-needs", - Usage: `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`, - }, - cli.BoolFlag{ - Name: "include-needs", - Usage: `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`, - }, - cli.BoolFlag{ - Name: "include-transitive-needs", - Usage: `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`, - }, - cli.BoolFlag{ - Name: "skip-diff-on-install", - Usage: "Skips running helm-diff on releases being newly installed on this apply. Useful when the release manifests are too huge to be reviewed, or it's too time-consuming to diff at all", - }, - cli.BoolFlag{ - Name: "include-tests", - Usage: "enable the diffing of the helm test hooks", - }, - cli.StringSliceFlag{ - Name: "suppress", - Usage: "suppress specified Kubernetes objects in the diff output. Can be provided multiple times. For example: --suppress KeycloakClient --suppress VaultSecret", - }, - cli.BoolFlag{ - Name: "suppress-secrets", - Usage: "suppress secrets in the diff output. highly recommended to specify on CI/CD use-cases", - }, - cli.BoolFlag{ - Name: "show-secrets", - Usage: "do not redact secret values in the diff output. should be used for debug purpose only", - }, - cli.BoolFlag{ - Name: "suppress-diff", - Usage: "suppress diff in the output. Usable in new installs", - }, - cli.BoolFlag{ - Name: "skip-deps", - Usage: `skip running "helm repo update" and "helm dependency build"`, - }, - cli.BoolFlag{ - Name: "wait", - Usage: `Override helmDefaults.wait setting "helm upgrade --install --wait"`, - }, - cli.BoolFlag{ - Name: "wait-for-jobs", - Usage: `Override helmDefaults.waitForJobs setting "helm upgrade --install --wait-for-jobs"`, - }, +// NewApplyCmd returm apply subcmd +func NewApplyCmd(globalCfg *config.GlobalImpl) *cobra.Command { + applyOptions := &config.ApplyOptions{} + applyImpl := config.NewApplyImpl(globalCfg, applyOptions) + + cmd := &cobra.Command{ + Use: "apply", + Short: "Apply all resources from state file only when there are changes", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(applyImpl.GlobalImpl) + if err != nil { + return err + } + + if err := applyImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(applyImpl) + return toCLIError(applyImpl.GlobalImpl, a.Apply(applyImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Apply(c) - }), - }) + } + + f := cmd.Flags() + f.StringSliceVar(&applyImpl.ApplyOptions.Set, "selector", []string{}, "additional values to be merged into the command") + f.StringSliceVar(&applyImpl.ApplyOptions.Values, "values", []string{}, "additional value files to be merged into the command") + f.IntVar(&applyImpl.ApplyOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited") + f.BoolVar(&applyImpl.ApplyOptions.Validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. Note that this requires access to a Kubernetes cluster to obtain information necessary for validating, like the list of available API versions") + f.IntVar(&applyImpl.ApplyOptions.Context, "context", 0, "output NUM lines of context around changes") + f.StringVar(&applyImpl.ApplyOptions.Output, "output", "", "output format for diff plugin") + f.BoolVar(&applyImpl.ApplyOptions.DetailedExitcode, "detailed-exitcode", false, "return a non-zero exit code 2 instead of 0 when there were changes detected AND the changes are synced successfully") + f.StringVar(&applyImpl.ApplyOptions.Args, "args", "", "pass args to helm exec") + f.BoolVar(&applyImpl.ApplyOptions.RetainValuesFiles, "retain-values-files", false, "DEPRECATED: Use skip-cleanup instead") + f.BoolVar(&applyImpl.ApplyOptions.SkipCleanup, "skip-cleanup", false, "Stop cleaning up temporary values generated by helmfile and helm-secrets. Useful for debugging. Don't use in production for security") + f.BoolVar(&applyImpl.ApplyOptions.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed on sync. By default, CRDs are installed if not already present") + f.BoolVar(&applyImpl.ApplyOptions.SkipNeeds, "skip-needs", false, `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`) + f.BoolVar(&applyImpl.ApplyOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`) + f.BoolVar(&applyImpl.ApplyOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`) + f.BoolVar(&applyImpl.ApplyOptions.SkipDiffOnInstall, "skip-diff-on-install", false, "Skips running helm-diff on releases being newly installed on this apply. Useful when the release manifests are too huge to be reviewed, or it's too time-consuming to diff at all0") + f.BoolVar(&applyImpl.ApplyOptions.IncludeTests, "include-tests", false, "enable the diffing of the helm test hooks") + f.StringArrayVar(&applyImpl.ApplyOptions.Suppress, "suppress", []string{}, "suppress specified Kubernetes objects in the diff output. Can be provided multiple times. For example: --suppress KeycloakClient --suppress VaultSecret") + f.BoolVar(&applyImpl.ApplyOptions.SuppressSecrets, "suppress-secrets", false, "suppress secrets in the diff output. highly recommended to specify on CI/CD use-cases") + f.BoolVar(&applyImpl.ApplyOptions.ShowSecrets, "show-secrets", false, "do not redact secret values in the diff output. should be used for debug purpose only") + f.BoolVar(&applyImpl.ApplyOptions.SuppressDiff, "suppress-diff", false, "suppress diff in the output. Usable in new installs") + f.BoolVar(&applyImpl.ApplyOptions.SkipDeps, "skip-deps", false, `skip running "helm repo update" and "helm dependency build"`) + f.Bool("wait", false, `Override helmDefaults.wait setting "helm upgrade --install --wait"`) + f.Bool("wait-for-jobs", false, `Override helmDefaults.waitForJobs setting "helm upgrade --install --wait-for-jobs"`) + + return cmd } diff --git a/cmd/build.go b/cmd/build.go index 58f73498..b21bec48 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -3,21 +3,34 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addBuildSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "build", - Usage: "output compiled helmfile state(s) as YAML", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "embed-values", - Usage: "Read all the values files for every release and embed into the output helmfile.yaml", - }, +// NewBuildCmd returm build subcmd +func NewBuildCmd(globalCfg *config.GlobalImpl) *cobra.Command { + buildOptions := config.NewBuildOptions() + buildImpl := config.NewBuildImpl(globalCfg, buildOptions) + + cmd := &cobra.Command{ + Use: "build", + Short: "Build all resources from state file only when there are changes", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(buildImpl.GlobalImpl) + if err != nil { + return err + } + + if err := buildImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(buildImpl) + return toCLIError(buildImpl.GlobalImpl, a.PrintState(buildImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.PrintState(c) - }), - }) + } + + f := cmd.Flags() + f.BoolVar(&buildOptions.EmbedValues, "embed-values", false, "Read all the values files for every release and embed into the output helmfile.yaml") + + return cmd } diff --git a/cmd/cache.go b/cmd/cache.go index 7cefb813..3ccd9234 100644 --- a/cmd/cache.go +++ b/cmd/cache.go @@ -3,33 +3,66 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -var cacheInfoSubcommand = cli.Command{ - Name: "info", - Usage: "cache info", - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.ShowCacheDir(c) - }), -} +func NewCacheInfoSubcommand(cacheImpl *config.CacheImpl) *cobra.Command { + cmd := &cobra.Command{ + Use: "info", + Short: "cache info", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(cacheImpl.GlobalImpl) + if err != nil { + return err + } -var cacheCleanupSubcommand = cli.Command{ - Name: "cleanup", - Usage: "clean up cache directory", - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.CleanCacheDir(c) - }), -} + if err := cacheImpl.ValidateConfig(); err != nil { + return err + } -func addCacheSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "cache", - Usage: "cache management", - ArgsUsage: "[command]", - Subcommands: []cli.Command{ - cacheCleanupSubcommand, - cacheInfoSubcommand, + a := app.New(cacheImpl) + return toCLIError(cacheImpl.GlobalImpl, a.ShowCacheDir(cacheImpl)) }, - }) + } + + return cmd +} + +func NewCacheCleanupSubcommand(cacheImpl *config.CacheImpl) *cobra.Command { + cmd := &cobra.Command{ + Use: "cleanup", + Short: "clean up cache directory", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(cacheImpl.GlobalImpl) + if err != nil { + return err + } + + if err := cacheImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(cacheImpl) + return toCLIError(cacheImpl.GlobalImpl, a.CleanCacheDir(cacheImpl)) + }, + } + return cmd +} + +// NewCacheCmd returm cache subcmd +func NewCacheCmd(globalCfg *config.GlobalImpl) *cobra.Command { + cacheOptions := config.NewCacheOptions() + cacheImpl := config.NewCacheImpl(globalCfg, cacheOptions) + + cmd := &cobra.Command{ + Use: "cache", + Short: "Cache management", + } + + cmd.AddCommand( + NewCacheCleanupSubcommand(cacheImpl), + NewCacheInfoSubcommand(cacheImpl), + ) + + return cmd } diff --git a/cmd/charts.go b/cmd/charts.go index a6a01649..ee88a384 100644 --- a/cmd/charts.go +++ b/cmd/charts.go @@ -3,35 +3,37 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addChartsSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "charts", - Usage: "DEPRECATED: sync releases from state file (helm upgrade --install)", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm exec", - }, - cli.StringSliceFlag{ - Name: "set", - Usage: "additional values to be merged into the command", - }, - cli.StringSliceFlag{ - Name: "values", - Usage: "additional value files to be merged into the command", - }, - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent helm processes to run, 0 is unlimited", - }, +// NewChartsCmd returm build subcmd +func NewChartsCmd(globalCfg *config.GlobalImpl) *cobra.Command { + chartsOptions := config.NewChartsOptions() + chartsImpl := config.NewChartsImpl(globalCfg, chartsOptions) + + cmd := &cobra.Command{ + Use: "charts", + Short: "DEPRECATED: sync releases from state file (helm upgrade --install)", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(chartsImpl.GlobalImpl) + if err != nil { + return err + } + + if err := chartsImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(chartsImpl) + return toCLIError(chartsImpl.GlobalImpl, a.DeprecatedSyncCharts(chartsImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.DeprecatedSyncCharts(c) - }), - }) + } + + f := cmd.Flags() + f.StringVar(&chartsOptions.Args, "args", chartsOptions.Args, "pass args to helm exec") + f.StringArrayVar(&chartsOptions.Set, "set", chartsOptions.Set, "additional values to be merged into the command") + f.StringArrayVar(&chartsOptions.Values, "values", chartsOptions.Values, "additional value files to be merged into the command") + f.IntVar(&chartsOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited") + + return cmd } diff --git a/cmd/delete.go b/cmd/delete.go index 96c9ad46..7b22ad23 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -3,35 +3,37 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addDeleteSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "delete", - Usage: "DEPRECATED: delete releases from state file (helm delete)", - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent helm processes to run, 0 is unlimited", - }, - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm exec", - }, - cli.BoolFlag{ - Name: "purge", - Usage: "purge releases i.e. free release names and histories", - }, - cli.BoolFlag{ - Name: "skip-deps", - Usage: `skip running "helm repo update" and "helm dependency build"`, - }, +// NewDeleteCmd returm build subcmd +func NewDeleteCmd(globalCfg *config.GlobalImpl) *cobra.Command { + deleteOptions := config.NewDeleteOptions() + deleteImpl := config.NewDeleteImpl(globalCfg, deleteOptions) + + cmd := &cobra.Command{ + Use: "delete", + Short: "DEPRECATED: delete releases from state file (helm delete)", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(deleteImpl.GlobalImpl) + if err != nil { + return err + } + + if err := deleteImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(deleteImpl) + return toCLIError(deleteImpl.GlobalImpl, a.Delete(deleteImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Delete(c) - }), - }) + } + + f := cmd.Flags() + f.StringVar(&deleteOptions.Args, "args", deleteOptions.Args, "pass args to helm exec") + f.IntVar(&deleteOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited") + f.BoolVar(&deleteOptions.Purge, "purge", deleteOptions.Purge, "purge releases i.e. free release names and histories") + f.BoolVar(&deleteOptions.SkipDeps, "skip-deps", deleteOptions.SkipDeps, `skip running "helm repo update" and "helm dependency build"`) + + return cmd } diff --git a/cmd/deps.go b/cmd/deps.go index a6179cc0..ab5f2bc4 100644 --- a/cmd/deps.go +++ b/cmd/deps.go @@ -3,26 +3,35 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addDepsSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "deps", - Usage: "update charts based on their requirements", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm exec", - }, - cli.BoolFlag{ - Name: "skip-repos", - Usage: `skip running "helm repo update" before running "helm dependency build"`, - }, +// NewDepsCmd returm build subcmd +func NewDepsCmd(globalCfg *config.GlobalImpl) *cobra.Command { + depsOptions := config.NewDepsOptions() + depsImpl := config.NewDepsImpl(globalCfg, depsOptions) + + cmd := &cobra.Command{ + Use: "deps", + Short: "Update charts based on their requirements", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(depsImpl.GlobalImpl) + if err != nil { + return err + } + + if err := depsImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(depsImpl) + return toCLIError(depsImpl.GlobalImpl, a.Deps(depsImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Deps(c) - }), - }) + } + + f := cmd.Flags() + f.StringVar(&depsOptions.Args, "args", depsOptions.Args, "pass args to helm exec") + f.BoolVar(&depsOptions.SkipRepos, "skip-deps", depsOptions.SkipRepos, `skip running "helm repo update" and "helm dependency build"`) + + return cmd } diff --git a/cmd/destroy.go b/cmd/destroy.go index 04f13c82..a88f36da 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -3,31 +3,36 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addDestroySubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "destroy", - Usage: "deletes and then purges releases", - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent helm processes to run, 0 is unlimited", - }, - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm exec", - }, - cli.BoolFlag{ - Name: "skip-deps", - Usage: `skip running "helm repo update" and "helm dependency build"`, - }, +// NewDestroyCmd returm build subcmd +func NewDestroyCmd(globalCfg *config.GlobalImpl) *cobra.Command { + destroyOptions := config.NewDestroyOptions() + destroyImpl := config.NewDestroyImpl(globalCfg, destroyOptions) + + cmd := &cobra.Command{ + Use: "destroy", + Short: "Destroys and then purges releases", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(destroyImpl.GlobalImpl) + if err != nil { + return err + } + + if err := destroyImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(destroyImpl) + return toCLIError(destroyImpl.GlobalImpl, a.Destroy(destroyImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Destroy(c) - }), - }) + } + + f := cmd.Flags() + f.StringVar(&destroyOptions.Args, "args", destroyOptions.Args, "pass args to helm exec") + f.IntVar(&destroyOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited") + f.BoolVar(&destroyOptions.SkipDeps, "skip-deps", destroyOptions.SkipDeps, `skip running "helm repo update" and "helm dependency build"`) + + return cmd } diff --git a/cmd/diff.go b/cmd/diff.go index f943ec95..3114ee58 100644 --- a/cmd/diff.go +++ b/cmd/diff.go @@ -3,90 +3,50 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addDiffSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "diff", - Usage: "diff releases from state file against env (helm diff)", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm exec", - }, - cli.StringSliceFlag{ - Name: "set", - Usage: "additional values to be merged into the command", - }, - cli.StringSliceFlag{ - Name: "values", - Usage: "additional value files to be merged into the command", - }, - cli.BoolFlag{ - Name: "skip-deps", - Usage: `skip running "helm repo update" and "helm dependency build"`, - }, - cli.BoolFlag{ - Name: "detailed-exitcode", - Usage: "return a non-zero exit code when there are changes", - }, - cli.BoolFlag{ - Name: "include-tests", - Usage: "enable the diffing of the helm test hooks", - }, - cli.BoolTFlag{ - Name: "skip-needs", - Usage: `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`, - }, - cli.BoolFlag{ - Name: "include-needs", - Usage: `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`, - }, - cli.BoolFlag{ - Name: "include-transitive-needs", - Usage: `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`, - }, - cli.BoolFlag{ - Name: "skip-diff-on-install", - Usage: "Skips running helm-diff on releases being newly installed on this apply. Useful when the release manifests are too huge to be reviewed, or it's too time-consuming to diff at all", - }, - cli.StringSliceFlag{ - Name: "suppress", - Usage: "suppress specified Kubernetes objects in the output. Can be provided multiple times. For example: --suppress KeycloakClient --suppress VaultSecret", - }, - cli.BoolFlag{ - Name: "suppress-secrets", - Usage: "suppress secrets in the output. highly recommended to specify on CI/CD use-cases", - }, - cli.BoolFlag{ - Name: "show-secrets", - Usage: "do not redact secret values in the output. should be used for debug purpose only", - }, - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent helm processes to run, 0 is unlimited", - }, - cli.BoolFlag{ - Name: "validate", - Usage: "validate your manifests against the Kubernetes cluster you are currently pointing at. Note that this requires access to a Kubernetes cluster to obtain information necessary for validating, like the list of available API versions", - }, +// NewDiffCmd returm build subcmd +func NewDiffCmd(globalCfg *config.GlobalImpl) *cobra.Command { + diffOptions := config.NewDiffOptions() + diffImpl := config.NewDiffImpl(globalCfg, diffOptions) - cli.IntFlag{ - Name: "context", - Value: 0, - Usage: "output NUM lines of context around changes", - }, - cli.StringFlag{ - Name: "output", - Value: "", - Usage: "output format for diff plugin", - }, + cmd := &cobra.Command{ + Use: "diff", + Short: "Diff releases defined in state file", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(diffImpl.GlobalImpl) + if err != nil { + return err + } + + if err := diffImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(diffImpl) + return toCLIError(diffImpl.GlobalImpl, a.Diff(diffImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Diff(c) - }), - }) + } + + f := cmd.Flags() + f.StringVar(&diffOptions.Args, "args", "", "pass args to helm diff") + f.StringArrayVar(&diffOptions.Set, "set", []string{}, "additional values to be merged into the command") + f.StringArrayVar(&diffOptions.Values, "values", []string{}, "additional value files to be merged into the command") + f.IntVar(&diffOptions.Concurrency, "concurrency", 0, "maximum number of concurrent downloads of release charts") + f.BoolVar(&diffOptions.Validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. Note that this requires access to a Kubernetes cluster to obtain information necessary for validating, like the diff of available API versions") + f.BoolVar(&diffOptions.SkipNeeds, "skip-needs", false, `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`) + f.BoolVar(&diffOptions.IncludeTests, "include-tests", false, "enable the diffing of the helm test hooks") + f.BoolVar(&diffOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`) + f.BoolVar(&diffOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`) + f.BoolVar(&diffOptions.SkipDeps, "skip-deps", false, `skip running "helm repo update" and "helm dependency build"`) + f.BoolVar(&diffOptions.SkipDiffOnInstall, "skip-diff-on-install", false, "Skips running helm-diff on releases being newly installed on this apply. Useful when the release manifests are too huge to be reviewed, or it's too time-consuming to diff at all") + f.BoolVar(&diffOptions.ShowSecrets, "show-secrets", false, "do not redact secret values in the output. should be used for debug purpose only") + f.BoolVar(&diffOptions.DetailedExitcode, "detailed-exitcode", false, "return a detailed exit code") + f.IntVar(&diffOptions.Context, "context", 0, "output NUM lines of context around changes") + f.StringVar(&diffOptions.Output, "output", "", "output format for diff plugin") + f.BoolVar(&diffOptions.SuppressSecrets, "suppress-secrets", false, "suppress secrets in the output. highly recommended to specify on CI/CD use-cases") + f.StringArrayVar(&diffOptions.Suppress, "suppress", []string{}, "suppress specified Kubernetes objects in the output. Can be provided multiple times. For example: --suppress KeycloakClient --suppress VaultSecret") + + return cmd } diff --git a/cmd/fetch.go b/cmd/fetch.go index f6c6b802..db2a5eb0 100644 --- a/cmd/fetch.go +++ b/cmd/fetch.go @@ -3,30 +3,36 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addFetchSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "fetch", - Usage: "fetch charts from state file", - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent downloads of release charts", - }, - cli.BoolFlag{ - Name: "skip-deps", - Usage: `skip running "helm repo update" and "helm dependency build"`, - }, - cli.StringFlag{ - Name: "output-dir", - Usage: "directory to store charts (default: temporary directory which is deleted when the command terminates)", - }, +// NewFetchCmd returm build subcmd +func NewFetchCmd(globalCfg *config.GlobalImpl) *cobra.Command { + fetchOptions := config.NewFetchOptions() + fetchImpl := config.NewFetchImpl(globalCfg, fetchOptions) + + cmd := &cobra.Command{ + Use: "fetch", + Short: "Fetch charts from state file", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(fetchImpl.GlobalImpl) + if err != nil { + return err + } + + if err := fetchImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(fetchImpl) + return toCLIError(fetchImpl.GlobalImpl, a.Fetch(fetchImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Fetch(c) - }), - }) + } + + f := cmd.Flags() + f.IntVar(&fetchOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited") + f.BoolVar(&fetchOptions.SkipDeps, "skip-deps", fetchOptions.SkipDeps, `skip running "helm repo update" and "helm dependency build"`) + f.StringVar(&fetchOptions.OutputDir, "output-dir", fetchOptions.OutputDir, "directory to store charts (default: temporary directory which is deleted when the command terminates)") + + return cmd } diff --git a/cmd/lint.go b/cmd/lint.go index 1185d4cd..99e252eb 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -3,39 +3,38 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addLintSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "lint", - Usage: "lint charts from state file (helm lint)", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm exec", - }, - cli.StringSliceFlag{ - Name: "set", - Usage: "additional values to be merged into the command", - }, - cli.StringSliceFlag{ - Name: "values", - Usage: "additional value files to be merged into the command", - }, - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent downloads of release charts", - }, - cli.BoolFlag{ - Name: "skip-deps", - Usage: `skip running "helm repo update" and "helm dependency build"`, - }, +// NewLintCmd returm build subcmd +func NewLintCmd(globalCfg *config.GlobalImpl) *cobra.Command { + lintOptions := config.NewLintOptions() + lintImpl := config.NewLintImpl(globalCfg, lintOptions) + + cmd := &cobra.Command{ + Use: "lint", + Short: "Lint charts from state file (helm lint)", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(lintImpl.GlobalImpl) + if err != nil { + return err + } + + if err := lintImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(lintImpl) + return toCLIError(lintImpl.GlobalImpl, a.Lint(lintImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Lint(c) - }), - }) + } + + f := cmd.Flags() + f.IntVar(&lintOptions.Concurrency, "concurrency", 0, "maximum number of concurrent downloads of release charts") + f.BoolVar(&lintOptions.SkipDeps, "skip-deps", lintOptions.SkipDeps, `skip running "helm repo update" and "helm dependency build"`) + f.StringVar(&lintOptions.Args, "args", lintOptions.Args, "pass args to helm exec") + f.StringArrayVar(&lintOptions.Set, "set", lintOptions.Set, "additional values to be merged into the command") + f.StringArrayVar(&lintOptions.Values, "values", lintOptions.Values, "additional value files to be merged into the command") + + return cmd } diff --git a/cmd/list.go b/cmd/list.go index 81735cbd..5b2c7997 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -3,26 +3,35 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addListSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "list", - Usage: "list releases defined in state file", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "output", - Value: "", - Usage: "output releases list as a json string", - }, - cli.BoolFlag{ - Name: "keep-temp-dir", - Usage: "Keep temporary directory", - }, +// NewListCmd returm build subcmd +func NewListCmd(globalCfg *config.GlobalImpl) *cobra.Command { + listOptions := config.NewListOptions() + listImpl := config.NewListImpl(globalCfg, listOptions) + + cmd := &cobra.Command{ + Use: "list", + Short: "List releases defined in state file", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(listImpl.GlobalImpl) + if err != nil { + return err + } + + if err := listImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(listImpl) + return toCLIError(listImpl.GlobalImpl, a.ListReleases(listImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.ListReleases(c) - }), - }) + } + + f := cmd.Flags() + f.BoolVar(&listOptions.KeepTempDir, "keep-temp-dir", listOptions.KeepTempDir, "Keep temporary directory") + f.StringVar(&listOptions.Output, "output", listOptions.Output, "output releases list as a json string") + + return cmd } diff --git a/cmd/repos.go b/cmd/repos.go index dca5bb95..5cf1fe87 100644 --- a/cmd/repos.go +++ b/cmd/repos.go @@ -3,22 +3,34 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addReposSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "repos", - Usage: "sync repositories from state file (helm repo add && helm repo update)", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm exec", - }, +// NewReposCmd returm build subcmd +func NewReposCmd(globalCfg *config.GlobalImpl) *cobra.Command { + reposOptions := config.NewReposOptions() + reposImpl := config.NewReposImpl(globalCfg, reposOptions) + + cmd := &cobra.Command{ + Use: "repos", + Short: "Repos releases defined in state file", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(reposImpl.GlobalImpl) + if err != nil { + return err + } + + if err := reposImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(reposImpl) + return toCLIError(reposImpl.GlobalImpl, a.Repos(reposImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Repos(c) - }), - }) + } + + f := cmd.Flags() + f.StringVar(&reposOptions.Args, "args", reposOptions.Args, "pass args to helm exec") + + return cmd } diff --git a/cmd/root.go b/cmd/root.go index 7a6ad44d..f5a8f797 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -8,125 +8,21 @@ import ( "github.com/helmfile/helmfile/pkg/app/version" "github.com/helmfile/helmfile/pkg/config" "github.com/helmfile/helmfile/pkg/helmexec" + "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/urfave/cli" "go.uber.org/zap" ) var logger *zap.SugaredLogger +var globalUsage = "Declaratively deploy your Kubernetes manifests, Kustomize configs, and Charts as Helm releases in one shot" -// RootCmd is the root command for helmfile. -func RootCommand() *cli.App { - cliApp := cli.NewApp() - cliApp.Name = "helmfile" - cliApp.Usage = "" - cliApp.Version = version.GetVersion() - cliApp.EnableBashCompletion = true - cliApp.Before = configureLogging - setRootCommandFlags(cliApp) - - // add subcommands - addDepsSubcommand(cliApp) - addReposSubcommand(cliApp) - addChartsSubcommand(cliApp) - addDiffSubcommand(cliApp) - addTemplateSubcommand(cliApp) - addWriteValuesSubcommand(cliApp) - addLintSubcommand(cliApp) - addFetchSubcommand(cliApp) - addSyncSubcommand(cliApp) - addApplySubcommand(cliApp) - addStatusSubcommand(cliApp) - addDeleteSubcommand(cliApp) - addDestroySubcommand(cliApp) - addTestSubcommand(cliApp) - addBuildSubcommand(cliApp) - addListSubcommand(cliApp) - addCacheSubcommand(cliApp) - addVersionSubcommand(cliApp) - - return cliApp -} - -// setRootCommandFlags sets the flags for the root command. -func setRootCommandFlags(cliApp *cli.App) { - cliApp.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "helm-binary, b", - Usage: "path to helm binary", - Value: app.DefaultHelmBinary, - }, - cli.StringFlag{ - Name: "file, f", - Usage: "load config from file or directory. defaults to `helmfile.yaml` or `helmfile.d`(means `helmfile.d/*.yaml`) in this preference", - }, - cli.StringFlag{ - Name: "environment, e", - Usage: `specify the environment name. defaults to "default"`, - }, - cli.StringSliceFlag{ - Name: "state-values-set", - Usage: "set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)", - }, - cli.StringSliceFlag{ - Name: "state-values-file", - Usage: "specify state values in a YAML file", - }, - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Silence output. Equivalent to log-level warn", - }, - cli.StringFlag{ - Name: "kube-context", - Usage: "Set kubectl context. Uses current context by default", - }, - cli.BoolFlag{ - Name: "debug", - Usage: "Enable verbose output for Helm and set log-level to debug, this disables --quiet/-q effect", - }, - cli.BoolFlag{ - Name: "color", - Usage: "Output with color", - }, - cli.BoolFlag{ - Name: "no-color", - Usage: "Output without color", - }, - cli.StringFlag{ - Name: "log-level", - Usage: "Set log level, default info", - }, - cli.StringFlag{ - Name: "namespace, n", - Usage: "Set namespace. Uses the namespace set in the context by default, and is available in templates as {{ .Namespace }}", - }, - cli.StringFlag{ - Name: "chart, c", - Usage: "Set chart. Uses the chart set in release by default, and is available in template as {{ .Chart }}", - }, - cli.StringSliceFlag{ - Name: "selector, l", - Usage: `Only run using the releases that match labels. Labels can take the form of foo=bar or foo!=bar. - A release must match all labels in a group in order to be used. Multiple groups can be specified at once. - --selector tier=frontend,tier!=proxy --selector tier=backend. Will match all frontend, non-proxy releases AND all backend releases. - The name of a release can be used as a label. --selector name=myrelease`, - }, - cli.BoolFlag{ - Name: "allow-no-matching-release", - Usage: `Do not exit with an error code if the provided selector has no matching releases.`, - }, - cli.BoolFlag{ - Name: "interactive, i", - Usage: "Request confirmation before attempting to modify clusters", - }, - } -} - -func toCliError(c *cli.Context, err error) error { +func toCLIError(g *config.GlobalImpl, err error) error { if err != nil { switch e := err.(type) { case *app.NoMatchingHelmfileError: noMatchingExitCode := 3 - if c.GlobalBool("allow-no-matching-release") { + if g.AllowNoMatchingRelease { noMatchingExitCode = 0 } return cli.NewExitError(e.Error(), noMatchingExitCode) @@ -141,42 +37,88 @@ func toCliError(c *cli.Context, err error) error { return err } -func configureLogging(c *cli.Context) error { - // Valid levels: - // https://github.com/uber-go/zap/blob/7e7e266a8dbce911a49554b945538c5b950196b8/zapcore/level.go#L126 - logLevel := c.GlobalString("log-level") - if c.GlobalBool("debug") { - logLevel = "debug" - } else if c.GlobalBool("quiet") { - logLevel = "warn" +// NewRootCmd creates the root command for the CLI. +func NewRootCmd(globalConfig *config.GlobalOptions, args []string) (*cobra.Command, error) { + cmd := &cobra.Command{ + Use: "helmfile", + Short: globalUsage, + Long: globalUsage, + Args: cobra.MinimumNArgs(1), + Version: version.GetVersion(), + SilenceUsage: true, + PersistentPreRunE: func(c *cobra.Command, args []string) error { + // Valid levels: + // https://github.com/uber-go/zap/blob/7e7e266a8dbce911a49554b945538c5b950196b8/zapcore/level.go#L126 + logLevel := globalConfig.LogLevel + switch { + case globalConfig.Debug: + logLevel = "debug" + case globalConfig.Quiet: + logLevel = "warn" + } + logger = helmexec.NewLogger(os.Stderr, logLevel) + globalConfig.SetLogger(logger) + return nil + }, } - logger = helmexec.NewLogger(os.Stderr, logLevel) - if c.App.Metadata == nil { - // Auto-initialized in 1.19.0 - // https://github.com/urfave/cli/blob/master/CHANGELOG.md#1190---2016-11-19 - c.App.Metadata = make(map[string]interface{}) + flags := cmd.PersistentFlags() + + // Set the global options for the root command. + setGlobalOptionsForRootCmd(flags, globalConfig) + + // We can safely ignore any errors that flags.Parse encounters since + // those errors will be caught later during the call to cmd.Execution. + // This call is required to gather configuration information prior to + // execution. + flags.ParseErrorsWhitelist.UnknownFlags = true + + err := flags.Parse(args) + if err != nil { + return nil, err } - c.App.Metadata["logger"] = logger - return nil + globalImpl := config.NewGlobalImpl(globalConfig) + cmd.AddCommand( + NewApplyCmd(globalImpl), + NewBuildCmd(globalImpl), + NewCacheCmd(globalImpl), + NewChartsCmd(globalImpl), + NewDeleteCmd(globalImpl), + NewDepsCmd(globalImpl), + NewDestroyCmd(globalImpl), + NewFetchCmd(globalImpl), + NewListCmd(globalImpl), + NewReposCmd(globalImpl), + NewVersionCmd(), + NewLintCmd(globalImpl), + NewWriteValuesCmd(globalImpl), + NewTestCmd(globalImpl), + NewTemplateCmd(globalImpl), + NewSyncCmd(globalImpl), + NewDiffCmd(globalImpl), + NewStatusCmd(globalImpl), + ) + + return cmd, nil } -func Action(do func(*app.App, config.ConfigImpl) error) func(*cli.Context) error { - return func(implCtx *cli.Context) error { - conf, err := config.NewUrfaveCliConfigImpl(implCtx) - if err != nil { - return err - } - - if err := app.ValidateConfig(conf); err != nil { - return err - } - - a := app.New(conf) - - if err := do(a, conf); err != nil { - return toCliError(implCtx, err) - } - - return nil - } +func setGlobalOptionsForRootCmd(fs *pflag.FlagSet, globalOptions *config.GlobalOptions) { + fs.StringVarP(&globalOptions.HelmBinary, "helm-binary", "b", app.DefaultHelmBinary, "Path to the helm binary") + fs.StringVarP(&globalOptions.File, "file", "f", globalOptions.File, "load config from file or directory. defaults to `helmfile.yaml` or `helmfile.d`(means `helmfile.d/*.yaml`) in this preference") + fs.StringVarP(&globalOptions.Environment, "env", "e", globalOptions.Environment, `specify the environment name. defaults to "default"`) + fs.StringArrayVarP(&globalOptions.StateValuesSet, "state-values-set", "s", globalOptions.StateValuesSet, "set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") + fs.StringArrayVarP(&globalOptions.StateValuesFile, "state-values-file", "", globalOptions.StateValuesFile, "specify state values in a YAML file") + fs.BoolVarP(&globalOptions.Quiet, "quiet", "q", globalOptions.Quiet, "Silence output. Equivalent to log-level warn") + fs.StringVar(&globalOptions.KubeContext, "kube-context", globalOptions.KubeContext, "Set kubectl context. Uses current context by default") + fs.BoolVar(&globalOptions.Debug, "debug", globalOptions.Debug, "Enable verbose output for Helm and set log-level to debug, this disables --quiet/-q effect") + fs.BoolVar(&globalOptions.Color, "color", globalOptions.Color, "Output with color") + fs.BoolVar(&globalOptions.NoColor, "no-color", globalOptions.NoColor, "Output without color") + fs.StringVar(&globalOptions.LogLevel, "log-level", globalOptions.LogLevel, "Set log level, default info") + fs.StringVar(&globalOptions.Namespace, "namespace", globalOptions.Namespace, "Set namespace. Uses the namespace set in the context by default, and is available in templates as {{ .Namespace }}") + fs.StringVar(&globalOptions.Chart, "chart", globalOptions.Chart, "Set chart. Uses the chart set in release by default, and is available in template as {{ .Chart }}") + fs.StringArrayVarP(&globalOptions.Selector, "selector", "l", globalOptions.Selector, `Only run using the releases that match labels. Labels can take the form of foo=bar or foo!=bar. + A release must match all labels in a group in order to be used. Multiple groups can be specified at once. + --selector tier=frontend,tier!=proxy --selector tier=backend. Will match all frontend, non-proxy releases AND all backend releases. + The name of a release can be used as a label. --selector name=myrelease`) + fs.BoolVar(&globalOptions.AllowNoMatchingRelease, "allow-no-matching-release", globalOptions.AllowNoMatchingRelease, `Do not exit with an error code if the provided selector has no matching releases.`) + fs.BoolVarP(&globalOptions.Interactive, "interactive", "i", globalOptions.Interactive, "Request confirmation before attempting to modify clusters") } diff --git a/cmd/status.go b/cmd/status.go index 0a423fb1..8b30a107 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -3,27 +3,34 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addStatusSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "status", - Usage: "retrieve status of releases in state file", - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent helm processes to run, 0 is unlimited", - }, - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm exec", - }, +// NewStatusCmd returm build subcmd +func NewStatusCmd(globalCfg *config.GlobalImpl) *cobra.Command { + statusOptions := config.NewStatusOptions() + statusImpl := config.NewStatusImpl(globalCfg, statusOptions) + + cmd := &cobra.Command{ + Use: "status", + Short: "Retrieve status of releases in state file", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(statusImpl.GlobalImpl) + if err != nil { + return err + } + + if err := statusImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(statusImpl) + return toCLIError(statusImpl.GlobalImpl, a.Status(statusImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Status(c) - }), - }) + } + + f := cmd.Flags() + f.StringVar(&statusOptions.Args, "args", statusOptions.Args, "pass args to helm exec") + + return cmd } diff --git a/cmd/sync.go b/cmd/sync.go index 7d0a7ae4..7f7a86c3 100644 --- a/cmd/sync.go +++ b/cmd/sync.go @@ -3,67 +3,45 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addSyncSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "sync", - Usage: "sync all resources from state file (repos, releases and chart deps)", - Flags: []cli.Flag{ - cli.StringSliceFlag{ - Name: "set", - Usage: "additional values to be merged into the command", - }, - cli.StringSliceFlag{ - Name: "values", - Usage: "additional value files to be merged into the command", - }, - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent helm processes to run, 0 is unlimited", - }, - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm exec", - }, - cli.BoolFlag{ - Name: "skip-crds", - Usage: "if set, no CRDs will be installed on sync. By default, CRDs are installed if not already present", - }, - cli.BoolFlag{ - Name: "skip-deps", - Usage: `skip running "helm repo update" and "helm dependency build"`, - }, - cli.BoolTFlag{ - Name: "skip-needs", - Usage: `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`, - }, - cli.BoolFlag{ - Name: "include-needs", - Usage: `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`, - }, - cli.BoolFlag{ - Name: "include-transitive-needs", - Usage: `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`, - }, - cli.BoolFlag{ - Name: "validate", - Usage: `ADVANCED CONFIGURATION: When sync is going to involve helm-template as a part of the "chartify" process, it might fail due to missing .Capabilities. This flag makes instructs helmfile to pass --validate to helm-template so it populates .Capabilities and validates your manifests against the Kubernetes cluster you are currently pointing at. Note that this requires access to a Kubernetes cluster to obtain information necessary for validating, like the list of available API versions`, - }, - cli.BoolFlag{ - Name: "wait", - Usage: `Override helmDefaults.wait setting "helm upgrade --install --wait"`, - }, - cli.BoolFlag{ - Name: "wait-for-jobs", - Usage: `Override helmDefaults.waitForJobs setting "helm upgrade --install --wait-for-jobs"`, - }, +// NewSyncCmd returm build subcmd +func NewSyncCmd(globalCfg *config.GlobalImpl) *cobra.Command { + syncOptions := config.NewSyncOptions() + syncImpl := config.NewSyncImpl(globalCfg, syncOptions) + + cmd := &cobra.Command{ + Use: "sync", + Short: "Sync releases defined in state file", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(syncImpl.GlobalImpl) + if err != nil { + return err + } + + if err := syncImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(syncImpl) + return toCLIError(syncImpl.GlobalImpl, a.Sync(syncImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Sync(c) - }), - }) + } + + f := cmd.Flags() + f.StringVar(&syncOptions.Args, "args", "", "pass args to helm sync") + f.StringArrayVar(&syncOptions.Set, "set", []string{}, "additional values to be merged into the command") + f.StringArrayVar(&syncOptions.Values, "values", []string{}, "additional value files to be merged into the command") + f.IntVar(&syncOptions.Concurrency, "concurrency", 0, "maximum number of concurrent downloads of release charts") + f.BoolVar(&syncOptions.Validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. Note that this requires access to a Kubernetes cluster to obtain information necessary for validating, like the sync of available API versions") + f.BoolVar(&syncOptions.SkipNeeds, "skip-needs", false, `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`) + f.BoolVar(&syncOptions.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed on sync. By default, CRDs are installed if not already present") + f.BoolVar(&syncOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`) + f.BoolVar(&syncOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`) + f.BoolVar(&syncOptions.SkipDeps, "skip-deps", false, `skip running "helm repo update" and "helm dependency build"`) + f.BoolVar(&syncOptions.Wait, "wait", false, `Override helmDefaults.wait setting "helm upgrade --install --wait"`) + f.BoolVar(&syncOptions.WaitForJobs, "wait-for-jobs", false, `Override helmDefaults.waitForJobs setting "helm upgrade --install --wait-for-jobs"`) + + return cmd } diff --git a/cmd/template.go b/cmd/template.go index 4b6b98d1..a2d57306 100644 --- a/cmd/template.go +++ b/cmd/template.go @@ -3,75 +3,47 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addTemplateSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "template", - Usage: "template releases from state file against env (helm template)", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass args to helm template", - }, - cli.StringSliceFlag{ - Name: "set", - Usage: "additional values to be merged into the command", - }, - cli.StringSliceFlag{ - Name: "values", - Usage: "additional value files to be merged into the command", - }, - cli.StringFlag{ - Name: "output-dir", - Usage: "output directory to pass to helm template (helm template --output-dir)", - }, - cli.StringFlag{ - Name: "output-dir-template", - Usage: "go text template for generating the output directory. Default: {{ .OutputDir }}/{{ .State.BaseName }}-{{ .State.AbsPathSHA1 }}-{{ .Release.Name}}", - }, - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent downloads of release charts", - }, - cli.BoolFlag{ - Name: "validate", - Usage: "validate your manifests against the Kubernetes cluster you are currently pointing at. Note that this requires access to a Kubernetes cluster to obtain information necessary for validating, like the list of available API versions", - }, - cli.BoolFlag{ - Name: "include-crds", - Usage: "include CRDs in the templated output", - }, - cli.BoolFlag{ - Name: "skip-tests", - Usage: "skip tests from templated output", - }, - cli.BoolFlag{ - Name: "skip-needs", - Usage: `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`, - }, - cli.BoolFlag{ - Name: "include-needs", - Usage: `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`, - }, - cli.BoolFlag{ - Name: "include-transitive-needs", - Usage: `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`, - }, - cli.BoolFlag{ - Name: "skip-deps", - Usage: `skip running "helm repo update" and "helm dependency build"`, - }, - cli.BoolFlag{ - Name: "skip-cleanup", - Usage: "Stop cleaning up temporary values generated by helmfile and helm-secrets. Useful for debugging. Don't use in production for security", - }, +// NewTemplateCmd returm build subcmd +func NewTemplateCmd(globalCfg *config.GlobalImpl) *cobra.Command { + templateOptions := config.NewTemplateOptions() + templateImpl := config.NewTemplateImpl(globalCfg, templateOptions) + + cmd := &cobra.Command{ + Use: "template", + Short: "Template releases defined in state file", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(templateImpl.GlobalImpl) + if err != nil { + return err + } + + if err := templateImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(templateImpl) + return toCLIError(templateImpl.GlobalImpl, a.Template(templateImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Template(c) - }), - }) + } + + f := cmd.Flags() + f.StringVar(&templateOptions.Args, "args", "", "pass args to helm template") + f.StringArrayVar(&templateOptions.Set, "set", []string{}, "additional values to be merged into the command") + f.StringArrayVar(&templateOptions.Values, "values", []string{}, "additional value files to be merged into the command") + f.StringVar(&templateOptions.OutputDir, "output-dir", "", "output directory to pass to helm template (helm template --output-dir)") + f.StringVar(&templateOptions.OutputDirTemplate, "output-dir-template", "", "go text template for generating the output directory. Default: {{ .OutputDir }}/{{ .State.BaseName }}-{{ .State.AbsPathSHA1 }}-{{ .Release.Name}}") + f.IntVar(&templateOptions.Concurrency, "concurrency", 0, "maximum number of concurrent downloads of release charts") + f.BoolVar(&templateOptions.Validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. Note that this requires access to a Kubernetes cluster to obtain information necessary for validating, like the template of available API versions") + f.BoolVar(&templateOptions.IncludeCRDs, "include-crds", false, "include CRDs in the templated output") + f.BoolVar(&templateOptions.SkipTests, "skip-tests", false, "skip tests from templated output") + f.BoolVar(&templateOptions.SkipNeeds, "skip-needs", false, `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`) + f.BoolVar(&templateOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`) + f.BoolVar(&templateOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`) + f.BoolVar(&templateOptions.SkipDeps, "skip-deps", false, `skip running "helm repo update" and "helm dependency build"`) + f.BoolVar(&templateOptions.SkipCleanup, "skip-cleanup", false, "Stop cleaning up temporary values generated by helmfile and helm-secrets. Useful for debugging. Don't use in production for security") + + return cmd } diff --git a/cmd/test.go b/cmd/test.go index 3c4119e6..c4661cc4 100644 --- a/cmd/test.go +++ b/cmd/test.go @@ -3,44 +3,40 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addTestSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "test", - Usage: "test releases from state file (helm test)", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "cleanup", - Usage: "delete test pods upon completion", - }, - cli.BoolFlag{ - Name: "logs", - Usage: "Dump the logs from test pods (this runs after all tests are complete, but before any cleanup)", - }, - cli.StringFlag{ - Name: "args", - Value: "", - Usage: "pass additional args to helm exec", - }, - cli.IntFlag{ - Name: "timeout", - Value: 300, - Usage: "maximum time for tests to run before being considered failed", - }, - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent helm processes to run, 0 is unlimited", - }, - cli.BoolFlag{ - Name: "skip-deps", - Usage: `skip running "helm repo update" and "helm dependency build"`, - }, +// NewTestCmd returm build subcmd +func NewTestCmd(globalCfg *config.GlobalImpl) *cobra.Command { + testOptions := config.NewTestOptions() + testImpl := config.NewTestImpl(globalCfg, testOptions) + + cmd := &cobra.Command{ + Use: "test", + Short: "Test charts from state file (helm test)", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(testImpl.GlobalImpl) + if err != nil { + return err + } + + if err := testImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(testImpl) + return toCLIError(testImpl.GlobalImpl, a.Test(testImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.Test(c) - }), - }) + } + testImpl.Cmd = cmd + + f := cmd.Flags() + f.IntVar(&testOptions.Concurrency, "concurrency", 0, "maximum number of concurrent downloads of release charts") + f.BoolVar(&testOptions.SkipDeps, "skip-deps", testOptions.SkipDeps, `skip running "helm repo update" and "helm dependency build"`) + f.BoolVar(&testOptions.Cleanup, "cleanup", testOptions.Cleanup, "delete test pods upon completion") + f.BoolVar(&testOptions.Logs, "logs", testOptions.Logs, "Dump the logs from test pods (this runs after all tests are complete, but before any cleanup)") + f.StringVar(&testOptions.Args, "args", testOptions.Args, "pass args to helm exec") + f.IntVar(&testOptions.Timeout, "timeout", 300, "maximum time for tests to run before being considered failed") + + return cmd } diff --git a/cmd/version.go b/cmd/version.go index 0c55d847..3910437b 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,17 +1,22 @@ package cmd import ( - "github.com/urfave/cli" + "fmt" + + "github.com/helmfile/helmfile/pkg/app/version" + "github.com/spf13/cobra" ) -func addVersionSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "version", - Usage: "Show the version for Helmfile.", - ArgsUsage: "[command]", - Action: func(c *cli.Context) error { - cli.ShowVersion(c) +// NewVersionCmd returm build subcmd +func NewVersionCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "version", + Short: "Show the version for Helmfile.", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Helmfile version " + version.GetVersion()) return nil }, - }) + } + + return cmd } diff --git a/cmd/write-values.go b/cmd/write-values.go index b1117fbc..393f7196 100644 --- a/cmd/write-values.go +++ b/cmd/write-values.go @@ -3,38 +3,38 @@ package cmd import ( "github.com/helmfile/helmfile/pkg/app" "github.com/helmfile/helmfile/pkg/config" - "github.com/urfave/cli" + "github.com/spf13/cobra" ) -func addWriteValuesSubcommand(cliApp *cli.App) { - cliApp.Commands = append(cliApp.Commands, cli.Command{ - Name: "write-values", - Usage: "write values files for releases. Similar to `helmfile template`, write values files instead of manifests.", - Flags: []cli.Flag{ - cli.StringSliceFlag{ - Name: "set", - Usage: "additional values to be merged into the command", - }, - cli.StringSliceFlag{ - Name: "values", - Usage: "additional value files to be merged into the command", - }, - cli.StringFlag{ - Name: "output-file-template", - Usage: "go text template for generating the output file. Default: {{ .State.BaseName }}-{{ .State.AbsPathSHA1 }}/{{ .Release.Name}}.yaml", - }, - cli.IntFlag{ - Name: "concurrency", - Value: 0, - Usage: "maximum number of concurrent downloads of release charts", - }, - cli.BoolFlag{ - Name: "skip-deps", - Usage: `skip running "helm repo update" and "helm dependency build"`, - }, +// NewWriteValuesCmd returm build subcmd +func NewWriteValuesCmd(globalCfg *config.GlobalImpl) *cobra.Command { + writeValuesOptions := config.NewWriteValuesOptions() + writeValuesImpl := config.NewWriteValuesImpl(globalCfg, writeValuesOptions) + + cmd := &cobra.Command{ + Use: "write-values", + Short: "Write values files for releases. Similar to `helmfile template`, write values files instead of manifests.", + RunE: func(cmd *cobra.Command, args []string) error { + err := config.NewUrfaveCliConfigImplIns(writeValuesImpl.GlobalImpl) + if err != nil { + return err + } + + if err := writeValuesImpl.ValidateConfig(); err != nil { + return err + } + + a := app.New(writeValuesImpl) + return toCLIError(writeValuesImpl.GlobalImpl, a.WriteValues(writeValuesImpl)) }, - Action: Action(func(a *app.App, c config.ConfigImpl) error { - return a.WriteValues(c) - }), - }) + } + + f := cmd.Flags() + f.IntVar(&writeValuesOptions.Concurrency, "concurrency", 0, "maximum number of concurrent downloads of release charts") + f.BoolVar(&writeValuesOptions.SkipDeps, "skip-deps", writeValuesOptions.SkipDeps, `skip running "helm repo update" and "helm dependency build"`) + f.StringArrayVar(&writeValuesOptions.Set, "set", writeValuesOptions.Set, "additional values to be merged into the command") + f.StringArrayVar(&writeValuesOptions.Values, "values", writeValuesOptions.Values, "additional value files to be merged into the command") + f.StringVar(&writeValuesOptions.OutputFileTemplate, "output-file-template", writeValuesOptions.OutputFileTemplate, "go text template for generating the output file. Default: {{ .State.BaseName }}-{{ .State.AbsPathSHA1 }}/{{ .Release.Name}}.yaml") + + return cmd } diff --git a/go.mod b/go.mod index 132de0ec..4c124e45 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/mattn/go-isatty v0.0.14 github.com/r3labs/diff v1.1.0 github.com/spf13/cobra v1.5.0 + github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.0 github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939 github.com/urfave/cli v1.22.9 @@ -112,7 +113,6 @@ require ( github.com/shopspring/decimal v1.2.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/cast v1.4.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/ulikunitz/xz v0.5.8 // indirect go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a // indirect go.mozilla.org/sops/v3 v3.7.1 // indirect @@ -132,7 +132,7 @@ require ( gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/urfave/cli.v1 v1.20.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/main.go b/main.go index 801f0ec1..1fa6dac1 100644 --- a/main.go +++ b/main.go @@ -5,13 +5,23 @@ import ( "os" "github.com/helmfile/helmfile/cmd" + "github.com/helmfile/helmfile/pkg/config" + "github.com/urfave/cli" ) +func warning(format string, v ...interface{}) { + format = fmt.Sprintf("WARNING: %s\n", format) + fmt.Fprintf(os.Stderr, format, v...) +} + func main() { - rootCmd := cmd.RootCommand() - err := rootCmd.Run(os.Args) + globalConfig := new(config.GlobalOptions) + rootCmd, err := cmd.NewRootCmd(globalConfig, os.Args[1:]) if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) - os.Exit(3) + warning("%+v", err) + os.Exit(1) + } + if err := rootCmd.Execute(); err != nil { + cli.HandleExitCoder(err) } } diff --git a/pkg/app/app.go b/pkg/app/app.go index e04ae2a9..06a7e4cf 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -2059,7 +2059,7 @@ func (c context) wrapErrs(errs ...error) error { return nil } -func (a *App) ShowCacheDir(c ListConfigProvider) error { +func (a *App) ShowCacheDir(c CacheConfigProvider) error { fmt.Printf("Cache directory: %s\n", remote.CacheDir()) if !directoryExistsAt(remote.CacheDir()) { @@ -2076,7 +2076,7 @@ func (a *App) ShowCacheDir(c ListConfigProvider) error { return nil } -func (a *App) CleanCacheDir(c ListConfigProvider) error { +func (a *App) CleanCacheDir(c CacheConfigProvider) error { if !directoryExistsAt(remote.CacheDir()) { return nil } diff --git a/pkg/app/config.go b/pkg/app/config.go index 16b74d37..a9b59596 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -232,3 +232,5 @@ type interactive interface { type ListConfigProvider interface { Output() string } + +type CacheConfigProvider interface{} diff --git a/pkg/config/apply.go b/pkg/config/apply.go new file mode 100644 index 00000000..3bae4765 --- /dev/null +++ b/pkg/config/apply.go @@ -0,0 +1,197 @@ +package config + +import "fmt" + +// ApplyOptoons is the options for the apply command +type ApplyOptions struct { + // Set is a list of key value pairs to be merged into the command + Set []string + // Values is a list of value files to be merged into the command + Values []string + // Concurrency is the maximum number of concurrent helm processes to run + Concurrency int + // Validate is validate your manifests against the Kubernetes cluster you are currently pointing at. Note that this requires access to a Kubernetes cluster to obtain information necessary for validating, like the list of available API versions + Validate bool + // Context is the number of lines of context to show around changes + Context int + // Output is the output format for the diff plugin + Output string + // DetailedExitcode is true if the exit code should be 2 instead of 0 if there were changes detected and the changes were synced successfully + DetailedExitcode bool + // Args is the arguments to pass to helm exec + Args string + // DEPRECATED: Use skip-cleanup instead + RetainValuesFiles bool + // SkipCleanup is true if the cleanup of temporary values files should be skipped + SkipCleanup bool + // SkipCRDs is true if the CRDs should be skipped + SkipCRDs bool + // SkipNeeds is true if the needs should be skipped + SkipNeeds bool + // IncludeNeeds is true if the needs should be included + IncludeNeeds bool + // IncludeTransitiveNeeds is true if the transitive needs should be included + IncludeTransitiveNeeds bool + // SkipDiffOnInstall is true if the diff should be skipped on install + SkipDiffOnInstall bool + // IncludeTests is true if the tests should be included + IncludeTests bool + // Suppress is true if the output should be suppressed + Suppress []string + // SuppressSecrets is true if the secrets should be suppressed + SuppressSecrets bool + // SuppressDiff is true if the diff should be suppressed + ShowSecrets bool + // SkipDeps is true if the running "helm repo update" and "helm dependency build" should be skipped + SuppressDiff bool + // ShowSecrets is true if the secrets should be shown + SkipDeps bool + // Wait is true if the helm command should wait for the release to be deployed + Wait bool + // WaitForJobs is true if the helm command should wait for the jobs to be completed + WaitForJobs bool +} + +// NewApply creates a new Apply +func NewApplyOptions() *ApplyOptions { + return &ApplyOptions{} +} + +// ApplyImpl is impl for applyOptions +type ApplyImpl struct { + *GlobalImpl + *ApplyOptions +} + +// NewApplyImpl creates a new ApplyImpl +func NewApplyImpl(g *GlobalImpl, a *ApplyOptions) *ApplyImpl { + return &ApplyImpl{ + GlobalImpl: g, + ApplyOptions: a, + } +} + +// Set returns the set. +func (a *ApplyImpl) Set() []string { + return a.ApplyOptions.Set +} + +// Args returns the args. +func (a *ApplyImpl) Args() string { + args := a.ApplyOptions.Args + enableHelmDebug := a.GlobalImpl.Debug + + if enableHelmDebug { + args = fmt.Sprintf("%s %s", args, "--debug") + } + + return args +} + +// Concurrency returns the concurrency. +func (a *ApplyImpl) Concurrency() int { + return a.ApplyOptions.Concurrency +} + +// Context returns the context. +func (a *ApplyImpl) Context() int { + return a.ApplyOptions.Context +} + +// DetailedExitcode returns the detailed exitcode. +func (a *ApplyImpl) DetailedExitcode() bool { + return a.ApplyOptions.DetailedExitcode +} + +// DiffOutput returns the diff output. +func (a *ApplyImpl) DiffOutput() string { + return a.ApplyOptions.Output +} + +// IncludeNeeds returns the include needs. +func (a *ApplyImpl) IncludeNeeds() bool { + return a.ApplyOptions.IncludeNeeds || a.ApplyOptions.IncludeTransitiveNeeds +} + +// IncludeTests returns the include tests. +func (a *ApplyImpl) IncludeTests() bool { + return a.ApplyOptions.IncludeTests +} + +// IncludeTransitiveNeeds returns the include transitive needs. +func (a *ApplyImpl) IncludeTransitiveNeeds() bool { + return a.ApplyOptions.IncludeTransitiveNeeds +} + +// RetainValuesFiles returns the retain values files. +func (a *ApplyImpl) RetainValuesFiles() bool { + return a.ApplyOptions.RetainValuesFiles +} + +// ShowSecrets returns the show secrets. +func (a *ApplyImpl) ShowSecrets() bool { + return a.ApplyOptions.ShowSecrets +} + +// SkipCRDs returns the skip crds. +func (a *ApplyImpl) SkipCRDs() bool { + return a.ApplyOptions.SkipCRDs +} + +// SkipCleanup returns the skip cleanup. +func (a *ApplyImpl) SkipCleanup() bool { + return a.ApplyOptions.SkipCleanup +} + +// SkipDeps returns the skip deps. +func (a *ApplyImpl) SkipDeps() bool { + return a.ApplyOptions.SkipDeps +} + +// SkipDiffOnInstall returns the skip diff on install. +func (a *ApplyImpl) SkipDiffOnInstall() bool { + return a.ApplyOptions.SkipDiffOnInstall +} + +// SkipNeeds returns the skip needs. +func (a *ApplyImpl) SkipNeeds() bool { + if !a.ApplyOptions.IncludeNeeds { + return a.ApplyOptions.SkipNeeds + } + return false +} + +// Suppress returns the suppress. +func (a *ApplyImpl) Suppress() []string { + return a.ApplyOptions.Suppress +} + +// SuppressDiff returns the suppress diff. +func (a *ApplyImpl) SuppressDiff() bool { + return a.ApplyOptions.SuppressDiff +} + +// SuppressSecrets returns the suppress secrets. +func (a *ApplyImpl) SuppressSecrets() bool { + return a.ApplyOptions.SuppressSecrets +} + +// Validate returns the validate. +func (a *ApplyImpl) Validate() bool { + return a.ApplyOptions.Validate +} + +// Values returns the values. +func (a *ApplyImpl) Values() []string { + return a.ApplyOptions.Values +} + +// Wait returns the wait. +func (a *ApplyImpl) Wait() bool { + return a.ApplyOptions.Wait +} + +// WaitForJobs returns the wait for jobs. +func (a *ApplyImpl) WaitForJobs() bool { + return a.ApplyOptions.WaitForJobs +} diff --git a/pkg/config/build.go b/pkg/config/build.go new file mode 100644 index 00000000..6c3562cc --- /dev/null +++ b/pkg/config/build.go @@ -0,0 +1,36 @@ +package config + +// BuildOptions is the options for the build command +type BuildOptions struct { + // EmbedValues is true if the values should be embedded + EmbedValues bool +} + +// NewBuildOptions creates a new Apply +func NewBuildOptions() *BuildOptions { + return &BuildOptions{} +} + +// BuildImpl is impl for applyOptions +type BuildImpl struct { + *GlobalImpl + *BuildOptions +} + +// NewBuildImpl creates a new BuildImpl +func NewBuildImpl(g *GlobalImpl, b *BuildOptions) *BuildImpl { + return &BuildImpl{ + GlobalImpl: g, + BuildOptions: b, + } +} + +// Args returns the args. +func (b *BuildImpl) Args() string { + return "" +} + +// EmbedValues returns the embed values. +func (b *BuildImpl) EmbedValues() bool { + return b.BuildOptions.EmbedValues +} diff --git a/pkg/config/cache.go b/pkg/config/cache.go new file mode 100644 index 00000000..bf9b68fa --- /dev/null +++ b/pkg/config/cache.go @@ -0,0 +1,28 @@ +package config + +// CacheOptions is the options for the build command +type CacheOptions struct{} + +// NewCacheOptions creates a new Apply +func NewCacheOptions() *CacheOptions { + return &CacheOptions{} +} + +// CacheImpl is impl for applyOptions +type CacheImpl struct { + *GlobalImpl + *CacheOptions +} + +// NewCacheImpl creates a new CacheImpl +func NewCacheImpl(g *GlobalImpl, b *CacheOptions) *CacheImpl { + return &CacheImpl{ + GlobalImpl: g, + CacheOptions: b, + } +} + +// Args returns the args. +func (b *CacheImpl) Args() string { + return "" +} diff --git a/pkg/config/charts.go b/pkg/config/charts.go new file mode 100644 index 00000000..e615b1c2 --- /dev/null +++ b/pkg/config/charts.go @@ -0,0 +1,52 @@ +package config + +// ChartsOptions is the options for the build command +type ChartsOptions struct { + // Args is the args to pass to helm exec + Args string + // Set is the additional values to be merged into the command + Set []string + // Values is the additional value files to be merged into the command + Values []string + // Concurrency is the maximum number of concurrent helm processes to run, 0 is unlimited + Concurrency int +} + +// NewChartsOptions creates a new Apply +func NewChartsOptions() *ChartsOptions { + return &ChartsOptions{} +} + +// ChartsImpl is impl for applyOptions +type ChartsImpl struct { + *GlobalImpl + *ChartsOptions +} + +// NewChartsImpl creates a new ChartsImpl +func NewChartsImpl(g *GlobalImpl, b *ChartsOptions) *ChartsImpl { + return &ChartsImpl{ + GlobalImpl: g, + ChartsOptions: b, + } +} + +// Concurrency returns the concurrency +func (c *ChartsImpl) Concurrency() int { + return c.ChartsOptions.Concurrency +} + +// Args returns the args +func (c *ChartsImpl) Args() string { + return c.ChartsOptions.Args +} + +// IncludeTransitiveNeeds returns the includeTransitiveNeeds +func (c *ChartsImpl) IncludeTransitiveNeeds() bool { + return false +} + +// Values returns the values +func (c *ChartsImpl) Values() []string { + return c.ChartsOptions.Values +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 9e4192d7..242f3341 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -51,6 +51,26 @@ func NewUrfaveCliConfigImpl(c *cli.Context) (ConfigImpl, error) { return conf, nil } +func NewUrfaveCliConfigImplIns(g *GlobalImpl) error { + optsSet := g.RawStateValuesSet() + if len(optsSet) > 0 { + set := map[string]interface{}{} + for i := range optsSet { + ops := strings.Split(optsSet[i], ",") + for j := range ops { + op := strings.SplitN(ops[j], "=", 2) + k := maputil.ParseKey(op[0]) + v := op[1] + + maputil.Set(set, k, v) + } + } + g.SetSet(set) + } + + return nil +} + func (c ConfigImpl) Set() []string { return c.c.StringSlice("set") } diff --git a/pkg/config/delete.go b/pkg/config/delete.go new file mode 100644 index 00000000..3778512f --- /dev/null +++ b/pkg/config/delete.go @@ -0,0 +1,52 @@ +package config + +// DeleteOptions is the options for the build command +type DeleteOptions struct { + // Args is the args to pass to helm exec + Args string + // Concurrency is the maximum number of concurrent helm processes to run, 0 is unlimited + Concurrency int + // Purge is the purge flag + Purge bool + // SkipDeps is the skip deps flag + SkipDeps bool +} + +// NewDeleteOptions creates a new Apply +func NewDeleteOptions() *DeleteOptions { + return &DeleteOptions{} +} + +// DeleteImpl is impl for applyOptions +type DeleteImpl struct { + *GlobalImpl + *DeleteOptions +} + +// NewDeleteImpl creates a new DeleteImpl +func NewDeleteImpl(g *GlobalImpl, b *DeleteOptions) *DeleteImpl { + return &DeleteImpl{ + GlobalImpl: g, + DeleteOptions: b, + } +} + +// Concurrency returns the concurrency +func (c *DeleteImpl) Concurrency() int { + return c.DeleteOptions.Concurrency +} + +// Args returns the args +func (c *DeleteImpl) Args() string { + return c.DeleteOptions.Args +} + +// Purge returns the purge +func (c *DeleteImpl) Purge() bool { + return c.DeleteOptions.Purge +} + +// SkipDeps returns the skip deps +func (c *DeleteImpl) SkipDeps() bool { + return c.DeleteOptions.SkipDeps +} diff --git a/pkg/config/deps.go b/pkg/config/deps.go new file mode 100644 index 00000000..966aaabb --- /dev/null +++ b/pkg/config/deps.go @@ -0,0 +1,43 @@ +package config + +// DepsOptions is the options for the build command +type DepsOptions struct { + // Args is the args to pass to helm exec + Args string + // SkipRepos is the skip repos flag + SkipRepos bool +} + +// NewDepsOptions creates a new Apply +func NewDepsOptions() *DepsOptions { + return &DepsOptions{} +} + +// DepsImpl is impl for applyOptions +type DepsImpl struct { + *GlobalImpl + *DepsOptions +} + +// NewDepsImpl creates a new DepsImpl +func NewDepsImpl(g *GlobalImpl, b *DepsOptions) *DepsImpl { + return &DepsImpl{ + GlobalImpl: g, + DepsOptions: b, + } +} + +// Args returns the args +func (d *DepsImpl) Args() string { + return d.DepsOptions.Args +} + +// SkipDeps returns the skip deps +func (d *DepsImpl) SkipRepos() bool { + return d.DepsOptions.SkipRepos +} + +// IncludeTransitiveNeeds returns the includeTransitiveNeeds +func (d *DepsImpl) IncludeTransitiveNeeds() bool { + return false +} diff --git a/pkg/config/destroy.go b/pkg/config/destroy.go new file mode 100644 index 00000000..5cfbaf26 --- /dev/null +++ b/pkg/config/destroy.go @@ -0,0 +1,45 @@ +package config + +// DestroyOptions is the options for the build command +type DestroyOptions struct { + // Args is the args to pass to helm exec + Args string + // Concurrency is the maximum number of concurrent helm processes to run, 0 is unlimited + Concurrency int + // SkipDeps is the skip deps flag + SkipDeps bool +} + +// NewDestroyOptions creates a new Apply +func NewDestroyOptions() *DestroyOptions { + return &DestroyOptions{} +} + +// DestroyImpl is impl for applyOptions +type DestroyImpl struct { + *GlobalImpl + *DestroyOptions +} + +// NewDestroyImpl creates a new DestroyImpl +func NewDestroyImpl(g *GlobalImpl, b *DestroyOptions) *DestroyImpl { + return &DestroyImpl{ + GlobalImpl: g, + DestroyOptions: b, + } +} + +// Concurrency returns the concurrency +func (c *DestroyImpl) Concurrency() int { + return c.DestroyOptions.Concurrency +} + +// Args returns the args +func (c *DestroyImpl) Args() string { + return c.DestroyOptions.Args +} + +// SkipDeps returns the skip deps +func (c *DestroyImpl) SkipDeps() bool { + return c.DestroyOptions.SkipDeps +} diff --git a/pkg/config/diff.go b/pkg/config/diff.go new file mode 100644 index 00000000..69f22e08 --- /dev/null +++ b/pkg/config/diff.go @@ -0,0 +1,157 @@ +package config + +// DiffOptions is the options for the build command +type DiffOptions struct { + // Args is the args to pass to helm template + Args string + // Set is the set flag + Set []string + // Values is the values flag + Values []string + // SkipDeps is the skip deps flag + SkipDeps bool + // DetailedExitcode is the detailed exit code + DetailedExitcode bool + // IncludeTests is the include tests flag + IncludeTests bool + // SkipNeeds is the include crds flag + SkipNeeds bool + // IncludeNeeds is the include needs flag + IncludeNeeds bool + // IncludeTransitiveNeeds is the include transitive needs flag + IncludeTransitiveNeeds bool + // SkipDiffOnInstall is the skip diff on install flag + SkipDiffOnInstall bool + // ShowSecrets is the show secrets flag + ShowSecrets bool + // Suppress is the suppress flag + Suppress []string + // SuppressSecrets is the suppress secrets flag + SuppressSecrets bool + // Concurrency is the concurrency flag + Concurrency int + // Validate is the validate flag + Validate bool + // Context is the context flag + Context int + // Output is output flag + Output string +} + +// NewDiffOptions creates a new Apply +func NewDiffOptions() *DiffOptions { + return &DiffOptions{} +} + +// DiffImpl is impl for applyOptions +type DiffImpl struct { + *GlobalImpl + *DiffOptions +} + +// NewDiffImpl creates a new DiffImpl +func NewDiffImpl(g *GlobalImpl, t *DiffOptions) *DiffImpl { + return &DiffImpl{ + GlobalImpl: g, + DiffOptions: t, + } +} + +// Args returns the args +func (t *DiffImpl) Args() string { + return t.DiffOptions.Args +} + +// Concurrency returns the concurrency +func (t *DiffImpl) Concurrency() int { + return t.DiffOptions.Concurrency +} + +// IncludeNeeds returns the include needs +func (t *DiffImpl) IncludeNeeds() bool { + return t.DiffOptions.IncludeNeeds || t.DiffOptions.IncludeTransitiveNeeds +} + +// IncludeTransitiveNeeds returns the include transitive needs +func (t *DiffImpl) IncludeTransitiveNeeds() bool { + return t.DiffOptions.IncludeTransitiveNeeds +} + +// Set returns the Set +func (t *DiffImpl) Set() []string { + return t.DiffOptions.Set +} + +// SkipDeps returns the skip deps +func (t *DiffImpl) SkipDeps() bool { + return t.DiffOptions.SkipDeps +} + +// SkipNeeds returns the skip needs +func (t *DiffImpl) SkipNeeds() bool { + if !t.DiffOptions.IncludeNeeds { + return t.DiffOptions.SkipNeeds + } + + return false +} + +// Validate returns the validate +func (t *DiffImpl) Validate() bool { + return t.DiffOptions.Validate +} + +// Values returns the values +func (t *DiffImpl) Values() []string { + return t.DiffOptions.Values +} + +// Context returns the context +func (t *DiffImpl) Context() int { + return 0 +} + +// DetailedExitCode returns the detailed exit code +func (t *DiffImpl) DetailedExitcode() bool { + return t.DiffOptions.DetailedExitcode +} + +// Output returns the output +func (t *DiffImpl) DiffOutput() string { + return "" +} + +// IncludeTests returns the include tests +func (t *DiffImpl) IncludeTests() bool { + return t.DiffOptions.IncludeTests +} + +// ShowSecrets returns the show secrets +func (t *DiffImpl) ShowSecrets() bool { + return t.DiffOptions.ShowSecrets +} + +// ShowCRDs returns the show crds +func (t *DiffImpl) SkipCRDs() bool { + return false +} + +// SkipDiffOnInstall returns the skip diff on install +func (t *DiffImpl) SkipDiffOnInstall() bool { + return t.DiffOptions.SkipDiffOnInstall +} + +// Suppress returns the suppress +func (t *DiffImpl) Suppress() []string { + return t.DiffOptions.Suppress +} + +// SuppressDiff returns the suppress diff +func (t *DiffImpl) SuppressDiff() bool { + return false +} + +// SuppressSecrets returns the suppress secrets +func (t *DiffImpl) SuppressSecrets() bool { + return t.DiffOptions.SuppressSecrets +} diff --git a/pkg/config/fetch.go b/pkg/config/fetch.go new file mode 100644 index 00000000..1b2d9ccb --- /dev/null +++ b/pkg/config/fetch.go @@ -0,0 +1,50 @@ +package config + +// FetchOptions is the options for the build command +type FetchOptions struct { + // Concurrency is the maximum number of concurrent helm processes to run, 0 is unlimited + Concurrency int + // SkipDeps is the skip deps flag + SkipDeps bool + // OutputDir is the output directory + OutputDir string +} + +// NewFetchOptions creates a new Apply +func NewFetchOptions() *FetchOptions { + return &FetchOptions{} +} + +// FetchImpl is impl for applyOptions +type FetchImpl struct { + *GlobalImpl + *FetchOptions +} + +// NewFetchImpl creates a new FetchImpl +func NewFetchImpl(g *GlobalImpl, b *FetchOptions) *FetchImpl { + return &FetchImpl{ + GlobalImpl: g, + FetchOptions: b, + } +} + +// Concurrency returns the concurrency +func (c *FetchImpl) Concurrency() int { + return c.FetchOptions.Concurrency +} + +// SkipDeps returns the skip deps +func (c *FetchImpl) SkipDeps() bool { + return c.FetchOptions.SkipDeps +} + +// Args returns the args +func (c *FetchImpl) Args() string { + return "" +} + +// OutputDir returns the args +func (c *FetchImpl) OutputDir() string { + return c.FetchOptions.OutputDir +} diff --git a/pkg/config/global.go b/pkg/config/global.go new file mode 100644 index 00000000..68a66138 --- /dev/null +++ b/pkg/config/global.go @@ -0,0 +1,180 @@ +package config + +import ( + "errors" + "os" + + "github.com/helmfile/helmfile/pkg/state" + "go.uber.org/zap" + "golang.org/x/term" +) + +// GlobalOptions is the global configuration for the Helmfile CLI. +type GlobalOptions struct { + // helmBinary is the path to the Helm binary. + HelmBinary string + // File is the path to the Helmfile. + File string + // Environment is the name of the environment to use. + Environment string + // StateValuesSet is a list of state values to set on the command line. + StateValuesSet []string + // StateValuesFiles is a list of state values files to use. + StateValuesFile []string + // Quiet is true if the output should be quiet. + Quiet bool + // KubeContext is the name of the kubectl context to use. + KubeContext string + // Debug is true if the output should be verbose. + Debug bool + // Color is true if the output should be colorized. + Color bool + // NoColor is true if the output should not be colorized. + NoColor bool + // LogLevel is the log level to use. + LogLevel string + // Namespace is the namespace to use. + Namespace string + // Chart is the chart to use. + Chart string + // Selector is a list of selectors to use. + Selector []string + // AllowNoMatchingRelease is not exit with an error code if the provided selector has no matching releases. + AllowNoMatchingRelease bool + // Interactive is true if the user should be prompted for input. + Interactive bool + // logger is the logger to use. + logger *zap.SugaredLogger +} + +// Logger returns the logger to use. +func (g *GlobalOptions) Logger() *zap.SugaredLogger { + return g.logger +} + +// GetLogLevel returns the log level to use. +func (g *GlobalOptions) SetLogger(logger *zap.SugaredLogger) { + g.logger = logger +} + +// GlobalImpl is the global configuration for the Helmfile CLI. +type GlobalImpl struct { + *GlobalOptions + set map[string]interface{} +} + +// NewGlobalImpl creates a new GlobalImpl. +func NewGlobalImpl(opts *GlobalOptions) *GlobalImpl { + return &GlobalImpl{ + GlobalOptions: opts, + set: make(map[string]interface{}), + } +} + +// Setset sets the set +func (g *GlobalImpl) SetSet(set map[string]interface{}) { + g.set = set +} + +// HelmBinary returns the path to the Helm binary. +func (g *GlobalImpl) HelmBinary() string { + return g.GlobalOptions.HelmBinary +} + +// KubeContext returns the name of the kubectl context to use. +func (g *GlobalImpl) KubeContext() string { + return g.GlobalOptions.KubeContext +} + +// Namespace returns the namespace to use. +func (g *GlobalImpl) Namespace() string { + return g.GlobalOptions.Namespace +} + +// Chart returns the chart to use. +func (g *GlobalImpl) Chart() string { + return g.GlobalOptions.Chart +} + +// FileOrDir returns the path to the Helmfile. +func (g *GlobalImpl) FileOrDir() string { + return g.GlobalOptions.File +} + +// Selectors returns the selectors to use. +func (g *GlobalImpl) Selectors() []string { + return g.GlobalOptions.Selector +} + +// StateValuesSet returns the set +func (g *GlobalImpl) StateValuesSet() map[string]interface{} { + return g.set +} + +// StateValuesSet returns the set +func (g *GlobalImpl) RawStateValuesSet() []string { + return g.GlobalOptions.StateValuesSet +} + +// StateValuesFiles returns the state values files +func (g *GlobalImpl) StateValuesFiles() []string { + return g.GlobalOptions.StateValuesFile +} + +// Interactive return interactive mode +func (g *GlobalImpl) Interactive() bool { + return g.GlobalOptions.Interactive +} + +// Logger returns the logger +func (g *GlobalImpl) Logger() *zap.SugaredLogger { + return g.GlobalOptions.logger +} + +func (g *GlobalImpl) Color() bool { + if c := g.GlobalOptions.Color; c { + return c + } + + if g.GlobalOptions.NoColor { + return false + } + + // We replicate the helm-diff behavior in helmfile + // because when when helmfile calls helm-diff, helm-diff has no access to term and therefore + // we can't rely on helm-diff's ability to auto-detect term for color output. + // See https://github.com/roboll/helmfile/issues/2043 + + terminal := term.IsTerminal(int(os.Stdout.Fd())) + // https://github.com/databus23/helm-diff/issues/281 + dumb := os.Getenv("TERM") == "dumb" + return terminal && !dumb +} + +// NoColor returns the no color flag +func (g *GlobalImpl) NoColor() bool { + return g.GlobalOptions.NoColor +} + +// Env returns the environment to use. +func (g *GlobalImpl) Env() string { + var env string + + switch { + case g.GlobalOptions.Environment != "": + env = g.GlobalOptions.Environment + case os.Getenv("HELMFILE_ENVIRONMENT") != "": + env = os.Getenv("HELMFILE_ENVIRONMENT") + default: + env = state.DefaultEnv + } + return env +} + +// ValidateConfig validates the global options. +func (g *GlobalImpl) ValidateConfig() error { + if g.NoColor() && g.Color() { + return errors.New("--color and --no-color cannot be specified at the same time") + } + return nil +} diff --git a/pkg/config/lint.go b/pkg/config/lint.go new file mode 100644 index 00000000..4017b2ae --- /dev/null +++ b/pkg/config/lint.go @@ -0,0 +1,79 @@ +package config + +// LintOptions is the options for the build command +type LintOptions struct { + // Concurrency is the maximum number of concurrent helm processes to run, 0 is unlimited + Concurrency int + // SkipDeps is the skip deps flag + SkipDeps bool + // Args is the args to pass to helm lint + Args string + // Set is the set flags to pass to helm lint + Set []string + // Values is the values flags to pass to helm lint + Values []string +} + +// NewLintOptions creates a new Apply +func NewLintOptions() *LintOptions { + return &LintOptions{} +} + +// LintImpl is impl for applyOptions +type LintImpl struct { + *GlobalImpl + *LintOptions +} + +// NewLintImpl creates a new LintImpl +func NewLintImpl(g *GlobalImpl, b *LintOptions) *LintImpl { + return &LintImpl{ + GlobalImpl: g, + LintOptions: b, + } +} + +// Concurrency returns the concurrency +func (c *LintImpl) Concurrency() int { + return c.LintOptions.Concurrency +} + +// SkipDeps returns the skip deps +func (c *LintImpl) SkipDeps() bool { + return c.LintOptions.SkipDeps +} + +// Args returns the args +func (c *LintImpl) Args() string { + return c.LintOptions.Args +} + +// Set returns the Set +func (c *LintImpl) Set() []string { + return c.LintOptions.Set +} + +// Values returns the Values +func (c *LintImpl) Values() []string { + return c.LintOptions.Values +} + +// SkipCleanUp returns the skip clean up +func (c *LintImpl) SkipCleanup() bool { + return false +} + +// SkipNeeds returns the skip needs +func (c *LintImpl) SkipNeeds() bool { + return false +} + +// IncludeNeeds returns the include needs +func (c *LintImpl) IncludeNeeds() bool { + return false +} + +// IncludeTransitiveNeeds returns the include transitive needs +func (c *LintImpl) IncludeTransitiveNeeds() bool { + return false +} diff --git a/pkg/config/list.go b/pkg/config/list.go new file mode 100644 index 00000000..1529a742 --- /dev/null +++ b/pkg/config/list.go @@ -0,0 +1,38 @@ +package config + +// ListOptions is the options for the build command +type ListOptions struct { + // Output is the output format + Output string + // KeepTempDir is the keep temp dir flag + KeepTempDir bool +} + +// NewListOptions creates a new Apply +func NewListOptions() *ListOptions { + return &ListOptions{} +} + +// ListImpl is impl for applyOptions +type ListImpl struct { + *GlobalImpl + *ListOptions +} + +// NewListImpl creates a new ListImpl +func NewListImpl(g *GlobalImpl, b *ListOptions) *ListImpl { + return &ListImpl{ + GlobalImpl: g, + ListOptions: b, + } +} + +// Args returns the args +func (c *ListImpl) Args() string { + return "" +} + +// Output returns the output +func (c *ListImpl) Output() string { + return c.ListOptions.Output +} diff --git a/pkg/config/repos.go b/pkg/config/repos.go new file mode 100644 index 00000000..78838db1 --- /dev/null +++ b/pkg/config/repos.go @@ -0,0 +1,36 @@ +package config + +// ReposOptions is the options for the build command +type ReposOptions struct { + // Args is the args + Args string +} + +// NewReposOptions creates a new Apply +func NewReposOptions() *ReposOptions { + return &ReposOptions{} +} + +// ReposImpl is impl for applyOptions +type ReposImpl struct { + *GlobalImpl + *ReposOptions +} + +// NewReposImpl creates a new ReposImpl +func NewReposImpl(g *GlobalImpl, b *ReposOptions) *ReposImpl { + return &ReposImpl{ + GlobalImpl: g, + ReposOptions: b, + } +} + +// Args returns the args +func (r *ReposImpl) Args() string { + return r.ReposOptions.Args +} + +// IncludeTransitiveNeeds returns the include transitive needs +func (r *ReposImpl) IncludeTransitiveNeeds() bool { + return false +} diff --git a/pkg/config/status.go b/pkg/config/status.go new file mode 100644 index 00000000..63afadd3 --- /dev/null +++ b/pkg/config/status.go @@ -0,0 +1,43 @@ +package config + +// StatusOptions is the options for the build command +type StatusOptions struct { + // Args is the args + Args string + // Concurrency is the concurrent flag + Concurrency int +} + +// NewStatusOptions creates a new Apply +func NewStatusOptions() *StatusOptions { + return &StatusOptions{} +} + +// StatusImpl is impl for applyOptions +type StatusImpl struct { + *GlobalImpl + *StatusOptions +} + +// NewStatusImpl creates a new StatusImpl +func NewStatusImpl(g *GlobalImpl, b *StatusOptions) *StatusImpl { + return &StatusImpl{ + GlobalImpl: g, + StatusOptions: b, + } +} + +// Args returns the args +func (s *StatusImpl) Args() string { + return s.StatusOptions.Args +} + +// IncludeTransitiveNeeds returns the include transitive needs +func (s *StatusImpl) IncludeTransitiveNeeds() bool { + return false +} + +// Concurrency returns the concurrency +func (s *StatusImpl) Concurrency() int { + return s.StatusOptions.Concurrency +} diff --git a/pkg/config/sync.go b/pkg/config/sync.go new file mode 100644 index 00000000..5932f3b4 --- /dev/null +++ b/pkg/config/sync.go @@ -0,0 +1,112 @@ +package config + +// SyncOptions is the options for the build command +type SyncOptions struct { + // Args is the args to pass to helm template + Args string + // Set is the set flag + Set []string + // Values is the values flag + Values []string + // Concurrency is the concurrency flag + Concurrency int + // Validate is the validate flag + Validate bool + // IncludeCRDs is the include crds flag + SkipNeeds bool + // IncludeNeeds is the include needs flag + IncludeNeeds bool + // IncludeTransitiveNeeds is the include transitive needs flag + IncludeTransitiveNeeds bool + // SkipDeps is the skip deps flag + SkipDeps bool + // SkipCrds is the skip crds flag + SkipCRDs bool + // Wait is the wait flag + Wait bool + // WaitForJobs is the wait for jobs flag + WaitForJobs bool +} + +// NewSyncOptions creates a new Apply +func NewSyncOptions() *SyncOptions { + return &SyncOptions{} +} + +// SyncImpl is impl for applyOptions +type SyncImpl struct { + *GlobalImpl + *SyncOptions +} + +// NewSyncImpl creates a new SyncImpl +func NewSyncImpl(g *GlobalImpl, t *SyncOptions) *SyncImpl { + return &SyncImpl{ + GlobalImpl: g, + SyncOptions: t, + } +} + +// Args returns the args +func (t *SyncImpl) Args() string { + return t.SyncOptions.Args +} + +// Concurrency returns the concurrency +func (t *SyncImpl) Concurrency() int { + return t.SyncOptions.Concurrency +} + +// IncludeNeeds returns the include needs +func (t *SyncImpl) IncludeNeeds() bool { + return t.SyncOptions.IncludeNeeds || t.SyncOptions.IncludeTransitiveNeeds +} + +// IncludeTransitiveNeeds returns the include transitive needs +func (t *SyncImpl) IncludeTransitiveNeeds() bool { + return t.SyncOptions.IncludeTransitiveNeeds +} + +// Set returns the Set +func (t *SyncImpl) Set() []string { + return t.SyncOptions.Set +} + +// SkipDeps returns the skip deps +func (t *SyncImpl) SkipDeps() bool { + return t.SyncOptions.SkipDeps +} + +// SkipNeeds returns the skip needs +func (t *SyncImpl) SkipNeeds() bool { + if !t.SyncOptions.IncludeNeeds { + return t.SyncOptions.SkipNeeds + } + + return false +} + +// Validate returns the validate +func (t *SyncImpl) Validate() bool { + return t.SyncOptions.Validate +} + +// Values returns the values +func (t *SyncImpl) Values() []string { + return t.SyncOptions.Values +} + +// SkipCRDS returns the skip crds +func (t *SyncImpl) SkipCRDs() bool { + return t.SyncOptions.SkipCRDs +} + +// Wait returns the wait +func (t *SyncImpl) Wait() bool { + return t.SyncOptions.Wait +} + +// WaitForJobs returns the wait for jobs +func (t *SyncImpl) WaitForJobs() bool { + return t.SyncOptions.WaitForJobs +} diff --git a/pkg/config/template.go b/pkg/config/template.go new file mode 100644 index 00000000..21d7bb8d --- /dev/null +++ b/pkg/config/template.go @@ -0,0 +1,132 @@ +package config + +import ( + "fmt" + "os" + "strings" +) + +// TemplateOptions is the options for the build command +type TemplateOptions struct { + // Args is the args to pass to helm template + Args string + // Set is the set flag + Set []string + // Values is the values flag + Values []string + // OutputDir is the output dir flag + OutputDir string + // OutputDirTemplate is the output dir template flag + OutputDirTemplate string + // Concurrency is the concurrency flag + Concurrency int + // Validate is the validate flag + Validate bool + // IncludeCRDs is the include crds flag + IncludeCRDs bool + // SkipTests is the skip tests flag + SkipTests bool + // SkipNeeds is the skip needs flag + SkipNeeds bool + // IncludeNeeds is the include needs flag + IncludeNeeds bool + // IncludeTransitiveNeeds is the include transitive needs flag + IncludeTransitiveNeeds bool + // SkipDeps is the skip deps flag + SkipDeps bool + // SkipCleanup is the skip cleanup flag + SkipCleanup bool +} + +// NewTemplateOptions creates a new Apply +func NewTemplateOptions() *TemplateOptions { + return &TemplateOptions{} +} + +// TemplateImpl is impl for applyOptions +type TemplateImpl struct { + *GlobalImpl + *TemplateOptions +} + +// NewTemplateImpl creates a new TemplateImpl +func NewTemplateImpl(g *GlobalImpl, t *TemplateOptions) *TemplateImpl { + return &TemplateImpl{ + GlobalImpl: g, + TemplateOptions: t, + } +} + +// Args returns the args +func (t *TemplateImpl) Args() string { + return t.TemplateOptions.Args +} + +// Concurrency returns the concurrency +func (t *TemplateImpl) Concurrency() int { + return t.TemplateOptions.Concurrency +} + +// IncludeCRDs returns the include crds +func (t *TemplateImpl) IncludeCRDs() bool { + return t.TemplateOptions.IncludeCRDs +} + +// IncludeNeeds returns the include needs +func (t *TemplateImpl) IncludeNeeds() bool { + return t.TemplateOptions.IncludeNeeds || t.TemplateOptions.IncludeTransitiveNeeds +} + +// IncludeTransitiveNeeds returns the include transitive needs +func (t *TemplateImpl) IncludeTransitiveNeeds() bool { + return t.TemplateOptions.IncludeTransitiveNeeds +} + +// OutputDir returns the output dir +func (t *TemplateImpl) OutputDir() string { + return strings.TrimRight(t.TemplateOptions.OutputDir, fmt.Sprintf("%c", os.PathSeparator)) +} + +// OutputDirTemplate returns the output dir template +func (t *TemplateImpl) OutputDirTemplate() string { + return t.TemplateOptions.OutputDirTemplate +} + +// Set returns the Set +func (t *TemplateImpl) Set() []string { + return t.TemplateOptions.Set +} + +// SkipCleanup returns the skip cleanup +func (t *TemplateImpl) SkipCleanup() bool { + return t.TemplateOptions.SkipCleanup +} + +// SkipDeps returns the skip deps +func (t *TemplateImpl) SkipDeps() bool { + return t.TemplateOptions.SkipDeps +} + +// SkipNeeds returns the skip needs +func (t *TemplateImpl) SkipNeeds() bool { + if !t.TemplateOptions.IncludeNeeds { + return t.TemplateOptions.SkipNeeds + } + + return false +} + +// SkipTests returns the skip tests +func (t *TemplateImpl) SkipTests() bool { + return t.TemplateOptions.SkipTests +} + +// Validate returns the validate +func (t *TemplateImpl) Validate() bool { + return t.TemplateOptions.Validate +} + +// Values returns the values +func (t *TemplateImpl) Values() []string { + return t.TemplateOptions.Values +} diff --git a/pkg/config/test.go b/pkg/config/test.go new file mode 100644 index 00000000..a992632a --- /dev/null +++ b/pkg/config/test.go @@ -0,0 +1,75 @@ +package config + +import ( + "github.com/helmfile/helmfile/pkg/state" + "github.com/spf13/cobra" +) + +// TestOptions is the options for the build command +type TestOptions struct { + // Concurrency is the maximum number of concurrent helm processes to run, 0 is unlimited + Concurrency int + // SkipDeps is the skip deps flag + SkipDeps bool + // Args is the args to pass to helm lint + Args string + // Cleanup is the cleanup flag + Cleanup bool + // Logs is the logs flagj + Logs bool + // Timeout is the timeout flag + Timeout int +} + +// NewTestOptions creates a new Apply +func NewTestOptions() *TestOptions { + return &TestOptions{} +} + +// TestImpl is impl for applyOptions +type TestImpl struct { + *GlobalImpl + *TestOptions + Cmd *cobra.Command +} + +// NewTestImpl creates a new TestImpl +func NewTestImpl(g *GlobalImpl, t *TestOptions) *TestImpl { + return &TestImpl{ + GlobalImpl: g, + TestOptions: t, + } +} + +// Concurrency returns the concurrency +func (t *TestImpl) Concurrency() int { + return t.TestOptions.Concurrency +} + +// SkipDeps returns the skip deps +func (t *TestImpl) SkipDeps() bool { + return t.TestOptions.SkipDeps +} + +// Args returns the args +func (t *TestImpl) Args() string { + return t.TestOptions.Args +} + +// Cleanup returns the cleanup +func (t *TestImpl) Cleanup() bool { + return t.TestOptions.Cleanup +} + +// Logs returns the logs +func (t *TestImpl) Logs() bool { + return t.TestOptions.Logs +} + +// Timeout returns the timeout +func (t *TestImpl) Timeout() int { + if !t.Cmd.Flags().Changed("timeout") { + return state.EmptyTimeout + } + return t.TestOptions.Timeout +} diff --git a/pkg/config/write-values.go b/pkg/config/write-values.go new file mode 100644 index 00000000..3539b782 --- /dev/null +++ b/pkg/config/write-values.go @@ -0,0 +1,74 @@ +package config + +// WriteValuesOptions is the options for the build command +type WriteValuesOptions struct { + // Concurrency is the maximum number of concurrent helm processes to run, 0 is unlimited + Concurrency int + // SkipDeps is the skip deps flag + SkipDeps bool + // Set is the set flags to pass to helm write values + Set []string + // Values is the values flags to pass to helm write values + Values []string + // OutputFileTemplate is the output file template + OutputFileTemplate string +} + +// NewWriteValuesOptions creates a new Apply +func NewWriteValuesOptions() *WriteValuesOptions { + return &WriteValuesOptions{} +} + +// WriteValuesImpl is impl for applyOptions +type WriteValuesImpl struct { + *GlobalImpl + *WriteValuesOptions +} + +// NewWriteValuesImpl creates a new WriteValuesImpl +func NewWriteValuesImpl(g *GlobalImpl, b *WriteValuesOptions) *WriteValuesImpl { + return &WriteValuesImpl{ + GlobalImpl: g, + WriteValuesOptions: b, + } +} + +// Concurrency returns the concurrency +func (c *WriteValuesImpl) Concurrency() int { + return c.WriteValuesOptions.Concurrency +} + +// SkipDeps returns the skip deps +func (c *WriteValuesImpl) SkipDeps() bool { + return c.WriteValuesOptions.SkipDeps +} + +// Set returns the Set +func (c *WriteValuesImpl) Set() []string { + return c.WriteValuesOptions.Set +} + +// Values returns the Values +func (c *WriteValuesImpl) Values() []string { + return c.WriteValuesOptions.Values +} + +// SkipCleanUp returns the skip clean up +func (c *WriteValuesImpl) SkipCleanup() bool { + return false +} + +// IncludeTransitiveNeeds returns the include transitive needs +func (c *WriteValuesImpl) IncludeTransitiveNeeds() bool { + return false +} + +// OutputFileTemplate returns the output file template +func (c *WriteValuesImpl) OutputFileTemplate() string { + return c.WriteValuesOptions.OutputFileTemplate +} + +// Args returns the args +func (c *WriteValuesImpl) Args() string { + return "" +}