From d1416ec7b4ec8764a0be2058f2e4af24a424838a Mon Sep 17 00:00:00 2001 From: Zubair Haque Date: Thu, 24 Oct 2024 07:53:18 -0500 Subject: [PATCH] feat: add skip json schema validation during the install /upgrade of a Chart (#1737) * open PR for --skip-schema-validation flag Signed-off-by: zhaque44 --- cmd/apply.go | 1 + cmd/sync.go | 1 + cmd/template.go | 1 + pkg/app/app.go | 24 +++++++++++++----------- pkg/app/app_test.go | 28 +++++++++++++++++++--------- pkg/app/config.go | 2 ++ pkg/app/diff_test.go | 5 +++++ pkg/config/apply.go | 7 +++++++ pkg/config/diff.go | 5 +++++ pkg/config/sync.go | 7 +++++++ pkg/config/template.go | 7 +++++++ pkg/exectest/helm.go | 2 ++ pkg/state/helmx.go | 16 ++++++++++++++++ pkg/state/state.go | 42 +++++++++++++++++++++++++++++++----------- pkg/state/temp_test.go | 12 ++++++------ 15 files changed, 123 insertions(+), 37 deletions(-) diff --git a/cmd/apply.go b/cmd/apply.go index c2efe0fa..b019e6f2 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -69,6 +69,7 @@ func NewApplyCmd(globalCfg *config.GlobalImpl) *cobra.Command { f.BoolVar(&applyOptions.ResetValues, "reset-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reset-values"`) f.StringVar(&applyOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`) f.StringArrayVar(&applyOptions.PostRendererArgs, "post-renderer-args", nil, `pass --post-renderer-args to "helm template" or "helm upgrade --install"`) + f.BoolVar(&applyOptions.SkipSchemaValidation, "skip-schema-validation", false, `pass --skip-schema-validation to "helm template" or "helm upgrade --install"`) f.StringVar(&applyOptions.Cascade, "cascade", "", "pass cascade to helm exec, default: background") f.StringArrayVar(&applyOptions.SuppressOutputLineRegex, "suppress-output-line-regex", nil, "a list of regex patterns to suppress output lines from the diff output") diff --git a/cmd/sync.go b/cmd/sync.go index d8782dc0..ae2af7fc 100644 --- a/cmd/sync.go +++ b/cmd/sync.go @@ -48,6 +48,7 @@ func NewSyncCmd(globalCfg *config.GlobalImpl) *cobra.Command { f.BoolVar(&syncOptions.ResetValues, "reset-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reset-values"`) f.StringVar(&syncOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`) f.StringArrayVar(&syncOptions.PostRendererArgs, "post-renderer-args", nil, `pass --post-renderer-args to "helm template" or "helm upgrade --install"`) + f.BoolVar(&syncOptions.SkipSchemaValidation, "skip-schema-validation", false, `pass --skip-schema-validation to "helm template" or "helm upgrade --install"`) f.StringVar(&syncOptions.Cascade, "cascade", "", "pass cascade to helm exec, default: background") return cmd diff --git a/cmd/template.go b/cmd/template.go index 8d85f903..fb26ff3a 100644 --- a/cmd/template.go +++ b/cmd/template.go @@ -47,6 +47,7 @@ func NewTemplateCmd(globalCfg *config.GlobalImpl) *cobra.Command { f.BoolVar(&templateOptions.SkipCleanup, "skip-cleanup", false, "Stop cleaning up temporary values generated by helmfile and helm-secrets. Useful for debugging. Don't use in production for security") f.StringVar(&templateOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`) f.StringArrayVar(&templateOptions.PostRendererArgs, "post-renderer-args", nil, `pass --post-renderer-args to "helm template" or "helm upgrade --install"`) + f.BoolVar(&templateOptions.SkipSchemaValidation, "skip-schema-validation", false, `pass skip-schema-validation to "helm template" or "helm upgrade --install"`) f.StringVar(&templateOptions.KubeVersion, "kube-version", "", `pass --kube-version to "helm template". Overrides kubeVersion in helmfile.yaml`) f.StringArrayVar(&templateOptions.ShowOnly, "show-only", nil, `pass --show-only to "helm template"`) diff --git a/pkg/app/app.go b/pkg/app/app.go index 2a1ce50e..7d0e0e11 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -1537,17 +1537,18 @@ Do you really want to apply? subst.Releases = rs syncOpts := &state.SyncOpts{ - Set: c.Set(), - SkipCleanup: c.RetainValuesFiles() || c.SkipCleanup(), - SkipCRDs: c.SkipCRDs(), - Wait: c.Wait(), - WaitForJobs: c.WaitForJobs(), - ReuseValues: c.ReuseValues(), - ResetValues: c.ResetValues(), - PostRenderer: c.PostRenderer(), - PostRendererArgs: c.PostRendererArgs(), - SyncArgs: c.SyncArgs(), - HideNotes: c.HideNotes(), + Set: c.Set(), + SkipCleanup: c.RetainValuesFiles() || c.SkipCleanup(), + SkipCRDs: c.SkipCRDs(), + Wait: c.Wait(), + WaitForJobs: c.WaitForJobs(), + ReuseValues: c.ReuseValues(), + ResetValues: c.ResetValues(), + PostRenderer: c.PostRenderer(), + PostRendererArgs: c.PostRendererArgs(), + SkipSchemaValidation: c.SkipSchemaValidation(), + SyncArgs: c.SyncArgs(), + HideNotes: c.HideNotes(), } return subst.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency(), syncOpts) })) @@ -1673,6 +1674,7 @@ func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error) ResetValues: c.ResetValues(), PostRenderer: c.PostRenderer(), PostRendererArgs: c.PostRendererArgs(), + SkipSchemaValidation: c.SkipSchemaValidation(), SuppressOutputLineRegex: c.SuppressOutputLineRegex(), } diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index d84c8049..f6094f79 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -2085,15 +2085,16 @@ services: } type configImpl struct { - selectors []string - set []string - output string - includeCRDs bool - skipCleanup bool - skipCRDs bool - skipDeps bool - skipRefresh bool - skipTests bool + selectors []string + set []string + output string + includeCRDs bool + skipCleanup bool + skipCRDs bool + skipDeps bool + skipTests bool + skipSchemaValidation bool + skipRefresh bool skipNeeds bool includeNeeds bool @@ -2194,6 +2195,10 @@ func (c configImpl) KubeVersion() string { return c.kubeVersion } +func (c configImpl) SkipSchemaValidation() bool { + return c.skipSchemaValidation +} + func (c configImpl) ShowOnly() []string { return nil } @@ -2238,6 +2243,7 @@ type applyConfig struct { reuseValues bool postRenderer string postRendererArgs []string + skipSchemaValidation bool kubeVersion string suppressOutputLineRegex []string showOnly []string @@ -2422,6 +2428,10 @@ func (a applyConfig) KubeVersion() string { return a.kubeVersion } +func (a applyConfig) SkipSchemaValidation() bool { + return a.skipSchemaValidation +} + func (a applyConfig) ShowOnly() []string { return a.showOnly } diff --git a/pkg/app/config.go b/pkg/app/config.go index ba1b5173..d52feabe 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -51,6 +51,7 @@ type ApplyConfigProvider interface { Args() string PostRenderer() string PostRendererArgs() []string + SkipSchemaValidation() bool Cascade() string HideNotes() bool SuppressOutputLineRegex() []string @@ -130,6 +131,7 @@ type DiffConfigProvider interface { Args() string PostRenderer() string PostRendererArgs() []string + SkipSchemaValidation() bool SuppressOutputLineRegex() []string Values() []string diff --git a/pkg/app/diff_test.go b/pkg/app/diff_test.go index 08ec45cd..f1987143 100644 --- a/pkg/app/diff_test.go +++ b/pkg/app/diff_test.go @@ -43,6 +43,7 @@ type diffConfig struct { stripTrailingCR bool interactive bool skipDiffOnInstall bool + skipSchemaValidation bool reuseValues bool logger *zap.SugaredLogger } @@ -175,6 +176,10 @@ func (a diffConfig) PostRendererArgs() []string { return nil } +func (a diffConfig) SkipSchemaValidation() bool { + return a.skipSchemaValidation +} + func (a diffConfig) SuppressOutputLineRegex() []string { return a.suppressOutputLineRegex } diff --git a/pkg/config/apply.go b/pkg/config/apply.go index 296c17cc..fdb157e0 100644 --- a/pkg/config/apply.go +++ b/pkg/config/apply.go @@ -52,6 +52,8 @@ type ApplyOptions struct { Wait bool // WaitForJobs is true if the helm command should wait for the jobs to be completed WaitForJobs bool + // Propagate '--skipSchemaValidation' to helmv3 template and helm install + SkipSchemaValidation bool // ReuseValues is true if the helm command should reuse the values ReuseValues bool // ResetValues is true if helm command should reset values to charts' default @@ -235,6 +237,11 @@ func (a *ApplyImpl) PostRendererArgs() []string { return a.ApplyOptions.PostRendererArgs } +// SkipSchemaValidation returns the SkipSchemaValidation. +func (a *ApplyImpl) SkipSchemaValidation() bool { + return a.ApplyOptions.SkipSchemaValidation +} + // Cascade returns cascade flag func (a *ApplyImpl) Cascade() string { return a.ApplyOptions.Cascade diff --git a/pkg/config/diff.go b/pkg/config/diff.go index 0fe525e8..c2717b89 100644 --- a/pkg/config/diff.go +++ b/pkg/config/diff.go @@ -48,6 +48,7 @@ type DiffOptions struct { DiffArgs string // SuppressOutputLineRegex is a list of regexes to suppress output lines SuppressOutputLineRegex []string + SkipSchemaValidation bool } // NewDiffOptions creates a new Apply @@ -199,3 +200,7 @@ func (t *DiffImpl) PostRendererArgs() []string { func (t *DiffImpl) SuppressOutputLineRegex() []string { return t.DiffOptions.SuppressOutputLineRegex } + +func (t *DiffImpl) SkipSchemaValidation() bool { + return t.DiffOptions.SkipSchemaValidation +} diff --git a/pkg/config/sync.go b/pkg/config/sync.go index 7c01598e..1ee69e5c 100644 --- a/pkg/config/sync.go +++ b/pkg/config/sync.go @@ -30,6 +30,8 @@ type SyncOptions struct { PostRenderer string // Propagate '--post-renderer-args' to helmv3 template and helm install PostRendererArgs []string + // Propagate '--skipSchemaValidation' to helmv3 template and helm install + SkipSchemaValidation bool // Cascade '--cascade' to helmv3 delete, available values: background, foreground, or orphan, default: background Cascade string // SyncArgs is the list of arguments to pass to the helm upgrade command. @@ -132,6 +134,11 @@ func (t *SyncImpl) PostRendererArgs() []string { return t.SyncOptions.PostRendererArgs } +// SkipSchemaValidation returns the SkipSchemaValidation. +func (t *SyncImpl) SkipSchemaValidation() bool { + return t.SyncOptions.SkipSchemaValidation +} + // Cascade returns cascade flag func (t *SyncImpl) Cascade() string { return t.SyncOptions.Cascade diff --git a/pkg/config/template.go b/pkg/config/template.go index 5ef781ea..30d2eea4 100644 --- a/pkg/config/template.go +++ b/pkg/config/template.go @@ -36,6 +36,8 @@ type TemplateOptions struct { PostRenderer string // Propagate '--post-renderer-args' to helmv3 template and helm install PostRendererArgs []string + // Propagate '--skipSchemaValidation' to helmv3 template and helm install + SkipSchemaValidation bool // KubeVersion is the kube-version flag KubeVersion string // Propagate '--show-only` to helm template @@ -135,6 +137,11 @@ func (t *TemplateImpl) PostRendererArgs() []string { return t.TemplateOptions.PostRendererArgs } +// SkipSchemaValidation returns the SkipSchemaValidation. +func (t *TemplateImpl) SkipSchemaValidation() bool { + return t.TemplateOptions.SkipSchemaValidation +} + // KubeVersion returns the the KubeVersion. func (t *TemplateImpl) KubeVersion() string { return t.TemplateOptions.KubeVersion diff --git a/pkg/exectest/helm.go b/pkg/exectest/helm.go index 3b7c405c..9daaf0ec 100644 --- a/pkg/exectest/helm.go +++ b/pkg/exectest/helm.go @@ -94,6 +94,8 @@ func (helm *Helm) SetEnableLiveOutput(enableLiveOutput bool) { } func (helm *Helm) SetDisableForceUpdate(forceUpdate bool) { } +func (helm *Helm) SkipSchemaValidation(skipSchemaValidation bool) { +} func (helm *Helm) AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string, passCredentials, skipTLSVerify bool) error { helm.Repo = []string{name, repository, cafile, certfile, keyfile, username, password, managed, fmt.Sprintf("%v", passCredentials), fmt.Sprintf("%v", skipTLSVerify)} return nil diff --git a/pkg/state/helmx.go b/pkg/state/helmx.go index b1b2bb8d..bf584e34 100644 --- a/pkg/state/helmx.go +++ b/pkg/state/helmx.go @@ -58,6 +58,22 @@ func (st *HelmState) appendPostRenderArgsFlags(flags []string, release *ReleaseS return flags } +// append skip-schema-validation flags to helm flags +func (st *HelmState) appendSkipSchemaValidationFlags(flags []string, release *ReleaseSpec, skipSchemaValidation bool) []string { + switch { + // Check if SkipSchemaValidation is true in the release spec. + case release.SkipSchemaValidation != nil && *release.SkipSchemaValidation: + flags = append(flags, "--skip-schema-validation") + // Check if skipSchemaValidation argument is true. + case skipSchemaValidation: + flags = append(flags, "--skip-schema-validation") + // Check if SkipSchemaValidation is true in HelmDefaults. + case st.HelmDefaults.SkipSchemaValidation != nil && *st.HelmDefaults.SkipSchemaValidation: + flags = append(flags, "--skip-schema-validation") + } + return flags +} + // append suppress-output-line-regex flags to helm diff flags func (st *HelmState) appendSuppressOutputLineRegexFlags(flags []string, release *ReleaseSpec, suppressOutputLineRegex []string) []string { suppressOutputLineRegexFlags := []string{} diff --git a/pkg/state/state.go b/pkg/state/state.go index 8e5b3e76..717ed80d 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -160,6 +160,8 @@ type HelmSpec struct { Keyring string `yaml:"keyring,omitempty"` // EnableDNS, when set to true, enable DNS lookups when rendering templates EnableDNS bool `yaml:"enableDNS"` + // Propagate '--skipSchemaValidation' to helmv3 template and helm install + SkipSchemaValidation *bool `yaml:"skipSchemaValidation,omitempty"` // Devel, when set to true, use development versions, too. Equivalent to version '>0.0.0-0' Devel bool `yaml:"devel"` // Wait, if set to true, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful @@ -382,6 +384,9 @@ type ReleaseSpec struct { // Propagate '--post-renderer' to helmv3 template and helm install PostRenderer *string `yaml:"postRenderer,omitempty"` + // Propagate '--skipSchemaValidation' to helmv3 template and helm install + SkipSchemaValidation *bool `yaml:"skipSchemaValidation,omitempty"` + // Propagate '--post-renderer-args' to helmv3 template and helm install PostRendererArgs []string `yaml:"postRendererArgs,omitempty"` @@ -770,17 +775,18 @@ func (st *HelmState) DetectReleasesToBeDeleted(helm helmexec.Interface, releases } type SyncOpts struct { - Set []string - SkipCleanup bool - SkipCRDs bool - Wait bool - WaitForJobs bool - ReuseValues bool - ResetValues bool - PostRenderer string - PostRendererArgs []string - SyncArgs string - HideNotes bool + Set []string + SkipCleanup bool + SkipCRDs bool + Wait bool + WaitForJobs bool + ReuseValues bool + ResetValues bool + PostRenderer string + SkipSchemaValidation bool + PostRendererArgs []string + SyncArgs string + HideNotes bool } type SyncOpt interface{ Apply(*SyncOpts) } @@ -1997,6 +2003,7 @@ type DiffOpts struct { PostRenderer string PostRendererArgs []string SuppressOutputLineRegex []string + SkipSchemaValidation bool } func (o *DiffOpts) Apply(opts *DiffOpts) { @@ -2742,6 +2749,13 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp } flags = st.appendPostRenderArgsFlags(flags, release, postRendererArgs) + skipSchemaValidation := false + if opt != nil { + skipSchemaValidation = opt.SkipSchemaValidation + } + + flags = st.appendSkipSchemaValidationFlags(flags, release, skipSchemaValidation) + // append hide-notes flag flags = st.appendHideNotesFlags(flags, helm, opt) @@ -2849,6 +2863,12 @@ func (st *HelmState) flagsForDiff(helm helmexec.Interface, release *ReleaseSpec, } flags = st.appendPostRenderArgsFlags(flags, release, postRendererArgs) + skipSchemaValidation := false + if opt != nil { + skipSchemaValidation = opt.SkipSchemaValidation + } + flags = st.appendSkipSchemaValidationFlags(flags, release, skipSchemaValidation) + suppressOutputLineRegex := []string{} if opt != nil { suppressOutputLineRegex = opt.SuppressOutputLineRegex diff --git a/pkg/state/temp_test.go b/pkg/state/temp_test.go index 975456ef..1fb59c3a 100644 --- a/pkg/state/temp_test.go +++ b/pkg/state/temp_test.go @@ -38,39 +38,39 @@ func TestGenerateID(t *testing.T) { run(testcase{ subject: "baseline", release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, - want: "foo-values-d566bffd8", + want: "foo-values-5db58595d7", }) run(testcase{ subject: "different bytes content", release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, data: []byte(`{"k":"v"}`), - want: "foo-values-65b557f8c5", + want: "foo-values-78d88d86dd", }) run(testcase{ subject: "different map content", release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, data: map[string]any{"k": "v"}, - want: "foo-values-74d5cd8cc7", + want: "foo-values-f9c8967cd", }) run(testcase{ subject: "different chart", release: ReleaseSpec{Name: "foo", Chart: "stable/envoy"}, - want: "foo-values-85db6bbb4c", + want: "foo-values-cdfb97444", }) run(testcase{ subject: "different name", release: ReleaseSpec{Name: "bar", Chart: "incubator/raw"}, - want: "bar-values-85cf974b7", + want: "bar-values-749bc4c6d4", }) run(testcase{ subject: "specific ns", release: ReleaseSpec{Name: "foo", Chart: "incubator/raw", Namespace: "myns"}, - want: "myns-foo-values-574676fbb9", + want: "myns-foo-values-7b74fbd6d6", }) for id, n := range ids {