1
2
3
4 package framework
5
6 import (
7 "strings"
8
9 validationErrors "k8s.io/kube-openapi/pkg/validation/errors"
10 "k8s.io/kube-openapi/pkg/validation/spec"
11 "k8s.io/kube-openapi/pkg/validation/strfmt"
12 "k8s.io/kube-openapi/pkg/validation/validate"
13 "sigs.k8s.io/kustomize/kyaml/errors"
14 "sigs.k8s.io/kustomize/kyaml/kio"
15 "sigs.k8s.io/kustomize/kyaml/kio/filters"
16 "sigs.k8s.io/kustomize/kyaml/openapi"
17 "sigs.k8s.io/kustomize/kyaml/yaml"
18 k8syaml "sigs.k8s.io/yaml"
19 )
20
21
22
23
24
25
26
27
28 type SimpleProcessor struct {
29
30
31 Filter kio.Filter
32
33
34 Config interface{}
35 }
36
37
38
39
40 func (p SimpleProcessor) Process(rl *ResourceList) error {
41 if err := LoadFunctionConfig(rl.FunctionConfig, p.Config); err != nil {
42 return errors.WrapPrefixf(err, "loading function config")
43 }
44 return errors.WrapPrefixf(rl.Filter(p.Filter), "processing filter")
45 }
46
47
48
49 type GVKFilterMap map[string]map[string]kio.Filter
50
51
52
53
54 func (m GVKFilterMap) ProviderFor(apiVersion, kind string) (kio.Filter, error) {
55 if kind == "" {
56 return nil, errors.Errorf("kind is required")
57 }
58 if apiVersion == "" {
59 return nil, errors.Errorf("apiVersion is required")
60 }
61
62 var ok bool
63 var versionMap map[string]kio.Filter
64 if versionMap, ok = m[kind]; !ok {
65 return nil, errors.Errorf("kind %q is not supported", kind)
66 }
67
68 var p kio.Filter
69 if p, ok = versionMap[apiVersion]; !ok {
70 return nil, errors.Errorf("apiVersion %q is not supported for kind %q", apiVersion, kind)
71 }
72 return p, nil
73 }
74
75
76
77
78
79 type FilterProvider interface {
80
81 ProviderFor(apiVersion, kind string) (kio.Filter, error)
82 }
83
84
85 type FilterProviderFunc func(apiVersion, kind string) (kio.Filter, error)
86
87
88 func (f FilterProviderFunc) ProviderFor(apiVersion, kind string) (kio.Filter, error) {
89 return f(apiVersion, kind)
90 }
91
92
93
94
95
96
97
98
99 type VersionedAPIProcessor struct {
100
101
102 FilterProvider FilterProvider
103 }
104
105
106
107
108
109 func (p *VersionedAPIProcessor) Process(rl *ResourceList) error {
110 api, err := p.FilterProvider.ProviderFor(extractGVK(rl.FunctionConfig))
111 if err != nil {
112 return errors.WrapPrefixf(err, "unable to identify provider for resource")
113 }
114 if err := LoadFunctionConfig(rl.FunctionConfig, api); err != nil {
115 return errors.Wrap(err)
116 }
117 return errors.Wrap(rl.Filter(api))
118 }
119
120
121
122 func extractGVK(src *yaml.RNode) (apiVersion, kind string) {
123 if src == nil {
124 return "", ""
125 }
126 if versionNode := src.Field("apiVersion"); versionNode != nil {
127 if a, err := versionNode.Value.String(); err == nil {
128 apiVersion = strings.TrimSpace(a)
129 }
130 }
131 if kindNode := src.Field("kind"); kindNode != nil {
132 if k, err := kindNode.Value.String(); err == nil {
133 kind = strings.TrimSpace(k)
134 }
135 }
136 return apiVersion, kind
137 }
138
139
140
141
142 func LoadFunctionConfig(src *yaml.RNode, api interface{}) error {
143 if api == nil {
144 return nil
145 }
146
147 var schemaValidationError error
148 if s, ok := api.(ValidationSchemaProvider); ok {
149 schema, err := s.Schema()
150 if err != nil {
151 return errors.WrapPrefixf(err, "loading provided schema")
152 }
153 schemaValidationError = errors.Wrap(validate.AgainstSchema(schema, src, strfmt.Default))
154
155 }
156
157
158
159 if err := k8syaml.Unmarshal([]byte(src.MustString()), api); err != nil {
160 if schemaValidationError != nil {
161
162 return schemaValidationError
163 }
164 return errors.Wrap(err)
165 }
166
167 if d, ok := api.(Defaulter); ok {
168 if err := d.Default(); err != nil {
169 return errors.Wrap(err)
170 }
171 }
172
173 if v, ok := api.(Validator); ok {
174 return combineErrors(schemaValidationError, v.Validate())
175 }
176 return schemaValidationError
177 }
178
179 func combineErrors(schemaErr, customErr error) error {
180 combined := validationErrors.CompositeValidationError()
181 if compositeSchemaErr, ok := schemaErr.(*validationErrors.CompositeError); ok {
182 combined.Errors = append(combined.Errors, compositeSchemaErr.Errors...)
183 } else if schemaErr != nil {
184 combined.Errors = append(combined.Errors, schemaErr)
185 }
186 if compositeCustomErr, ok := customErr.(*validationErrors.CompositeError); ok {
187 combined.Errors = append(combined.Errors, compositeCustomErr.Errors...)
188 } else if customErr != nil {
189 combined.Errors = append(combined.Errors, customErr)
190 }
191 if len(combined.Errors) > 0 {
192 return combined
193 }
194 return nil
195 }
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211 type TemplateProcessor struct {
212
213
214
215
216 TemplateData interface{}
217
218
219
220
221
222 ResourceTemplates []ResourceTemplate
223
224
225
226
227 PatchTemplates []PatchTemplate
228
229
230
231 MergeResources bool
232
233
234
235 PreProcessFilters []kio.Filter
236
237
238
239 PostProcessFilters []kio.Filter
240
241
242
243 AdditionalSchemas SchemaParser
244 }
245
246 type SchemaParser interface {
247 Parse() ([]*spec.Definitions, error)
248 }
249
250 type SchemaParserFunc func() ([]*spec.Definitions, error)
251
252 func (s SchemaParserFunc) Parse() ([]*spec.Definitions, error) {
253 return s()
254 }
255
256
257
258
259 func (tp TemplateProcessor) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) {
260 if tp.AdditionalSchemas != nil {
261 defs, err := tp.AdditionalSchemas.Parse()
262 if err != nil {
263 return nil, errors.WrapPrefixf(err, "parsing AdditionalSchemas")
264 }
265 defer openapi.ResetOpenAPI()
266 for i := range defs {
267 openapi.AddDefinitions(*defs[i])
268 }
269 }
270
271 buf := &kio.PackageBuffer{Nodes: items}
272 pipeline := kio.Pipeline{
273 Inputs: []kio.Reader{buf},
274 Filters: []kio.Filter{
275 kio.FilterFunc(tp.doPreProcess),
276 kio.FilterFunc(tp.doResourceTemplates),
277 kio.FilterFunc(tp.doPatchTemplates),
278 kio.FilterFunc(tp.doMerge),
279 kio.FilterFunc(tp.doPostProcess),
280 },
281 Outputs: []kio.Writer{buf},
282 ContinueOnEmptyResult: true,
283 }
284 if err := pipeline.Execute(); err != nil {
285 return nil, err
286 }
287
288 return buf.Nodes, nil
289 }
290
291
292
293
294 func (tp TemplateProcessor) Process(rl *ResourceList) error {
295 if err := LoadFunctionConfig(rl.FunctionConfig, tp.TemplateData); err != nil {
296 return errors.Wrap(err)
297 }
298 return errors.Wrap(rl.Filter(tp))
299 }
300
301
302
303 type PatchTemplate interface {
304
305
306 Filter(items []*yaml.RNode) ([]*yaml.RNode, error)
307
308
309 DefaultTemplateData(interface{})
310 }
311
312 func (tp *TemplateProcessor) doPreProcess(items []*yaml.RNode) ([]*yaml.RNode, error) {
313 if tp.PreProcessFilters == nil {
314 return items, nil
315 }
316 for i := range tp.PreProcessFilters {
317 filter := tp.PreProcessFilters[i]
318 var err error
319 items, err = filter.Filter(items)
320 if err != nil {
321 return nil, err
322 }
323 }
324 return items, nil
325 }
326
327 func (tp *TemplateProcessor) doMerge(items []*yaml.RNode) ([]*yaml.RNode, error) {
328 var err error
329 if tp.MergeResources {
330 items, err = filters.MergeFilter{}.Filter(items)
331 }
332 return items, err
333 }
334
335 func (tp *TemplateProcessor) doPostProcess(items []*yaml.RNode) ([]*yaml.RNode, error) {
336 if tp.PostProcessFilters == nil {
337 return items, nil
338 }
339 for i := range tp.PostProcessFilters {
340 filter := tp.PostProcessFilters[i]
341 var err error
342 items, err = filter.Filter(items)
343 if err != nil {
344 return nil, err
345 }
346 }
347 return items, nil
348 }
349
350 func (tp *TemplateProcessor) doResourceTemplates(items []*yaml.RNode) ([]*yaml.RNode, error) {
351 if tp.ResourceTemplates == nil {
352 return items, nil
353 }
354
355 for i := range tp.ResourceTemplates {
356 tp.ResourceTemplates[i].DefaultTemplateData(tp.TemplateData)
357 newItems, err := tp.ResourceTemplates[i].Render()
358 if err != nil {
359 return nil, err
360 }
361 if tp.MergeResources {
362
363 items = append(newItems, items...)
364 } else {
365
366 items = append(items, newItems...)
367 }
368 }
369 return items, nil
370 }
371
372 func (tp *TemplateProcessor) doPatchTemplates(items []*yaml.RNode) ([]*yaml.RNode, error) {
373 if tp.PatchTemplates == nil {
374 return items, nil
375 }
376
377 for i := range tp.PatchTemplates {
378
379 tp.PatchTemplates[i].DefaultTemplateData(tp.TemplateData)
380 var err error
381 if items, err = tp.PatchTemplates[i].Filter(items); err != nil {
382 return nil, err
383 }
384 }
385 return items, nil
386 }
387
View as plain text