diff --git a/internal/chart/v3/lint/lint.go b/internal/chart/v3/lint/lint.go index 231bb6803..0cd949065 100644 --- a/internal/chart/v3/lint/lint.go +++ b/internal/chart/v3/lint/lint.go @@ -57,7 +57,7 @@ func RunAll(baseDir string, values map[string]interface{}, namespace string, opt } rules.Chartfile(&result) - rules.ValuesWithOverrides(&result, values) + rules.ValuesWithOverrides(&result, values, lo.SkipSchemaValidation) rules.TemplatesWithSkipSchemaValidation(&result, values, namespace, lo.KubeVersion, lo.SkipSchemaValidation) rules.Dependencies(&result) rules.Crds(&result) diff --git a/internal/chart/v3/lint/rules/values.go b/internal/chart/v3/lint/rules/values.go index adf2e2c52..0af9765dd 100644 --- a/internal/chart/v3/lint/rules/values.go +++ b/internal/chart/v3/lint/rules/values.go @@ -32,7 +32,7 @@ import ( // they are only tested for well-formedness. // // If additional values are supplied, they are coalesced into the values in values.yaml. -func ValuesWithOverrides(linter *support.Linter, valueOverrides map[string]interface{}) { +func ValuesWithOverrides(linter *support.Linter, valueOverrides map[string]interface{}, skipSchemaValidation bool) { file := "values.yaml" vf := filepath.Join(linter.ChartDir, file) fileExists := linter.RunLinterRule(support.InfoSev, file, validateValuesFileExistence(vf)) @@ -41,7 +41,7 @@ func ValuesWithOverrides(linter *support.Linter, valueOverrides map[string]inter return } - linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf, valueOverrides)) + linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf, valueOverrides, skipSchemaValidation)) } func validateValuesFileExistence(valuesPath string) error { @@ -52,7 +52,7 @@ func validateValuesFileExistence(valuesPath string) error { return nil } -func validateValuesFile(valuesPath string, overrides map[string]interface{}) error { +func validateValuesFile(valuesPath string, overrides map[string]interface{}, skipSchemaValidation bool) error { values, err := common.ReadValuesFile(valuesPath) if err != nil { return fmt.Errorf("unable to parse YAML: %w", err) @@ -75,5 +75,10 @@ func validateValuesFile(valuesPath string, overrides map[string]interface{}) err if err != nil { return err } - return util.ValidateAgainstSingleSchema(coalescedValues, schema) + + if !skipSchemaValidation { + return util.ValidateAgainstSingleSchema(coalescedValues, schema) + } + + return nil } diff --git a/internal/chart/v3/lint/rules/values_test.go b/internal/chart/v3/lint/rules/values_test.go index 348695785..288b77436 100644 --- a/internal/chart/v3/lint/rules/values_test.go +++ b/internal/chart/v3/lint/rules/values_test.go @@ -67,7 +67,7 @@ func TestValidateValuesFileWellFormed(t *testing.T) { ` tmpdir := ensure.TempFile(t, "values.yaml", []byte(badYaml)) valfile := filepath.Join(tmpdir, "values.yaml") - if err := validateValuesFile(valfile, map[string]interface{}{}); err == nil { + if err := validateValuesFile(valfile, map[string]interface{}{}, false); err == nil { t.Fatal("expected values file to fail parsing") } } @@ -78,7 +78,7 @@ func TestValidateValuesFileSchema(t *testing.T) { createTestingSchema(t, tmpdir) valfile := filepath.Join(tmpdir, "values.yaml") - if err := validateValuesFile(valfile, map[string]interface{}{}); err != nil { + if err := validateValuesFile(valfile, map[string]interface{}{}, false); err != nil { t.Fatalf("Failed validation with %s", err) } } @@ -91,7 +91,7 @@ func TestValidateValuesFileSchemaFailure(t *testing.T) { valfile := filepath.Join(tmpdir, "values.yaml") - err := validateValuesFile(valfile, map[string]interface{}{}) + err := validateValuesFile(valfile, map[string]interface{}{}, false) if err == nil { t.Fatal("expected values file to fail parsing") } @@ -99,6 +99,20 @@ func TestValidateValuesFileSchemaFailure(t *testing.T) { assert.Contains(t, err.Error(), "- at '/username': got number, want string") } +func TestValidateValuesFileSchemaFailureButWithSkipSchemaValidation(t *testing.T) { + // 1234 is an int, not a string. This should fail normally but pass with skipSchemaValidation. + yaml := "username: 1234\npassword: swordfish" + tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml)) + createTestingSchema(t, tmpdir) + + valfile := filepath.Join(tmpdir, "values.yaml") + + err := validateValuesFile(valfile, map[string]interface{}{}, true) + if err != nil { + t.Fatal("expected values file to pass parsing because of skipSchemaValidation") + } +} + func TestValidateValuesFileSchemaOverrides(t *testing.T) { yaml := "username: admin" overrides := map[string]interface{}{ @@ -108,7 +122,7 @@ func TestValidateValuesFileSchemaOverrides(t *testing.T) { createTestingSchema(t, tmpdir) valfile := filepath.Join(tmpdir, "values.yaml") - if err := validateValuesFile(valfile, overrides); err != nil { + if err := validateValuesFile(valfile, overrides, false); err != nil { t.Fatalf("Failed validation with %s", err) } } @@ -145,7 +159,7 @@ func TestValidateValuesFile(t *testing.T) { valfile := filepath.Join(tmpdir, "values.yaml") - err := validateValuesFile(valfile, tt.overrides) + err := validateValuesFile(valfile, tt.overrides, false) switch { case err != nil && tt.errorMessage == "": diff --git a/pkg/chart/v2/lint/lint.go b/pkg/chart/v2/lint/lint.go index 773c9bc5e..b26d65a34 100644 --- a/pkg/chart/v2/lint/lint.go +++ b/pkg/chart/v2/lint/lint.go @@ -57,7 +57,7 @@ func RunAll(baseDir string, values map[string]interface{}, namespace string, opt } rules.Chartfile(&result) - rules.ValuesWithOverrides(&result, values) + rules.ValuesWithOverrides(&result, values, lo.SkipSchemaValidation) rules.TemplatesWithSkipSchemaValidation(&result, values, namespace, lo.KubeVersion, lo.SkipSchemaValidation) rules.Dependencies(&result) rules.Crds(&result) diff --git a/pkg/chart/v2/lint/rules/values.go b/pkg/chart/v2/lint/rules/values.go index 5260bf8b3..994a6a463 100644 --- a/pkg/chart/v2/lint/rules/values.go +++ b/pkg/chart/v2/lint/rules/values.go @@ -32,7 +32,7 @@ import ( // they are only tested for well-formedness. // // If additional values are supplied, they are coalesced into the values in values.yaml. -func ValuesWithOverrides(linter *support.Linter, valueOverrides map[string]interface{}) { +func ValuesWithOverrides(linter *support.Linter, valueOverrides map[string]interface{}, skipSchemaValidation bool) { file := "values.yaml" vf := filepath.Join(linter.ChartDir, file) fileExists := linter.RunLinterRule(support.InfoSev, file, validateValuesFileExistence(vf)) @@ -41,7 +41,7 @@ func ValuesWithOverrides(linter *support.Linter, valueOverrides map[string]inter return } - linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf, valueOverrides)) + linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf, valueOverrides, skipSchemaValidation)) } func validateValuesFileExistence(valuesPath string) error { @@ -52,7 +52,7 @@ func validateValuesFileExistence(valuesPath string) error { return nil } -func validateValuesFile(valuesPath string, overrides map[string]interface{}) error { +func validateValuesFile(valuesPath string, overrides map[string]interface{}, skipSchemaValidation bool) error { values, err := common.ReadValuesFile(valuesPath) if err != nil { return fmt.Errorf("unable to parse YAML: %w", err) @@ -75,5 +75,10 @@ func validateValuesFile(valuesPath string, overrides map[string]interface{}) err if err != nil { return err } - return util.ValidateAgainstSingleSchema(coalescedValues, schema) + + if !skipSchemaValidation { + return util.ValidateAgainstSingleSchema(coalescedValues, schema) + } + + return nil } diff --git a/pkg/chart/v2/lint/rules/values_test.go b/pkg/chart/v2/lint/rules/values_test.go index 348695785..288b77436 100644 --- a/pkg/chart/v2/lint/rules/values_test.go +++ b/pkg/chart/v2/lint/rules/values_test.go @@ -67,7 +67,7 @@ func TestValidateValuesFileWellFormed(t *testing.T) { ` tmpdir := ensure.TempFile(t, "values.yaml", []byte(badYaml)) valfile := filepath.Join(tmpdir, "values.yaml") - if err := validateValuesFile(valfile, map[string]interface{}{}); err == nil { + if err := validateValuesFile(valfile, map[string]interface{}{}, false); err == nil { t.Fatal("expected values file to fail parsing") } } @@ -78,7 +78,7 @@ func TestValidateValuesFileSchema(t *testing.T) { createTestingSchema(t, tmpdir) valfile := filepath.Join(tmpdir, "values.yaml") - if err := validateValuesFile(valfile, map[string]interface{}{}); err != nil { + if err := validateValuesFile(valfile, map[string]interface{}{}, false); err != nil { t.Fatalf("Failed validation with %s", err) } } @@ -91,7 +91,7 @@ func TestValidateValuesFileSchemaFailure(t *testing.T) { valfile := filepath.Join(tmpdir, "values.yaml") - err := validateValuesFile(valfile, map[string]interface{}{}) + err := validateValuesFile(valfile, map[string]interface{}{}, false) if err == nil { t.Fatal("expected values file to fail parsing") } @@ -99,6 +99,20 @@ func TestValidateValuesFileSchemaFailure(t *testing.T) { assert.Contains(t, err.Error(), "- at '/username': got number, want string") } +func TestValidateValuesFileSchemaFailureButWithSkipSchemaValidation(t *testing.T) { + // 1234 is an int, not a string. This should fail normally but pass with skipSchemaValidation. + yaml := "username: 1234\npassword: swordfish" + tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml)) + createTestingSchema(t, tmpdir) + + valfile := filepath.Join(tmpdir, "values.yaml") + + err := validateValuesFile(valfile, map[string]interface{}{}, true) + if err != nil { + t.Fatal("expected values file to pass parsing because of skipSchemaValidation") + } +} + func TestValidateValuesFileSchemaOverrides(t *testing.T) { yaml := "username: admin" overrides := map[string]interface{}{ @@ -108,7 +122,7 @@ func TestValidateValuesFileSchemaOverrides(t *testing.T) { createTestingSchema(t, tmpdir) valfile := filepath.Join(tmpdir, "values.yaml") - if err := validateValuesFile(valfile, overrides); err != nil { + if err := validateValuesFile(valfile, overrides, false); err != nil { t.Fatalf("Failed validation with %s", err) } } @@ -145,7 +159,7 @@ func TestValidateValuesFile(t *testing.T) { valfile := filepath.Join(tmpdir, "values.yaml") - err := validateValuesFile(valfile, tt.overrides) + err := validateValuesFile(valfile, tt.overrides, false) switch { case err != nil && tt.errorMessage == "":