Skip to content

Commit

Permalink
Merge pull request #154 from ian-howell/clean/update-yaml
Browse files Browse the repository at this point in the history
Update to sigs.k8s.io/yaml
  • Loading branch information
garethr committed Jul 27, 2019
2 parents 70e32d6 + 3cbcd68 commit 9c9c0a5
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 83 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ require (
github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609
golang.org/x/sys v0.0.0-20180821044426-4ea2f632f6e9 // indirect
golang.org/x/text v0.0.0-20180810153555-6e3c4e7365dd // indirect
gopkg.in/yaml.v2 v2.2.1
gopkg.in/yaml.v2 v2.2.1 // indirect
sigs.k8s.io/yaml v1.1.0
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
67 changes: 7 additions & 60 deletions kubeval/kubeval.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ package kubeval

import (
"bytes"
"errors"
"fmt"
"regexp"
"runtime"
"strings"

"github.com/hashicorp/go-multierror"
"github.com/spf13/viper"
"github.com/xeipuuv/gojsonschema"
"gopkg.in/yaml.v2"
"sigs.k8s.io/yaml"

"github.com/instrumenta/kubeval/log"
)
Expand Down Expand Up @@ -51,16 +49,6 @@ var ExitOnError bool
// schema validation
var KindsToSkip []string

// in is a method which tests whether the `key` is in the set
func in(set []string, key string) bool {
for _, k := range set {
if k == key {
return true
}
}
return false
}

// ValidFormat is a type for quickly forcing
// new formats on the gojsonschema loader
type ValidFormat struct{}
Expand All @@ -81,15 +69,6 @@ type ValidationResult struct {
Errors []gojsonschema.ResultError
}

// detectLineBreak returns the relevant platform specific line ending
func detectLineBreak(haystack []byte) string {
windowsLineEnding := bytes.Contains(haystack, []byte("\r\n"))
if windowsLineEnding && runtime.GOOS == "windows" {
return "\r\n"
}
return "\n"
}

func determineSchema(kind string, apiVersion string) string {
// We have both the upstream Kubernetes schemas and the OpenShift schemas available
// the tool can toggle between then using the --openshift boolean flag and here we
Expand Down Expand Up @@ -148,57 +127,26 @@ func determineSchema(kind string, apiVersion string) string {
return fmt.Sprintf("%s/%s-standalone%s/%s%s.json", baseURL, normalisedVersion, strictSuffix, strings.ToLower(kind), kindSuffix)
}

func determineKind(body interface{}) (string, error) {
cast, _ := body.(map[string]interface{})
if _, ok := cast["kind"]; !ok {
return "", errors.New("Missing a kind key")
}
if cast["kind"] == nil {
return "", errors.New("Missing a kind value")
}
return cast["kind"].(string), nil
}

func determineAPIVersion(body interface{}) (string, error) {
cast, _ := body.(map[string]interface{})
if _, ok := cast["apiVersion"]; !ok {
return "", errors.New("Missing a apiVersion key")
}
if cast["apiVersion"] == nil {
return "", errors.New("Missing a apiVersion value")
}
return cast["apiVersion"].(string), nil
}

// 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) (ValidationResult, error) {
var spec interface{}
result := ValidationResult{}
result.FileName = fileName
err := yaml.Unmarshal(data, &spec)
var body map[string]interface{}
err := yaml.Unmarshal(data, &body)
if err != nil {
return result, errors.New("Failed to decode YAML from " + fileName)
}

body := convertToStringKeys(spec)

if body == nil {
return result, fmt.Errorf("Failed to decode YAML from %s: %s", fileName, err.Error())
} else if body == nil {
return result, nil
}

cast, _ := body.(map[string]interface{})
if len(cast) == 0 {
return result, nil
}

kind, err := determineKind(body)
kind, err := getString(body, "kind")
if err != nil {
return result, err
}
result.Kind = kind

apiVersion, err := determineAPIVersion(body)
apiVersion, err := getString(body, "apiVersion")
if err != nil {
return result, err
}
Expand All @@ -216,7 +164,6 @@ func validateResource(data []byte, fileName string, schemaCache map[string]*gojs
return result, nil
}


func validateAgainstSchema(body interface{}, resource *ValidationResult, schemaCache map[string]*gojsonschema.Schema) ([]gojsonschema.ResultError, error) {
if IgnoreMissingSchemas {
log.Warn("Warning: Set to ignore missing schemas")
Expand Down
53 changes: 49 additions & 4 deletions kubeval/kubeval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,55 @@ func TestDetermineSchemaForSchemaLocation(t *testing.T) {
}
}

func TestDetermineKind(t *testing.T) {
_, err := determineKind("sample")
if err == nil {
t.Errorf("Shouldn't be able to find a kind when passed a blank string")
func TestGetString(t *testing.T) {
var tests = []struct{
body map[string]interface{}
key string
expectedVal string
expectError bool
}{
{
body: map[string]interface{}{"goodKey": "goodVal"},
key: "goodKey",
expectedVal: "goodVal",
expectError: false,
},
{
body: map[string]interface{}{},
key: "missingKey",
expectedVal: "",
expectError: true,
},
{
body: map[string]interface{}{"nilKey": nil},
key: "nilKey",
expectedVal: "",
expectError: true,
},
{
body: map[string]interface{}{"badKey": 5},
key: "badKey",
expectedVal: "",
expectError: true,
},
}

for _, test := range tests {
actualVal, err := getString(test.body, test.key)
if err != nil {
if !test.expectError {
t.Errorf("Unexpected error: %s", err.Error())
}
// We expected this error, so move to the next test
continue
}
if test.expectError {
t.Errorf("Expected an error, but didn't receive one")
continue
}
if actualVal != test.expectedVal {
t.Errorf("Expected %s, got %s", test.expectedVal, actualVal)
}
}
}

Expand Down
53 changes: 35 additions & 18 deletions kubeval/utils.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
package kubeval

import "fmt"
import (
"bytes"
"fmt"
"runtime"
)

// Based on https://stackoverflow.com/questions/40737122/convert-yaml-to-json-without-struct-golang
// We unmarshal yaml into a value of type interface{},
// go through the result recursively, and convert each encountered
// map[interface{}]interface{} to a map[string]interface{} value
// required to marshall to JSON.
func convertToStringKeys(i interface{}) interface{} {
switch x := i.(type) {
case map[interface{}]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[fmt.Sprintf("%v", k)] = convertToStringKeys(v)
}
return m2
case []interface{}:
for i, v := range x {
x[i] = convertToStringKeys(v)
func getString(body map[string]interface{}, key string) (string, error) {
value, found := body[key]
if !found {
return "", fmt.Errorf("Missing '%s' key", key)
}
if value == nil {
return "", fmt.Errorf("Missing '%s' value", key)
}
typedValue, ok := value.(string)
if !ok {
return "", fmt.Errorf("Expected string value for key '%s'", key)
}
return typedValue, nil
}

// detectLineBreak returns the relevant platform specific line ending
func detectLineBreak(haystack []byte) string {
windowsLineEnding := bytes.Contains(haystack, []byte("\r\n"))
if windowsLineEnding && runtime.GOOS == "windows" {
return "\r\n"
}
return "\n"
}

// in is a method which tests whether the `key` is in the set
func in(set []string, key string) bool {
for _, k := range set {
if k == key {
return true
}
}
return i
return false
}

0 comments on commit 9c9c0a5

Please sign in to comment.