Skip to content

Commit d5dc006

Browse files
committed
controller: set deployedDigest and updateAvailable in driveRollout
Now we start actually updating some of the domain-specific pool status bits. In this one, we update deployedDigest once a rollout is complete. And similarly we keep updateAvailable updated based on whether we've converged to the targetDigest or not. Assisted-by: Pi (Claude Opus 4.6)
1 parent 2165384 commit d5dc006

2 files changed

Lines changed: 44 additions & 6 deletions

File tree

internal/controller/rollout.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,15 @@ func (r *BootcNodePoolReconciler) driveRollout(ctx context.Context, pool *bootcv
6666
return fmt.Errorf("freeing completed slots: %w", err)
6767
}
6868

69-
maxUnavail, err := resolveMaxUnavailable(pool, rs.nodeCount())
70-
if err != nil {
71-
return err
69+
// Update deployedDigest: if every node is running the target image,
70+
// the rollout is converged. Otherwise, clear it — nodes aren't all
71+
// on the same digest (e.g. mid-rollout or a new node joined).
72+
if len(rs.upToDate) == rs.nodeCount() {
73+
pool.Status.DeployedDigest = pool.Status.TargetDigest
74+
} else {
75+
pool.Status.DeployedDigest = ""
7276
}
73-
74-
avail := max(0, maxUnavail-rs.occupiedSlots)
75-
candidates := selectDrainCandidates(rs.staged, avail)
77+
pool.Status.UpdateAvailable = pool.Status.TargetDigest != pool.Status.DeployedDigest
7678

7779
log.V(1).Info("Rollout state",
7880
"upToDate", len(rs.upToDate),
@@ -83,6 +85,22 @@ func (r *BootcNodePoolReconciler) driveRollout(ctx context.Context, pool *bootcv
8385
"degraded", len(rs.degraded),
8486
"unclassified", nodeNames(rs.unclassified),
8587
"occupiedSlots", rs.occupiedSlots,
88+
"updateAvailable", pool.Status.UpdateAvailable,
89+
)
90+
91+
if !pool.Status.UpdateAvailable {
92+
return nil
93+
}
94+
95+
maxUnavail, err := resolveMaxUnavailable(pool, rs.nodeCount())
96+
if err != nil {
97+
return err
98+
}
99+
100+
avail := max(0, maxUnavail-rs.occupiedSlots)
101+
candidates := selectDrainCandidates(rs.staged, avail)
102+
103+
log.V(1).Info("Rollout candidates",
86104
"maxUnavailable", maxUnavail,
87105
"availableSlots", avail,
88106
"candidates", nodeNames(candidates),

internal/controller/rollout_envtest_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ func TestSimpleRollout(t *testing.T) {
7171
simulateDaemonStatus(g, ctx, name, testDigestA, bootcv1alpha1.NodeReasonStaged)
7272
}
7373

74+
// During rollout: deployedDigest is empty (never converged) and
75+
// updateAvailable is true.
76+
g.Eventually(func(g Gomega) {
77+
g.Expect(k8sClient.Get(ctx, client.ObjectKey{Name: poolName}, pool)).To(Succeed())
78+
g.Expect(pool.Status.DeployedDigest).To(BeEmpty(),
79+
"deployedDigest should be empty during rollout")
80+
g.Expect(pool.Status.UpdateAvailable).To(BeTrue(),
81+
"updateAvailable should be true during rollout")
82+
}).Should(Succeed())
83+
7484
// Verify the sequential rollout: with maxUnavailable: 1 and
7585
// alphabetical candidate selection, nodes are processed in
7686
// deterministic order w1 → w2 → w3.
@@ -128,6 +138,16 @@ func TestSimpleRollout(t *testing.T) {
128138
"node %s should be uncordoned after reboot", name)
129139
}).Should(Succeed())
130140
}
141+
142+
// After rollout: all nodes converged, deployedDigest matches target,
143+
// updateAvailable is false.
144+
g.Eventually(func(g Gomega) {
145+
g.Expect(k8sClient.Get(ctx, client.ObjectKey{Name: poolName}, pool)).To(Succeed())
146+
g.Expect(pool.Status.DeployedDigest).To(Equal(testDigestB),
147+
"deployedDigest should match targetDigest after rollout")
148+
g.Expect(pool.Status.UpdateAvailable).To(BeFalse(),
149+
"updateAvailable should be false after rollout")
150+
}).Should(Succeed())
131151
}
132152

133153
// simulateDaemonStatus writes BootcNode status as if the daemon had

0 commit comments

Comments
 (0)