feat: add an option to set a custom kustomize binary (#1012)

Signed-off-by: Alessio Dionisi <me@alessiodionisi.com>
This commit is contained in:
Alessio Dionisi 2023-09-13 13:58:53 +02:00 committed by GitHub
parent dae5a21431
commit ad258463b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 55 additions and 24 deletions

View File

@ -115,6 +115,7 @@ func NewRootCmd(globalConfig *config.GlobalOptions) (*cobra.Command, error) {
func setGlobalOptionsForRootCmd(fs *pflag.FlagSet, globalOptions *config.GlobalOptions) { 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.HelmBinary, "helm-binary", "b", app.DefaultHelmBinary, "Path to the helm binary")
fs.StringVarP(&globalOptions.KustomizeBinary, "kustomize-binary", "k", app.DefaultKustomizeBinary, "Path to the kustomize binary")
fs.StringVarP(&globalOptions.File, "file", "f", "", "load config from file or directory. defaults to \"`helmfile.yaml`\" or \"helmfile.yaml.gotmpl\" or \"helmfile.d\" (means \"helmfile.d/*.yaml\" or \"helmfile.d/*.yaml.gotmpl\") in this preference. Specify - to load the config from the standard input.") fs.StringVarP(&globalOptions.File, "file", "f", "", "load config from file or directory. defaults to \"`helmfile.yaml`\" or \"helmfile.yaml.gotmpl\" or \"helmfile.d\" (means \"helmfile.d/*.yaml\" or \"helmfile.d/*.yaml.gotmpl\") in this preference. Specify - to load the config from the standard input.")
fs.StringVarP(&globalOptions.Environment, "environment", "e", "", `specify the environment name. Overrides "HELMFILE_ENVIRONMENT" OS environment variable when specified. defaults to "default"`) fs.StringVarP(&globalOptions.Environment, "environment", "e", "", `specify the environment name. Overrides "HELMFILE_ENVIRONMENT" OS environment variable when specified. defaults to "default"`)
fs.StringArrayVar(&globalOptions.StateValuesSet, "state-values-set", nil, "set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2). Used to override .Values within the helmfile template (not values template).") fs.StringArrayVar(&globalOptions.StateValuesSet, "state-values-set", nil, "set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2). Used to override .Values within the helmfile template (not values template).")

View File

@ -167,6 +167,8 @@ repositories:
# Path to alternative helm binary (--helm-binary) # Path to alternative helm binary (--helm-binary)
helmBinary: path/to/helm3 helmBinary: path/to/helm3
# Path to alternative kustomize binary (--kustomize-binary)
kustomizeBinary: path/to/kustomize
# Path to alternative lock file. The default is <state file name>.lock, i.e for helmfile.yaml it's helmfile.lock. # Path to alternative lock file. The default is <state file name>.lock, i.e for helmfile.yaml it's helmfile.lock.
lockFilePath: path/to/lock.file lockFilePath: path/to/lock.file
@ -554,6 +556,7 @@ Flags:
-h, --help help for helmfile -h, --help help for helmfile
-i, --interactive Request confirmation before attempting to modify clusters -i, --interactive Request confirmation before attempting to modify clusters
--kube-context string Set kubectl context. Uses current context by default --kube-context string Set kubectl context. Uses current context by default
-k, --kustomize-binary string Path to the kustomize binary (default "kustomize")
--log-level string Set log level, default info (default "info") --log-level string Set log level, default info (default "info")
-n, --namespace string Set namespace. Uses the namespace set in the context by default, and is available in templates as {{ .Namespace }} -n, --namespace string Set namespace. Uses the namespace set in the context by default, and is available in templates as {{ .Namespace }}
--no-color Output without color --no-color Output without color
@ -565,7 +568,7 @@ Flags:
--skip-deps skip running "helm repo update" and "helm dependency build" --skip-deps skip running "helm repo update" and "helm dependency build"
--state-values-file stringArray specify state values in a YAML file. Used to override .Values within the helmfile template (not values template). --state-values-file stringArray specify state values in a YAML file. Used to override .Values within the helmfile template (not values template).
--state-values-set stringArray set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2). Used to override .Values within the helmfile template (not values template). --state-values-set stringArray set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2). Used to override .Values within the helmfile template (not values template).
--strip-args-values-on-exit-error On exit error, strip the values of the args --strip-args-values-on-exit-error Strip the potential secret values of the helm command args contained in a helmfile error message (default true)
-v, --version version for helmfile -v, --version version for helmfile
Use "helmfile [command] --help" for more information about a command. Use "helmfile [command] --help" for more information about a command.

