1
2
3
4 package testutil
5
6 import (
7 "fmt"
8 "sort"
9
10 "github.com/google/go-cmp/cmp"
11 "github.com/google/go-cmp/cmp/cmpopts"
12 "sigs.k8s.io/cli-utils/pkg/apply/event"
13 "sigs.k8s.io/cli-utils/pkg/kstatus/status"
14 "sigs.k8s.io/cli-utils/pkg/object"
15 )
16
17 type ExpEvent struct {
18 EventType event.Type
19
20 InitEvent *ExpInitEvent
21 ErrorEvent *ExpErrorEvent
22 ActionGroupEvent *ExpActionGroupEvent
23 ApplyEvent *ExpApplyEvent
24 StatusEvent *ExpStatusEvent
25 PruneEvent *ExpPruneEvent
26 DeleteEvent *ExpDeleteEvent
27 WaitEvent *ExpWaitEvent
28 ValidationEvent *ExpValidationEvent
29 }
30
31 type ExpInitEvent struct {
32
33
34 }
35
36 type ExpErrorEvent struct {
37 Err error
38 }
39
40 type ExpActionGroupEvent struct {
41 GroupName string
42 Action event.ResourceAction
43 Type event.ActionGroupEventStatus
44 }
45
46 type ExpApplyEvent struct {
47 GroupName string
48 Status event.ApplyEventStatus
49 Identifier object.ObjMetadata
50 Error error
51 }
52
53 type ExpStatusEvent struct {
54 Status status.Status
55 Identifier object.ObjMetadata
56 Error error
57 }
58
59 type ExpPruneEvent struct {
60 GroupName string
61 Status event.PruneEventStatus
62 Identifier object.ObjMetadata
63 Error error
64 }
65
66 type ExpDeleteEvent struct {
67 GroupName string
68 Status event.DeleteEventStatus
69 Identifier object.ObjMetadata
70 Error error
71 }
72
73 type ExpWaitEvent struct {
74 GroupName string
75 Status event.WaitEventStatus
76 Identifier object.ObjMetadata
77 }
78
79 type ExpValidationEvent struct {
80 Identifiers object.ObjMetadataSet
81 Error error
82 }
83
84 func VerifyEvents(expEvents []ExpEvent, events []event.Event) error {
85 if len(expEvents) == 0 && len(events) == 0 {
86 return nil
87 }
88 expEventIndex := 0
89 for i := range events {
90 e := events[i]
91 ee := expEvents[expEventIndex]
92 if isMatch(ee, e) {
93 expEventIndex++
94 if expEventIndex >= len(expEvents) {
95 return nil
96 }
97 }
98 }
99 return fmt.Errorf("event %s not found", expEvents[expEventIndex].EventType)
100 }
101
102
103
104
105
106 func isMatch(ee ExpEvent, e event.Event) bool {
107 if ee.EventType != e.Type {
108 return false
109 }
110
111
112 switch e.Type {
113 case event.ErrorType:
114 a := ee.ErrorEvent
115
116 if a == nil {
117 return true
118 }
119
120 b := e.ErrorEvent
121
122 if a.Err != nil {
123 if !cmp.Equal(a.Err, b.Err, cmpopts.EquateErrors()) {
124 return false
125 }
126 }
127 return true
128
129 case event.ActionGroupType:
130 agee := ee.ActionGroupEvent
131
132 if agee == nil {
133 return true
134 }
135
136 age := e.ActionGroupEvent
137
138 if agee.GroupName != age.GroupName {
139 return false
140 }
141
142 if agee.Action != age.Action {
143 return false
144 }
145
146 if agee.Type != age.Status {
147 return false
148 }
149 return true
150
151 case event.ApplyType:
152 aee := ee.ApplyEvent
153
154 if aee == nil {
155 return true
156 }
157 ae := e.ApplyEvent
158
159 if aee.Identifier != object.NilObjMetadata {
160 if aee.Identifier != ae.Identifier {
161 return false
162 }
163 }
164
165 if aee.GroupName != "" {
166 if aee.GroupName != ae.GroupName {
167 return false
168 }
169 }
170
171 if aee.Status != ae.Status {
172 return false
173 }
174
175 if aee.Error != nil {
176 return ae.Error != nil
177 }
178 return ae.Error == nil
179
180 case event.StatusType:
181 see := ee.StatusEvent
182 if see == nil {
183 return true
184 }
185 se := e.StatusEvent
186
187 if see.Identifier != se.Identifier {
188 return false
189 }
190
191 if see.Status != se.PollResourceInfo.Status {
192 return false
193 }
194
195 if see.Error != nil {
196 return se.Error != nil
197 }
198 return se.Error == nil
199
200 case event.PruneType:
201 pee := ee.PruneEvent
202 if pee == nil {
203 return true
204 }
205 pe := e.PruneEvent
206
207 if pee.Identifier != object.NilObjMetadata {
208 if pee.Identifier != pe.Identifier {
209 return false
210 }
211 }
212
213 if pee.GroupName != "" {
214 if pee.GroupName != pe.GroupName {
215 return false
216 }
217 }
218
219 if pee.Status != pe.Status {
220 return false
221 }
222
223 if pee.Error != nil {
224 return pe.Error != nil
225 }
226 return pe.Error == nil
227
228 case event.DeleteType:
229 dee := ee.DeleteEvent
230 if dee == nil {
231 return true
232 }
233 de := e.DeleteEvent
234
235 if dee.Identifier != object.NilObjMetadata {
236 if dee.Identifier != de.Identifier {
237 return false
238 }
239 }
240
241 if dee.GroupName != "" {
242 if dee.GroupName != de.GroupName {
243 return false
244 }
245 }
246
247 if dee.Status != de.Status {
248 return false
249 }
250
251 if dee.Error != nil {
252 return de.Error != nil
253 }
254 return de.Error == nil
255
256 case event.WaitType:
257 wee := ee.WaitEvent
258 if wee == nil {
259 return true
260 }
261 we := e.WaitEvent
262
263 if wee.Identifier != object.NilObjMetadata {
264 if wee.Identifier != we.Identifier {
265 return false
266 }
267 }
268
269 if wee.GroupName != "" {
270 if wee.GroupName != we.GroupName {
271 return false
272 }
273 }
274
275 if wee.Status != we.Status {
276 return false
277 }
278 return true
279
280 case event.ValidationType:
281 vee := ee.ValidationEvent
282 if vee == nil {
283 return true
284 }
285 ve := e.ValidationEvent
286
287 if vee.Identifiers != nil {
288 if !vee.Identifiers.Equal(ve.Identifiers) {
289 return false
290 }
291 }
292
293 if vee.Error != nil {
294 return ve.Error != nil
295 }
296 return ve.Error == nil
297
298 default:
299 return true
300 }
301 }
302
303 func EventsToExpEvents(events []event.Event) []ExpEvent {
304 result := make([]ExpEvent, 0, len(events))
305 for _, event := range events {
306 result = append(result, EventToExpEvent(event))
307 }
308 return result
309 }
310
311 func EventToExpEvent(e event.Event) ExpEvent {
312 switch e.Type {
313 case event.InitType:
314 return ExpEvent{
315 EventType: event.InitType,
316 InitEvent: &ExpInitEvent{
317
318
319 },
320 }
321
322 case event.ErrorType:
323 return ExpEvent{
324 EventType: event.ErrorType,
325 ErrorEvent: &ExpErrorEvent{
326 Err: e.ErrorEvent.Err,
327 },
328 }
329
330 case event.ActionGroupType:
331 return ExpEvent{
332 EventType: event.ActionGroupType,
333 ActionGroupEvent: &ExpActionGroupEvent{
334 GroupName: e.ActionGroupEvent.GroupName,
335 Action: e.ActionGroupEvent.Action,
336 Type: e.ActionGroupEvent.Status,
337 },
338 }
339
340 case event.ApplyType:
341 return ExpEvent{
342 EventType: event.ApplyType,
343 ApplyEvent: &ExpApplyEvent{
344 GroupName: e.ApplyEvent.GroupName,
345 Identifier: e.ApplyEvent.Identifier,
346 Status: e.ApplyEvent.Status,
347 Error: e.ApplyEvent.Error,
348 },
349 }
350
351 case event.StatusType:
352 return ExpEvent{
353 EventType: event.StatusType,
354 StatusEvent: &ExpStatusEvent{
355 Identifier: e.StatusEvent.Identifier,
356 Status: e.StatusEvent.PollResourceInfo.Status,
357 Error: e.StatusEvent.Error,
358 },
359 }
360
361 case event.PruneType:
362 return ExpEvent{
363 EventType: event.PruneType,
364 PruneEvent: &ExpPruneEvent{
365 GroupName: e.PruneEvent.GroupName,
366 Identifier: e.PruneEvent.Identifier,
367 Status: e.PruneEvent.Status,
368 Error: e.PruneEvent.Error,
369 },
370 }
371
372 case event.DeleteType:
373 return ExpEvent{
374 EventType: event.DeleteType,
375 DeleteEvent: &ExpDeleteEvent{
376 GroupName: e.DeleteEvent.GroupName,
377 Identifier: e.DeleteEvent.Identifier,
378 Status: e.DeleteEvent.Status,
379 Error: e.DeleteEvent.Error,
380 },
381 }
382
383 case event.WaitType:
384 return ExpEvent{
385 EventType: event.WaitType,
386 WaitEvent: &ExpWaitEvent{
387 GroupName: e.WaitEvent.GroupName,
388 Identifier: e.WaitEvent.Identifier,
389 Status: e.WaitEvent.Status,
390 },
391 }
392
393 case event.ValidationType:
394 return ExpEvent{
395 EventType: event.ValidationType,
396 ValidationEvent: &ExpValidationEvent{
397 Identifiers: e.ValidationEvent.Identifiers,
398 Error: e.ValidationEvent.Error,
399 },
400 }
401 }
402 return ExpEvent{}
403 }
404
405 func RemoveEqualEvents(in []ExpEvent, expected ExpEvent) ([]ExpEvent, int) {
406 matches := 0
407 for i := 0; i < len(in); i++ {
408 if cmp.Equal(in[i], expected, cmpopts.EquateErrors()) {
409
410 in = append(in[:i], in[i+1:]...)
411 matches++
412 i--
413 }
414 }
415 return in, matches
416 }
417
418
419
420
421
422
423
424
425
426
427
428
429 func SortExpEvents(events []ExpEvent) {
430 sort.SliceStable(events, GroupedEventsByID(events).Less)
431 }
432
433
434
435
436
437
438
439
440 type GroupedEventsByID []ExpEvent
441
442 func (ape GroupedEventsByID) Len() int { return len(ape) }
443 func (ape GroupedEventsByID) Swap(i, j int) { ape[i], ape[j] = ape[j], ape[i] }
444 func (ape GroupedEventsByID) Less(i, j int) bool {
445 if ape[i].EventType != ape[j].EventType {
446
447 return i < j
448 }
449 switch ape[i].EventType {
450 case event.ValidationType:
451
452 case event.ApplyType:
453
454 case event.PruneType:
455
456 case event.DeleteType:
457
458 case event.WaitType:
459
460
461
462
463
464
465
466
467 if ape[i].WaitEvent.GroupName == ape[j].WaitEvent.GroupName {
468 if ape[i].WaitEvent.Status != ape[j].WaitEvent.Status {
469 return lessWaitStatus(ape[i].WaitEvent.Status, ape[j].WaitEvent.Status)
470 }
471 return ape[i].WaitEvent.Identifier.String() < ape[j].WaitEvent.Identifier.String()
472 }
473 }
474 return i < j
475 }
476
477 var waitStatusWeight = map[event.WaitEventStatus]int{
478 event.ReconcilePending: 0,
479 event.ReconcileSkipped: 1,
480 event.ReconcileSuccessful: 2,
481 event.ReconcileFailed: 3,
482 event.ReconcileTimeout: 4,
483 }
484
485 func lessWaitStatus(x, y event.WaitEventStatus) bool {
486 return waitStatusWeight[x] < waitStatusWeight[y]
487 }
488
View as plain text