Add skip-schema-validation flag

Adds a "--skip-schema-validation" flag option for helm install, upgrade, lint, and template that skips validation of the values.schema.json file.

Signed-off-by: JvD_Ericsson <jeff.van.dam@est.tech>
pull/11510/head
JvD_Ericsson 3 years ago
parent 269dd247d0
commit 2f6a267faa

@ -169,6 +169,7 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used") f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present") f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")
addValueOptionsFlags(f, valueOpts) addValueOptionsFlags(f, valueOpts)
addChartPathOptionsFlags(f, &client.ChartPathOptions) addChartPathOptionsFlags(f, &client.ChartPathOptions)

@ -137,6 +137,7 @@ func newLintCmd(out io.Writer) *cobra.Command {
f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings") f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings")
f.BoolVar(&client.WithSubcharts, "with-subcharts", false, "lint dependent charts") f.BoolVar(&client.WithSubcharts, "with-subcharts", false, "lint dependent charts")
f.BoolVar(&client.Quiet, "quiet", false, "print only warnings and errors") f.BoolVar(&client.Quiet, "quiet", false, "print only warnings and errors")
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")
addValueOptionsFlags(f, valueOpts) addValueOptionsFlags(f, valueOpts)
return cmd return cmd

@ -231,6 +231,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
f.StringVar(&client.Description, "description", "", "add a custom description") f.StringVar(&client.Description, "description", "", "add a custom description")
f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart") f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")
addChartPathOptionsFlags(f, &client.ChartPathOptions) addChartPathOptionsFlags(f, &client.ChartPathOptions)
addValueOptionsFlags(f, valueOpts) addValueOptionsFlags(f, valueOpts)
bindOutputFlag(cmd, &outfmt) bindOutputFlag(cmd, &outfmt)

Binary file not shown.