2
go.mod
View File

@ -13,7 +13,7 @@ require (
github.com/google/go-cmp v0.5.9 github.com/google/go-cmp v0.5.9
github.com/gosuri/uitable v0.0.4 github.com/gosuri/uitable v0.0.4
github.com/hashicorp/go-getter v1.7.2 github.com/hashicorp/go-getter v1.7.2
github.com/helmfile/chartify v0.15.0 github.com/helmfile/chartify v0.16.0
github.com/helmfile/vals v0.27.1 github.com/helmfile/vals v0.27.1
github.com/imdario/mergo v0.3.16 github.com/imdario/mergo v0.3.16
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0

4
go.sum
View File

@ -687,8 +687,8 @@ github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaak
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I= github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/helmfile/chartify v0.15.0 h1:dxDPK1GdGhijJ7bw2hYmq9mmp3Q6jvNGDoJc1LtTybg= github.com/helmfile/chartify v0.16.0 h1:dJhLUQuB5EklmfOS8zt5ljofvh0bhGUc3EsnXK7u4q0=
github.com/helmfile/chartify v0.15.0/go.mod h1:7pmJlGn79CDMBD1+NbDJtE3L7q0hZf5GyBCJrV78vLA= github.com/helmfile/chartify v0.16.0/go.mod h1:7pmJlGn79CDMBD1+NbDJtE3L7q0hZf5GyBCJrV78vLA=
github.com/helmfile/vals v0.27.1 h1:j1EzMeq5lCEePqmwNYpi2w791gDscKeKF95YpB4flig= github.com/helmfile/vals v0.27.1 h1:j1EzMeq5lCEePqmwNYpi2w791gDscKeKF95YpB4flig=
github.com/helmfile/vals v0.27.1/go.mod h1:VV8Wy8Bvm/K0hTb3f3H80P1NWollaRGRi8+nkWaS15E= github.com/helmfile/vals v0.27.1/go.mod h1:VV8Wy8Bvm/K0hTb3f3H80P1NWollaRGRi8+nkWaS15E=
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8=

View File

@ -30,6 +30,7 @@ var Cancel goContext.CancelFunc
type App struct { type App struct {
OverrideKubeContext string OverrideKubeContext string
OverrideHelmBinary string OverrideHelmBinary string
OverrideKustomizeBinary string
EnableLiveOutput bool EnableLiveOutput bool
StripArgsValuesOnExitError bool StripArgsValuesOnExitError bool
DisableForceUpdate bool DisableForceUpdate bool
@ -74,6 +75,7 @@ func New(conf ConfigProvider) *App {
return Init(&App{ return Init(&App{
OverrideKubeContext: conf.KubeContext(), OverrideKubeContext: conf.KubeContext(),
OverrideHelmBinary: conf.HelmBinary(), OverrideHelmBinary: conf.HelmBinary(),
OverrideKustomizeBinary: conf.KustomizeBinary(),
EnableLiveOutput: conf.EnableLiveOutput(), EnableLiveOutput: conf.EnableLiveOutput(),
StripArgsValuesOnExitError: conf.StripArgsValuesOnExitError(), StripArgsValuesOnExitError: conf.StripArgsValuesOnExitError(),
DisableForceUpdate: conf.DisableForceUpdate(), DisableForceUpdate: conf.DisableForceUpdate(),
@ -758,11 +760,12 @@ func (a *App) loadDesiredStateFromYaml(file string, opts ...LoadOpts) (*state.He
logger: a.Logger, logger: a.Logger,
remote: a.remote, remote: a.remote,
overrideKubeContext: a.OverrideKubeContext, overrideKubeContext: a.OverrideKubeContext,
overrideHelmBinary: a.OverrideHelmBinary, overrideHelmBinary: a.OverrideHelmBinary,
enableLiveOutput: a.EnableLiveOutput, overrideKustomizeBinary: a.OverrideKustomizeBinary,
getHelm: a.getHelm, enableLiveOutput: a.EnableLiveOutput,
valsRuntime: a.valsRuntime, getHelm: a.getHelm,
valsRuntime: a.valsRuntime,
} }
return ld.Load(file, op) return ld.Load(file, op)

View File

@ -5,6 +5,7 @@ import "go.uber.org/zap"
type ConfigProvider interface { type ConfigProvider interface {
Args() string Args() string
HelmBinary() string HelmBinary() string
KustomizeBinary() string
EnableLiveOutput() bool EnableLiveOutput() bool
StripArgsValuesOnExitError() bool StripArgsValuesOnExitError() bool
DisableForceUpdate() bool DisableForceUpdate() bool

View File

@ -20,13 +20,15 @@ import (
) )
const ( const (
DefaultHelmBinary = state.DefaultHelmBinary DefaultHelmBinary = state.DefaultHelmBinary
DefaultKustomizeBinary = state.DefaultKustomizeBinary
) )
type desiredStateLoader struct { type desiredStateLoader struct {
overrideKubeContext string overrideKubeContext string
overrideHelmBinary string overrideHelmBinary string
enableLiveOutput bool overrideKustomizeBinary string
enableLiveOutput bool
env string env string
namespace string namespace string
@ -152,7 +154,7 @@ func (ld *desiredStateLoader) loadFileWithOverrides(inheritedEnv, overrodeEnv *e
} }
func (a *desiredStateLoader) underlying() *state.StateCreator { func (a *desiredStateLoader) underlying() *state.StateCreator {
c := state.NewCreator(a.logger, a.fs, a.valsRuntime, a.getHelm, a.overrideHelmBinary, a.remote, a.enableLiveOutput, a.lockFilePath) c := state.NewCreator(a.logger, a.fs, a.valsRuntime, a.getHelm, a.overrideHelmBinary, a.overrideKustomizeBinary, a.remote, a.enableLiveOutput, a.lockFilePath)
c.LoadFile = a.loadFile c.LoadFile = a.loadFile
return c return c
} }

View File

@ -13,8 +13,10 @@ import (
// GlobalOptions is the global configuration for the Helmfile CLI. // GlobalOptions is the global configuration for the Helmfile CLI.
type GlobalOptions struct { type GlobalOptions struct {
// helmBinary is the path to the Helm binary. // HelmBinary is the path to the Helm binary.
HelmBinary string HelmBinary string
// KustomizeBinary is the path to the Kustomize binary.
KustomizeBinary string
// File is the path to the Helmfile. // File is the path to the Helmfile.
File string File string
// Environment is the name of the environment to use. // Environment is the name of the environment to use.
@ -93,6 +95,11 @@ func (g *GlobalImpl) HelmBinary() string {
return g.GlobalOptions.HelmBinary return g.GlobalOptions.HelmBinary
} }
// KustomizeBinary returns the path to the Kustomize binary.
func (g *GlobalImpl) KustomizeBinary() string {
return g.GlobalOptions.KustomizeBinary
}
// KubeContext returns the name of the kubectl context to use. // KubeContext returns the name of the kubectl context to use.
func (g *GlobalImpl) KubeContext() string { func (g *GlobalImpl) KubeContext() string {
return g.GlobalOptions.KubeContext return g.GlobalOptions.KubeContext

View File

@ -18,7 +18,8 @@ import (
) )
const ( const (
DefaultHelmBinary = "helm" DefaultHelmBinary = "helm"
DefaultKustomizeBinary = "kustomize"
) )
type StateLoadError struct { type StateLoadError struct {
@ -53,6 +54,8 @@ type StateCreator struct {
overrideHelmBinary string overrideHelmBinary string
overrideKustomizeBinary string
enableLiveOutput bool enableLiveOutput bool
remote *remote.Remote remote *remote.Remote
@ -60,7 +63,7 @@ type StateCreator struct {
lockFile string lockFile string
} }
func NewCreator(logger *zap.SugaredLogger, fs *filesystem.FileSystem, valsRuntime vals.Evaluator, getHelm func(*HelmState) helmexec.Interface, overrideHelmBinary string, remote *remote.Remote, enableLiveOutput bool, lockFile string) *StateCreator { func NewCreator(logger *zap.SugaredLogger, fs *filesystem.FileSystem, valsRuntime vals.Evaluator, getHelm func(*HelmState) helmexec.Interface, overrideHelmBinary string, overrideKustomizeBinary string, remote *remote.Remote, enableLiveOutput bool, lockFile string) *StateCreator {
return &StateCreator{ return &StateCreator{
logger: logger, logger: logger,
@ -69,8 +72,9 @@ func NewCreator(logger *zap.SugaredLogger, fs *filesystem.FileSystem, valsRuntim
valsRuntime: valsRuntime, valsRuntime: valsRuntime,
getHelm: getHelm, getHelm: getHelm,
overrideHelmBinary: overrideHelmBinary, overrideHelmBinary: overrideHelmBinary,
enableLiveOutput: enableLiveOutput, overrideKustomizeBinary: overrideKustomizeBinary,
enableLiveOutput: enableLiveOutput,
remote: remote, remote: remote,
@ -131,6 +135,13 @@ func (c *StateCreator) Parse(content []byte, baseDir, file string) (*HelmState,
state.DefaultHelmBinary = DefaultHelmBinary state.DefaultHelmBinary = DefaultHelmBinary
} }
if c.overrideKustomizeBinary != "" && c.overrideKustomizeBinary != DefaultKustomizeBinary {
state.DefaultKustomizeBinary = c.overrideKustomizeBinary
} else if state.DefaultKustomizeBinary == "" {
// Let `helmfile --kustomize-binary ""` not break this helmfile run
state.DefaultKustomizeBinary = DefaultKustomizeBinary
}
state.logger = c.logger state.logger = c.logger
state.valsRuntime = c.valsRuntime state.valsRuntime = c.valsRuntime

View File

@ -84,7 +84,7 @@ func (testEnv stateTestEnv) MustLoadStateWithEnableLiveOutput(t *testing.T, file
} }
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem()) r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", r, enableLiveOutput, ""). state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", "", r, enableLiveOutput, "").
ParseAndLoad([]byte(yamlContent), filepath.Dir(file), file, envName, true, true, nil, nil) ParseAndLoad([]byte(yamlContent), filepath.Dir(file), file, envName, true, true, nil, nil)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@ -154,7 +154,7 @@ releaseNamespace: mynamespace
env := environment.Environment{ env := environment.Environment{
Name: "production", Name: "production",
} }
state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", r, false, ""). state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", "", r, false, "").
ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, true, &env, nil) ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, true, &env, nil)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@ -241,7 +241,7 @@ overrideNamespace: myns
testFs.Cwd = "/example/path/to" testFs.Cwd = "/example/path/to"
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem()) r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", r, false, ""). state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", "", r, false, "").
ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, true, nil, nil) ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, true, nil, nil)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@ -525,7 +525,7 @@ releaseContext:
testFs.Cwd = "/example/path/to" testFs.Cwd = "/example/path/to"
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem()) r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", r, false, ""). state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", "", r, false, "").
ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, true, nil, nil) ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, true, nil, nil)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)

View File

@ -43,7 +43,8 @@ const (
// ReleaseSetSpec is release set spec // ReleaseSetSpec is release set spec
type ReleaseSetSpec struct { type ReleaseSetSpec struct {
DefaultHelmBinary string `yaml:"helmBinary,omitempty"` DefaultHelmBinary string `yaml:"helmBinary,omitempty"`
DefaultKustomizeBinary string `yaml:"kustomizeBinary,omitempty"`
// DefaultValues is the default values to be overrode by environment values and command-line overrides // DefaultValues is the default values to be overrode by environment values and command-line overrides
DefaultValues []any `yaml:"values,omitempty"` DefaultValues []any `yaml:"values,omitempty"`
@ -1199,6 +1200,7 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
if chartification != nil && helmfileCommand != "pull" { if chartification != nil && helmfileCommand != "pull" {
c := chartify.New( c := chartify.New(
chartify.HelmBin(st.DefaultHelmBinary), chartify.HelmBin(st.DefaultHelmBinary),
chartify.KustomizeBin(st.DefaultKustomizeBinary),
chartify.UseHelm3(true), chartify.UseHelm3(true),
chartify.WithLogf(st.logger.Debugf), chartify.WithLogf(st.logger.Debugf),
) )

View File

@ -3,6 +3,7 @@
filepath: input.yaml filepath: input.yaml
helmBinary: helm helmBinary: helm
kustomizeBinary: kustomize
environments: environments:
default: {} default: {}
repositories: repositories: