diff --git a/README.md b/README.md index a54dcee9..ed3d2a05 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,10 @@ releases: tlsCert: "path/to/cert.pem" # path to TLS key file (default "$HELM_HOME/key.pem") tlsKey: "path/to/key.pem" + # --kube-context to be passed to helm commands + # CAUTION: this doesn't work as expected for `tilerless: true`. + # See https://github.com/roboll/helmfile/issues/642 + kubeContext: kube-context # Local chart example - name: grafana # name of this release diff --git a/pkg/argparser/args.go b/pkg/argparser/args.go index 3208eae0..724f433f 100644 --- a/pkg/argparser/args.go +++ b/pkg/argparser/args.go @@ -82,10 +82,6 @@ func GetArgs(args string, state *state.HelmState) []string { } } - if state.HelmDefaults.KubeContext != "" { - argsMap.SetArg("--kube-context", state.HelmDefaults.KubeContext, false) - } - var argArr []string for _, flag := range argsMap.flags { diff --git a/pkg/argparser/args_test.go b/pkg/argparser/args_test.go index adfadd63..75b2c165 100644 --- a/pkg/argparser/args_test.go +++ b/pkg/argparser/args_test.go @@ -13,7 +13,7 @@ func TestGetArgs(t *testing.T) { testState := &state.HelmState{HelmDefaults: Helmdefaults} receivedArgs := GetArgs(args, testState) - expectedOutput := "--timeout=3600 --set app1.bootstrap=true --set app2.bootstrap=false --tiller-namespace ns --recreate-pods --force --kube-context=test" + expectedOutput := "--timeout=3600 --set app1.bootstrap=true --set app2.bootstrap=false --tiller-namespace ns --recreate-pods --force" if compareArgs(expectedOutput, receivedArgs) == false { t.Errorf("expected %s, got %s", expectedOutput, strings.Join(receivedArgs, " ")) @@ -27,7 +27,7 @@ func Test2(t *testing.T) { testState := &state.HelmState{HelmDefaults: Helmdefaults} receivedArgs := GetArgs(args, testState) - expectedOutput := "--timeout=3600 --set app1.bootstrap=true --set app2.bootstrap=false,app3.bootstrap=true --tiller-namespace ns --recreate-pods --force --kube-context=test" + expectedOutput := "--timeout=3600 --set app1.bootstrap=true --set app2.bootstrap=false,app3.bootstrap=true --tiller-namespace ns --recreate-pods --force" if compareArgs(expectedOutput, receivedArgs) == false { t.Errorf("expected %s, got %s", expectedOutput, strings.Join(receivedArgs, " ")) diff --git a/pkg/state/create.go b/pkg/state/create.go index 07064803..c0265873 100644 --- a/pkg/state/create.go +++ b/pkg/state/create.go @@ -209,7 +209,7 @@ func (st *HelmState) loadEnvValues(name string, ctxEnv *environment.Environment, // installed on the cluster, just for decrypting secrets! // Related: https://github.com/futuresimple/helm-secrets/issues/83 release := &ReleaseSpec{} - flags := st.appendTillerFlags([]string{}, release) + flags := st.appendConnectionFlags([]string{}, release) decFile, err := helm.DecryptSecret(st.createHelmContext(release, 0), path, flags...) if err != nil { return nil, err diff --git a/pkg/state/state.go b/pkg/state/state.go index 3bf450fa..30cdc986 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -153,6 +153,8 @@ type ReleaseSpec struct { TillerNamespace string `yaml:"tillerNamespace"` Tillerless *bool `yaml:"tillerless"` + KubeContext string `yaml:"kubeContext"` + TLS *bool `yaml:"tls"` TLSCACert string `yaml:"tlsCACert"` TLSKey string `yaml:"tlsKey"` @@ -318,7 +320,7 @@ func (st *HelmState) prepareSyncReleases(helm helmexec.Interface, additionalValu } func (st *HelmState) isReleaseInstalled(context helmexec.HelmContext, helm helmexec.Interface, release ReleaseSpec) (bool, error) { - out, err := helm.List(context, "^"+release.Name+"$", st.tillerFlags(&release)...) + out, err := helm.List(context, "^"+release.Name+"$", st.connectionFlags(&release)...) if err != nil { return false, err } else if out != "" { @@ -438,7 +440,7 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme func (st *HelmState) getDeployedVersion(context helmexec.HelmContext, helm helmexec.Interface, release *ReleaseSpec) (string, error) { //retrieve the version - if out, err := helm.List(context, "^"+release.Name+"$", st.tillerFlags(release)...); err == nil { + if out, err := helm.List(context, "^"+release.Name+"$", st.connectionFlags(release)...); err == nil { chartName := filepath.Base(release.Chart) //the regexp without escapes : .*\s.*\s.*\s.*\schartName-(.*?)\s pat := regexp.MustCompile(".*\\s.*\\s.*\\s.*\\s" + chartName + "-(.*?)\\s") @@ -835,7 +837,7 @@ func (st *HelmState) ReleaseStatuses(helm helmexec.Interface, workerLimit int) [ } flags := []string{} - flags = st.appendTillerFlags(flags, &release) + flags = st.appendConnectionFlags(flags, &release) return helm.ReleaseStatus(st.createHelmContext(&release, workerIndex), release.Name, flags...) }) @@ -852,7 +854,7 @@ func (st *HelmState) DeleteReleases(affectedReleases *AffectedReleases, helm hel if purge { flags = append(flags, "--purge") } - flags = st.appendTillerFlags(flags, &release) + flags = st.appendConnectionFlags(flags, &release) context := st.createHelmContext(&release, workerIndex) installed, err := st.isReleaseInstalled(context, helm, release) @@ -884,7 +886,7 @@ func (st *HelmState) TestReleases(helm helmexec.Interface, cleanup bool, timeout flags = append(flags, "--cleanup") } flags = append(flags, "--timeout", strconv.Itoa(timeout)) - flags = st.appendTillerFlags(flags, &release) + flags = st.appendConnectionFlags(flags, &release) return helm.TestRelease(st.createHelmContext(&release, workerIndex), release.Name, flags...) }) @@ -1116,15 +1118,16 @@ func findChartDirectory(topLevelDir string) (string, error) { return topLevelDir, errors.New("No Chart.yaml found") } -func (st *HelmState) appendTillerFlags(flags []string, release *ReleaseSpec) []string { - adds := st.tillerFlags(release) +// appendConnectionFlags append all the helm command-line flags related to K8s API and Tiller connection including the kubecontext +func (st *HelmState) appendConnectionFlags(flags []string, release *ReleaseSpec) []string { + adds := st.connectionFlags(release) for _, a := range adds { flags = append(flags, a) } return flags } -func (st *HelmState) tillerFlags(release *ReleaseSpec) []string { +func (st *HelmState) connectionFlags(release *ReleaseSpec) []string { flags := []string{} tillerless := st.HelmDefaults.Tillerless if release.Tillerless != nil { @@ -1158,6 +1161,12 @@ func (st *HelmState) tillerFlags(release *ReleaseSpec) []string { } else if st.HelmDefaults.TLSCACert != "" { flags = append(flags, "--tls-ca-cert", st.HelmDefaults.TLSCACert) } + + if release.KubeContext != "" { + flags = append(flags, "--kube-context", release.KubeContext) + } else if st.HelmDefaults.KubeContext != "" { + flags = append(flags, "--kube-context", st.HelmDefaults.KubeContext) + } } return flags @@ -1201,7 +1210,7 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp flags = append(flags, "--atomic") } - flags = st.appendTillerFlags(flags, release) + flags = st.appendConnectionFlags(flags, release) var err error flags, err = st.appendHelmXFlags(flags, release) @@ -1244,7 +1253,7 @@ func (st *HelmState) flagsForDiff(helm helmexec.Interface, release *ReleaseSpec, flags = append(flags, "--devel") } - flags = st.appendTillerFlags(flags, release) + flags = st.appendConnectionFlags(flags, release) var err error flags, err = st.appendHelmXFlags(flags, release) @@ -1417,7 +1426,7 @@ func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *R } path := paths[0] - decryptFlags := st.appendTillerFlags([]string{}, release) + decryptFlags := st.appendConnectionFlags([]string{}, release) valfile, err := helm.DecryptSecret(st.createHelmContext(release, workerIndex), path, decryptFlags...) if err != nil { return nil, err diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go index 636e2d5c..a8ced524 100644 --- a/pkg/state/state_test.go +++ b/pkg/state/state_test.go @@ -1917,6 +1917,8 @@ func TestHelmState_Delete(t *testing.T) { purge bool flags string tillerNamespace string + kubeContext string + defKubeContext string }{ { name: "desired and installed (purge=false)", @@ -2008,6 +2010,37 @@ func TestHelmState_Delete(t *testing.T) { flags: "--tiller-namespacetillerns", deleted: []mockRelease{{"releaseA", []string{"--purge", "--tiller-namespace", "tillerns"}}}, }, + { + name: "with kubecontext", + wantErr: false, + desired: nil, + installed: true, + purge: true, + kubeContext: "ctx", + flags: "--kube-contextctx", + deleted: []mockRelease{{"releaseA", []string{"--purge", "--kube-context", "ctx"}}}, + }, + { + name: "with default kubecontext", + wantErr: false, + desired: nil, + installed: true, + purge: true, + defKubeContext: "defctx", + flags: "--kube-contextdefctx", + deleted: []mockRelease{{"releaseA", []string{"--purge", "--kube-context", "defctx"}}}, + }, + { + name: "with non-default and default kubecontexts", + wantErr: false, + desired: nil, + installed: true, + purge: true, + kubeContext: "ctx", + defKubeContext: "defctx", + flags: "--kube-contextctx", + deleted: []mockRelease{{"releaseA", []string{"--purge", "--kube-context", "ctx"}}}, + }, } for i := range tests { tt := tests[i] @@ -2020,11 +2053,15 @@ func TestHelmState_Delete(t *testing.T) { Name: name, Installed: tt.desired, TillerNamespace: tt.tillerNamespace, + KubeContext: tt.kubeContext, } releases := []ReleaseSpec{ release, } state := &HelmState{ + HelmDefaults: HelmSpec{ + KubeContext: tt.defKubeContext, + }, Releases: releases, logger: logger, } @@ -2043,7 +2080,7 @@ func TestHelmState_Delete(t *testing.T) { return } } else if !(reflect.DeepEqual(tt.deleted, helm.deleted) && (len(affectedReleases.Deleted) == len(tt.deleted))) { - t.Errorf("unexpected deletions happened: expected %v, got %v", &affectedReleases.Deleted, tt.deleted) + t.Errorf("unexpected deletions happened: expected %v, got %v", tt.deleted, &affectedReleases.Deleted) } } t.Run(tt.name, f)