1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package filters
22
23 import (
24 "bytes"
25 "fmt"
26 "io"
27 "sort"
28
29 "sigs.k8s.io/kustomize/kyaml/kio"
30 "sigs.k8s.io/kustomize/kyaml/openapi"
31 "sigs.k8s.io/kustomize/kyaml/yaml"
32 )
33
34 type FormattingStrategy = string
35
36 const (
37
38 FmtAnnotation string = "config.kubernetes.io/formatting"
39
40
41
42 FmtStrategyStandard FormattingStrategy = "standard"
43
44
45 FmtStrategyNone FormattingStrategy = "none"
46 )
47
48
49 func FormatInput(input io.Reader) (*bytes.Buffer, error) {
50 buff := &bytes.Buffer{}
51 err := kio.Pipeline{
52 Inputs: []kio.Reader{&kio.ByteReader{Reader: input}},
53 Filters: []kio.Filter{FormatFilter{}},
54 Outputs: []kio.Writer{kio.ByteWriter{Writer: buff}},
55 }.Execute()
56
57 return buff, err
58 }
59
60
61
62 func FormatFileOrDirectory(path string) error {
63 return kio.Pipeline{
64 Inputs: []kio.Reader{kio.LocalPackageReader{
65 PackagePath: path,
66 }},
67 Filters: []kio.Filter{FormatFilter{}},
68 Outputs: []kio.Writer{kio.LocalPackageWriter{PackagePath: path}},
69 }.Execute()
70 }
71
72 type FormatFilter struct {
73 Process func(n *yaml.Node) error
74 UseSchema bool
75 }
76
77 var _ kio.Filter = FormatFilter{}
78
79 func (f FormatFilter) Filter(slice []*yaml.RNode) ([]*yaml.RNode, error) {
80 for i := range slice {
81 fmtStrategy, err := getFormattingStrategy(slice[i])
82 if err != nil {
83 return nil, err
84 }
85
86 if fmtStrategy == FmtStrategyNone {
87 continue
88 }
89
90 kindNode, err := slice[i].Pipe(yaml.Get("kind"))
91 if err != nil {
92 return nil, err
93 }
94 if kindNode == nil {
95 continue
96 }
97 apiVersionNode, err := slice[i].Pipe(yaml.Get("apiVersion"))
98 if err != nil {
99 return nil, err
100 }
101 if apiVersionNode == nil {
102 continue
103 }
104 kind, apiVersion := kindNode.YNode().Value, apiVersionNode.YNode().Value
105 var s *openapi.ResourceSchema
106 if f.UseSchema {
107 s = openapi.SchemaForResourceType(yaml.TypeMeta{APIVersion: apiVersion, Kind: kind})
108 } else {
109 s = nil
110 }
111 err = (&formatter{apiVersion: apiVersion, kind: kind, process: f.Process}).
112 fmtNode(slice[i].YNode(), "", s)
113 if err != nil {
114 return nil, err
115 }
116 }
117 return slice, nil
118 }
119
120
121
122
123 func getFormattingStrategy(node *yaml.RNode) (FormattingStrategy, error) {
124 value, err := node.Pipe(yaml.GetAnnotation(FmtAnnotation))
125 if err != nil || value == nil {
126 return FmtStrategyStandard, err
127 }
128
129 fmtStrategy := value.YNode().Value
130
131 switch fmtStrategy {
132 case FmtStrategyStandard:
133 return FmtStrategyStandard, nil
134 case FmtStrategyNone:
135 return FmtStrategyNone, nil
136 default:
137 return "", fmt.Errorf(
138 "formatting annotation has illegal value %s", fmtStrategy)
139 }
140 }
141
142 type formatter struct {
143 apiVersion string
144 kind string
145 process func(n *yaml.Node) error
146 }
147
148
149
150 func (f *formatter) fmtNode(n *yaml.Node, path string, schema *openapi.ResourceSchema) error {
151 if n.Kind == yaml.ScalarNode && schema != nil && schema.Schema != nil {
152
153
154 yaml.FormatNonStringStyle(n, *schema.Schema)
155 }
156
157
158 if n.Kind == yaml.MappingNode {
159 sort.Sort(sortedMapContents(*n))
160 }
161
162
163 if n.Kind == yaml.SequenceNode {
164 if yaml.WhitelistedListSortKinds.Has(f.kind) &&
165 yaml.WhitelistedListSortApis.Has(f.apiVersion) {
166 if sortField, found := yaml.WhitelistedListSortFields[path]; found {
167 sort.Sort(sortedSeqContents{Node: *n, sortField: sortField})
168 }
169 }
170 }
171
172
173 for i := range n.Content {
174
175
176
177 isFieldKey := n.Kind == yaml.MappingNode && i%2 == 0
178 isFieldValue := n.Kind == yaml.MappingNode && i%2 == 1
179 isElement := n.Kind == yaml.SequenceNode
180
181
182
183 if f.process != nil && !isFieldKey {
184 if err := f.process(n.Content[i]); err != nil {
185 return err
186 }
187 }
188
189
190 p := path
191 var s *openapi.ResourceSchema
192 switch {
193 case isFieldValue:
194
195 p = fmt.Sprintf("%s.%s", path, n.Content[i-1].Value)
196 if schema != nil {
197 s = schema.Field(n.Content[i-1].Value)
198 }
199 case isElement:
200
201 if schema != nil {
202 s = schema.Elements()
203 }
204 }
205
206 err := f.fmtNode(n.Content[i], p, s)
207 if err != nil {
208 return err
209 }
210 }
211 return nil
212 }
213
214
215
216 type sortedMapContents yaml.Node
217
218 func (s sortedMapContents) Len() int {
219 return len(s.Content) / 2
220 }
221 func (s sortedMapContents) Swap(i, j int) {
222
223
224
225
226
227 iFieldNameIndex := i * 2
228 jFieldNameIndex := j * 2
229 iFieldValueIndex := iFieldNameIndex + 1
230 jFieldValueIndex := jFieldNameIndex + 1
231
232
233 s.Content[iFieldNameIndex], s.Content[jFieldNameIndex] =
234 s.Content[jFieldNameIndex], s.Content[iFieldNameIndex]
235
236
237 s.Content[iFieldValueIndex], s.Content[jFieldValueIndex] = s.
238 Content[jFieldValueIndex], s.Content[iFieldValueIndex]
239 }
240
241 func (s sortedMapContents) Less(i, j int) bool {
242 iFieldNameIndex := i * 2
243 jFieldNameIndex := j * 2
244 iFieldName := s.Content[iFieldNameIndex].Value
245 jFieldName := s.Content[jFieldNameIndex].Value
246
247
248 iOrder, foundI := yaml.FieldOrder[iFieldName]
249 jOrder, foundJ := yaml.FieldOrder[jFieldName]
250 if foundI && foundJ {
251 return iOrder < jOrder
252 }
253
254
255 if foundI {
256 return true
257 }
258 if foundJ {
259 return false
260 }
261
262
263 return iFieldName < jFieldName
264 }
265
266
267
268
269 type sortedSeqContents struct {
270 yaml.Node
271 sortField string
272 }
273
274 func (s sortedSeqContents) Len() int {
275 return len(s.Content)
276 }
277 func (s sortedSeqContents) Swap(i, j int) {
278 s.Content[i], s.Content[j] = s.Content[j], s.Content[i]
279 }
280 func (s sortedSeqContents) Less(i, j int) bool {
281
282 if s.sortField == "" {
283 iValue := s.Content[i].Value
284 jValue := s.Content[j].Value
285 return iValue < jValue
286 }
287
288
289 var iValue, jValue string
290 for a := range s.Content[i].Content {
291 if a%2 != 0 {
292 continue
293 }
294
295 if s.Content[i].Content[a].Value == s.sortField {
296
297 iValue = s.Content[i].Content[a+1].Value
298 }
299 }
300 for a := range s.Content[j].Content {
301 if a%2 != 0 {
302 continue
303 }
304
305
306 if s.Content[j].Content[a].Value == s.sortField {
307
308 jValue = s.Content[j].Content[a+1].Value
309 }
310 }
311
312
313 return iValue < jValue
314 }
315
View as plain text