1061 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			1061 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Go
		
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/roboll/helmfile/pkg/app"
 | |
| 	"github.com/roboll/helmfile/pkg/app/version"
 | |
| 	"github.com/roboll/helmfile/pkg/helmexec"
 | |
| 	"github.com/roboll/helmfile/pkg/maputil"
 | |
| 	"github.com/roboll/helmfile/pkg/state"
 | |
| 	"github.com/urfave/cli"
 | |
| 	"go.uber.org/zap"
 | |
| 	"golang.org/x/crypto/ssh/terminal"
 | |
| )
 | |
| 
 | |
| var logger *zap.SugaredLogger
 | |
| 
 | |
| 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"
 | |
| 	}
 | |
| 	logger = helmexec.NewLogger(os.Stderr, logLevel)
 | |
| 	if c.App.Metadata == nil {
 | |
| 		// Auto-initialised in 1.19.0
 | |
| 		// https://github.com/urfave/cli/blob/master/CHANGELOG.md#1190---2016-11-19
 | |
| 		c.App.Metadata = make(map[string]interface{})
 | |
| 	}
 | |
| 	c.App.Metadata["logger"] = logger
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func main() {
 | |
| 
 | |
| 	cliApp := cli.NewApp()
 | |
| 	cliApp.Name = "helmfile"
 | |
| 	cliApp.Usage = ""
 | |
| 	cliApp.Version = version.Version
 | |
| 	cliApp.EnableBashCompletion = true
 | |
| 	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",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	cliApp.Before = configureLogging
 | |
| 	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"`,
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Deps(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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",
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Repos(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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",
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.DeprecatedSyncCharts(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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:  "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 requiers 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",
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Diff(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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 requiers 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:  "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",
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Template(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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"`,
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.WriteValues(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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"`,
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Lint(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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)",
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Fetch(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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:  "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"`,
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Sync(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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 requiers 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"`,
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Apply(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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",
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Status(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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"`,
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Delete(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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"`,
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Destroy(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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"`,
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.Test(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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",
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.PrintState(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			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",
 | |
| 				},
 | |
| 			},
 | |
| 			Action: action(func(a *app.App, c configImpl) error {
 | |
| 				return a.ListReleases(c)
 | |
| 			}),
 | |
| 		},
 | |
| 		{
 | |
| 			Name:      "cache",
 | |
| 			Usage:     "cache management",
 | |
| 			ArgsUsage: "[command]",
 | |
| 			Subcommands: []cli.Command{
 | |
| 				{
 | |
| 					Name:  "info",
 | |
| 					Usage: "cache info",
 | |
| 					Action: action(func(a *app.App, c configImpl) error {
 | |
| 						return a.ShowCacheDir(c)
 | |
| 					}),
 | |
| 				},
 | |
| 				{
 | |
| 					Name:  "cleanup",
 | |
| 					Usage: "clean up cache directory",
 | |
| 					Action: action(func(a *app.App, c configImpl) error {
 | |
| 						return a.CleanCacheDir(c)
 | |
| 					}),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			Name:      "version",
 | |
| 			Usage:     "Show the version for Helmfile.",
 | |
| 			ArgsUsage: "[command]",
 | |
| 			Action: func(c *cli.Context) error {
 | |
| 				cli.ShowVersion(c)
 | |
| 				return nil
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	err := cliApp.Run(os.Args)
 | |
| 	if err != nil {
 | |
| 		fmt.Fprintf(os.Stderr, "%v\n", err)
 | |
| 		os.Exit(3)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type configImpl struct {
 | |
| 	c *cli.Context
 | |
| 
 | |
| 	set map[string]interface{}
 | |
| }
 | |
| 
 | |
| func NewUrfaveCliConfigImpl(c *cli.Context) (configImpl, error) {
 | |
| 	if c.NArg() > 0 {
 | |
| 		err := cli.ShowAppHelp(c)
 | |
| 		if err != nil {
 | |
| 			return configImpl{}, err
 | |
| 		}
 | |
| 		return configImpl{}, fmt.Errorf("err: extraneous arguments: %s", strings.Join(c.Args(), ", "))
 | |
| 	}
 | |
| 
 | |
| 	conf := configImpl{
 | |
| 		c: c,
 | |
| 	}
 | |
| 
 | |
| 	optsSet := c.GlobalStringSlice("state-values-set")
 | |
| 	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)
 | |
| 			}
 | |
| 		}
 | |
| 		conf.set = set
 | |
| 	}
 | |
| 
 | |
| 	return conf, nil
 | |
| }
 | |
| 
 | |
| func (c configImpl) Set() []string {
 | |
| 	return c.c.StringSlice("set")
 | |
| }
 | |
| 
 | |
| func (c configImpl) SkipRepos() bool {
 | |
| 	return c.c.Bool("skip-repos")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Wait() bool {
 | |
| 	return c.c.Bool("wait")
 | |
| }
 | |
| 
 | |
| func (c configImpl) WaitForJobs() bool {
 | |
| 	return c.c.Bool("wait-for-jobs")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Values() []string {
 | |
| 	return c.c.StringSlice("values")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Args() string {
 | |
| 	args := c.c.String("args")
 | |
| 	enableHelmDebug := c.c.GlobalBool("debug")
 | |
| 
 | |
| 	if enableHelmDebug {
 | |
| 		args = fmt.Sprintf("%s %s", args, "--debug")
 | |
| 	}
 | |
| 	return args
 | |
| }
 | |
| 
 | |
| func (c configImpl) OutputDir() string {
 | |
| 	return strings.TrimRight(c.c.String("output-dir"), fmt.Sprintf("%c", os.PathSeparator))
 | |
| }
 | |
| 
 | |
| func (c configImpl) OutputDirTemplate() string {
 | |
| 	return c.c.String("output-dir-template")
 | |
| }
 | |
| 
 | |
| func (c configImpl) OutputFileTemplate() string {
 | |
| 	return c.c.String("output-file-template")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Validate() bool {
 | |
| 	return c.c.Bool("validate")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Concurrency() int {
 | |
| 	return c.c.Int("concurrency")
 | |
| }
 | |
| 
 | |
| func (c configImpl) HasCommandName(name string) bool {
 | |
| 	return c.c.Command.HasName(name)
 | |
| }
 | |
| 
 | |
| func (c configImpl) SkipNeeds() bool {
 | |
| 	if !c.IncludeNeeds() {
 | |
| 		return c.c.Bool("skip-needs")
 | |
| 	}
 | |
| 
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (c configImpl) IncludeNeeds() bool {
 | |
| 	return c.c.Bool("include-needs") || c.IncludeTransitiveNeeds()
 | |
| }
 | |
| 
 | |
| func (c configImpl) IncludeTransitiveNeeds() bool {
 | |
| 	return c.c.Bool("include-transitive-needs")
 | |
| }
 | |
| 
 | |
| // DiffConfig
 | |
| 
 | |
| func (c configImpl) SkipDeps() bool {
 | |
| 	return c.c.Bool("skip-deps")
 | |
| }
 | |
| 
 | |
| func (c configImpl) DetailedExitcode() bool {
 | |
| 	return c.c.Bool("detailed-exitcode")
 | |
| }
 | |
| 
 | |
| func (c configImpl) RetainValuesFiles() bool {
 | |
| 	return c.c.Bool("retain-values-files")
 | |
| }
 | |
| 
 | |
| func (c configImpl) IncludeTests() bool {
 | |
| 	return c.c.Bool("include-tests")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Suppress() []string {
 | |
| 	return c.c.StringSlice("suppress")
 | |
| }
 | |
| 
 | |
| func (c configImpl) SuppressSecrets() bool {
 | |
| 	return c.c.Bool("suppress-secrets")
 | |
| }
 | |
| 
 | |
| func (c configImpl) ShowSecrets() bool {
 | |
| 	return c.c.Bool("show-secrets")
 | |
| }
 | |
| 
 | |
| func (c configImpl) SuppressDiff() bool {
 | |
| 	return c.c.Bool("suppress-diff")
 | |
| }
 | |
| 
 | |
| // DeleteConfig
 | |
| 
 | |
| func (c configImpl) Purge() bool {
 | |
| 	return c.c.Bool("purge")
 | |
| }
 | |
| 
 | |
| // TestConfig
 | |
| 
 | |
| func (c configImpl) Cleanup() bool {
 | |
| 	return c.c.Bool("cleanup")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Logs() bool {
 | |
| 	return c.c.Bool("logs")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Timeout() int {
 | |
| 	if !c.c.IsSet("timeout") {
 | |
| 		return state.EmptyTimeout
 | |
| 	}
 | |
| 	return c.c.Int("timeout")
 | |
| }
 | |
| 
 | |
| // ListConfig
 | |
| 
 | |
| func (c configImpl) Output() string {
 | |
| 	return c.c.String("output")
 | |
| }
 | |
| 
 | |
| func (c configImpl) KeepTempDir() bool {
 | |
| 	return c.c.Bool("keep-temp-dir")
 | |
| }
 | |
| 
 | |
| // GlobalConfig
 | |
| 
 | |
| func (c configImpl) HelmBinary() string {
 | |
| 	return c.c.GlobalString("helm-binary")
 | |
| }
 | |
| 
 | |
| func (c configImpl) KubeContext() string {
 | |
| 	return c.c.GlobalString("kube-context")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Namespace() string {
 | |
| 	return c.c.GlobalString("namespace")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Chart() string {
 | |
| 	return c.c.GlobalString("chart")
 | |
| }
 | |
| 
 | |
| func (c configImpl) FileOrDir() string {
 | |
| 	return c.c.GlobalString("file")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Selectors() []string {
 | |
| 	return c.c.GlobalStringSlice("selector")
 | |
| }
 | |
| 
 | |
| func (c configImpl) StateValuesSet() map[string]interface{} {
 | |
| 	return c.set
 | |
| }
 | |
| 
 | |
| func (c configImpl) StateValuesFiles() []string {
 | |
| 	return c.c.GlobalStringSlice("state-values-file")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Interactive() bool {
 | |
| 	return c.c.GlobalBool("interactive")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Color() bool {
 | |
| 	if c := c.c.GlobalBool("color"); c {
 | |
| 		return c
 | |
| 	}
 | |
| 
 | |
| 	if c.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
 | |
| 
 | |
| 	term := terminal.IsTerminal(int(os.Stdout.Fd()))
 | |
| 	// https://github.com/databus23/helm-diff/issues/281
 | |
| 	dumb := os.Getenv("TERM") == "dumb"
 | |
| 	return term && !dumb
 | |
| }
 | |
| 
 | |
| func (c configImpl) NoColor() bool {
 | |
| 	return c.c.GlobalBool("no-color")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Context() int {
 | |
| 	return c.c.Int("context")
 | |
| }
 | |
| 
 | |
| func (c configImpl) DiffOutput() string {
 | |
| 	return c.c.String("output")
 | |
| }
 | |
| 
 | |
| func (c configImpl) SkipCleanup() bool {
 | |
| 	return c.c.Bool("skip-cleanup")
 | |
| }
 | |
| 
 | |
| func (c configImpl) SkipCRDs() bool {
 | |
| 	return c.c.Bool("skip-crds")
 | |
| }
 | |
| 
 | |
| func (c configImpl) SkipDiffOnInstall() bool {
 | |
| 	return c.c.Bool("skip-diff-on-install")
 | |
| }
 | |
| 
 | |
| func (c configImpl) EmbedValues() bool {
 | |
| 	return c.c.Bool("embed-values")
 | |
| }
 | |
| 
 | |
| func (c configImpl) IncludeCRDs() bool {
 | |
| 	return c.c.Bool("include-crds")
 | |
| }
 | |
| 
 | |
| func (c configImpl) SkipTests() bool {
 | |
| 	return c.c.Bool("skip-tests")
 | |
| }
 | |
| 
 | |
| func (c configImpl) Logger() *zap.SugaredLogger {
 | |
| 	return c.c.App.Metadata["logger"].(*zap.SugaredLogger)
 | |
| }
 | |
| 
 | |
| func (c configImpl) Env() string {
 | |
| 	env := c.c.GlobalString("environment")
 | |
| 	if env == "" {
 | |
| 		env = os.Getenv("HELMFILE_ENVIRONMENT")
 | |
| 		if env == "" {
 | |
| 			env = state.DefaultEnv
 | |
| 		}
 | |
| 	}
 | |
| 	return env
 | |
| }
 | |
| 
 | |
| func action(do func(*app.App, configImpl) error) func(*cli.Context) error {
 | |
| 	return func(implCtx *cli.Context) error {
 | |
| 		conf, err := 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 toCliError(c *cli.Context, err error) error {
 | |
| 	if err != nil {
 | |
| 		switch e := err.(type) {
 | |
| 		case *app.NoMatchingHelmfileError:
 | |
| 			noMatchingExitCode := 3
 | |
| 			if c.GlobalBool("allow-no-matching-release") {
 | |
| 				noMatchingExitCode = 0
 | |
| 			}
 | |
| 			return cli.NewExitError(e.Error(), noMatchingExitCode)
 | |
| 		case *app.MultiError:
 | |
| 			return cli.NewExitError(e.Error(), 1)
 | |
| 		case *app.Error:
 | |
| 			return cli.NewExitError(e.Error(), e.Code())
 | |
| 		default:
 | |
| 			panic(fmt.Errorf("BUG: please file an github issue for this unhandled error: %T: %v", e, e))
 | |
| 		}
 | |
| 	}
 | |
| 	return err
 | |
| }
 |