Skip to content

Commit 1c05fd9

Browse files
authored
feat: add --kube-context flag support (#924)
* feat: add --kube-context flag support Add native --kube-context flag to all helm-diff commands (upgrade, revision, rollback, release) to allow users to specify kubeconfig context directly instead of relying on HELM_KUBECONTEXT environment variable workaround. Fixes #603 Signed-off-by: yxxhero <aiopsclub@163.com> * test: add kube-context flag tests Add tests to verify that --kube-context flag is properly passed to helm commands, addressing review comments from PR #924: - Test basic --kube-context flag propagation to helm commands - Test --kube-context with --reuse-values flag - Test that --namespace is also passed when using --kube-context - Add wildcard argument matching for dynamic temp file paths Signed-off-by: yxxhero <aiopsclub@163.com> * test: add kube-context flag tests for revision, rollback and release commands Signed-off-by: yxxhero <aiopsclub@163.com> * fix: address PR review comments - Use local copy of envSettings in actionConfig.Init to avoid mutating shared state and prevent data races - Fix test stub flag order to match production code (--namespace before --kube-context) - Extract helmDiffTestHelper to reduce duplicated test setup code Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: yxxhero <aiopsclub@163.com>
1 parent f60c1a7 commit 1c05fd9

6 files changed

Lines changed: 203 additions & 43 deletions

File tree

cmd/helm.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,38 +131,50 @@ func compatibleHelm3Version() error {
131131
return nil
132132
}
133133

134-
func getRelease(release, namespace string) ([]byte, error) {
134+
func getRelease(release, namespace, kubeContext string) ([]byte, error) {
135135
args := []string{"get", "manifest", release}
136136
if namespace != "" {
137137
args = append(args, "--namespace", namespace)
138138
}
139+
if kubeContext != "" {
140+
args = append(args, "--kube-context", kubeContext)
141+
}
139142
cmd := exec.Command(os.Getenv("HELM_BIN"), args...)
140143
return outputWithRichError(cmd)
141144
}
142145

143-
func getHooks(release, namespace string) ([]byte, error) {
146+
func getHooks(release, namespace, kubeContext string) ([]byte, error) {
144147
args := []string{"get", "hooks", release}
145148
if namespace != "" {
146149
args = append(args, "--namespace", namespace)
147150
}
151+
if kubeContext != "" {
152+
args = append(args, "--kube-context", kubeContext)
153+
}
148154
cmd := exec.Command(os.Getenv("HELM_BIN"), args...)
149155
return outputWithRichError(cmd)
150156
}
151157

152-
func getRevision(release string, revision int, namespace string) ([]byte, error) {
158+
func getRevision(release string, revision int, namespace, kubeContext string) ([]byte, error) {
153159
args := []string{"get", "manifest", release, "--revision", strconv.Itoa(revision)}
154160
if namespace != "" {
155161
args = append(args, "--namespace", namespace)
156162
}
163+
if kubeContext != "" {
164+
args = append(args, "--kube-context", kubeContext)
165+
}
157166
cmd := exec.Command(os.Getenv("HELM_BIN"), args...)
158167
return outputWithRichError(cmd)
159168
}
160169

161-
func getChart(release, namespace string) (string, error) {
170+
func getChart(release, namespace, kubeContext string) (string, error) {
162171
args := []string{"get", "all", release, "--template", "{{.Release.Chart.Name}}"}
163172
if namespace != "" {
164173
args = append(args, "--namespace", namespace)
165174
}
175+
if kubeContext != "" {
176+
args = append(args, "--kube-context", kubeContext)
177+
}
166178
cmd := exec.Command(os.Getenv("HELM_BIN"), args...)
167179
out, err := outputWithRichError(cmd)
168180
if err != nil {
@@ -191,6 +203,9 @@ func (d *diffCmd) template(isUpgrade bool) ([]byte, error) {
191203
if d.namespace != "" {
192204
flags = append(flags, "--namespace", d.namespace)
193205
}
206+
if d.kubeContext != "" {
207+
flags = append(flags, "--kube-context", d.kubeContext)
208+
}
194209
if d.postRenderer != "" {
195210
flags = append(flags, "--post-renderer", d.postRenderer)
196211
}
@@ -411,6 +426,12 @@ func (d *diffCmd) writeExistingValues(f *os.File, all bool) error {
411426
if all {
412427
args = append(args, "--all")
413428
}
429+
if d.namespace != "" {
430+
args = append(args, "--namespace", d.namespace)
431+
}
432+
if d.kubeContext != "" {
433+
args = append(args, "--kube-context", d.kubeContext)
434+
}
414435
cmd := exec.Command(os.Getenv("HELM_BIN"), args...)
415436
debugPrint("Executing %s", strings.Join(cmd.Args, " "))
416437
defer func() {

cmd/release.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
)
1414

1515
type release struct {
16+
kubeContext string
1617
detailedExitCode bool
1718
releases []string
1819
includeTests bool
@@ -63,6 +64,7 @@ func releaseCmd() *cobra.Command {
6364
releaseCmd.Flags().BoolVar(&diff.detailedExitCode, "detailed-exitcode", false, "return a non-zero exit code when there are changes")
6465
releaseCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
6566
releaseCmd.Flags().BoolVar(&diff.normalizeManifests, "normalize-manifests", false, "normalize manifests before running diff to exclude style differences from the output")
67+
releaseCmd.Flags().StringVar(&diff.kubeContext, "kube-context", "", "name of the kubeconfig context to use")
6668
AddDiffOptions(releaseCmd.Flags(), &diff.Options)
6769

6870
releaseCmd.SuggestionsMinimumDistance = 1
@@ -82,11 +84,11 @@ func (d *release) differentiateHelm3() error {
8284
namespace1 = strings.Split(release1, "/")[0]
8385
release1 = strings.Split(release1, "/")[1]
8486
}
85-
releaseResponse1, err := getRelease(release1, namespace1)
87+
releaseResponse1, err := getRelease(release1, namespace1, d.kubeContext)
8688
if err != nil {
8789
return err
8890
}
89-
releaseChart1, err := getChart(release1, namespace1)
91+
releaseChart1, err := getChart(release1, namespace1, d.kubeContext)
9092
if err != nil {
9193
return err
9294
}
@@ -97,11 +99,11 @@ func (d *release) differentiateHelm3() error {
9799
namespace2 = strings.Split(release2, "/")[0]
98100
release2 = strings.Split(release2, "/")[1]
99101
}
100-
releaseResponse2, err := getRelease(release2, namespace2)
102+
releaseResponse2, err := getRelease(release2, namespace2, d.kubeContext)
101103
if err != nil {
102104
return err
103105
}
104-
releaseChart2, err := getChart(release2, namespace2)
106+
releaseChart2, err := getChart(release2, namespace2, d.kubeContext)
105107
if err != nil {
106108
return err
107109
}

cmd/revision.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
type revision struct {
1616
release string
17+
kubeContext string
1718
detailedExitCode bool
1819
revisions []string
1920
includeTests bool
@@ -70,6 +71,7 @@ func revisionCmd() *cobra.Command {
7071
revisionCmd.Flags().BoolVar(&diff.detailedExitCode, "detailed-exitcode", false, "return a non-zero exit code when there are changes")
7172
revisionCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
7273
revisionCmd.Flags().BoolVar(&diff.normalizeManifests, "normalize-manifests", false, "normalize manifests before running diff to exclude style differences from the output")
74+
revisionCmd.Flags().StringVar(&diff.kubeContext, "kube-context", "", "name of the kubeconfig context to use")
7375
AddDiffOptions(revisionCmd.Flags(), &diff.Options)
7476

7577
revisionCmd.SuggestionsMinimumDistance = 1
@@ -85,14 +87,14 @@ func (d *revision) differentiateHelm3() error {
8587
}
8688
switch len(d.revisions) {
8789
case 1:
88-
releaseResponse, err := getRelease(d.release, namespace)
90+
releaseResponse, err := getRelease(d.release, namespace, d.kubeContext)
8991

9092
if err != nil {
9193
return err
9294
}
9395

9496
revision, _ := strconv.Atoi(d.revisions[0])
95-
revisionResponse, err := getRevision(d.release, revision, namespace)
97+
revisionResponse, err := getRevision(d.release, revision, namespace, d.kubeContext)
9698
if err != nil {
9799
return err
98100
}
@@ -110,12 +112,12 @@ func (d *revision) differentiateHelm3() error {
110112
revision1, revision2 = revision2, revision1
111113
}
112114

113-
revisionResponse1, err := getRevision(d.release, revision1, namespace)
115+
revisionResponse1, err := getRevision(d.release, revision1, namespace, d.kubeContext)
114116
if err != nil {
115117
return err
116118
}
117119

118-
revisionResponse2, err := getRevision(d.release, revision2, namespace)
120+
revisionResponse2, err := getRevision(d.release, revision2, namespace, d.kubeContext)
119121
if err != nil {
120122
return err
121123
}

cmd/rollback.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
type rollback struct {
1616
release string
17+
kubeContext string
1718
detailedExitCode bool
1819
revisions []string
1920
includeTests bool
@@ -60,6 +61,7 @@ func rollbackCmd() *cobra.Command {
6061
rollbackCmd.Flags().BoolVar(&diff.detailedExitCode, "detailed-exitcode", false, "return a non-zero exit code when there are changes")
6162
rollbackCmd.Flags().BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
6263
rollbackCmd.Flags().BoolVar(&diff.normalizeManifests, "normalize-manifests", false, "normalize manifests before running diff to exclude style differences from the output")
64+
rollbackCmd.Flags().StringVar(&diff.kubeContext, "kube-context", "", "name of the kubeconfig context to use")
6365
AddDiffOptions(rollbackCmd.Flags(), &diff.Options)
6466

6567
rollbackCmd.SuggestionsMinimumDistance = 1
@@ -74,15 +76,15 @@ func (d *rollback) backcastHelm3() error {
7476
excludes = []string{}
7577
}
7678
// get manifest of the latest release
77-
releaseResponse, err := getRelease(d.release, namespace)
79+
releaseResponse, err := getRelease(d.release, namespace, d.kubeContext)
7880

7981
if err != nil {
8082
return err
8183
}
8284

8385
// get manifest of the release to rollback
8486
revision, _ := strconv.Atoi(d.revisions[0])
85-
revisionResponse, err := getRevision(d.release, revision, namespace)
87+
revisionResponse, err := getRevision(d.release, revision, namespace, d.kubeContext)
8688
if err != nil {
8789
return err
8890
}

cmd/upgrade.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ type diffCmd struct {
7373
// - "server": dry run is performed with remote cluster access
7474
// - "true": same as "client"
7575
// - "false": same as "none"
76-
dryRunMode string
76+
dryRunMode string
77+
kubeContext string
7778
}
7879

7980
func (d *diffCmd) isAllowUnreleased() bool {
@@ -212,7 +213,7 @@ func newChartCommand() *cobra.Command {
212213
var kubeconfig string
213214
f.StringVar(&kubeconfig, "kubeconfig", "", "This flag is ignored, to allow passing of this top level flag to helm")
214215
f.BoolVar(&diff.threeWayMerge, "three-way-merge", false, "use three-way-merge to compute patch and generate diff output")
215-
// f.StringVar(&diff.kubeContext, "kube-context", "", "name of the kubeconfig context to use")
216+
f.StringVar(&diff.kubeContext, "kube-context", "", "name of the kubeconfig context to use")
216217
f.StringVar(&diff.chartVersion, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used")
217218
f.StringVar(&diff.chartRepo, "repo", "", "specify the chart repository url to locate the requested chart")
218219
f.BoolVar(&diff.detailedExitCode, "detailed-exitcode", false, "return a non-zero exit code when there are changes")
@@ -271,7 +272,7 @@ func (d *diffCmd) runHelm3() error {
271272
}
272273

273274
if d.clusterAccessAllowed() {
274-
releaseManifest, err = getRelease(d.release, d.namespace)
275+
releaseManifest, err = getRelease(d.release, d.namespace, d.kubeContext)
275276
}
276277

277278
var newInstall bool
@@ -296,7 +297,12 @@ func (d *diffCmd) runHelm3() error {
296297
var actionConfig *action.Configuration
297298
if d.threeWayMerge || d.takeOwnership {
298299
actionConfig = new(action.Configuration)
299-
if err := actionConfig.Init(envSettings.RESTClientGetter(), envSettings.Namespace(), os.Getenv("HELM_DRIVER")); err != nil {
300+
localEnv := cli.New()
301+
*localEnv = *envSettings
302+
if d.kubeContext != "" {
303+
localEnv.KubeContext = d.kubeContext
304+
}
305+
if err := actionConfig.Init(localEnv.RESTClientGetter(), localEnv.Namespace(), os.Getenv("HELM_DRIVER")); err != nil {
300306
log.Fatalf("%+v", err)
301307
}
302308
if err := actionConfig.KubeClient.IsReachable(); err != nil {
@@ -314,7 +320,7 @@ func (d *diffCmd) runHelm3() error {
314320
currentSpecs := make(map[string]*manifest.MappingResult)
315321
if !newInstall && d.clusterAccessAllowed() {
316322
if !d.noHooks && !d.threeWayMerge {
317-
hooks, err := getHooks(d.release, d.namespace)
323+
hooks, err := getHooks(d.release, d.namespace, d.kubeContext)
318324
if err != nil {
319325
return err
320326
}

0 commit comments

Comments
 (0)