1
2
3
4 package framework
5
6 import (
7 "bytes"
8 "fmt"
9 "strings"
10 "text/template"
11
12 "sigs.k8s.io/kustomize/kyaml/errors"
13 "sigs.k8s.io/kustomize/kyaml/kio"
14 "sigs.k8s.io/kustomize/kyaml/openapi"
15 "sigs.k8s.io/kustomize/kyaml/yaml"
16 "sigs.k8s.io/kustomize/kyaml/yaml/merge2"
17 "sigs.k8s.io/kustomize/kyaml/yaml/walk"
18 )
19
20
21 type ResourcePatchTemplate struct {
22
23 Templates TemplateParser
24
25
26
27
28
29
30
31 Selector kio.Filter
32
33
34 TemplateData interface{}
35 }
36
37
38
39 func (t *ResourcePatchTemplate) DefaultTemplateData(data interface{}) {
40 if t.TemplateData == nil {
41 t.TemplateData = data
42 }
43 }
44
45
46
47
48 func (t ResourcePatchTemplate) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) {
49 var err error
50 target := items
51 if t.Selector != nil {
52 target, err = t.Selector.Filter(items)
53 if err != nil {
54 return nil, err
55 }
56 }
57 if len(target) == 0 {
58
59 return items, nil
60 }
61
62 if err := t.apply(target); err != nil {
63 return nil, errors.Wrap(err)
64 }
65 return items, nil
66 }
67
68 func (t *ResourcePatchTemplate) apply(matches []*yaml.RNode) error {
69 templates, err := t.Templates.Parse()
70 if err != nil {
71 return errors.Wrap(err)
72 }
73 var patches []*yaml.RNode
74 for i := range templates {
75 newP, err := renderPatches(templates[i], t.TemplateData)
76 if err != nil {
77 return errors.Wrap(err)
78 }
79 patches = append(patches, newP...)
80 }
81
82
83 for j := range matches {
84 for i := range patches {
85 matches[j], err = merge2.Merge(patches[i], matches[j], yaml.MergeOptions{})
86 if err != nil {
87 return errors.WrapPrefixf(err, "failed to apply templated patch")
88 }
89 }
90 }
91 return nil
92 }
93
94
95 type ContainerPatchTemplate struct {
96
97
98 Templates TemplateParser
99
100
101
102
103
104
105
106
107 Selector kio.Filter
108
109
110 TemplateData interface{}
111
112
113
114
115
116
117
118
119 ContainerMatcher func(node *yaml.RNode) bool
120 }
121
122
123
124 func (cpt *ContainerPatchTemplate) DefaultTemplateData(data interface{}) {
125 if cpt.TemplateData == nil {
126 cpt.TemplateData = data
127 }
128 }
129
130
131
132
133
134 func (cpt ContainerPatchTemplate) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) {
135 var err error
136 target := items
137 if cpt.Selector != nil {
138 target, err = cpt.Selector.Filter(items)
139 if err != nil {
140 return nil, err
141 }
142 }
143 if len(target) == 0 {
144
145 return items, nil
146 }
147
148 if err := cpt.apply(target); err != nil {
149 return nil, err
150 }
151
152 return items, nil
153 }
154
155
156 func (cpt ContainerPatchTemplate) apply(matches []*yaml.RNode) error {
157 templates, err := cpt.Templates.Parse()
158 if err != nil {
159 return errors.Wrap(err)
160 }
161 var patches []*yaml.RNode
162 for i := range templates {
163 newP, err := renderPatches(templates[i], cpt.TemplateData)
164 if err != nil {
165 return errors.Wrap(err)
166 }
167 patches = append(patches, newP...)
168 }
169
170 for i := range matches {
171 containers, err := matches[i].Pipe(yaml.LookupFirstMatch(yaml.ConventionalContainerPaths))
172 if err != nil {
173 return errors.Wrap(err)
174 }
175 if containers == nil {
176 continue
177 }
178 err = containers.VisitElements(func(node *yaml.RNode) error {
179 if cpt.ContainerMatcher != nil && !cpt.ContainerMatcher(node) {
180 return nil
181 }
182 for j := range patches {
183 merger := walk.Walker{
184 Sources: []*yaml.RNode{node, patches[j]},
185 Visitor: merge2.Merger{},
186 MergeOptions: yaml.MergeOptions{},
187 Schema: openapi.SchemaForResourceType(yaml.TypeMeta{
188 APIVersion: "v1",
189 Kind: "Pod",
190 }).Lookup("spec", "containers").Elements(),
191 }
192 _, err = merger.Walk()
193 if err != nil {
194 return errors.WrapPrefixf(err, "failed to apply templated patch")
195 }
196 }
197 return nil
198 })
199 if err != nil {
200 return errors.Wrap(err)
201 }
202 }
203 return nil
204 }
205
206 func renderPatches(t *template.Template, data interface{}) ([]*yaml.RNode, error) {
207
208 var b bytes.Buffer
209 if err := t.Execute(&b, data); err != nil {
210 return nil, errors.WrapPrefixf(err, "failed to render patch template %v", t.DefinedTemplates())
211 }
212
213
214 var nodes []*yaml.RNode
215 for _, s := range strings.Split(b.String(), "\n---\n") {
216 s = strings.TrimSpace(s)
217 if s == "" {
218 continue
219 }
220 r := &kio.ByteReader{Reader: bytes.NewBufferString(s), OmitReaderAnnotations: true}
221 newNodes, err := r.Read()
222 if err != nil {
223 return nil, errors.WrapPrefixf(err,
224 "failed to parse rendered patch template into a resource:\n%s\n", addLineNumbers(s))
225 }
226 if err := yaml.ErrorIfAnyInvalidAndNonNull(yaml.MappingNode, newNodes...); err != nil {
227 return nil, errors.WrapPrefixf(err,
228 "failed to parse rendered patch template into a resource:\n%s\n", addLineNumbers(s))
229 }
230 nodes = append(nodes, newNodes...)
231 }
232 return nodes, nil
233 }
234
235 func addLineNumbers(s string) string {
236 lines := strings.Split(s, "\n")
237 for j := range lines {
238 lines[j] = fmt.Sprintf("%03d %s", j+1, lines[j])
239 }
240 return strings.Join(lines, "\n")
241 }
242
View as plain text