Skip to content

Commit dec36b5

Browse files
Merge pull request #615 from anmazzotti/prevent_unwanted_rollout
Add e2e test to prevent Machine rollout on provider upgrade
2 parents fbf6bf4 + 4ebc27a commit dec36b5

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

test/e2e/e2e_upgrade_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424
"fmt"
2525
"os"
2626
"path/filepath"
27+
"strings"
28+
"time"
2729

2830
. "github.com/onsi/ginkgo/v2"
2931
. "github.com/onsi/gomega"
@@ -115,6 +117,24 @@ var _ = Describe("Workload cluster creation", func() {
115117
ControlPlane: client.ObjectKeyFromObject(result.ControlPlane),
116118
}, e2eConfig.GetIntervals(specName, "wait-control-plane")...)
117119

120+
WaitForClusterReady(ctx, WaitForClusterReadyInput{
121+
Getter: bootstrapClusterProxy.GetClient(),
122+
Name: result.Cluster.Name,
123+
Namespace: result.Cluster.Namespace,
124+
}, e2eConfig.GetIntervals(specName, "wait-cluster")...)
125+
126+
By("Fetching all Machines")
127+
machineList := GetMachinesByCluster(ctx, GetMachinesByClusterInput{
128+
Lister: bootstrapClusterProxy.GetClient(),
129+
ClusterName: result.Cluster.Name,
130+
Namespace: result.Cluster.Namespace,
131+
})
132+
Expect(machineList.Items).ShouldNot(BeEmpty(), "There must be at least one Machine")
133+
machinesNames := []string{}
134+
for _, machine := range machineList.Items {
135+
machinesNames = append(machinesNames, machine.Name)
136+
}
137+
118138
By("Upgrading to next boostrap/controlplane provider version")
119139
UpgradeManagementCluster(ctx, clusterctl.UpgradeManagementClusterAndWaitInput{
120140
ClusterProxy: bootstrapClusterProxy,
@@ -129,6 +149,41 @@ var _ = Describe("Workload cluster creation", func() {
129149
ControlPlane: client.ObjectKeyFromObject(result.ControlPlane),
130150
}, e2eConfig.GetIntervals(specName, "wait-control-plane")...)
131151

152+
WaitForClusterReady(ctx, WaitForClusterReadyInput{
153+
Getter: bootstrapClusterProxy.GetClient(),
154+
Name: result.Cluster.Name,
155+
Namespace: result.Cluster.Namespace,
156+
}, e2eConfig.GetIntervals(specName, "wait-cluster")...)
157+
158+
By("Verifying machine rollout did not happen")
159+
Consistently(func() error {
160+
updatedMachineList := GetMachinesByCluster(ctx, GetMachinesByClusterInput{
161+
Lister: bootstrapClusterProxy.GetClient(),
162+
ClusterName: result.Cluster.Name,
163+
Namespace: result.Cluster.Namespace,
164+
})
165+
if len(updatedMachineList.Items) == 0 {
166+
return fmt.Errorf("There must be at least one Machine after provider upgrade")
167+
}
168+
updatedMachinesNames := []string{}
169+
for _, machine := range updatedMachineList.Items {
170+
updatedMachinesNames = append(updatedMachinesNames, machine.Name)
171+
}
172+
sameMachines, err := ContainElements(machinesNames).Match(updatedMachinesNames)
173+
if err != nil {
174+
return fmt.Errorf("matching machines: %w", err)
175+
}
176+
if !sameMachines {
177+
fmt.Printf("Pre-upgrade machines: [%s]\n", strings.Join(machinesNames, ","))
178+
fmt.Printf("Post-upgrade machines: [%s]\n", strings.Join(updatedMachinesNames, ","))
179+
return fmt.Errorf("Machines should not have been rolled out after provider upgrade")
180+
}
181+
if len(updatedMachinesNames) != len(machinesNames) {
182+
return fmt.Errorf("Number of Machines '%d' should match after provider upgrade '%d'", len(machinesNames), len(updatedMachinesNames))
183+
}
184+
return nil
185+
}).WithTimeout(2 * time.Minute).WithPolling(10 * time.Second).Should(Succeed())
186+
132187
By("Scaling down control plane to 2 and workers up to 2")
133188
ApplyClusterTemplateAndWait(ctx, ApplyClusterTemplateAndWaitInput{
134189
ClusterProxy: bootstrapClusterProxy,

test/e2e/helpers.go

+61
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636

3737
pkgerrors "github.com/pkg/errors"
3838
controlplanev1 "github.com/rancher/cluster-api-provider-rke2/controlplane/api/v1beta1"
39+
corev1 "k8s.io/api/core/v1"
3940
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
4041
"sigs.k8s.io/cluster-api/test/framework"
4142
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
@@ -305,6 +306,30 @@ func GetRKE2ControlPlaneByCluster(ctx context.Context, input GetRKE2ControlPlane
305306
return nil
306307
}
307308

309+
// GetMachinesByClusterInput is the input for GetRKE2ControlPlaneByCluster.
310+
type GetMachinesByClusterInput struct {
311+
Lister framework.Lister
312+
ClusterName string
313+
Namespace string
314+
}
315+
316+
// GetMachinesByCluster returns the Machine objects for a cluster.
317+
func GetMachinesByCluster(ctx context.Context, input GetMachinesByClusterInput) *clusterv1.MachineList {
318+
opts := []client.ListOption{
319+
client.InNamespace(input.Namespace),
320+
client.MatchingLabels{
321+
clusterv1.ClusterNameLabel: input.ClusterName,
322+
},
323+
}
324+
325+
machineList := &clusterv1.MachineList{}
326+
Eventually(func() error {
327+
return input.Lister.List(ctx, machineList, opts...)
328+
}, retryableOperationTimeout, retryableOperationInterval).Should(Succeed(), "Failed to list Machine objects for Cluster %s", klog.KRef(input.Namespace, input.ClusterName))
329+
330+
return machineList
331+
}
332+
308333
// WaitForControlPlaneAndMachinesReadyInput is the input type for WaitForControlPlaneAndMachinesReady.
309334
type WaitForControlPlaneAndMachinesReadyInput struct {
310335
GetLister framework.GetLister
@@ -509,6 +534,42 @@ func WaitForClusterToUpgrade(ctx context.Context, input WaitForClusterToUpgradeI
509534
}, intervals...).Should(Succeed())
510535
}
511536

537+
// WaitForClusterReadyInput is the input type for WaitForClusterReady.
538+
type WaitForClusterReadyInput struct {
539+
Getter framework.Getter
540+
Name string
541+
Namespace string
542+
}
543+
544+
// WaitForClusterReady will wait for a Cluster to be Ready.
545+
func WaitForClusterReady(ctx context.Context, input WaitForClusterReadyInput, intervals ...interface{}) {
546+
By("Waiting for Cluster to be Ready")
547+
548+
Eventually(func() error {
549+
cluster := &clusterv1.Cluster{}
550+
key := types.NamespacedName{Name: input.Name, Namespace: input.Namespace}
551+
552+
if err := input.Getter.Get(ctx, key, cluster); err != nil {
553+
return fmt.Errorf("getting Cluster %s/%s: %w", input.Namespace, input.Name, err)
554+
}
555+
556+
readyCondition := conditions.Get(cluster, clusterv1.ReadyCondition)
557+
if readyCondition == nil {
558+
return fmt.Errorf("Cluster Ready condition is not found")
559+
}
560+
561+
switch readyCondition.Status {
562+
case corev1.ConditionTrue:
563+
//Cluster is ready
564+
return nil
565+
case corev1.ConditionFalse:
566+
return fmt.Errorf("Cluster is not Ready")
567+
default:
568+
return fmt.Errorf("Cluster Ready condition is unknown")
569+
}
570+
}, intervals...).Should(Succeed())
571+
}
572+
512573
// setDefaults sets the default values for ApplyCustomClusterTemplateAndWaitInput if not set.
513574
// Currently, we set the default ControlPlaneWaiters here, which are implemented for RKE2ControlPlane.
514575
func setDefaults(input *ApplyCustomClusterTemplateAndWaitInput) {

0 commit comments

Comments
 (0)