Skip to content

Commit e56dd25

Browse files
committed
Fix ImageBuildDegraded Status updates
After an image was built and rolled out successfully, and we triggered a new build, the ImageBuildDegraded status wasn't updating correctly as it kept focusing on the successful build and not the build currently active. Fix the logic to be able to keep track of the active build instead and update the status accordingly. Signed-off-by: Urvashi <[email protected]>
1 parent 8dc0f4d commit e56dd25

File tree

3 files changed

+271
-49
lines changed

3 files changed

+271
-49
lines changed

pkg/controller/build/reconciler.go

Lines changed: 98 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -263,56 +263,20 @@ func (b *buildReconciler) updateMachineOSBuild(ctx context.Context, old, current
263263
}
264264

265265
if !oldState.IsBuildFailure() && curState.IsBuildFailure() {
266-
klog.Infof("MachineOSBuild %s failed, leaving ephemeral objects in place for inspection and setting BuildDegraded condition", current.Name)
267-
268-
// Before setting BuildDegraded, check if another MachineOSBuild is active or if this one has been superseded
269-
mosbList, err := b.getMachineOSBuildsForMachineOSConfig(mosc)
270-
if err != nil {
271-
return fmt.Errorf("could not get MachineOSBuilds for MachineOSConfig %q: %w", mosc.Name, err)
272-
}
273-
274-
// Check if there are any newer or active builds that would supersede this failure
275-
hasActiveBuild := false
276-
isCurrentBuildStale := false
277-
for _, mosb := range mosbList {
278-
// Skip the current failed build
279-
if mosb.Name == current.Name {
280-
continue
281-
}
282-
mosbState := ctrlcommon.NewMachineOSBuildState(mosb)
283-
// Check if there's an active build (building, prepared, or succeeded)
284-
if mosbState.IsBuilding() || mosbState.IsBuildPrepared() || mosbState.IsBuildSuccess() {
285-
hasActiveBuild = true
286-
klog.Infof("Found active MachineOSBuild %s, skipping BuildDegraded condition for failed build %s", mosb.Name, current.Name)
287-
break
288-
}
289-
}
290-
291-
// Also check if the failed MachineOSBuild is no longer referenced by the MachineOSConfig
292-
if mosc.Status.CurrentImagePullSpec != "" && !isMachineOSBuildCurrentForMachineOSConfigWithPullspec(mosc, current) {
293-
isCurrentBuildStale = true
294-
klog.Infof("Failed MachineOSBuild %s is no longer current for MachineOSConfig %s, skipping BuildDegraded condition", current.Name, mosc.Name)
295-
}
296-
297-
// Only set BuildDegraded if there are no active builds and this build is still current
298-
if hasActiveBuild || isCurrentBuildStale {
299-
klog.Infof("Skipping BuildDegraded condition for failed MachineOSBuild %s (hasActiveBuild=%v, isStale=%v)", current.Name, hasActiveBuild, isCurrentBuildStale)
300-
return nil
301-
}
266+
klog.Infof("MachineOSBuild %s failed, leaving ephemeral objects in place for inspection", current.Name)
302267

303268
mcp, err := b.machineConfigPoolLister.Get(mosc.Spec.MachineConfigPool.Name)
304269
if err != nil {
305270
return fmt.Errorf("could not get MachineConfigPool from MachineOSConfig %q: %w", mosc.Name, err)
306271
}
307272

308-
// Set BuildDegraded condition
309-
buildError := getBuildErrorFromMOSB(current)
310-
return b.syncBuildFailureStatus(ctx, mcp, buildError, current.Name)
273+
// Always update ImageBuildDegraded condition based on current active build status
274+
return b.updateImageBuildDegradedCondition(ctx, mcp, mosc)
311275
}
312276

