// Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 package yaml_test import ( "testing" "github.com/stretchr/testify/assert" "sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/yaml" ) func TestIsYaml1_1NonString(t *testing.T) { type testCase struct { val string expected bool } testCases := []testCase{ {val: "hello world", expected: false}, {val: "2.0", expected: true}, {val: "2", expected: true}, {val: "true", expected: true}, {val: "1.0\nhello", expected: false}, // multiline strings should always be false {val: "", expected: false}, // empty string should be considered a string } for k := range valueToTagMap { testCases = append(testCases, testCase{val: k, expected: true}) } for _, test := range testCases { assert.Equal(t, test.expected, yaml.IsYaml1_1NonString(&yaml.Node{Kind: yaml.ScalarNode, Value: test.val}), test.val) } } func TestFormatNonStringStyle(t *testing.T) { n := yaml.MustParse(`apiVersion: apps/v1 kind: Deployment spec: template: spec: containers: - name: foo args: - bar - on image: nginx:1.7.9 ports: - name: http containerPort: "80" `) s := openapi.SchemaForResourceType( yaml.TypeMeta{APIVersion: "apps/v1", Kind: "Deployment"}) args, err := n.Pipe(yaml.Lookup( "spec", "template", "spec", "containers", "[name=foo]", "args")) if !assert.NoError(t, err) { t.FailNow() } if !assert.NotNil(t, args) { t.FailNow() } on := args.YNode().Content[1] onS := s.Lookup( "spec", "template", "spec", "containers", openapi.Elements, "args", openapi.Elements) yaml.FormatNonStringStyle(on, *onS.Schema) containerPort, err := n.Pipe(yaml.Lookup( "spec", "template", "spec", "containers", "[name=foo]", "ports", "[name=http]", "containerPort")) if !assert.NoError(t, err) { t.FailNow() } if !assert.NotNil(t, containerPort) { t.FailNow() } cpS := s.Lookup("spec", "template", "spec", "containers", openapi.Elements, "ports", openapi.Elements, "containerPort") if !assert.NotNil(t, cpS) { t.FailNow() } yaml.FormatNonStringStyle(containerPort.YNode(), *cpS.Schema) actual := n.MustString() expected := `apiVersion: apps/v1 kind: Deployment spec: template: spec: containers: - name: foo args: - bar - "on" image: nginx:1.7.9 ports: - name: http containerPort: 80 ` assert.Equal(t, expected, actual) } // valueToTagMap is a map of values interpreted as non-strings in yaml 1.1 when left // unquoted. // To keep compatibility with the yaml parser used by Kubernetes (yaml 1.1) make sure the values // which are treated as non-string values are kept as non-string values. // https://github.com/go-yaml/yaml/blob/v2/resolve.go var valueToTagMap = func() map[string]string { val := map[string]string{} // https://yaml.org/type/null.html values := []string{"~", "null", "Null", "NULL"} for i := range values { val[values[i]] = yaml.NodeTagNull } // https://yaml.org/type/bool.html values = []string{ "y", "Y", "yes", "Yes", "YES", "true", "True", "TRUE", "on", "On", "ON", "n", "N", "no", "No", "NO", "false", "False", "FALSE", "off", "Off", "OFF"} for i := range values { val[values[i]] = yaml.NodeTagBool } // https://yaml.org/type/float.html values = []string{ ".nan", ".NaN", ".NAN", ".inf", ".Inf", ".INF", "+.inf", "+.Inf", "+.INF", "-.inf", "-.Inf", "-.INF"} for i := range values { val[values[i]] = yaml.NodeTagFloat } return val }()