github.com/x-helm/helm@v3.0.0-beta.3+incompatible/pkg/chartutil/values.go (about) 1 /* 2 Copyright The Helm 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 chartutil 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "strings" 25 26 "github.com/pkg/errors" 27 "sigs.k8s.io/yaml" 28 29 "helm.sh/helm/pkg/chart" 30 ) 31 32 // GlobalKey is the name of the Values key that is used for storing global vars. 33 const GlobalKey = "global" 34 35 // Values represents a collection of chart values. 36 type Values map[string]interface{} 37 38 // YAML encodes the Values into a YAML string. 39 func (v Values) YAML() (string, error) { 40 b, err := yaml.Marshal(v) 41 return string(b), err 42 } 43 44 // Table gets a table (YAML subsection) from a Values object. 45 // 46 // The table is returned as a Values. 47 // 48 // Compound table names may be specified with dots: 49 // 50 // foo.bar 51 // 52 // The above will be evaluated as "The table bar inside the table 53 // foo". 54 // 55 // An ErrNoTable is returned if the table does not exist. 56 func (v Values) Table(name string) (Values, error) { 57 table := v 58 var err error 59 60 for _, n := range parsePath(name) { 61 if table, err = tableLookup(table, n); err != nil { 62 break 63 } 64 } 65 return table, err 66 } 67 68 // AsMap is a utility function for converting Values to a map[string]interface{}. 69 // 70 // It protects against nil map panics. 71 func (v Values) AsMap() map[string]interface{} { 72 if v == nil || len(v) == 0 { 73 return map[string]interface{}{} 74 } 75 return v 76 } 77 78 // Encode writes serialized Values information to the given io.Writer. 79 func (v Values) Encode(w io.Writer) error { 80 out, err := yaml.Marshal(v) 81 if err != nil { 82 return err 83 } 84 _, err = w.Write(out) 85 return err 86 } 87 88 func tableLookup(v Values, simple string) (Values, error) { 89 v2, ok := v[simple] 90 if !ok { 91 return v, ErrNoTable(simple) 92 } 93 if vv, ok := v2.(map[string]interface{}); ok { 94 return vv, nil 95 } 96 97 // This catches a case where a value is of type Values, but doesn't (for some 98 // reason) match the map[string]interface{}. This has been observed in the 99 // wild, and might be a result of a nil map of type Values. 100 if vv, ok := v2.(Values); ok { 101 return vv, nil 102 } 103 104 return Values{}, ErrNoTable(simple) 105 } 106 107 // ReadValues will parse YAML byte data into a Values. 108 func ReadValues(data []byte) (vals Values, err error) { 109 err = yaml.Unmarshal(data, &vals, func(d *json.Decoder) *json.Decoder { 110 d.UseNumber() 111 return d 112 }) 113 if len(vals) == 0 { 114 vals = Values{} 115 } 116 return vals, err 117 } 118 119 // ReadValuesFile will parse a YAML file into a map of values. 120 func ReadValuesFile(filename string) (Values, error) { 121 data, err := ioutil.ReadFile(filename) 122 if err != nil { 123 return map[string]interface{}{}, err 124 } 125 return ReadValues(data) 126 } 127 128 // ReleaseOptions represents the additional release options needed 129 // for the composition of the final values struct 130 type ReleaseOptions struct { 131 Name string 132 Namespace string 133 IsUpgrade bool 134 IsInstall bool 135 } 136 137 // ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files 138 // 139 // This takes both ReleaseOptions and Capabilities to merge into the render values. 140 func ToRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}, options ReleaseOptions, caps *Capabilities) (Values, error) { 141 if caps == nil { 142 caps = DefaultCapabilities 143 } 144 top := map[string]interface{}{ 145 "Chart": chrt.Metadata, 146 "Capabilities": caps, 147 "Release": map[string]interface{}{ 148 "Name": options.Name, 149 "Namespace": options.Namespace, 150 "IsUpgrade": options.IsUpgrade, 151 "IsInstall": options.IsInstall, 152 "Service": "Helm", 153 }, 154 } 155 156 vals, err := CoalesceValues(chrt, chrtVals) 157 if err != nil { 158 return top, err 159 } 160 161 if err := ValidateAgainstSchema(chrt, vals); err != nil { 162 errFmt := "values don't meet the specifications of the schema(s) in the following chart(s):\n%s" 163 return top, fmt.Errorf(errFmt, err.Error()) 164 } 165 166 top["Values"] = vals 167 return top, nil 168 } 169 170 // istable is a special-purpose function to see if the present thing matches the definition of a YAML table. 171 func istable(v interface{}) bool { 172 _, ok := v.(map[string]interface{}) 173 return ok 174 } 175 176 // PathValue takes a path that traverses a YAML structure and returns the value at the end of that path. 177 // The path starts at the root of the YAML structure and is comprised of YAML keys separated by periods. 178 // Given the following YAML data the value at path "chapter.one.title" is "Loomings". 179 // 180 // chapter: 181 // one: 182 // title: "Loomings" 183 func (v Values) PathValue(path string) (interface{}, error) { 184 if path == "" { 185 return nil, errors.New("YAML path cannot be empty") 186 } 187 return v.pathValue(parsePath(path)) 188 } 189 190 func (v Values) pathValue(path []string) (interface{}, error) { 191 if len(path) == 1 { 192 // if exists must be root key not table 193 if _, ok := v[path[0]]; ok && !istable(v[path[0]]) { 194 return v[path[0]], nil 195 } 196 return nil, ErrNoValue(path[0]) 197 } 198 199 key, path := path[len(path)-1], path[:len(path)-1] 200 // get our table for table path 201 t, err := v.Table(joinPath(path...)) 202 if err != nil { 203 return nil, ErrNoValue(key) 204 } 205 // check table for key and ensure value is not a table 206 if k, ok := t[key]; ok && !istable(k) { 207 return k, nil 208 } 209 return nil, ErrNoValue(key) 210 } 211 212 func parsePath(key string) []string { return strings.Split(key, ".") } 213 214 func joinPath(path ...string) string { return strings.Join(path, ".") }