313277
// If the build was successful, clean up the build objects and propagate the
314278
// final image pushspec onto the MachineOSConfig object.
315-
// Also clear BuildDegraded condition if it was set due to a previously failed build
279+
// Also update BuildDegraded condition based on current active build status
316280
if !oldState.IsBuildSuccess() && curState.IsBuildSuccess() {
317281
klog.Infof("MachineOSBuild %s succeeded, cleaning up all ephemeral objects used for the build", current.Name)
318282

@@ -321,9 +285,9 @@ func (b *buildReconciler) updateMachineOSBuild(ctx context.Context, old, current
321285
return fmt.Errorf("could not get MachineConfigPool from MachineOSConfig %q: %w", mosc.Name, err)
322286
}
323287

324-
// Clear BuildDegraded condition if set
325-
if err := b.syncBuildSuccessStatus(ctx, mcp); err != nil {
326-
klog.Errorf("Failed to clear BuildDegraded condition for pool %s: %v", mcp.Name, err)
288+
// Update BuildDegraded condition based on current active build status
289+
if err := b.updateImageBuildDegradedCondition(ctx, mcp, mosc); err != nil {
290+
klog.Errorf("Failed to update ImageBuildDegraded condition for pool %s: %v", mcp.Name, err)
327291
}
328292

329293
// Clean up ephemeral objects
@@ -369,6 +333,18 @@ func (b *buildReconciler) updateMachineOSConfigStatus(ctx context.Context, mosc
369333
klog.Infof("Updated annotations on MachineOSConfig %q", mosc.Name)
370334

371335
mosc = updatedMosc
336+
337+
// When annotations are updated (new build starts), also update observedGeneration
338+
// to signal that the controller is processing the current generation
339+
if mosc.Status.ObservedGeneration != mosc.GetGeneration() {
340+
mosc.Status.ObservedGeneration = mosc.GetGeneration()
341+
_, err = b.mcfgclient.MachineconfigurationV1().MachineOSConfigs().UpdateStatus(ctx, mosc, metav1.UpdateOptions{})
342+
if err != nil {
343+
klog.Errorf("Failed to update observedGeneration on MachineOSConfig %q: %v", mosc.Name, err)
344+
} else {
345+
klog.Infof("Updated observedGeneration on MachineOSConfig %q to %d", mosc.Name, mosc.GetGeneration())
346+
}
347+
}
372348
}
373349

374350
// Skip the status update if digest image pushspec hasn't been set yet.
@@ -457,14 +433,14 @@ func (b *buildReconciler) startBuild(ctx context.Context, mosb *mcfgv1.MachineOS
457433
return fmt.Errorf("could not update MachineOSConfig %q status for MachineOSBuild %q: %w", mosc.Name, mosb.Name, err)
458434
}
459435

460-
// Initialize BuildDegraded condition to False when build starts
436+
// Update BuildDegraded condition based on current active build status when build starts
461437
mcp, err := b.machineConfigPoolLister.Get(mosc.Spec.MachineConfigPool.Name)
462438
if err != nil {
463439
return fmt.Errorf("could not get MachineConfigPool from MachineOSConfig %q: %w", mosc.Name, err)
464440
}
465441

466-
if err := b.initializeBuildDegradedCondition(ctx, mcp); err != nil {
467-
klog.Errorf("Failed to initialize BuildDegraded condition for pool %s: %v", mcp.Name, err)
442+
if err := b.updateImageBuildDegradedCondition(ctx, mcp, mosc); err != nil {
443+
klog.Errorf("Failed to update ImageBuildDegraded condition for pool %s: %v", mcp.Name, err)
468444
}
469445

470446
return nil
@@ -1633,3 +1609,79 @@ func (b *buildReconciler) syncBuildFailureStatus(ctx context.Context, pool *mcfg
16331609
}
16341610
return buildErr
16351611
}
1612+
1613+
// getCurrentBuild finds the currently active (most relevant) build from a list of MachineOSBuilds
1614+
// Priority: 1) Currently referenced by MOSC, 2) Building/Prepared, 3) Most recent
1615+
func (b *buildReconciler) getCurrentBuild(mosc *mcfgv1.MachineOSConfig, mosbList []*mcfgv1.MachineOSBuild) *mcfgv1.MachineOSBuild {
1616+
var activeBuild *mcfgv1.MachineOSBuild
1617+
var mostRecentBuild *mcfgv1.MachineOSBuild
1618+
1619+
// First, look for the build currently referenced by the MachineOSConfig
1620+
for _, mosb := range mosbList {
1621+
if isMachineOSBuildCurrentForMachineOSConfig(mosc, mosb) {
1622+
activeBuild = mosb
1623+
break
1624+
}
1625+
}
1626+
1627+
// If no current build found, look for active builds (building/prepared)
1628+
if activeBuild == nil {
1629+
for _, mosb := range mosbList {
1630+
mosbState := ctrlcommon.NewMachineOSBuildState(mosb)
1631+
if mosbState.IsBuilding() || mosbState.IsBuildPrepared() {
1632+
activeBuild = mosb
1633+
break
1634+
}
1635+
}
1636+
}
1637+
1638+
// Keep track of the most recent build regardless
1639+
for _, mosb := range mosbList {
1640+
if mostRecentBuild == nil || mosb.CreationTimestamp.After(mostRecentBuild.CreationTimestamp.Time) {
1641+
mostRecentBuild = mosb
1642+
}
1643+
}
1644+
1645+
// If still no active build, use the most recent one
1646+
if activeBuild == nil {
1647+
activeBuild = mostRecentBuild
1648+
}
1649+
1650+
return activeBuild
1651+
}
1652+
1653+
// updateImageBuildDegradedCondition examines all MachineOSBuilds for the MachineOSConfig
1654+
// and sets the ImageBuildDegraded condition based on the status of the currently active build
1655+
func (b *buildReconciler) updateImageBuildDegradedCondition(ctx context.Context, pool *mcfgv1.MachineConfigPool, mosc *mcfgv1.MachineOSConfig) error {
1656+
mosbList, err := b.getMachineOSBuildsForMachineOSConfig(mosc)
1657+
if err != nil {
1658+
return fmt.Errorf("could not get MachineOSBuilds for MachineOSConfig %q: %w", mosc.Name, err)
1659+
}
1660+
1661+
// Find the currently active build
1662+
activeBuild := b.getCurrentBuild(mosc, mosbList)
1663+
1664+
// If no builds exist at all, clear any existing BuildDegraded condition
1665+
if activeBuild == nil {
1666+
return b.syncBuildSuccessStatus(ctx, pool)
1667+
}
1668+
1669+
// Update condition based on the active build's status
1670+
activeState := ctrlcommon.NewMachineOSBuildState(activeBuild)
1671+
1672+
switch {
1673+
case activeState.IsBuildFailure():
1674+
// Set BuildDegraded=True for failed builds
1675+
buildError := getBuildErrorFromMOSB(activeBuild)
1676+
return b.syncBuildFailureStatus(ctx, pool, buildError, activeBuild.Name)
1677+
case activeState.IsBuildSuccess():
1678+
// Clear BuildDegraded=False for successful builds
1679+
return b.syncBuildSuccessStatus(ctx, pool)
1680+
case activeState.IsBuilding(), activeState.IsBuildPrepared():
1681+
// Clear BuildDegraded=False for builds in progress (allow retry after previous failure)
1682+
return b.initializeBuildDegradedCondition(ctx, pool)
1683+
}
1684+
1685+
// For any other states, don't change the condition
1686+
return nil
1687+
}

pkg/controller/node/status.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ func (ctrl *Controller) calculateStatus(mcns []*mcfgv1.MachineConfigNode, cconfi
247247
// if the pool is paused or true if the pool is not paused and the PIS is not degraded
248248
allUpdated := updatedMachineCount == totalMachineCount &&
249249
readyMachineCount == totalMachineCount &&
250-
unavailableMachineCount == 0
250+
unavailableMachineCount == 0 &&
251+
!isLayeredPoolBuilding(isLayeredPool, mosc, mosb)
251252
if allUpdated {
252253
//TODO: update api to only have one condition regarding status of update.
253254
updatedMsg := fmt.Sprintf("All nodes are updated with %s", getPoolUpdateLine(pool, mosc, isLayeredPool))
@@ -504,3 +505,35 @@ func isPinnedImageSetsInProgressForPool(pool *mcfgv1.MachineConfigPool) bool {
504505
}
505506
return false
506507
}
508+
509+
// isLayeredPoolBuilding checks if a layered pool has an active build or failed build that would
510+
// make nodes not truly "updated" even if they have the current machine config
511+
func isLayeredPoolBuilding(isLayeredPool bool, mosc *mcfgv1.MachineOSConfig, mosb *mcfgv1.MachineOSBuild) bool {
512+
if !isLayeredPool || mosc == nil || mosb == nil {
513+
return false
514+
}
515+
516+
mosbState := ctrlcommon.NewMachineOSBuildState(mosb)
517+
518+
// Check if there's an active build (building or prepared)
519+
if mosbState.IsBuilding() || mosbState.IsBuildPrepared() {
520+
return true
521+
}
522+
523+
// Check if there's a failed build - this means the update attempt failed
524+
// so nodes should not be considered "updated"
525+
if mosbState.IsBuildFailure() {
526+
return true
527+
}
528+
529+
// Check if there's a successful build that nodes haven't applied yet
530+
// This happens when a build completes but the MOSC status hasn't been updated
531+
// or nodes haven't picked up the new image yet
532+
if mosbState.IsBuildSuccess() && mosb.Status.DigestedImagePushSpec != "" {
533+
// If the successful build's image differs from what MOSC thinks is current,
534+
// then nodes are not truly updated to the latest successful build
535+
return string(mosb.Status.DigestedImagePushSpec) != string(mosc.Status.CurrentImagePullSpec)
536+
}
537+
538+
return false
539+
}

test/e2e-ocl/onclusterlayering_test.go

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,11 +1448,148 @@ func TestImageBuildDegradedOnFailureAndClearedOnBuildStart(t *testing.T) {
14481448
moscChangeMosb := buildrequest.NewMachineOSBuildFromAPIOrDie(ctx, cs.GetKubeclient(), updated, mcp)
14491449

14501450
// Wait for the second build to start
1451-
waitForBuildToStart(t, cs, moscChangeMosb)
1451+
secondMosb := waitForBuildToStart(t, cs, moscChangeMosb)
1452+
t.Logf("Second build started successfully: %s", secondMosb.Name)
14521453

14531454
// Wait for and verify ImageBuildDegraded condition is False after the new build starts.
1455+
// The condition should be cleared when the build starts.
14541456
degradedCondition = waitForImageBuildDegradedCondition(ctx, t, cs, layeredMCPName, corev1.ConditionFalse)
14551457
require.NotNil(t, degradedCondition, "ImageBuildDegraded condition should still be present")
14561458
assert.Equal(t, string(mcfgv1.MachineConfigPoolBuilding), degradedCondition.Reason, "ImageBuildDegraded reason should be Building")
1457-
t.Logf("ImageBuildDegraded condition correctly cleared to False with message: %s", degradedCondition.Message)
1459+
t.Logf("ImageBuildDegraded condition correctly cleared to False when build started with message: %s", degradedCondition.Message)
1460+
1461+
// Wait for the second build to complete successfully
1462+
finishedBuild := waitForBuildToComplete(t, cs, secondMosb)
1463+
t.Logf("Second build completed successfully: %s", finishedBuild.Name)
1464+
1465+
// Wait for the MachineOSConfig to get the new pullspec, which indicates full reconciliation
1466+
waitForMOSCToGetNewPullspec(ctx, t, cs, mosc.Name, string(finishedBuild.Status.DigestedImagePushSpec))
1467+
1468+
// Wait for and verify ImageBuildDegraded condition is False with reason BuildSucceeded
1469+
degradedCondition = waitForImageBuildDegradedCondition(ctx, t, cs, layeredMCPName, corev1.ConditionFalse)
1470+
require.NotNil(t, degradedCondition, "ImageBuildDegraded condition should still be present")
1471+
assert.Equal(t, string(mcfgv1.MachineConfigPoolBuildSuccess), degradedCondition.Reason, "ImageBuildDegraded reason should be BuildSuccess")
1472+
t.Logf("ImageBuildDegraded condition correctly set to False when build succeeded with message: %s", degradedCondition.Message)
1473+
1474+
// Verify MCP status is correct after successful build and full reconciliation
1475+
successMcp, err := cs.MachineconfigurationV1Interface.MachineConfigPools().Get(ctx, layeredMCPName, metav1.GetOptions{})
1476+
require.NoError(t, err)
1477+
1478+
// After successful build completion and full reconciliation, MCP should show:
1479+
// Updated=True, Updating=False, Degraded=False, ImageBuildDegraded=False
1480+
kubeassert.Eventually().MachineConfigPoolReachesState(successMcp, func(mcp *mcfgv1.MachineConfigPool, err error) (bool, error) {
1481+
if err != nil {
1482+
return false, err
1483+
}
1484+
// Return false (keep polling) if conditions don't match expected state
1485+
if !apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdated) ||
1486+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdating) ||
1487+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolDegraded) ||
1488+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolImageBuildDegraded) {
1489+
return false, nil
1490+
}
1491+
1492+
// Return true when expected state is reached
1493+
t.Logf("MCP status after successful build - Updated: %v, Updating: %v, Degraded: %v, ImageBuildDegraded: %v",
1494+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdated),
1495+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdating),
1496+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolDegraded),
1497+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolImageBuildDegraded))
1498+
1499+
return true, nil
1500+
}, "MCP should reach correct state after successful build (Updated=True, Updating=False, Degraded=False, ImageBuildDegraded=False)")
1501+
1502+
// Now trigger another build to test MCP status transitions when a new build starts
1503+
t.Logf("Triggering a third build to test MCP status transitions")
1504+
1505+
// Modify the containerfile slightly to trigger a new build
1506+
apiMosc, err = cs.MachineconfigurationV1Interface.MachineOSConfigs().Get(ctx, mosc.Name, metav1.GetOptions{})
1507+
require.NoError(t, err)
1508+
1509+
// Add a comment to the containerfile to change it and trigger a new build
1510+
modifiedDockerfile := cowsayDockerfile + "\n# Comment to trigger new build"
1511+
apiMosc.Spec.Containerfile = []mcfgv1.MachineOSContainerfile{
1512+
{
1513+
ContainerfileArch: mcfgv1.NoArch,
1514+
Content: modifiedDockerfile,
1515+
},
1516+
}
1517+
1518+
updated, err = cs.MachineconfigurationV1Interface.MachineOSConfigs().Update(ctx, apiMosc, metav1.UpdateOptions{})
1519+
require.NoError(t, err)
1520+
1521+
t.Logf("Modified containerfile, waiting for third build to start")
1522+
1523+
// Get the updated MCP to compute the new build
1524+
mcp, err = cs.MachineconfigurationV1Interface.MachineConfigPools().Get(ctx, layeredMCPName, metav1.GetOptions{})
1525+
require.NoError(t, err)
1526+
1527+
// Compute the new MachineOSBuild name for the third build
1528+
thirdMoscMosb := buildrequest.NewMachineOSBuildFromAPIOrDie(ctx, cs.GetKubeclient(), updated, mcp)
1529+
1530+
// Wait for the third build to start
1531+
thirdMosb := waitForBuildToStart(t, cs, thirdMoscMosb)
1532+
t.Logf("Third build started: %s", thirdMosb.Name)
1533+
1534+
// Verify MCP status during active build:
1535+
// Updated=False, Updating=True, Degraded=False, ImageBuildDegraded=False
1536+
buildingMcp, err := cs.MachineconfigurationV1Interface.MachineConfigPools().Get(ctx, layeredMCPName, metav1.GetOptions{})
1537+
require.NoError(t, err)
1538+
1539+
kubeassert.Eventually().MachineConfigPoolReachesState(buildingMcp, func(mcp *mcfgv1.MachineConfigPool, err error) (bool, error) {
1540+
if err != nil {
1541+
return false, err
1542+
}
1543+
// During build, MCP should show: Updated=False, Updating=True
1544+
if apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdated) ||
1545+
!apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdating) ||
1546+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolDegraded) ||
1547+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolImageBuildDegraded) {
1548+
return false, nil
1549+
}
1550+
1551+
t.Logf("MCP status during active build - Updated: %v, Updating: %v, Degraded: %v, ImageBuildDegraded: %v",
1552+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdated),
1553+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdating),
1554+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolDegraded),
1555+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolImageBuildDegraded))
1556+
1557+
return true, nil
1558+
}, "MCP should reach correct state during active build (Updated=False, Updating=True, Degraded=False, ImageBuildDegraded=False)")
1559+
1560+
// Wait for the third build to complete successfully
1561+
finalBuild := waitForBuildToComplete(t, cs, thirdMosb)
1562+
t.Logf("Third build completed successfully: %s", finalBuild.Name)
1563+
1564+
// Wait for the MachineOSConfig to get the new pullspec, which indicates full reconciliation
1565+
waitForMOSCToGetNewPullspec(ctx, t, cs, mosc.Name, string(finalBuild.Status.DigestedImagePushSpec))
1566+
1567+
// Final verification: MCP status should return to:
1568+
// Updated=True, Updating=False, Degraded=False, ImageBuildDegraded=False
1569+
finalMcp, err := cs.MachineconfigurationV1Interface.MachineConfigPools().Get(ctx, layeredMCPName, metav1.GetOptions{})
1570+
require.NoError(t, err)
1571+
1572+
kubeassert.Eventually().MachineConfigPoolReachesState(finalMcp, func(mcp *mcfgv1.MachineConfigPool, err error) (bool, error) {
1573+
if err != nil {
1574+
return false, err
1575+
}
1576+
// Return false (keep polling) if conditions don't match expected state
1577+
if !apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdated) ||
1578+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdating) ||
1579+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolDegraded) ||
1580+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolImageBuildDegraded) {
1581+
return false, nil
1582+
}
1583+
1584+
// Return true when expected state is reached
1585+
t.Logf("Final MCP status after third build completion - Updated: %v, Updating: %v, Degraded: %v, ImageBuildDegraded: %v",
1586+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdated),
1587+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolUpdating),
1588+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolDegraded),
1589+
apihelpers.IsMachineConfigPoolConditionTrue(mcp.Status.Conditions, mcfgv1.MachineConfigPoolImageBuildDegraded))
1590+
1591+
return true, nil
1592+
}, "MCP should return to correct state after final build completion (Updated=True, Updating=False, Degraded=False, ImageBuildDegraded=False)")
1593+
1594+
t.Logf("All MCP status transitions verified successfully across build failure, success, and subsequent new build")
14581595
}

0 commit comments

Comments
 (0)