...

Source file src/sigs.k8s.io/kustomize/kyaml/yaml/compatibility.go

Documentation: sigs.k8s.io/kustomize/kyaml/yaml

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package yaml
     5  
     6  import (
     7  	"reflect"
     8  	"strings"
     9  
    10  	"k8s.io/kube-openapi/pkg/validation/spec"
    11  	y1_1 "sigs.k8s.io/yaml/goyaml.v2"
    12  	y1_2 "sigs.k8s.io/yaml/goyaml.v3"
    13  )
    14  
    15  // typeToTag maps OpenAPI schema types to yaml 1.2 tags
    16  var typeToTag = map[string]string{
    17  	"string":  NodeTagString,
    18  	"integer": NodeTagInt,
    19  	"boolean": NodeTagBool,
    20  	"number":  NodeTagFloat,
    21  }
    22  
    23  // FormatNonStringStyle makes sure that values which parse as non-string values in yaml 1.1
    24  // are correctly formatted given the Schema type.
    25  func FormatNonStringStyle(node *Node, schema spec.Schema) {
    26  	if len(schema.Type) != 1 {
    27  		return
    28  	}
    29  	t := schema.Type[0]
    30  
    31  	if !IsYaml1_1NonString(node) {
    32  		return
    33  	}
    34  	switch {
    35  	case t == "string" && schema.Format != "int-or-string":
    36  		if (node.Style&DoubleQuotedStyle == 0) && (node.Style&SingleQuotedStyle == 0) {
    37  			// must quote values so they are parsed as strings
    38  			node.Style = DoubleQuotedStyle
    39  		}
    40  	case t == "boolean" || t == "integer" || t == "number":
    41  		if (node.Style&DoubleQuotedStyle != 0) || (node.Style&SingleQuotedStyle != 0) {
    42  			// must NOT quote the values so they aren't parsed as strings
    43  			node.Style = 0
    44  		}
    45  	default:
    46  		return
    47  	}
    48  
    49  	// if the node tag is null, make sure we don't add any non-null tags
    50  	// https://github.com/kptdev/kpt/issues/2321
    51  	if node.Tag == NodeTagNull {
    52  		// must NOT quote null values
    53  		node.Style = 0
    54  		return
    55  	}
    56  	if tag, found := typeToTag[t]; found {
    57  		// make sure the right tag is set
    58  		node.Tag = tag
    59  	}
    60  }
    61  
    62  // IsYaml1_1NonString returns true if the value parses as a non-string value in yaml 1.1
    63  // when unquoted.
    64  //
    65  // Note: yaml 1.2 uses different keywords than yaml 1.1.  Example: yaml 1.2 interprets
    66  // `field: on` and `field: "on"` as equivalent (both strings).  However Yaml 1.1 interprets
    67  // `field: on` as on being a bool and `field: "on"` as on being a string.
    68  // If an input is read with `field: "on"`, and the style is changed from DoubleQuote to 0,
    69  // it will change the type of the field from a string  to a bool.  For this reason, fields
    70  // which are keywords in yaml 1.1 should never have their style changed, as it would break
    71  // backwards compatibility with yaml 1.1 -- which is what is used by the Kubernetes apiserver.
    72  func IsYaml1_1NonString(node *Node) bool {
    73  	if node.Kind != y1_2.ScalarNode {
    74  		// not a keyword
    75  		return false
    76  	}
    77  	return IsValueNonString(node.Value)
    78  }
    79  
    80  func IsValueNonString(value string) bool {
    81  	if value == "" {
    82  		return false
    83  	}
    84  	if strings.Contains(value, "\n") {
    85  		// multi-line strings will fail to unmarshal
    86  		return false
    87  	}
    88  	// check if the value will unmarshal into a non-string value using a yaml 1.1 parser
    89  	var i1 interface{}
    90  	if err := y1_1.Unmarshal([]byte(value), &i1); err != nil {
    91  		return false
    92  	}
    93  	if reflect.TypeOf(i1) != stringType {
    94  		return true
    95  	}
    96  
    97  	return false
    98  }
    99  
   100  var stringType = reflect.TypeOf("string")
   101  

View as plain text