4
4
"fmt"
5
5
"github.com/ghodss/yaml"
6
6
"github.com/golang/protobuf/ptypes/any"
7
+ "github.com/random-dwi/helm-doc/helm"
7
8
"github.com/random-dwi/helm-doc/output"
8
9
"k8s.io/helm/pkg/proto/hapi/chart"
9
10
"regexp"
@@ -33,54 +34,85 @@ type ConfigDoc struct {
33
34
ExampleValue interface {}
34
35
}
35
36
36
- func GenerateDocs (chart * chart.Chart , ignoredPrefixes []string , flags CommandFlags ) (map [string ]* ConfigDoc , error ) {
37
+ func GenerateDocs (c * chart.Chart , ignoredPrefixes []string , parentCharts map [ * chart. Chart ] * chart. Chart , flags CommandFlags ) (map [string ]* ConfigDoc , error ) {
37
38
38
- values , err := parseYaml ([]byte (chart .Values .Raw ))
39
+ var allValues = make (map [string ]map [string ]interface {})
40
+ var valueSource []string
39
41
40
- if err != nil {
41
- return nil , fmt .Errorf ("unable to read values for %s:%s: %v" , chart .Metadata .Name , chart .Metadata .Version , err )
42
+ var currentChart = c
43
+ var currentPrefix = ""
44
+
45
+ for currentChart != nil {
46
+ valueSource = append (valueSource , currentChart .Metadata .Name )
47
+
48
+ var rawValues []byte
49
+ if currentChart .Values == nil {
50
+ output .Debugf ("chart has no values.yaml" )
51
+ rawValues = []byte ("" )
52
+ } else {
53
+ rawValues = []byte (currentChart .Values .Raw )
54
+ }
55
+
56
+ values , err := parseYaml (rawValues )
57
+
58
+ if err != nil {
59
+ return nil , fmt .Errorf ("unable to read values for %s:%s: %v" , currentChart .Metadata .Name , currentChart .Metadata .Version , err )
60
+ }
61
+
62
+ if currentPrefix != "" {
63
+ val := findValueForKeyAndGlobal (currentPrefix , values , values ["global" ])
64
+ values , _ = val .(map [string ]interface {})
65
+ currentPrefix = currentChart .Metadata .Name + "." + currentPrefix
66
+ } else {
67
+ currentPrefix = currentChart .Metadata .Name
68
+ }
69
+
70
+ allValues [currentChart .Metadata .Name ] = values
71
+ currentChart = parentCharts [currentChart ]
42
72
}
43
73
44
- definitions , err := findAndParseYaml (chart .Files , "definitions.yaml" )
74
+ definitions , err := findAndParseYaml (c .Files , "definitions.yaml" )
45
75
46
76
if err != nil {
47
- return nil , fmt .Errorf ("unable to read definitions for %s:%s: %v" , chart .Metadata .Name , chart .Metadata .Version , err )
77
+ return nil , fmt .Errorf ("unable to read definitions for %s:%s: %v" , c .Metadata .Name , c .Metadata .Version , err )
48
78
}
49
79
50
- examples , err := findAndParseYaml (chart .Files , "examples.yaml" )
80
+ examples , err := findAndParseYaml (c .Files , "examples.yaml" )
51
81
52
82
if err != nil {
53
83
if flags .VerifyExamples {
54
- return nil , fmt .Errorf ("unable to read examples for %s:%s: %v" , chart .Metadata .Name , chart .Metadata .Version , err )
84
+ return nil , fmt .Errorf ("unable to read examples for %s:%s: %v" , c .Metadata .Name , c .Metadata .Version , err )
55
85
} else {
56
- output .Warnf ("unable to read examples for %s:%s: %v" , chart .Metadata .Name , chart .Metadata .Version , err )
86
+ output .Warnf ("unable to read examples for %s:%s: %v" , c .Metadata .Name , c .Metadata .Version , err )
57
87
}
58
88
}
59
89
60
- return generate (definitions , values , examples , ignoredPrefixes , flags ), nil
90
+ return generate (definitions , allValues , valueSource , examples , ignoredPrefixes , flags ), nil
61
91
}
62
92
63
- func generate (definitions map [string ]interface {}, values map [string ]interface {}, examples map [string ]interface {}, ignoredPrefixes []string , flags CommandFlags ) map [string ]* ConfigDoc {
93
+ func generate (definitions map [string ]interface {}, allValues map [string ]map [ string ] interface {}, valueSource [] string , examples map [string ]interface {}, ignoredPrefixes []string , flags CommandFlags ) map [string ]* ConfigDoc {
64
94
65
95
docs := convertToConfigDocs ("" , definitions )
66
96
67
97
if len (ignoredPrefixes ) > 0 {
68
- for key := range values {
69
- if containsString (ignoredPrefixes , key ) {
70
- delete (values , key )
98
+ for _ , source := range valueSource {
99
+ for key := range allValues [source ] {
100
+ if containsString (ignoredPrefixes , key ) {
101
+ delete (allValues [source ], key )
102
+ }
71
103
}
72
104
}
73
105
}
74
106
75
107
if flags .VerifyValues {
76
- missingKeys := validateDefaultValues ("" , definitions , values )
108
+ missingKeys := validateDefaultValues ("" , definitions , allValues [ valueSource [ 0 ]] )
77
109
if len (missingKeys ) > 0 {
78
110
var prefix = "\n \t "
79
111
output .Failf ("undocumented values detected: %s%s" , prefix , strings .Join (missingKeys , prefix ))
80
112
}
81
113
}
82
114
83
- docs = insertDefaultValues (docs , values )
115
+ docs = insertDefaultValues (docs , allValues , valueSource )
84
116
85
117
if examples != nil {
86
118
docs = insertExampleValues (docs , examples , flags )
@@ -184,15 +216,34 @@ func validateDefaultValues(parentKey string, definitions map[string]interface{},
184
216
return missingKeys
185
217
}
186
218
187
- func insertDefaultValues (docs map [string ]* ConfigDoc , values map [string ]interface {}) map [string ]* ConfigDoc {
219
+ func insertDefaultValues (docs map [string ]* ConfigDoc , allValues map [string ]map [ string ] interface {}, valueSource [] string ) map [string ]* ConfigDoc {
188
220
189
221
for globalKey , configDoc := range docs {
190
- configDoc .DefaultValue = findValueForKey (globalKey , values , false )
222
+ var defaultValue interface {} = nil
223
+ for _ , source := range valueSource {
224
+ defaultValue = mergeValues (findValueForKey (globalKey , allValues [source ], false ), defaultValue )
225
+ }
226
+ configDoc .DefaultValue = defaultValue
191
227
}
192
228
193
229
return docs
194
230
}
195
231
232
+ func mergeValues (defaultParent interface {}, defaultChild interface {}) interface {} {
233
+ parentMap , parentIsMap := defaultParent .(map [string ]interface {})
234
+ childMap , childIsMap := defaultChild .(map [string ]interface {})
235
+ if ! parentIsMap || ! childIsMap {
236
+ // parent overwrites child
237
+ if defaultParent != nil {
238
+ return defaultParent
239
+ } else {
240
+ return defaultChild
241
+ }
242
+ } else {
243
+ return helm .MergeValues (childMap , parentMap )
244
+ }
245
+ }
246
+
196
247
func insertExampleValues (docs map [string ]* ConfigDoc , examples map [string ]interface {}, flags CommandFlags ) map [string ]* ConfigDoc {
197
248
198
249
var missingExamples []string
@@ -206,7 +257,7 @@ func insertExampleValues(docs map[string]*ConfigDoc, examples map[string]interfa
206
257
207
258
if missingExamples != nil {
208
259
var prefix = "\n \t "
209
- output .Failf ("when --force -examples is true an example needs to be provided for every config without default: %s%s" , prefix , strings .Join (missingExamples , prefix ))
260
+ output .Failf ("when --verify -examples is true an example needs to be provided for every config without default: %s%s" , prefix , strings .Join (missingExamples , prefix ))
210
261
}
211
262
212
263
return docs
@@ -257,6 +308,36 @@ func findDefinitionForKeyOrParentKey(globalKey string, definitions map[string]in
257
308
return false
258
309
}
259
310
311
+ func findValueForKeyAndGlobal (globalKey string , values map [string ]interface {}, global interface {}) interface {} {
312
+
313
+ keys := strings .Split (globalKey , "." )
314
+
315
+ for i := range keys {
316
+ leftMostKeys := keys [:len (keys )- i ]
317
+ joinedKey := strings .Join (leftMostKeys , "." )
318
+
319
+ if subValues , exists := values [joinedKey ]; exists {
320
+ subKey := strings .TrimPrefix (strings .TrimPrefix (globalKey , joinedKey ), "." )
321
+
322
+ newMap , isMap := subValues .(map [string ]interface {})
323
+ if isMap {
324
+ if subKey == "" {
325
+ newMap ["global" ] = global
326
+ return newMap
327
+ } else {
328
+ return findValueForKeyAndGlobal (subKey , newMap , global )
329
+ }
330
+ } else {
331
+ globalMap := make (map [string ]interface {})
332
+ globalMap ["global" ] = global
333
+ return globalMap
334
+ }
335
+ }
336
+ }
337
+
338
+ return nil
339
+ }
340
+
260
341
// find value for a given key or nil if it does not exist
261
342
// if `useParentValue` is true, instead of nil the parent value is returned if available
262
343
func findValueForKey (globalKey string , values map [string ]interface {}, useParentValue bool ) interface {} {
0 commit comments