Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions test/e2e/control_plane_upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func TestUpgradeControlPlane(t *testing.T) {
// Sanity check the cluster by waiting for the nodes to report ready
guestClient := e2eutil.WaitForGuestClient(t, ctx, mgtClient, hostedCluster)

var startingVersion string
if len(hostedCluster.Status.Version.History) > 0 {
startingVersion = hostedCluster.Status.Version.History[0].Version
}

Comment on lines +37 to +41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential nil-pointer dereference on HostedCluster.Status.Version

Status.Version is a pointer; if nil, accessing History panics. Guard it like elsewhere (e.g., WaitForImageRollout).

-		var startingVersion string
-		if len(hostedCluster.Status.Version.History) > 0 {
-			startingVersion = hostedCluster.Status.Version.History[0].Version
-		}
+		var startingVersion string
+		if hostedCluster.Status.Version != nil && len(hostedCluster.Status.Version.History) > 0 {
+			startingVersion = hostedCluster.Status.Version.History[0].Version
+		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var startingVersion string
if len(hostedCluster.Status.Version.History) > 0 {
startingVersion = hostedCluster.Status.Version.History[0].Version
}
var startingVersion string
if hostedCluster.Status.Version != nil && len(hostedCluster.Status.Version.History) > 0 {
startingVersion = hostedCluster.Status.Version.History[0].Version
}
🤖 Prompt for AI Agents
In test/e2e/control_plane_upgrade_test.go around lines 37 to 41, the code
accesses hostedCluster.Status.Version.History without checking if Status.Version
is nil which can cause a nil-pointer panic; update the logic to first check if
hostedCluster.Status.Version != nil and only then inspect Version.History (and
its length) to set startingVersion, otherwise leave startingVersion empty or
handle the absent version path the same way other tests do (e.g.,
WaitForImageRollout) to avoid dereferencing a nil pointer.

// Set the semantic version to the latest release image for version gating tests
err := e2eutil.SetReleaseImageVersion(testContext, globalOpts.LatestReleaseImage, globalOpts.ConfigurableClusterOptions.PullSecretFile)
if err != nil {
Expand All @@ -54,6 +59,11 @@ func TestUpgradeControlPlane(t *testing.T) {
})
g.Expect(err).NotTo(HaveOccurred(), "failed update hostedcluster image")

t.Run("Wait for control plane components to complete rollout", func(t *testing.T) {
e2eutil.AtLeast(t, e2eutil.Version420)
e2eutil.WaitForControlPlaneComponentRollout(t, ctx, mgtClient, hostedCluster, startingVersion)
})

// Wait for the new rollout to be complete
e2eutil.WaitForImageRollout(t, ctx, mgtClient, hostedCluster)
err = mgtClient.Get(ctx, crclient.ObjectKeyFromObject(hostedCluster), hostedCluster)
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/util/eventually.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,8 @@ func Conditions(item client.Object) ([]Condition, error) {
return adaptConditions(obj.Status.Conditions), nil
case *certificatesv1alpha1.CertificateRevocationRequest:
return adaptConditions(obj.Status.Conditions), nil
case *hyperv1.ControlPlaneComponent:
return adaptConditions(obj.Status.Conditions), nil
case *certificatesv1.CertificateSigningRequest:
conditions := make([]Condition, len(obj.Status.Conditions))
for i := range obj.Status.Conditions {
Expand Down
34 changes: 34 additions & 0 deletions test/e2e/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,40 @@ func WaitForImageRollout(t *testing.T, ctx context.Context, client crclient.Clie
)
}

func WaitForControlPlaneComponentRollout(t *testing.T, ctx context.Context, client crclient.Client, hostedCluster *hyperv1.HostedCluster, initialVersion string) {
controlPlaneComponents := &hyperv1.ControlPlaneComponentList{}
controlPlaneNamespace := manifests.HostedControlPlaneNamespace(hostedCluster.Namespace, hostedCluster.Name)
EventuallyObjects(t, ctx, "control plane components to complete rollout",
func(ctx context.Context) ([]*hyperv1.ControlPlaneComponent, error) {
err := client.List(ctx, controlPlaneComponents, crclient.InNamespace(controlPlaneNamespace))
items := make([]*hyperv1.ControlPlaneComponent, len(controlPlaneComponents.Items))
for i := range controlPlaneComponents.Items {
items[i] = &controlPlaneComponents.Items[i]
}
return items, err
},
[]Predicate[[]*hyperv1.ControlPlaneComponent]{
func(cpComponents []*hyperv1.ControlPlaneComponent) (done bool, reasons string, err error) {
return len(cpComponents) > 10, "expecting more than 10 control plane components", nil
},
},
[]Predicate[*hyperv1.ControlPlaneComponent]{
ConditionPredicate[*hyperv1.ControlPlaneComponent](Condition{
Type: string(hyperv1.ControlPlaneComponentRolloutComplete),
Copy link
Member

@enxebre enxebre Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw a component can only be at version if roll out has completed for it.
we'll want to check controlPlaneUptoda once this merges #6300

Status: metav1.ConditionTrue,
}),
func(cpComponent *hyperv1.ControlPlaneComponent) (done bool, reasons string, err error) {
if initialVersion != "" && cpComponent.Status.Version == initialVersion {
return false, fmt.Sprintf("component %s is still on version %s", cpComponent.Name, cpComponent.Status.Version), nil
}
return true, fmt.Sprintf("component %s has version: %s", cpComponent.Name, cpComponent.Status.Version), nil
},
},
WithTimeout(30*time.Minute),
WithInterval(10*time.Second),
)
}

func WaitForConditionsOnHostedControlPlane(t *testing.T, ctx context.Context, client crclient.Client, hostedCluster *hyperv1.HostedCluster, image string) {
var predicates []Predicate[*hyperv1.HostedControlPlane]
for _, conditionType := range []hyperv1.ConditionType{
Expand Down