Skip to content

Commit b7200d9

Browse files
committed
Remake dry-run=server optional
This is a follow-up on #499 which redoes it without relying on DisableParsing. DisableParsing turned out to break far many things than I imagined!
1 parent 83a48fe commit b7200d9

File tree

2 files changed

+38
-85
lines changed

2 files changed

+38
-85
lines changed

cmd/upgrade.go

Lines changed: 26 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
jsonpatch "github.com/evanphx/json-patch"
1414
jsoniterator "github.com/json-iterator/go"
1515
"github.com/spf13/cobra"
16-
"github.com/spf13/pflag"
1716
"helm.sh/helm/v3/pkg/action"
1817
"helm.sh/helm/v3/pkg/cli"
1918
"helm.sh/helm/v3/pkg/kube"
@@ -38,8 +37,6 @@ type diffCmd struct {
3837
devel bool
3938
disableValidation bool
4039
disableOpenAPIValidation bool
41-
dryRunMode string
42-
dryRunModeSpecified bool
4340
enableDNS bool
4441
namespace string // namespace to assume the release to be installed into. Defaults to the current kube config namespace.
4542
valueFiles valueFiles
@@ -62,6 +59,14 @@ type diffCmd struct {
6259
kubeVersion string
6360
useUpgradeDryRun bool
6461
diff.Options
62+
63+
// dryRunMode can take the following values:
64+
// - "none": no dry run is performed
65+
// - "client": dry run is performed without remote cluster access
66+
// - "server": dry run is performed with remote cluster access
67+
// - "true": same as "client"
68+
// - "false": same as "none"
69+
dryRunMode string
6570
}
6671

6772
func (d *diffCmd) isAllowUnreleased() bool {
@@ -73,10 +78,9 @@ func (d *diffCmd) isAllowUnreleased() bool {
7378

7479
// clusterAccessAllowed returns true if the diff command is allowed to access the cluster at some degree.
7580
//
76-
// helm-diff basically have 3 modes of operation:
77-
// 1. no cluster access at all when --dry-run, --dry-run=client, or --dry-run=true is specified.
78-
// 2. basic cluster access when --dry-run is not specified.
79-
// 3. extra cluster access when --dry-run=server is specified.
81+
// helm-diff basically have 2 modes of operation:
82+
// 1. without cluster access at all when --dry-run=true or --dry-run=client is specified.
83+
// 2. with cluster access when --dry-run is unspecified, false, or server.
8084
//
8185
// clusterAccessAllowed returns true when the mode is either 2 or 3.
8286
//
@@ -89,8 +93,7 @@ func (d *diffCmd) isAllowUnreleased() bool {
8993
//
9094
// See also https://github.com/helm/helm/pull/9426#discussion_r1181397259
9195
func (d *diffCmd) clusterAccessAllowed() bool {
92-
clientOnly := (d.dryRunModeSpecified && d.dryRunMode == "") || d.dryRunMode == "true" || d.dryRunMode == "client"
93-
return !clientOnly
96+
return d.dryRunMode == "none" || d.dryRunMode == "false" || d.dryRunMode == "server"
9497
}
9598

9699
const globalUsage = `Show a diff explaining what a helm upgrade would change.
@@ -111,10 +114,9 @@ func newChartCommand() *cobra.Command {
111114
unknownFlags := os.Getenv("HELM_DIFF_IGNORE_UNKNOWN_FLAGS") == "true"
112115

113116
cmd := &cobra.Command{
114-
Use: "upgrade [flags] [RELEASE] [CHART]",
115-
Short: "Show a diff explaining what a helm upgrade would change.",
116-
Long: globalUsage,
117-
DisableFlagParsing: true,
117+
Use: "upgrade [flags] [RELEASE] [CHART]",
118+
Short: "Show a diff explaining what a helm upgrade would change.",
119+
Long: globalUsage,
118120
Example: strings.Join([]string{
119121
" helm diff upgrade my-release stable/postgresql --values values.yaml",
120122
"",
@@ -153,71 +155,16 @@ func newChartCommand() *cobra.Command {
153155
"# Read the flag usage below for more information on --context.",
154156
"HELM_DIFF_OUTPUT_CONTEXT=5 helm diff upgrade my-release datadog/datadog",
155157
}, "\n"),
158+
Args: func(cmd *cobra.Command, args []string) error {
159+
return checkArgsLength(len(args), "release name", "chart path")
160+
},
156161
RunE: func(cmd *cobra.Command, args []string) error {
157-
// Note that we can't just move this block to PersistentPreRunE,
158-
// because cmd.SetArgs(args) does not persist between PersistentPreRunE and RunE.
159-
// The choice is between:
160-
// 1. Doing this in RunE
161-
// 2. Doing this in PersistentPreRunE, saving args somewhere, and calling cmd.SetArgs(args) again in RunE
162-
// 2 is more complicated without much benefit, so we choose 1.
163-
{
164-
const (
165-
dryRunUsage = "--dry-run, --dry-run=client, or --dry-run=true disables cluster access and show diff as if it was install. Implies --install, --reset-values, and --disable-validation." +
166-
" --dry-run=server enables the cluster access with helm-get and the lookup template function."
167-
)
168-
169-
cmdFlags := cmd.Flags()
170-
171-
// see: https://github.com/databus23/helm-diff/issues/537
172-
cmdFlags.ParseErrorsWhitelist.UnknownFlags = unknownFlags
173-
174-
legacyDryRunFlagSet := pflag.NewFlagSet("upgrade", pflag.ContinueOnError)
175-
legacyDryRun := legacyDryRunFlagSet.Bool("dry-run", false, dryRunUsage)
176-
if err := legacyDryRunFlagSet.Parse(args); err == nil && *legacyDryRun {
177-
diff.dryRunModeSpecified = true
178-
args = legacyDryRunFlagSet.Args()
179-
} else {
180-
cmdFlags.StringVar(&diff.dryRunMode, "dry-run", "", dryRunUsage)
181-
}
182-
183-
// Here we parse the flags ourselves so that we can support
184-
// both --dry-run and --dry-run=ARG syntaxes.
185-
//
186-
// If you don't do this, then cobra will treat --dry-run as
187-
// a "flag needs an argument: --dry-run" error.
188-
//
189-
// This works becase we have `DisableFlagParsing: true`` above.
190-
// Never turn that off, or you'll get the error again.
191-
if err := cmdFlags.Parse(args); err != nil {
192-
return err
193-
}
194-
195-
args = cmdFlags.Args()
196-
197-
if !diff.dryRunModeSpecified {
198-
dryRunModeSpecified := cmd.Flags().Changed("dry-run")
199-
diff.dryRunModeSpecified = dryRunModeSpecified
200-
201-
if dryRunModeSpecified && !(diff.dryRunMode == "client" || diff.dryRunMode == "server") {
202-
return fmt.Errorf("flag %q must take an argument of %q or %q but got %q", "dry-run", "client", "server", diff.dryRunMode)
203-
}
204-
}
162+
if diff.dryRunMode == "" {
163+
diff.dryRunMode = "none"
164+
}
205165

206-
cmd.SetArgs(args)
207-
208-
// We have to do this here because we have to sort out the
209-
// --dry-run flag ambiguity to determine the args to be checked.
210-
//
211-
// In other words, we can't just do:
212-
//
213-
// cmd.Args = func(cmd *cobra.Command, args []string) error {
214-
// return checkArgsLength(len(args), "release name", "chart path")
215-
// }
216-
//
217-
// Because it seems to take precedence over the PersistentPreRunE
218-
if err := checkArgsLength(len(args), "release name", "chart path"); err != nil {
219-
return err
220-
}
166+
if diff.dryRunMode != "true" && diff.dryRunMode != "false" && diff.dryRunMode != "client" && diff.dryRunMode != "server" {
167+
return fmt.Errorf("flag %q must take a bool value or either %q or %q, but got %q", "dry-run", "client", "server", diff.dryRunMode)
221168
}
222169

223170
// Suppress the command usage on error. See #77 for more info
@@ -293,6 +240,9 @@ func newChartCommand() *cobra.Command {
293240
f.BoolVar(&diff.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
294241
f.BoolVar(&diff.disableValidation, "disable-validation", false, "disables rendered templates validation against the Kubernetes cluster you are currently pointing to. This is the same validation performed on an install")
295242
f.BoolVar(&diff.disableOpenAPIValidation, "disable-openapi-validation", false, "disables rendered templates validation against the Kubernetes OpenAPI Schema")
243+
f.StringVar(&diff.dryRunMode, "dry-run", "", "--dry-run, --dry-run=client, or --dry-run=true disables cluster access and show diff as if it was install. Implies --install, --reset-values, and --disable-validation."+
244+
" --dry-run=server enables the cluster access with helm-get and the lookup template function.")
245+
f.Lookup("dry-run").NoOptDefVal = "client"
296246
f.BoolVar(&diff.enableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
297247
f.StringVar(&diff.postRenderer, "post-renderer", "", "the path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path")
298248
f.StringArrayVar(&diff.postRendererArgs, "post-renderer-args", []string{}, "an argument to the post-renderer (can specify multiple)")

cmd/upgrade_test.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,37 @@ func TestIsRemoteAccessAllowed(t *testing.T) {
1414
expected: true,
1515
},
1616
{
17-
name: "legacy explicit dry-run flag",
17+
name: "legacy explicit dry-run=true flag",
1818
cmd: diffCmd{
19-
dryRunModeSpecified: true,
20-
dryRunMode: "true",
19+
dryRunMode: "true",
20+
},
21+
expected: false,
22+
},
23+
{
24+
name: "legacy explicit dry-run=false flag",
25+
cmd: diffCmd{
26+
dryRunMode: "false",
2127
},
2228
expected: false,
2329
},
2430
{
2531
name: "legacy empty dry-run flag",
2632
cmd: diffCmd{
27-
dryRunModeSpecified: true,
28-
dryRunMode: "",
33+
dryRunMode: "none",
2934
},
3035
expected: false,
3136
},
3237
{
3338
name: "server-side dry-run flag",
3439
cmd: diffCmd{
35-
dryRunModeSpecified: true,
36-
dryRunMode: "server",
40+
dryRunMode: "server",
3741
},
3842
expected: true,
3943
},
4044
{
4145
name: "client-side dry-run flag",
4246
cmd: diffCmd{
43-
dryRunModeSpecified: true,
44-
dryRunMode: "client",
47+
dryRunMode: "client",
4548
},
4649
expected: false,
4750
},

0 commit comments

Comments
 (0)