@@ -50,6 +50,10 @@ const (
50
50
lastElemPrefix = `└─`
51
51
indent = " "
52
52
pipe = `│ `
53
+
54
+ // lastObjectAnnotation defines the last object in the ObjectTree.
55
+ // This is necessary to built the prefix for multiline condition messages.
56
+ lastObjectAnnotation = "tree.cluster.x-k8s.io.io/last-object"
53
57
)
54
58
55
59
var (
@@ -304,9 +308,10 @@ func addObjectRowV1Beta2(prefix string, tbl *tablewriter.Table, objectTree *tree
304
308
rowDescriptor .age ,
305
309
msg0 })
306
310
311
+ multilinePrefix := getRootMultiLineObjectPrefix (obj , objectTree )
307
312
for _ , m := range msg [1 :] {
308
313
tbl .Append ([]string {
309
- getMultilinePrefix ( gray .Sprint (prefix ) ),
314
+ gray .Sprint (multilinePrefix ),
310
315
"" ,
311
316
"" ,
312
317
"" ,
@@ -324,7 +329,14 @@ func addObjectRowV1Beta2(prefix string, tbl *tablewriter.Table, objectTree *tree
324
329
325
330
// Add a row for each object's children, taking care of updating the tree view prefix.
326
331
childrenObj := objectTree .GetObjectsByParent (obj .GetUID ())
332
+ childrenObj = orderChildrenObjects (childrenObj )
327
333
334
+ for i , child := range childrenObj {
335
+ addObjectRowV1Beta2 (getChildPrefix (prefix , i , len (childrenObj )), tbl , objectTree , child )
336
+ }
337
+ }
338
+
339
+ func orderChildrenObjects (childrenObj []ctrlclient.Object ) []ctrlclient.Object {
328
340
// printBefore returns true if children[i] should be printed before children[j]. Objects are sorted by z-order and
329
341
// row name such that objects with higher z-order are printed first, and objects with the same z-order are
330
342
// printed in alphabetical order.
@@ -336,10 +348,7 @@ func addObjectRowV1Beta2(prefix string, tbl *tablewriter.Table, objectTree *tree
336
348
return tree .GetZOrder (childrenObj [i ]) > tree .GetZOrder (childrenObj [j ])
337
349
}
338
350
sort .Slice (childrenObj , printBefore )
339
-
340
- for i , child := range childrenObj {
341
- addObjectRowV1Beta2 (getChildPrefix (prefix , i , len (childrenObj )), tbl , objectTree , child )
342
- }
351
+ return childrenObj
343
352
}
344
353
345
354
// addObjectRow add a row for a given object, and recursively for all the object's children.
@@ -452,7 +461,7 @@ func addOtherConditionsV1Beta2(prefix string, tbl *tablewriter.Table, objectTree
452
461
453
462
for _ , m := range msg [1 :] {
454
463
tbl .Append ([]string {
455
- gray .Sprint (getMultilinePrefix (childPrefix )),
464
+ gray .Sprint (getMultilineConditionPrefix (childPrefix )),
456
465
"" ,
457
466
"" ,
458
467
"" ,
@@ -510,8 +519,8 @@ func getChildPrefix(currentPrefix string, childIndex, childCount int) string {
510
519
return nextPrefix + lastElemPrefix
511
520
}
512
521
513
- // getMultilinePrefix return the tree view prefix for a multiline condition.
514
- func getMultilinePrefix (currentPrefix string ) string {
522
+ // getMultilineConditionPrefix return the tree view prefix for a multiline condition.
523
+ func getMultilineConditionPrefix (currentPrefix string ) string {
515
524
// All ├─ should be replaced by |, so all the existing hierarchic dependencies are carried on
516
525
if strings .HasSuffix (currentPrefix , firstElemPrefix ) {
517
526
return strings .TrimSuffix (currentPrefix , firstElemPrefix ) + pipe
@@ -524,6 +533,81 @@ func getMultilinePrefix(currentPrefix string) string {
524
533
return "?"
525
534
}
526
535
536
+ // getRootMultiLineObjectPrefix generates the multiline prefix for an object.
537
+ func getRootMultiLineObjectPrefix (obj ctrlclient.Object , objectTree * tree.ObjectTree ) string {
538
+ // If the object is the last one in the tree, no prefix is needed.
539
+ if ensureLastObjectInTree (objectTree ) == string (obj .GetUID ()) {
540
+ return ""
541
+ }
542
+
543
+ // Determine the prefix for the current object.
544
+ // If it is a leaf we don't have to add any prefix.
545
+ var prefix string
546
+ if len (objectTree .GetObjectsByParent (obj .GetUID ())) > 0 {
547
+ prefix = pipe
548
+ }
549
+
550
+ // Traverse upward through the tree, calculating each parent's prefix.
551
+ // The parent of the root object is nil, so we stop when we reach that point.
552
+ previousUID := obj .GetUID ()
553
+ parent := objectTree .GetParent (obj .GetUID ())
554
+ for parent != nil {
555
+ // We have to break the loop if the previous ID is the same as the current ID.
556
+ // This should never happen as the root object doesn't have set the parentship.
557
+ if previousUID == parent .GetUID () {
558
+ break
559
+ }
560
+
561
+ // Use pipe if the parent has children and the current node is not the last child.
562
+ parentChildren := orderChildrenObjects (objectTree .GetObjectsByParent (parent .GetUID ()))
563
+ isLastChild := len (parentChildren ) > 0 && parentChildren [len (parentChildren )- 1 ].GetUID () == previousUID
564
+ if objectTree .IsObjectWithChild (parent .GetUID ()) && ! isLastChild {
565
+ prefix = pipe + prefix
566
+ } else {
567
+ prefix = indent + prefix
568
+ }
569
+
570
+ // Set prefix and move up the tree.
571
+ previousUID = parent .GetUID ()
572
+ parent = objectTree .GetParent (parent .GetUID ())
573
+ }
574
+ return prefix
575
+ }
576
+
577
+ func ensureLastObjectInTree (objectTree * tree.ObjectTree ) string {
578
+ // Compute last object in the tree and set it in the annotations.
579
+ annotations := objectTree .GetRoot ().GetAnnotations ()
580
+ if annotations == nil {
581
+ annotations = map [string ]string {}
582
+ }
583
+
584
+ // Return if last object is already set.
585
+ if val , ok := annotations [lastObjectAnnotation ]; ok {
586
+ return val
587
+ }
588
+
589
+ lastObjectInTree := string (getLastObjectInTree (objectTree ).GetUID ())
590
+ annotations [lastObjectAnnotation ] = lastObjectInTree
591
+ objectTree .GetRoot ().SetAnnotations (annotations )
592
+ return lastObjectInTree
593
+ }
594
+
595
+ func getLastObjectInTree (objectTree * tree.ObjectTree ) ctrlclient.Object {
596
+ var objs []ctrlclient.Object
597
+
598
+ var traverse func (obj ctrlclient.Object )
599
+ traverse = func (obj ctrlclient.Object ) {
600
+ objs = append (objs , obj )
601
+ children := orderChildrenObjects (objectTree .GetObjectsByParent (obj .GetUID ()))
602
+ for _ , child := range children {
603
+ traverse (child )
604
+ }
605
+ }
606
+
607
+ traverse (objectTree .GetRoot ())
608
+ return objs [len (objs )- 1 ]
609
+ }
610
+
527
611
// getRowName returns the object name in the tree view according to following rules:
528
612
// - group objects are represented as #of objects kind, e.g. 3 Machines...
529
613
// - other virtual objects are represented using the object name, e.g. Workers, or meta name if provided.
0 commit comments