github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/schema/util/util.go (about) 1 /* 2 Copyright 2019 The Skaffold Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "reflect" 23 "strings" 24 25 yamlpatch "github.com/krishicks/yaml-patch" 26 27 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/yaml" 28 ) 29 30 type VersionedConfig interface { 31 GetVersion() string 32 Upgrade() (VersionedConfig, error) 33 } 34 35 // HelmOverrides is a helper struct to aid with json serialization of map[string]interface{} 36 type HelmOverrides struct { 37 Values map[string]interface{} `yaml:",inline"` 38 } 39 40 // FlatMap flattens deeply nested yaml into a map with corresponding dot separated keys 41 type FlatMap map[string]string 42 43 // MarshalJSON implements JSON marshalling by including the value as an inline yaml fragment. 44 func (h *HelmOverrides) MarshalJSON() ([]byte, error) { 45 return marshalInlineYaml(h) 46 } 47 48 // UnmarshalYAML implements JSON unmarshalling by reading an inline yaml fragment. 49 func (h *HelmOverrides) UnmarshalJSON(text []byte) error { 50 yml, err := unmarshalInlineYaml(text) 51 if err != nil { 52 return err 53 } 54 return yaml.Unmarshal([]byte(yml), h) 55 } 56 57 // YamlpatchNode wraps a `yamlpatch.Node` and makes it serializable to JSON. 58 // The yaml serialization needs to be implemented manually, because the node may be 59 // an arbitrary yaml fragment so that a field tag `yaml:",inline"` does not work here. 60 type YamlpatchNode struct { 61 // node is an arbitrary yaml fragment 62 Node yamlpatch.Node 63 } 64 65 // MarshalJSON implements JSON marshalling by including the value as an inline yaml fragment. 66 func (n *YamlpatchNode) MarshalJSON() ([]byte, error) { 67 return marshalInlineYaml(n) 68 } 69 70 // UnmarshalYAML implements JSON unmarshalling by reading an inline yaml fragment. 71 func (n *YamlpatchNode) UnmarshalJSON(text []byte) error { 72 yml, err := unmarshalInlineYaml(text) 73 if err != nil { 74 return err 75 } 76 return yaml.Unmarshal([]byte(yml), n) 77 } 78 79 // MarshalYAML implements yaml.Marshaler. 80 func (n *YamlpatchNode) MarshalYAML() (interface{}, error) { 81 return n.Node.MarshalYAML() 82 } 83 84 // UnmarshalYAML implements yaml.Unmarshaler 85 func (n *YamlpatchNode) UnmarshalYAML(unmarshal func(interface{}) error) error { 86 return n.Node.UnmarshalYAML(unmarshal) 87 } 88 89 func (m *FlatMap) UnmarshalYAML(unmarshal func(interface{}) error) error { 90 var obj map[string]interface{} 91 if err := unmarshal(&obj); err != nil { 92 return err 93 } 94 result := make(map[string]string) 95 if err := buildFlatMap(obj, result, ""); err != nil { 96 return err 97 } 98 *m = result 99 return nil 100 } 101 102 func buildFlatMap(obj map[string]interface{}, result map[string]string, currK string) (err error) { 103 var prevK string 104 for k, v := range obj { 105 prevK = currK 106 if currK == "" { 107 currK = fmt.Sprintf("%v", k) 108 } else { 109 currK = fmt.Sprintf("%v.%v", currK, k) 110 } 111 112 switch v := v.(type) { 113 case map[string]interface{}: 114 if err = buildFlatMap(v, result, currK); err != nil { 115 return 116 } 117 case []interface{}: 118 for idx, i := range v { 119 if m, ok := i.(map[string]interface{}); ok { 120 currIdx := fmt.Sprintf("%v[%d]", currK, idx) 121 if err = buildFlatMap(m, result, currIdx); err != nil { 122 return 123 } 124 } 125 } 126 case string: 127 result[currK] = v 128 default: 129 result[currK] = fmt.Sprintf("%v", v) 130 } 131 currK = prevK 132 } 133 return err 134 } 135 136 func marshalInlineYaml(in interface{}) ([]byte, error) { 137 yaml, err := yaml.Marshal(in) 138 if err != nil { 139 return nil, err 140 } 141 return json.Marshal(string(yaml)) 142 } 143 144 func unmarshalInlineYaml(text []byte) (string, error) { 145 var in string 146 err := json.Unmarshal(text, &in) 147 return in, err 148 } 149 150 // IsOneOfField checks if a field is tagged with oneOf 151 func IsOneOfField(field reflect.StructField) bool { 152 for _, tag := range strings.Split(field.Tag.Get("yamltags"), ",") { 153 tagParts := strings.Split(tag, "=") 154 155 if tagParts[0] == "oneOf" { 156 return true 157 } 158 } 159 return false 160 }