Skip to content

Commit

Permalink
Use config.FileName
Browse files Browse the repository at this point in the history
  • Loading branch information
ian-howell committed Jul 29, 2019
1 parent fe7303a commit fdd1738
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 32 deletions.
2 changes: 1 addition & 1 deletion kubeval/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func AddKubevalFlags(cmd *cobra.Command, config *Config) *cobra.Command {
cmd.Flags().BoolVar(&config.IgnoreMissingSchemas, "ignore-missing-schemas", false, "Skip validation for resource definitions without a schema")
cmd.Flags().BoolVar(&config.OpenShift, "openshift", false, "Use OpenShift schemas instead of upstream Kubernetes")
cmd.Flags().BoolVar(&config.Strict, "strict", false, "Disallow additional properties not in schema")
cmd.Flags().StringP("filename", "f", "stdin", "filename to be displayed when testing manifests read from stdin")
cmd.Flags().StringVarP(&config.FileName, "filename", "f", "stdin", "filename to be displayed when testing manifests read from stdin")
cmd.Flags().StringSliceVar(&config.KindsToSkip, "skip-kinds", []string{}, "Comma-separated list of case-sensitive kinds to skip when validating against schemas")
cmd.Flags().StringVar(&config.SchemaLocation, "schema-location", "", "Base URL used to download schemas. Can also be specified with the environment variable KUBEVAL_SCHEMA_LOCATION")
cmd.Flags().StringVarP(&config.KubernetesVersion, "kubernetes-version", "v", "master", "Version of Kubernetes to validate against")
Expand Down
34 changes: 19 additions & 15 deletions kubeval/kubeval.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ func determineBaseURL(config *Config) string {

// validateResource validates a single Kubernetes resource against
// the relevant schema, detecting the type of resource automatically
func validateResource(data []byte, fileName string, schemaCache map[string]*gojsonschema.Schema, config *Config) (ValidationResult, error) {
func validateResource(data []byte, schemaCache map[string]*gojsonschema.Schema, config *Config) (ValidationResult, error) {
result := ValidationResult{}
result.FileName = fileName
result.FileName = config.FileName
var body map[string]interface{}
err := yaml.Unmarshal(data, &body)
if err != nil {
return result, fmt.Errorf("Failed to decode YAML from %s: %s", fileName, err.Error())
return result, fmt.Errorf("Failed to decode YAML from %s: %s", result.FileName, err.Error())
} else if body == nil {
return result, nil
}
Expand Down Expand Up @@ -184,16 +184,16 @@ func NewSchemaCache() map[string]*gojsonschema.Schema {

// Validate a Kubernetes YAML file, parsing out individual resources
// and validating them all according to the relevant schemas
func Validate(input []byte, fileName string, conf ...*Config) ([]ValidationResult, error) {
func Validate(input []byte, conf ...*Config) ([]ValidationResult, error) {
schemaCache := NewSchemaCache()
return ValidateWithCache(input, fileName, schemaCache, conf...)
return ValidateWithCache(input, schemaCache, conf...)
}

// ValidateWithCache validates a Kubernetes YAML file, parsing out individual resources
// and validating them all according to the relevant schemas
// Allows passing a kubeval.NewSchemaCache() to cache schemas in-memory
// between validations
func ValidateWithCache(input []byte, fileName string, schemaCache map[string]*gojsonschema.Schema, conf ...*Config) ([]ValidationResult, error) {
func ValidateWithCache(input []byte, schemaCache map[string]*gojsonschema.Schema, conf ...*Config) ([]ValidationResult, error) {
config := NewDefaultConfig()
if len(conf) == 1 {
config = conf[0]
Expand All @@ -203,29 +203,33 @@ func ValidateWithCache(input []byte, fileName string, schemaCache map[string]*go

if len(input) == 0 {
result := ValidationResult{}
result.FileName = fileName
result.FileName = config.FileName
results = append(results, result)
return results, nil
}

bits := bytes.Split(input, []byte(detectLineBreak(input)+"---"+detectLineBreak(input)))

var errors *multierror.Error

// special case regexp for helm
helmSourcePattern := regexp.MustCompile(`^(?:---` + detectLineBreak(input) + `)?# Source: (.*)`)

var errors *multierror.Error

// Start with the fileName we were provided; if we detect a new fileName
// we'll use that until we find a new one.
detectedFileName := fileName
// Save the fileName we were provided; if we detect a new fileName
// we'll use that, but we'll need to revert to the default afterward
originalFileName := config.FileName
defer func() {
// revert the filename back to the original
config.FileName = originalFileName
}()

for _, element := range bits {
if len(element) > 0 {
if found := helmSourcePattern.FindStringSubmatch(string(element)); found != nil {
detectedFileName = found[1]
config.FileName = found[1]
}

result, err := validateResource(element, detectedFileName, schemaCache, config)
result, err := validateResource(element, schemaCache, config)
if err != nil {
errors = multierror.Append(errors, err)
if config.ExitOnError {
Expand All @@ -235,7 +239,7 @@ func ValidateWithCache(input []byte, fileName string, schemaCache map[string]*go
results = append(results, result)
} else {
result := ValidationResult{}
result.FileName = detectedFileName
result.FileName = config.FileName
results = append(results, result)
}
}
Expand Down
44 changes: 30 additions & 14 deletions kubeval/kubeval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import (

func TestValidateBlankInput(t *testing.T) {
blank := []byte("")
_, err := Validate(blank, "sample")
config := NewDefaultConfig()
config.FileName = "blank"
_, err := Validate(blank, config)
if err != nil {
t.Errorf("Validate should pass when passed a blank string")
}
Expand All @@ -36,7 +38,9 @@ func TestValidateValidInputs(t *testing.T) {
for _, test := range tests {
filePath, _ := filepath.Abs("../fixtures/" + test)
fileContents, _ := ioutil.ReadFile(filePath)
_, err := Validate(fileContents, test)
config := NewDefaultConfig()
config.FileName = test
_, err := Validate(fileContents, config)
if err != nil {
t.Errorf("Validate should pass when testing valid configuration in " + test)
}
Expand All @@ -62,7 +66,9 @@ func TestValidateValidInputsWithCache(t *testing.T) {
for _, test := range tests {
filePath, _ := filepath.Abs("../fixtures/" + test)
fileContents, _ := ioutil.ReadFile(filePath)
_, err := ValidateWithCache(fileContents, test, schemaCache)
config := NewDefaultConfig()
config.FileName = test
_, err := ValidateWithCache(fileContents, schemaCache, config)
if err != nil {
t.Errorf("Validate should pass when testing valid configuration in " + test)
}
Expand All @@ -77,7 +83,9 @@ func TestValidateInvalidInputs(t *testing.T) {
for _, test := range tests {
filePath, _ := filepath.Abs("../fixtures/" + test)
fileContents, _ := ioutil.ReadFile(filePath)
_, err := Validate(fileContents, test)
config := NewDefaultConfig()
config.FileName = test
_, err := Validate(fileContents, config)
if err == nil {
t.Errorf("Validate should not pass when testing invalid configuration in " + test)
}
Expand All @@ -97,7 +105,9 @@ func TestValidateSourceExtraction(t *testing.T) {
}
filePath, _ := filepath.Abs("../fixtures/multi_valid_source.yaml")
fileContents, _ := ioutil.ReadFile(filePath)
results, err := Validate(fileContents, "multi_valid_source.yaml")
config := NewDefaultConfig()
config.FileName = "multi_valid_source.yaml"
results, err := Validate(fileContents, config)
if err != nil {
t.Fatalf("Unexpected error while validating source: %v", err)
}
Expand All @@ -111,9 +121,10 @@ func TestValidateSourceExtraction(t *testing.T) {
func TestStrictCatchesAdditionalErrors(t *testing.T) {
config := NewDefaultConfig()
config.Strict = true
config.FileName = "extra_property.yaml"
filePath, _ := filepath.Abs("../fixtures/extra_property.yaml")
fileContents, _ := ioutil.ReadFile(filePath)
results, _ := Validate(fileContents, "extra_property.yaml", config)
results, _ := Validate(fileContents, config)
if len(results[0].Errors) == 0 {
t.Errorf("Validate should not pass when testing for additional properties not in schema")
}
Expand All @@ -122,10 +133,11 @@ func TestStrictCatchesAdditionalErrors(t *testing.T) {
func TestValidateMultipleVersions(t *testing.T) {
config := NewDefaultConfig()
config.Strict = true
config.FileName = "valid_version.yaml"
config.KubernetesVersion = "1.14.0"
filePath, _ := filepath.Abs("../fixtures/valid_version.yaml")
fileContents, _ := ioutil.ReadFile(filePath)
results, err := Validate(fileContents, "valid_version.yaml", config)
results, err := Validate(fileContents, config)
if err != nil || len(results[0].Errors) > 0 {
t.Errorf("Validate should pass when testing valid configuration with multiple versions: %v", err)
}
Expand All @@ -139,7 +151,9 @@ func TestValidateInputsWithErrors(t *testing.T) {
for _, test := range tests {
filePath, _ := filepath.Abs("../fixtures/" + test)
fileContents, _ := ioutil.ReadFile(filePath)
results, _ := Validate(fileContents, test)
config := NewDefaultConfig()
config.FileName = test
results, _ := Validate(fileContents, config)
if len(results[0].Errors) == 0 {
t.Errorf("Validate should not pass when testing invalid configuration in " + test)
}
Expand All @@ -155,7 +169,8 @@ func TestValidateMultipleResourcesWithErrors(t *testing.T) {
filePath, _ := filepath.Abs("../fixtures/" + test)
fileContents, _ := ioutil.ReadFile(filePath)
config.ExitOnError = true
_, err := Validate(fileContents, test, config)
config.FileName = test
_, err := Validate(fileContents, config)
if err == nil {
t.Errorf("Validate should not pass when testing invalid configuration in " + test)
} else if merr, ok := err.(*multierror.Error); ok {
Expand All @@ -164,7 +179,7 @@ func TestValidateMultipleResourcesWithErrors(t *testing.T) {
}
}
config.ExitOnError = false
_, err = Validate(fileContents, test, config)
_, err = Validate(fileContents, config)
if err == nil {
t.Errorf("Validate should not pass when testing invalid configuration in " + test)
} else if merr, ok := err.(*multierror.Error); ok {
Expand Down Expand Up @@ -284,23 +299,24 @@ func TestGetString(t *testing.T) {
}

func TestSkipCrdSchemaMiss(t *testing.T) {
config := NewDefaultConfig()
config.FileName = "test_crd.yaml"
filePath, _ := filepath.Abs("../fixtures/test_crd.yaml")
fileContents, _ := ioutil.ReadFile(filePath)
_, err := Validate(fileContents, "test_crd.yaml")
_, err := Validate(fileContents)
if err == nil {
t.Errorf("For custom CRD's with schema missing we should error without IgnoreMissingSchemas flag")
}

config := NewDefaultConfig()
config.IgnoreMissingSchemas = true
results, _ := Validate(fileContents, "test_crd.yaml", config)
results, _ := Validate(fileContents, config)
if len(results[0].Errors) != 0 {
t.Errorf("For custom CRD's with schema missing we should skip with IgnoreMissingSchemas flag")
}

config.IgnoreMissingSchemas = false
config.KindsToSkip = []string{"SealedSecret"}
results, _ = Validate(fileContents, "test_crd.yaml", config)
results, _ = Validate(fileContents, config)
if len(results[0].Errors) != 0 {
t.Errorf("We should skip resources listed in KindsToSkip")
}
Expand Down
6 changes: 4 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ var RootCmd = &cobra.Command{
buffer.WriteString(scanner.Text() + "\n")
}
schemaCache := kubeval.NewSchemaCache()
results, err := kubeval.ValidateWithCache(buffer.Bytes(), viper.GetString("filename"), schemaCache, config)
config.FileName = viper.GetString("filename")
results, err := kubeval.ValidateWithCache(buffer.Bytes(), schemaCache, config)
if err != nil {
log.Error(err)
os.Exit(1)
Expand All @@ -92,7 +93,8 @@ var RootCmd = &cobra.Command{
success = false
continue
}
results, err := kubeval.ValidateWithCache(fileContents, fileName, schemaCache, config)
config.FileName = fileName
results, err := kubeval.ValidateWithCache(fileContents, schemaCache, config)
if err != nil {
log.Error(err)
earlyExit()
Expand Down

0 comments on commit fdd1738

Please sign in to comment.