@ -86,6 +86,7 @@ type Install struct {
OutputDir string OutputDir string
Atomic bool Atomic bool
SkipCRDs bool SkipCRDs bool
SkipSchemaValidation bool
SubNotes bool SubNotes bool
DisableOpenAPIValidation bool DisableOpenAPIValidation bool
IncludeCRDs bool IncludeCRDs bool
@ -248,10 +249,19 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
IsInstall: !isUpgrade, IsInstall: !isUpgrade,
IsUpgrade: isUpgrade, IsUpgrade: isUpgrade,
} }
valuesToRender, err := chartutil.ToRenderValues(chrt, vals, options, caps)
var valuesToRender chartutil.Values
if i.SkipSchemaValidation {
valuesToRender, err = chartutil.ToRenderValuesSkipSchemaValidation(chrt, vals, options, caps)
if err != nil {
return nil, err
}
} else {
valuesToRender, err = chartutil.ToRenderValues(chrt, vals, options, caps)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
rel := i.createRelease(chrt, vals) rel := i.createRelease(chrt, vals)

@ -33,6 +33,7 @@ import (
// //
// It provides the implementation of 'helm lint'. // It provides the implementation of 'helm lint'.
type Lint struct { type Lint struct {
SkipSchemaValidation bool
Strict bool Strict bool
Namespace string Namespace string
WithSubcharts bool WithSubcharts bool
@ -59,7 +60,7 @@ func (l *Lint) Run(paths []string, vals map[string]interface{}) *LintResult {
} }
result := &LintResult{} result := &LintResult{}
for _, path := range paths { for _, path := range paths {
linter, err := lintChart(path, vals, l.Namespace, l.Strict) linter, err := lintChart(path, vals, l.Namespace, l.Strict, l.SkipSchemaValidation)
if err != nil { if err != nil {
result.Errors = append(result.Errors, err) result.Errors = append(result.Errors, err)
continue continue
@ -86,7 +87,7 @@ func HasWarningsOrErrors(result *LintResult) bool {
return false return false
} }
func lintChart(path string, vals map[string]interface{}, namespace string, strict bool) (support.Linter, error) { func lintChart(path string, vals map[string]interface{}, namespace string, strict bool, skipSchemaValidation bool) (support.Linter, error) {
var chartPath string var chartPath string
linter := support.Linter{} linter := support.Linter{}
@ -125,5 +126,5 @@ func lintChart(path string, vals map[string]interface{}, namespace string, stric
return linter, errors.Wrap(err, "unable to check Chart.yaml file in chart") return linter, errors.Wrap(err, "unable to check Chart.yaml file in chart")
} }
return lint.All(chartPath, vals, namespace, strict), nil return lint.All(chartPath, vals, namespace, strict, skipSchemaValidation), nil
} }

@ -23,6 +23,7 @@ import (
var ( var (
values = make(map[string]interface{}) values = make(map[string]interface{})
namespace = "testNamespace" namespace = "testNamespace"
skipSchemaValidation = false
strict = false strict = false
chart1MultipleChartLint = "testdata/charts/multiplecharts-lint-chart-1" chart1MultipleChartLint = "testdata/charts/multiplecharts-lint-chart-1"
chart2MultipleChartLint = "testdata/charts/multiplecharts-lint-chart-2" chart2MultipleChartLint = "testdata/charts/multiplecharts-lint-chart-2"
@ -78,7 +79,7 @@ func TestLintChart(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
_, err := lintChart(tt.chartPath, map[string]interface{}{}, namespace, strict) _, err := lintChart(tt.chartPath, map[string]interface{}{}, namespace, strict, skipSchemaValidation)
switch { switch {
case err != nil && !tt.err: case err != nil && !tt.err:
t.Errorf("%s", err) t.Errorf("%s", err)

@ -101,6 +101,8 @@ type Upgrade struct {
DisableOpenAPIValidation bool DisableOpenAPIValidation bool
// Get missing dependencies // Get missing dependencies
DependencyUpdate bool DependencyUpdate bool
// Skip JSON schema validation
SkipSchemaValidation bool
// Lock to control raceconditions when the process receives a SIGTERM // Lock to control raceconditions when the process receives a SIGTERM
Lock sync.Mutex Lock sync.Mutex
} }
@ -226,10 +228,19 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
valuesToRender, err := chartutil.ToRenderValues(chart, vals, options, caps)
var valuesToRender chartutil.Values
if u.SkipSchemaValidation {
valuesToRender, err = chartutil.ToRenderValuesSkipSchemaValidation(chart, vals, options, caps)
if err != nil {
return nil, nil, err
}
} else {
valuesToRender, err = chartutil.ToRenderValues(chart, vals, options, caps)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
}
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun) hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun)
if err != nil { if err != nil {

@ -135,6 +135,17 @@ type ReleaseOptions struct {
// //
// This takes both ReleaseOptions and Capabilities to merge into the render values. // This takes both ReleaseOptions and Capabilities to merge into the render values.
func ToRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}, options ReleaseOptions, caps *Capabilities) (Values, error) { func ToRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}, options ReleaseOptions, caps *Capabilities) (Values, error) {
return toRenderValues(chrt, chrtVals, options, caps, true)
}
// ToRenderValuesSkipSchemaValidation composes the struct from the data coming from the Releases, Charts and Values files but skips schema validation
//
// This takes both ReleaseOptions and Capabilities to merge into the render values.
func ToRenderValuesSkipSchemaValidation(chrt *chart.Chart, chrtVals map[string]interface{}, options ReleaseOptions, caps *Capabilities) (Values, error) {
return toRenderValues(chrt, chrtVals, options, caps, false)
}
func toRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}, options ReleaseOptions, caps *Capabilities, schemaValidation bool) (Values, error) {
if caps == nil { if caps == nil {
caps = DefaultCapabilities caps = DefaultCapabilities
} }
@ -156,10 +167,12 @@ func ToRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}, options
return top, err return top, err
} }
if schemaValidation {
if err := ValidateAgainstSchema(chrt, vals); err != nil { if err := ValidateAgainstSchema(chrt, vals); err != nil {
errFmt := "values don't meet the specifications of the schema(s) in the following chart(s):\n%s" errFmt := "values don't meet the specifications of the schema(s) in the following chart(s):\n%s"
return top, fmt.Errorf(errFmt, err.Error()) return top, fmt.Errorf(errFmt, err.Error())
} }
}
top["Values"] = vals top["Values"] = vals
return top, nil return top, nil

@ -24,14 +24,14 @@ import (
) )
// All runs all of the available linters on the given base directory. // All runs all of the available linters on the given base directory.
func All(basedir string, values map[string]interface{}, namespace string, strict bool) support.Linter { func All(basedir string, values map[string]interface{}, namespace string, strict bool, skipSchemaValidation bool) support.Linter {
// Using abs path to get directory context // Using abs path to get directory context
chartDir, _ := filepath.Abs(basedir) chartDir, _ := filepath.Abs(basedir)
linter := support.Linter{ChartDir: chartDir} linter := support.Linter{ChartDir: chartDir}
rules.Chartfile(&linter) rules.Chartfile(&linter)
rules.ValuesWithOverrides(&linter, values) rules.ValuesWithOverrides(&linter, values)
rules.Templates(&linter, values, namespace, strict) rules.Templates(&linter, values, namespace, strict, skipSchemaValidation)
rules.Dependencies(&linter) rules.Dependencies(&linter)
return linter return linter
} }

@ -27,6 +27,7 @@ import (
var values map[string]interface{} var values map[string]interface{}
const namespace = "testNamespace" const namespace = "testNamespace"
const skipSchemaValidation = false
const strict = false const strict = false
const badChartDir = "rules/testdata/badchartfile" const badChartDir = "rules/testdata/badchartfile"
@ -36,7 +37,7 @@ const goodChartDir = "rules/testdata/goodone"
const subChartValuesDir = "rules/testdata/withsubchart" const subChartValuesDir = "rules/testdata/withsubchart"
func TestBadChart(t *testing.T) { func TestBadChart(t *testing.T) {
m := All(badChartDir, values, namespace, strict).Messages m := All(badChartDir, values, namespace, strict, skipSchemaValidation).Messages
if len(m) != 8 { if len(m) != 8 {
t.Errorf("Number of errors %v", len(m)) t.Errorf("Number of errors %v", len(m))
t.Errorf("All didn't fail with expected errors, got %#v", m) t.Errorf("All didn't fail with expected errors, got %#v", m)
@ -85,7 +86,7 @@ func TestBadChart(t *testing.T) {
} }
func TestInvalidYaml(t *testing.T) { func TestInvalidYaml(t *testing.T) {
m := All(badYamlFileDir, values, namespace, strict).Messages m := All(badYamlFileDir, values, namespace, strict, skipSchemaValidation).Messages
if len(m) != 1 { if len(m) != 1 {
t.Fatalf("All didn't fail with expected errors, got %#v", m) t.Fatalf("All didn't fail with expected errors, got %#v", m)
} }
@ -95,7 +96,7 @@ func TestInvalidYaml(t *testing.T) {
} }
func TestBadValues(t *testing.T) { func TestBadValues(t *testing.T) {
m := All(badValuesFileDir, values, namespace, strict).Messages m := All(badValuesFileDir, values, namespace, strict, skipSchemaValidation).Messages
if len(m) < 1 { if len(m) < 1 {
t.Fatalf("All didn't fail with expected errors, got %#v", m) t.Fatalf("All didn't fail with expected errors, got %#v", m)
} }
@ -105,7 +106,7 @@ func TestBadValues(t *testing.T) {
} }
func TestGoodChart(t *testing.T) { func TestGoodChart(t *testing.T) {
m := All(goodChartDir, values, namespace, strict).Messages m := All(goodChartDir, values, namespace, strict, skipSchemaValidation).Messages
if len(m) != 0 { if len(m) != 0 {
t.Error("All returned linter messages when it shouldn't have") t.Error("All returned linter messages when it shouldn't have")
for i, msg := range m { for i, msg := range m {
@ -129,7 +130,7 @@ func TestHelmCreateChart(t *testing.T) {
// Note: we test with strict=true here, even though others have // Note: we test with strict=true here, even though others have
// strict = false. // strict = false.
m := All(createdChart, values, namespace, true).Messages m := All(createdChart, values, namespace, true, skipSchemaValidation).Messages
if ll := len(m); ll != 1 { if ll := len(m); ll != 1 {
t.Errorf("All should have had exactly 1 error. Got %d", ll) t.Errorf("All should have had exactly 1 error. Got %d", ll)
for i, msg := range m { for i, msg := range m {
@ -143,7 +144,7 @@ func TestHelmCreateChart(t *testing.T) {
// lint ignores import-values // lint ignores import-values
// See https://github.com/helm/helm/issues/9658 // See https://github.com/helm/helm/issues/9658
func TestSubChartValuesChart(t *testing.T) { func TestSubChartValuesChart(t *testing.T) {
m := All(subChartValuesDir, values, namespace, strict).Messages m := All(subChartValuesDir, values, namespace, strict, skipSchemaValidation).Messages
if len(m) != 0 { if len(m) != 0 {
t.Error("All returned linter messages when it shouldn't have") t.Error("All returned linter messages when it shouldn't have")
for i, msg := range m { for i, msg := range m {

@ -45,7 +45,7 @@ var (
) )
// Templates lints the templates in the Linter. // Templates lints the templates in the Linter.
func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool) { func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool, skipSchemaValidation bool) {
fpath := "templates/" fpath := "templates/"
templatesPath := filepath.Join(linter.ChartDir, fpath) templatesPath := filepath.Join(linter.ChartDir, fpath)
@ -80,11 +80,22 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace
if err != nil { if err != nil {
return return
} }
valuesToRender, err := chartutil.ToRenderValues(chart, cvals, options, nil)
var valuesToRender chartutil.Values
if skipSchemaValidation {
valuesToRender, err = chartutil.ToRenderValuesSkipSchemaValidation(chart, cvals, options, nil)
if err != nil { if err != nil {
linter.RunLinterRule(support.ErrorSev, fpath, err) linter.RunLinterRule(support.ErrorSev, fpath, err)
return return
} }
} else {
valuesToRender, err = chartutil.ToRenderValues(chart, cvals, options, nil)
if err != nil {
linter.RunLinterRule(support.ErrorSev, fpath, err)
return
}
}
var e engine.Engine var e engine.Engine
e.LintMode = true e.LintMode = true
renderedContentMap, err := e.Render(chart, valuesToRender) renderedContentMap, err := e.Render(chart, valuesToRender)

@ -51,11 +51,12 @@ func TestValidateAllowedExtension(t *testing.T) {
var values = map[string]interface{}{"nameOverride": "", "httpPort": 80} var values = map[string]interface{}{"nameOverride": "", "httpPort": 80}
const namespace = "testNamespace" const namespace = "testNamespace"
const skipSchemaValidation = false
const strict = false const strict = false
func TestTemplateParsing(t *testing.T) { func TestTemplateParsing(t *testing.T) {
linter := support.Linter{ChartDir: templateTestBasedir} linter := support.Linter{ChartDir: templateTestBasedir}
Templates(&linter, values, namespace, strict) Templates(&linter, values, namespace, strict, skipSchemaValidation)
res := linter.Messages res := linter.Messages
if len(res) != 1 { if len(res) != 1 {
@ -78,7 +79,7 @@ func TestTemplateIntegrationHappyPath(t *testing.T) {
defer os.Rename(ignoredTemplatePath, wrongTemplatePath) defer os.Rename(ignoredTemplatePath, wrongTemplatePath)
linter := support.Linter{ChartDir: templateTestBasedir} linter := support.Linter{ChartDir: templateTestBasedir}
Templates(&linter, values, namespace, strict) Templates(&linter, values, namespace, strict, skipSchemaValidation)
res := linter.Messages res := linter.Messages
if len(res) != 0 { if len(res) != 0 {
@ -88,7 +89,7 @@ func TestTemplateIntegrationHappyPath(t *testing.T) {
func TestV3Fail(t *testing.T) { func TestV3Fail(t *testing.T) {
linter := support.Linter{ChartDir: "./testdata/v3-fail"} linter := support.Linter{ChartDir: "./testdata/v3-fail"}
Templates(&linter, values, namespace, strict) Templates(&linter, values, namespace, strict, skipSchemaValidation)
res := linter.Messages res := linter.Messages
if len(res) != 3 { if len(res) != 3 {
@ -108,7 +109,7 @@ func TestV3Fail(t *testing.T) {
func TestMultiTemplateFail(t *testing.T) { func TestMultiTemplateFail(t *testing.T) {
linter := support.Linter{ChartDir: "./testdata/multi-template-fail"} linter := support.Linter{ChartDir: "./testdata/multi-template-fail"}
Templates(&linter, values, namespace, strict) Templates(&linter, values, namespace, strict, skipSchemaValidation)
res := linter.Messages res := linter.Messages
if len(res) != 1 { if len(res) != 1 {
@ -229,7 +230,7 @@ func TestDeprecatedAPIFails(t *testing.T) {
} }
linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name())} linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name())}
Templates(&linter, values, namespace, strict) Templates(&linter, values, namespace, strict, skipSchemaValidation)
if l := len(linter.Messages); l != 1 { if l := len(linter.Messages); l != 1 {
for i, msg := range linter.Messages { for i, msg := range linter.Messages {
t.Logf("Message %d: %s", i, msg) t.Logf("Message %d: %s", i, msg)
@ -286,7 +287,7 @@ func TestStrictTemplateParsingMapError(t *testing.T) {
linter := &support.Linter{ linter := &support.Linter{
ChartDir: filepath.Join(dir, ch.Metadata.Name), ChartDir: filepath.Join(dir, ch.Metadata.Name),
} }
Templates(linter, ch.Values, namespace, strict) Templates(linter, ch.Values, namespace, strict, skipSchemaValidation)
if len(linter.Messages) != 0 { if len(linter.Messages) != 0 {
t.Errorf("expected zero messages, got %d", len(linter.Messages)) t.Errorf("expected zero messages, got %d", len(linter.Messages))
for i, msg := range linter.Messages { for i, msg := range linter.Messages {
@ -416,7 +417,7 @@ func TestEmptyWithCommentsManifests(t *testing.T) {
} }
linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name())} linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name())}
Templates(&linter, values, namespace, strict) Templates(&linter, values, namespace, strict, skipSchemaValidation)
if l := len(linter.Messages); l > 0 { if l := len(linter.Messages); l > 0 {
for i, msg := range linter.Messages { for i, msg := range linter.Messages {
t.Logf("Message %d: %s", i, msg) t.Logf("Message %d: %s", i, msg)

Loading…
Cancel
Save