@@ -290,11 +290,15 @@ func (r *Reconciler) syncWithForest(log logr.Logger, nsInst *corev1.Namespace, i
290
290
defer r .Forest .Unlock ()
291
291
ns := r .Forest .Get (inst .ObjectMeta .Namespace )
292
292
293
- // Clear locally-set conditions in the forest ; we'll re-add them if they're still relevant. But
294
- // first, record whether there were any critical ones since if this changes, we'll need to notify
293
+ // Clear all conditions; we'll re-add them if they're still relevant. But first, record whether
294
+ // this namespace (excluding ancestors) was halted, since if that changes, we'll need to notify
295
295
// other namespaces.
296
- hadCrit := ns .HasLocalCritCondition ()
296
+ wasHalted := ns .IsHalted ()
297
297
ns .ClearConditions ()
298
+ // We can figure out this condition pretty easily...
299
+ if deletingCRD {
300
+ ns .SetCondition (api .ConditionActivitiesHalted , api .ReasonDeletingCRD , "The HierarchyConfiguration CRD is being deleted; all propagation is disabled." )
301
+ }
298
302
299
303
// Set external tree labels in the forest if this is an external namespace.
300
304
r .syncExternalNamespace (log , nsInst , ns )
@@ -306,6 +310,9 @@ func (r *Reconciler) syncWithForest(log logr.Logger, nsInst *corev1.Namespace, i
306
310
r .syncParent (log , inst , ns )
307
311
initial := r .markExisting (log , ns )
308
312
313
+ // Sync labels and annotations, now that the structure's been updated.
314
+ nsCustomerLabelUpdated := r .syncTreeLabels (log , nsInst , ns )
315
+
309
316
// Sync other spec and spec-like info
310
317
r .syncAnchors (log , ns , anms )
311
318
if ns .UpdateAllowCascadingDeletion (inst .Spec .AllowCascadingDeletion ) {
@@ -315,11 +322,9 @@ func (r *Reconciler) syncWithForest(log logr.Logger, nsInst *corev1.Namespace, i
315
322
316
323
// Sync the status
317
324
inst .Status .Children = ns .ChildNames ()
318
- r .syncConditions (log , inst , ns , deletingCRD , hadCrit )
319
-
320
- // Sync the tree labels. This should go last since it can depend on the conditions.
321
- nsCustomerLabelUpdated := r .syncTreeLabels (log , nsInst , ns )
325
+ r .syncConditions (log , inst , ns , wasHalted )
322
326
327
+ r .HNCConfigReconciler .Enqueue ("namespace reconciled" )
323
328
return initial || nsCustomerLabelUpdated
324
329
}
325
330
@@ -431,6 +436,9 @@ func (r *Reconciler) markExisting(log logr.Logger, ns *forest.Namespace) bool {
431
436
}
432
437
433
438
func (r * Reconciler ) syncParent (log logr.Logger , inst * api.HierarchyConfiguration , ns * forest.Namespace ) {
439
+ // As soon as the structure has been updated, do a cycle check.
440
+ defer r .setCycleCondition (log , ns )
441
+
434
442
if ns .IsExternal () {
435
443
ns .SetParent (nil )
436
444
return
@@ -459,10 +467,10 @@ func (r *Reconciler) syncParent(log logr.Logger, inst *api.HierarchyConfiguratio
459
467
// Change the parent.
460
468
ns .SetParent (curParent )
461
469
462
- // Finally, enqueue all other namespaces that could be directly affected. The old and new parents
463
- // have just gained/lost a child, while the descendants need to have their tree labels updated and
464
- // their objects resynced. Note that it's fine if oldParent or curParent is nil - see
465
- // enqueueAffected for details.
470
+ // Enqueue all other namespaces that could be directly affected. The old and new parents have just
471
+ // gained/lost a child, while the descendants need to have their tree labels updated and their
472
+ // objects resynced. Note that it's fine if oldParent or curParent is nil - see enqueueAffected
473
+ // for details.
466
474
//
467
475
// If we've just created a cycle, all the members of that cycle will be listed as the descendants,
468
476
// so enqueuing them will ensure that the conditions show up in all members of the cycle.
@@ -471,6 +479,17 @@ func (r *Reconciler) syncParent(log logr.Logger, inst *api.HierarchyConfiguratio
471
479
r .enqueueAffected (log , "subtree root has changed" , ns .DescendantNames ()... )
472
480
}
473
481
482
+ func (r * Reconciler ) setCycleCondition (log logr.Logger , ns * forest.Namespace ) {
483
+ cycle := ns .CycleNames ()
484
+ if cycle == nil {
485
+ return
486
+ }
487
+
488
+ msg := fmt .Sprintf ("Namespace is a member of the cycle: %s" , strings .Join (cycle , " <- " ))
489
+ log .Info (msg )
490
+ ns .SetCondition (api .ConditionActivitiesHalted , api .ReasonInCycle , msg )
491
+ }
492
+
474
493
// syncAnchors updates the anchor list. If any anchor is created/deleted, it will enqueue
475
494
// the child to update its SubnamespaceAnchorMissing condition. A modified anchor will appear
476
495
// twice in the change list (one in deleted, one in created), both subnamespaces
@@ -498,27 +517,27 @@ func (r *Reconciler) syncTreeLabels(log logr.Logger, nsInst *corev1.Namespace, n
498
517
// Look for all ancestors. Stop as soon as we find a namespaces that has a critical condition in
499
518
// the forest (note that AncestorHaltActivities is never included in the forest). This should handle orphans
500
519
// and cycles.
501
- anc := ns
520
+ curNS := ns
502
521
depth := 0
503
- for anc != nil {
504
- l := anc .Name () + api .LabelTreeDepthSuffix
522
+ for curNS != nil {
523
+ l := curNS .Name () + api .LabelTreeDepthSuffix
505
524
metadata .SetLabel (nsInst , l , strconv .Itoa (depth ))
506
- if anc . HasLocalCritCondition () {
525
+ if curNS . IsHalted () {
507
526
break
508
527
}
509
528
510
529
// If the root is an external namespace, add all its external tree labels too.
511
530
// Note it's impossible to have an external namespace as a non-root, which is
512
531
// enforced by both admission controllers and the reconciler here.
513
- if anc .IsExternal () {
514
- for k , v := range anc .ExternalTreeLabels {
532
+ if curNS .IsExternal () {
533
+ for k , v := range curNS .ExternalTreeLabels {
515
534
l = k + api .LabelTreeDepthSuffix
516
535
metadata .SetLabel (nsInst , l , strconv .Itoa (depth + v ))
517
536
}
518
537
break
519
538
}
520
539
521
- anc = anc .Parent ()
540
+ curNS = curNS .Parent ()
522
541
depth ++
523
542
}
524
543
// Update the labels in the forest so that we can quickly access the labels and
@@ -530,61 +549,22 @@ func (r *Reconciler) syncTreeLabels(log logr.Logger, nsInst *corev1.Namespace, n
530
549
return false
531
550
}
532
551
533
- func (r * Reconciler ) syncConditions (log logr.Logger , inst * api.HierarchyConfiguration , ns * forest.Namespace , deletingCRD , hadCrit bool ) {
534
- // Sync critical conditions after all locally-set conditions are updated.
535
- r .syncCritConditions (log , ns , deletingCRD , hadCrit )
552
+ func (r * Reconciler ) syncConditions (log logr.Logger , inst * api.HierarchyConfiguration , ns * forest.Namespace , wasHalted bool ) {
553
+ // If the halted status has changed, notify
554
+ if ns .IsHalted () != wasHalted {
555
+ msg := ""
556
+ if wasHalted {
557
+ log .Info ("ActivitiesHalted condition removed" )
558
+ msg = "removed"
559
+ } else {
560
+ log .Info ("Setting ActivitiesHalted on namespace" , "conditions" , ns .Conditions ())
561
+ msg = "added"
562
+ }
563
+ r .enqueueAffected (log , "descendant of a namespace with ActivitiesHalted " + msg , ns .DescendantNames ()... )
564
+ }
536
565
537
566
// Convert and pass in-memory conditions to HierarchyConfiguration object.
538
567
inst .Status .Conditions = ns .Conditions ()
539
- setCritAncestorCondition (log , inst , ns )
540
- r .HNCConfigReconciler .Enqueue ("namespace reconciled" )
541
- }
542
-
543
- // syncCritConditions enqueues the children of a namespace if the existing critical conditions in the
544
- // namespace are gone or critical conditions are newly found.
545
- func (r * Reconciler ) syncCritConditions (log logr.Logger , ns * forest.Namespace , deletingCRD , hadCrit bool ) {
546
- // If we're in a cycle, determine that now
547
- if cycle := ns .CycleNames (); cycle != nil {
548
- msg := fmt .Sprintf ("Namespace is a member of the cycle: %s" , strings .Join (cycle , " <- " ))
549
- ns .SetCondition (api .ConditionActivitiesHalted , api .ReasonInCycle , msg )
550
- }
551
-
552
- if deletingCRD {
553
- ns .SetCondition (api .ConditionActivitiesHalted , api .ReasonDeletingCRD , "The HierarchyConfiguration CRD is being deleted; all syncing is disabled." )
554
- }
555
-
556
- // Early exit if nothing's changed and there's no need to enqueue relatives.
557
- if hadCrit == ns .HasLocalCritCondition () {
558
- return
559
- }
560
-
561
- msg := ""
562
- if hadCrit {
563
- log .Info ("ActivitiesHalted condition removed" )
564
- msg = "removed"
565
- } else {
566
- log .Info ("Setting ActivitiesHalted on namespace" , "conditions" , ns .Conditions ())
567
- msg = "added"
568
- }
569
- r .enqueueAffected (log , "descendant of a namespace with ActivitiesHalted " + msg , ns .DescendantNames ()... )
570
- }
571
-
572
- func setCritAncestorCondition (log logr.Logger , inst * api.HierarchyConfiguration , ns * forest.Namespace ) {
573
- if ns .HasLocalCritCondition () {
574
- return
575
- }
576
- ans := ns .Parent ()
577
- for ans != nil {
578
- if ! ans .HasLocalCritCondition () {
579
- ans = ans .Parent ()
580
- continue
581
- }
582
- log .Info ("Ancestor has a ActivitiesHalted condition" , "ancestor" , ans .Name ())
583
- msg := fmt .Sprintf ("Propagation paused in the subtree of %q due to ActivitiesHalted condition" , ans .Name ())
584
- condition := api .NewCondition (api .ConditionActivitiesHalted , api .ReasonAncestor , msg )
585
- inst .Status .Conditions = append (inst .Status .Conditions , condition )
586
- return
587
- }
588
568
}
589
569
590
570
// enqueueAffected enqueues all affected namespaces for later reconciliation. This occurs in a
0 commit comments