1
2
3
4 package builtins
5
6 import (
7 "sort"
8 "strings"
9
10 "sigs.k8s.io/kustomize/api/resmap"
11 "sigs.k8s.io/kustomize/api/resource"
12 "sigs.k8s.io/kustomize/api/types"
13 "sigs.k8s.io/kustomize/kyaml/errors"
14 "sigs.k8s.io/kustomize/kyaml/resid"
15 "sigs.k8s.io/yaml"
16 )
17
18
19
20
21
22
23 type SortOrderTransformerPlugin struct {
24 SortOptions *types.SortOptions `json:"sortOptions,omitempty" yaml:"sortOptions,omitempty"`
25 }
26
27 func (p *SortOrderTransformerPlugin) Config(
28 _ *resmap.PluginHelpers, c []byte) error {
29 return errors.WrapPrefixf(yaml.Unmarshal(c, p), "Failed to unmarshal SortOrderTransformer config")
30 }
31
32 func (p *SortOrderTransformerPlugin) applyDefaults() {
33
34 if p.SortOptions == nil {
35 p.SortOptions = &types.SortOptions{
36 Order: types.FIFOSortOrder,
37 }
38 }
39
40
41
42 if p.SortOptions.Order == types.LegacySortOrder && p.SortOptions.LegacySortOptions == nil {
43 p.SortOptions.LegacySortOptions = &types.LegacySortOptions{
44 OrderFirst: defaultOrderFirst,
45 OrderLast: defaultOrderLast,
46 }
47 }
48 }
49
50 func (p *SortOrderTransformerPlugin) validate() error {
51
52 if p.SortOptions.Order != types.FIFOSortOrder && p.SortOptions.Order != types.LegacySortOrder {
53 return errors.Errorf("the field 'sortOptions.order' must be one of [%s, %s]",
54 types.FIFOSortOrder, types.LegacySortOrder)
55 }
56
57
58
59 if p.SortOptions.Order == types.FIFOSortOrder &&
60 p.SortOptions.LegacySortOptions != nil {
61 return errors.Errorf("the field 'sortOptions.legacySortOptions' is"+
62 " set but the selected sort order is '%v', not 'legacy'",
63 p.SortOptions.Order)
64 }
65 return nil
66 }
67
68 func (p *SortOrderTransformerPlugin) Transform(m resmap.ResMap) (err error) {
69 p.applyDefaults()
70 err = p.validate()
71 if err != nil {
72 return err
73 }
74
75
76 if p.SortOptions.Order == types.LegacySortOrder {
77 s := newLegacyIDSorter(m.AllIds(), p.SortOptions.LegacySortOptions)
78 sort.Sort(s)
79 err = applyOrdering(m, s.resids)
80 if err != nil {
81 return err
82 }
83 }
84 return nil
85 }
86
87
88
89
90 func applyOrdering(m resmap.ResMap, ordering []resid.ResId) error {
91 var err error
92 resources := make([]*resource.Resource, m.Size())
93
94 for i, id := range ordering {
95 resources[i], err = m.GetByCurrentId(id)
96 if err != nil {
97 return errors.WrapPrefixf(err, "expected match for sorting")
98 }
99 }
100 m.Clear()
101 for _, r := range resources {
102 err = m.Append(r)
103 if err != nil {
104 return errors.WrapPrefixf(err, "SortOrderTransformer: Failed to append to resources")
105 }
106 }
107 return nil
108 }
109
110
111
112
113
114
115
116
117 type legacyIDSorter struct {
118
119
120 resids []resid.ResId
121 typeOrders map[string]int
122 }
123
124 func newLegacyIDSorter(
125 resids []resid.ResId,
126 options *types.LegacySortOptions) *legacyIDSorter {
127
128 var typeOrders = func() map[string]int {
129 m := map[string]int{}
130 for i, n := range options.OrderFirst {
131 m[n] = -len(options.OrderFirst) + i
132 }
133 for i, n := range options.OrderLast {
134 m[n] = 1 + i
135 }
136 return m
137 }()
138 return &legacyIDSorter{
139 resids: resids,
140 typeOrders: typeOrders,
141 }
142 }
143
144 var _ sort.Interface = legacyIDSorter{}
145
146 func (a legacyIDSorter) Len() int { return len(a.resids) }
147 func (a legacyIDSorter) Swap(i, j int) {
148 a.resids[i], a.resids[j] = a.resids[j], a.resids[i]
149 }
150 func (a legacyIDSorter) Less(i, j int) bool {
151 if !a.resids[i].Gvk.Equals(a.resids[j].Gvk) {
152 return gvkLessThan(a.resids[i].Gvk, a.resids[j].Gvk, a.typeOrders)
153 }
154 return legacyResIDSortString(a.resids[i]) < legacyResIDSortString(a.resids[j])
155 }
156
157 func gvkLessThan(gvk1, gvk2 resid.Gvk, typeOrders map[string]int) bool {
158 index1 := typeOrders[gvk1.Kind]
159 index2 := typeOrders[gvk2.Kind]
160 if index1 != index2 {
161 return index1 < index2
162 }
163 return legacyGVKSortString(gvk1) < legacyGVKSortString(gvk2)
164 }
165
166
167
168 func legacyGVKSortString(x resid.Gvk) string {
169 legacyNoGroup := "~G"
170 legacyNoVersion := "~V"
171 legacyNoKind := "~K"
172 legacyFieldSeparator := "_"
173
174 g := x.Group
175 if g == "" {
176 g = legacyNoGroup
177 }
178 v := x.Version
179 if v == "" {
180 v = legacyNoVersion
181 }
182 k := x.Kind
183 if k == "" {
184 k = legacyNoKind
185 }
186 return strings.Join([]string{g, v, k}, legacyFieldSeparator)
187 }
188
189
190
191 func legacyResIDSortString(id resid.ResId) string {
192 legacyNoNamespace := "~X"
193 legacyNoName := "~N"
194 legacySeparator := "|"
195
196 ns := id.Namespace
197 if ns == "" {
198 ns = legacyNoNamespace
199 }
200 nm := id.Name
201 if nm == "" {
202 nm = legacyNoName
203 }
204 return strings.Join(
205 []string{id.Gvk.String(), ns, nm}, legacySeparator)
206 }
207
208
209
210
211
212
213 var defaultOrderFirst = []string{
214 "Namespace",
215 "ResourceQuota",
216 "StorageClass",
217 "CustomResourceDefinition",
218 "ServiceAccount",
219 "PodSecurityPolicy",
220 "Role",
221 "ClusterRole",
222 "RoleBinding",
223 "ClusterRoleBinding",
224 "ConfigMap",
225 "Secret",
226 "Endpoints",
227 "Service",
228 "LimitRange",
229 "PriorityClass",
230 "PersistentVolume",
231 "PersistentVolumeClaim",
232 "Deployment",
233 "StatefulSet",
234 "CronJob",
235 "PodDisruptionBudget",
236 }
237 var defaultOrderLast = []string{
238 "MutatingWebhookConfiguration",
239 "ValidatingWebhookConfiguration",
240 }
241
242 func NewSortOrderTransformerPlugin() resmap.TransformerPlugin {
243 return &SortOrderTransformerPlugin{}
244 }
245
View as plain text