1
2
3
4
5
6
7 package graph
8
9 import (
10 "sort"
11
12 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
13 "k8s.io/klog/v2"
14 "sigs.k8s.io/cli-utils/pkg/multierror"
15 "sigs.k8s.io/cli-utils/pkg/object"
16 "sigs.k8s.io/cli-utils/pkg/object/dependson"
17 "sigs.k8s.io/cli-utils/pkg/object/mutation"
18 "sigs.k8s.io/cli-utils/pkg/object/validation"
19 "sigs.k8s.io/cli-utils/pkg/ordering"
20 )
21
22
23
24 func DependencyGraph(objs object.UnstructuredSet) (*Graph, error) {
25 g := New()
26 if len(objs) == 0 {
27 return g, nil
28 }
29 var errors []error
30
31
32
33 ids := object.UnstructuredSetToObjMetadataSet(objs)
34
35
36 addVertices(g, ids)
37
38 addCRDEdges(g, objs, ids)
39 addNamespaceEdges(g, objs, ids)
40 if err := addDependsOnEdges(g, objs, ids); err != nil {
41 errors = append(errors, err)
42 }
43 if err := addApplyTimeMutationEdges(g, objs, ids); err != nil {
44 errors = append(errors, err)
45 }
46 if len(errors) > 0 {
47 return g, multierror.Wrap(errors...)
48 }
49 return g, nil
50 }
51
52
53
54
55
56
57 func HydrateSetList(idSetList []object.ObjMetadataSet, objs object.UnstructuredSet) []object.UnstructuredSet {
58 var objSetList []object.UnstructuredSet
59
60
61 objToUnstructured := map[object.ObjMetadata]*unstructured.Unstructured{}
62 for _, obj := range objs {
63 objToUnstructured[object.UnstructuredToObjMetadata(obj)] = obj
64 }
65
66
67
68 for _, objSet := range idSetList {
69 currentSet := object.UnstructuredSet{}
70 for _, id := range objSet {
71 var found bool
72 var obj *unstructured.Unstructured
73 if obj, found = objToUnstructured[id]; found {
74 currentSet = append(currentSet, obj)
75 }
76 }
77 if len(currentSet) > 0 {
78
79 sort.Sort(ordering.SortableUnstructureds(currentSet))
80 objSetList = append(objSetList, currentSet)
81 }
82 }
83
84 return objSetList
85 }
86
87
88
89
90
91 func SortObjs(objs object.UnstructuredSet) ([]object.UnstructuredSet, error) {
92 var errors []error
93 if len(objs) == 0 {
94 return nil, nil
95 }
96
97 g, err := DependencyGraph(objs)
98 if err != nil {
99
100 errors = multierror.Unwrap(err)
101 }
102
103 idSetList, err := g.Sort()
104 if err != nil {
105 errors = append(errors, err)
106 }
107
108 objSetList := HydrateSetList(idSetList, objs)
109
110 if len(errors) > 0 {
111 return objSetList, multierror.Wrap(errors...)
112 }
113 return objSetList, nil
114 }
115
116
117 func ReverseSortObjs(objs object.UnstructuredSet) ([]object.UnstructuredSet, error) {
118
119 s, err := SortObjs(objs)
120 if err != nil {
121 return s, err
122 }
123 ReverseSetList(s)
124 return s, nil
125 }
126
127
128 func ReverseSetList(setList []object.UnstructuredSet) {
129
130 for i, j := 0, len(setList)-1; i < j; i, j = i+1, j-1 {
131 setList[i], setList[j] = setList[j], setList[i]
132 }
133
134 for _, set := range setList {
135 for i, j := 0, len(set)-1; i < j; i, j = i+1, j-1 {
136 set[i], set[j] = set[j], set[i]
137 }
138 }
139 }
140
141
142 func addVertices(g *Graph, ids object.ObjMetadataSet) {
143 for _, id := range ids {
144 klog.V(3).Infof("adding vertex: %s", id)
145 g.AddVertex(id)
146 }
147 }
148
149
150
151
152 func addApplyTimeMutationEdges(g *Graph, objs object.UnstructuredSet, ids object.ObjMetadataSet) error {
153 var errors []error
154 for i, obj := range objs {
155 if !mutation.HasAnnotation(obj) {
156 continue
157 }
158 id := ids[i]
159 subs, err := mutation.ReadAnnotation(obj)
160 if err != nil {
161 klog.V(3).Infof("failed to add edges from: %s: %v", id, err)
162 errors = append(errors, validation.NewError(err, id))
163 continue
164 }
165 seen := make(map[object.ObjMetadata]struct{})
166 var objErrors []error
167 for _, sub := range subs {
168 dep := sub.SourceRef.ToObjMetadata()
169
170 if _, found := seen[dep]; found {
171 continue
172 }
173
174 seen[dep] = struct{}{}
175
176
177 if !ids.Contains(dep) {
178 err := object.InvalidAnnotationError{
179 Annotation: mutation.Annotation,
180 Cause: ExternalDependencyError{
181 Edge: Edge{
182 From: id,
183 To: dep,
184 },
185 },
186 }
187 objErrors = append(objErrors, err)
188 klog.V(3).Infof("failed to add edges: %v", err)
189 continue
190 }
191 klog.V(3).Infof("adding edge from: %s, to: %s", id, dep)
192 g.AddEdge(id, dep)
193 }
194 if len(objErrors) > 0 {
195 errors = append(errors,
196 validation.NewError(multierror.Wrap(objErrors...), id))
197 }
198 }
199 if len(errors) > 0 {
200 return multierror.Wrap(errors...)
201 }
202 return nil
203 }
204
205
206
207
208 func addDependsOnEdges(g *Graph, objs object.UnstructuredSet, ids object.ObjMetadataSet) error {
209 var errors []error
210 for i, obj := range objs {
211 if !dependson.HasAnnotation(obj) {
212 continue
213 }
214 id := ids[i]
215 deps, err := dependson.ReadAnnotation(obj)
216 if err != nil {
217 klog.V(3).Infof("failed to add edges from: %s: %v", id, err)
218 errors = append(errors, validation.NewError(err, id))
219 continue
220 }
221 seen := make(map[object.ObjMetadata]struct{})
222 var objErrors []error
223 for _, dep := range deps {
224
225
226 if _, found := seen[dep]; found {
227 err := object.InvalidAnnotationError{
228 Annotation: dependson.Annotation,
229 Cause: DuplicateDependencyError{
230 Edge: Edge{
231 From: id,
232 To: dep,
233 },
234 },
235 }
236 objErrors = append(objErrors, err)
237 klog.V(3).Infof("failed to add edges from: %s: %v", id, err)
238 continue
239 }
240
241 seen[dep] = struct{}{}
242
243
244 if !ids.Contains(dep) {
245 err := object.InvalidAnnotationError{
246 Annotation: dependson.Annotation,
247 Cause: ExternalDependencyError{
248 Edge: Edge{
249 From: id,
250 To: dep,
251 },
252 },
253 }
254 objErrors = append(objErrors, err)
255 klog.V(3).Infof("failed to add edges: %v", err)
256 continue
257 }
258 klog.V(3).Infof("adding edge from: %s, to: %s", id, dep)
259 g.AddEdge(id, dep)
260 }
261 if len(objErrors) > 0 {
262 errors = append(errors,
263 validation.NewError(multierror.Wrap(objErrors...), id))
264 }
265 }
266 if len(errors) > 0 {
267 return multierror.Wrap(errors...)
268 }
269 return nil
270 }
271
272
273
274
275
276 func addCRDEdges(g *Graph, objs object.UnstructuredSet, ids object.ObjMetadataSet) {
277 crds := map[string]object.ObjMetadata{}
278
279 for i, u := range objs {
280 if object.IsCRD(u) {
281 groupKind, found := object.GetCRDGroupKind(u)
282 if found {
283 crds[groupKind.String()] = ids[i]
284 }
285 }
286 }
287
288
289 for i, u := range objs {
290 gvk := u.GroupVersionKind()
291 groupKind := gvk.GroupKind()
292 if to, found := crds[groupKind.String()]; found {
293 from := ids[i]
294 klog.V(3).Infof("adding edge from: custom resource %s, to CRD: %s", from, to)
295 g.AddEdge(from, to)
296 }
297 }
298 }
299
300
301
302
303
304 func addNamespaceEdges(g *Graph, objs object.UnstructuredSet, ids object.ObjMetadataSet) {
305 namespaces := map[string]object.ObjMetadata{}
306
307 for i, obj := range objs {
308 if object.IsKindNamespace(obj) {
309 namespace := obj.GetName()
310 namespaces[namespace] = ids[i]
311 }
312 }
313
314
315 for i, obj := range objs {
316 if object.IsNamespaced(obj) {
317 objNamespace := obj.GetNamespace()
318 if to, found := namespaces[objNamespace]; found {
319 from := ids[i]
320 klog.V(3).Infof("adding edge from: %s to namespace: %s", from, to)
321 g.AddEdge(from, to)
322 }
323 }
324 }
325 }
326
View as plain text