1
2
3
4 package runtimeutil
5
6 import (
7 "bytes"
8 "fmt"
9 "io"
10 "os"
11 "path"
12 "strings"
13
14 "sigs.k8s.io/kustomize/kyaml/comments"
15 "sigs.k8s.io/kustomize/kyaml/errors"
16 "sigs.k8s.io/kustomize/kyaml/kio"
17 "sigs.k8s.io/kustomize/kyaml/kio/kioutil"
18 "sigs.k8s.io/kustomize/kyaml/order"
19
20 "sigs.k8s.io/kustomize/kyaml/yaml"
21 )
22
23
24
25
26 type FunctionFilter struct {
27
28 Run func(reader io.Reader, writer io.Writer) error
29
30
31 FunctionConfig *yaml.RNode `yaml:"functionConfig,omitempty"`
32
33
34
35 GlobalScope bool
36
37
38
39 ResultsFile string
40
41
42
43 DeferFailure bool
44
45
46 Results *yaml.RNode
47
48
49 exit error
50
51 ids map[string]*yaml.RNode
52 }
53
54
55 func (c FunctionFilter) GetExit() error {
56 return c.exit
57 }
58
59
60 const functionsDirectoryName = "functions"
61
62
63
64 func (c *FunctionFilter) getFunctionScope() (string, error) {
65 m, err := c.FunctionConfig.GetMeta()
66 if err != nil {
67 return "", errors.Wrap(err)
68 }
69 var p string
70 var found bool
71 p, found = m.Annotations[kioutil.PathAnnotation]
72 if !found {
73 p, found = m.Annotations[kioutil.LegacyPathAnnotation]
74 if !found {
75 return "", nil
76 }
77 }
78
79 functionDir := path.Clean(path.Dir(p))
80
81 if path.Base(functionDir) == functionsDirectoryName {
82
83
84 functionDir = path.Dir(functionDir)
85 }
86 return functionDir, nil
87 }
88
89
90
91 func (c *FunctionFilter) scope(dir string, nodes []*yaml.RNode) ([]*yaml.RNode, []*yaml.RNode, error) {
92
93 var input, saved []*yaml.RNode
94 if c.GlobalScope {
95 return nodes, nil, nil
96 }
97
98
99 if dir == "" || dir == "." {
100 return nodes, nil, nil
101 }
102
103
104 for i := range nodes {
105 m, err := nodes[i].GetMeta()
106 if err != nil {
107 return nil, nil, err
108 }
109 var p string
110 var found bool
111 p, found = m.Annotations[kioutil.PathAnnotation]
112 if !found {
113 p, found = m.Annotations[kioutil.LegacyPathAnnotation]
114 if !found {
115
116
117 saved = append(saved, nodes[i])
118 continue
119 }
120 }
121
122 resourceDir := path.Clean(path.Dir(p))
123 if path.Base(resourceDir) == functionsDirectoryName {
124
125
126 resourceDir = path.Dir(resourceDir)
127 }
128 if !strings.HasPrefix(resourceDir, dir) {
129
130
131 saved = append(saved, nodes[i])
132 continue
133 }
134
135
136 input = append(input, nodes[i])
137 }
138
139 return input, saved, nil
140 }
141
142 func (c *FunctionFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
143 in := &bytes.Buffer{}
144 out := &bytes.Buffer{}
145
146
147 functionDir, err := c.getFunctionScope()
148 if err != nil {
149 return nil, err
150 }
151 input, saved, err := c.scope(functionDir, nodes)
152 if err != nil {
153 return nil, err
154 }
155
156
157 if err := c.setIds(input); err != nil {
158 return nil, err
159 }
160
161
162 err = kio.ByteWriter{
163 WrappingAPIVersion: kio.ResourceListAPIVersion,
164 WrappingKind: kio.ResourceListKind,
165 Writer: in,
166 KeepReaderAnnotations: true,
167 FunctionConfig: c.FunctionConfig}.Write(input)
168 if err != nil {
169 return nil, err
170 }
171
172
173 r := &kio.ByteReader{Reader: out}
174
175
176 c.exit = c.Run(in, out)
177
178 output, err := r.Read()
179 if err != nil {
180 return nil, err
181 }
182
183
184 if err := c.copyCommentsAndSyncOrder(output); err != nil {
185 return nil, err
186 }
187
188 if err := c.doResults(r); err != nil {
189 return nil, err
190 }
191
192 if c.exit != nil && !c.DeferFailure {
193 return append(output, saved...), c.exit
194 }
195
196
197 if err := kioutil.DefaultPathAnnotation(functionDir, output); err != nil {
198 return nil, err
199 }
200
201
202
203 return append(output, saved...), nil
204 }
205
206 func (c *FunctionFilter) setIds(nodes []*yaml.RNode) error {
207
208 var id int
209 c.ids = map[string]*yaml.RNode{}
210 for i := range nodes {
211 id++
212 idStr := fmt.Sprintf("%v", id)
213 err := nodes[i].PipeE(yaml.SetAnnotation(kioutil.IdAnnotation, idStr))
214 if err != nil {
215 return errors.Wrap(err)
216 }
217 err = nodes[i].PipeE(yaml.SetAnnotation(kioutil.LegacyIdAnnotation, idStr))
218 if err != nil {
219 return errors.Wrap(err)
220 }
221 c.ids[idStr] = nodes[i]
222 }
223 return nil
224 }
225
226 func (c *FunctionFilter) copyCommentsAndSyncOrder(nodes []*yaml.RNode) error {
227 for i := range nodes {
228 node := nodes[i]
229 anID, err := node.Pipe(yaml.GetAnnotation(kioutil.IdAnnotation))
230 if err != nil {
231 return errors.Wrap(err)
232 }
233 if anID == nil {
234 anID, err = node.Pipe(yaml.GetAnnotation(kioutil.LegacyIdAnnotation))
235 if err != nil {
236 return errors.Wrap(err)
237 }
238 if anID == nil {
239 continue
240 }
241 }
242
243 var in *yaml.RNode
244 var found bool
245 if in, found = c.ids[anID.YNode().Value]; !found {
246 continue
247 }
248 if err := comments.CopyComments(in, node); err != nil {
249 return errors.Wrap(err)
250 }
251 if err := order.SyncOrder(in, node); err != nil {
252 return errors.Wrap(err)
253 }
254 if err := node.PipeE(yaml.ClearAnnotation(kioutil.IdAnnotation)); err != nil {
255 return errors.Wrap(err)
256 }
257 if err := node.PipeE(yaml.ClearAnnotation(kioutil.LegacyIdAnnotation)); err != nil {
258 return errors.Wrap(err)
259 }
260 }
261 return nil
262 }
263
264 func (c *FunctionFilter) doResults(r *kio.ByteReader) error {
265
266 if c.ResultsFile != "" && r.Results != nil {
267 results, err := r.Results.String()
268 if err != nil {
269 return err
270 }
271 err = os.WriteFile(c.ResultsFile, []byte(results), 0600)
272 if err != nil {
273 return err
274 }
275 }
276
277 if r.Results != nil {
278 c.Results = r.Results
279 }
280 return nil
281 }
282
View as plain text