Merge pull request #615 from anmazzotti/prevent_unwanted_rollout
Add e2e test to prevent Machine rollout on provider upgrade
This commit is contained in:
commit
0cf4ca8166
|
|
@ -24,6 +24,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
@ -115,6 +117,24 @@ var _ = Describe("Workload cluster creation", func() {
|
||||||
ControlPlane: client.ObjectKeyFromObject(result.ControlPlane),
|
ControlPlane: client.ObjectKeyFromObject(result.ControlPlane),
|
||||||
}, e2eConfig.GetIntervals(specName, "wait-control-plane")...)
|
}, e2eConfig.GetIntervals(specName, "wait-control-plane")...)
|
||||||
|
|
||||||
|
WaitForClusterReady(ctx, WaitForClusterReadyInput{
|
||||||
|
Getter: bootstrapClusterProxy.GetClient(),
|
||||||
|
Name: result.Cluster.Name,
|
||||||
|
Namespace: result.Cluster.Namespace,
|
||||||
|
}, e2eConfig.GetIntervals(specName, "wait-cluster")...)
|
||||||
|
|
||||||
|
By("Fetching all Machines")
|
||||||
|
machineList := GetMachinesByCluster(ctx, GetMachinesByClusterInput{
|
||||||
|
Lister: bootstrapClusterProxy.GetClient(),
|
||||||
|
ClusterName: result.Cluster.Name,
|
||||||
|
Namespace: result.Cluster.Namespace,
|
||||||
|
})
|
||||||
|
Expect(machineList.Items).ShouldNot(BeEmpty(), "There must be at least one Machine")
|
||||||
|
machinesNames := []string{}
|
||||||
|
for _, machine := range machineList.Items {
|
||||||
|
machinesNames = append(machinesNames, machine.Name)
|
||||||
|
}
|
||||||
|
|
||||||
By("Upgrading to next boostrap/controlplane provider version")
|
By("Upgrading to next boostrap/controlplane provider version")
|
||||||
UpgradeManagementCluster(ctx, clusterctl.UpgradeManagementClusterAndWaitInput{
|
UpgradeManagementCluster(ctx, clusterctl.UpgradeManagementClusterAndWaitInput{
|
||||||
ClusterProxy: bootstrapClusterProxy,
|
ClusterProxy: bootstrapClusterProxy,
|
||||||
|
|
@ -129,6 +149,41 @@ var _ = Describe("Workload cluster creation", func() {
|
||||||
ControlPlane: client.ObjectKeyFromObject(result.ControlPlane),
|
ControlPlane: client.ObjectKeyFromObject(result.ControlPlane),
|
||||||
}, e2eConfig.GetIntervals(specName, "wait-control-plane")...)
|
}, e2eConfig.GetIntervals(specName, "wait-control-plane")...)
|
||||||
|
|
||||||
|
WaitForClusterReady(ctx, WaitForClusterReadyInput{
|
||||||
|
Getter: bootstrapClusterProxy.GetClient(),
|
||||||
|
Name: result.Cluster.Name,
|
||||||
|
Namespace: result.Cluster.Namespace,
|
||||||
|
}, e2eConfig.GetIntervals(specName, "wait-cluster")...)
|
||||||
|
|
||||||
|
By("Verifying machine rollout did not happen")
|
||||||
|
Consistently(func() error {
|
||||||
|
updatedMachineList := GetMachinesByCluster(ctx, GetMachinesByClusterInput{
|
||||||
|
Lister: bootstrapClusterProxy.GetClient(),
|
||||||
|
ClusterName: result.Cluster.Name,
|
||||||
|
Namespace: result.Cluster.Namespace,
|
||||||
|
})
|
||||||
|
if len(updatedMachineList.Items) == 0 {
|
||||||
|
return fmt.Errorf("There must be at least one Machine after provider upgrade")
|
||||||
|
}
|
||||||
|
updatedMachinesNames := []string{}
|
||||||
|
for _, machine := range updatedMachineList.Items {
|
||||||
|
updatedMachinesNames = append(updatedMachinesNames, machine.Name)
|
||||||
|
}
|
||||||
|
sameMachines, err := ContainElements(machinesNames).Match(updatedMachinesNames)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("matching machines: %w", err)
|
||||||
|
}
|
||||||
|
if !sameMachines {
|
||||||
|
fmt.Printf("Pre-upgrade machines: [%s]\n", strings.Join(machinesNames, ","))
|
||||||
|
fmt.Printf("Post-upgrade machines: [%s]\n", strings.Join(updatedMachinesNames, ","))
|
||||||
|
return fmt.Errorf("Machines should not have been rolled out after provider upgrade")
|
||||||
|
}
|
||||||
|
if len(updatedMachinesNames) != len(machinesNames) {
|
||||||
|
return fmt.Errorf("Number of Machines '%d' should match after provider upgrade '%d'", len(machinesNames), len(updatedMachinesNames))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}).WithTimeout(2 * time.Minute).WithPolling(10 * time.Second).Should(Succeed())
|
||||||
|
|
||||||
By("Scaling down control plane to 2 and workers up to 2")
|
By("Scaling down control plane to 2 and workers up to 2")
|
||||||
ApplyClusterTemplateAndWait(ctx, ApplyClusterTemplateAndWaitInput{
|
ApplyClusterTemplateAndWait(ctx, ApplyClusterTemplateAndWaitInput{
|
||||||
ClusterProxy: bootstrapClusterProxy,
|
ClusterProxy: bootstrapClusterProxy,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import (
|
||||||
|
|
||||||
pkgerrors "github.com/pkg/errors"
|
pkgerrors "github.com/pkg/errors"
|
||||||
controlplanev1 "github.com/rancher/cluster-api-provider-rke2/controlplane/api/v1beta1"
|
controlplanev1 "github.com/rancher/cluster-api-provider-rke2/controlplane/api/v1beta1"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
|
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
|
||||||
"sigs.k8s.io/cluster-api/test/framework"
|
"sigs.k8s.io/cluster-api/test/framework"
|
||||||
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
|
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
|
||||||
|
|
@ -305,6 +306,30 @@ func GetRKE2ControlPlaneByCluster(ctx context.Context, input GetRKE2ControlPlane
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMachinesByClusterInput is the input for GetRKE2ControlPlaneByCluster.
|
||||||
|
type GetMachinesByClusterInput struct {
|
||||||
|
Lister framework.Lister
|
||||||
|
ClusterName string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMachinesByCluster returns the Machine objects for a cluster.
|
||||||
|
func GetMachinesByCluster(ctx context.Context, input GetMachinesByClusterInput) *clusterv1.MachineList {
|
||||||
|
opts := []client.ListOption{
|
||||||
|
client.InNamespace(input.Namespace),
|
||||||
|
client.MatchingLabels{
|
||||||
|
clusterv1.ClusterNameLabel: input.ClusterName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
machineList := &clusterv1.MachineList{}
|
||||||
|
Eventually(func() error {
|
||||||
|
return input.Lister.List(ctx, machineList, opts...)
|
||||||
|
}, retryableOperationTimeout, retryableOperationInterval).Should(Succeed(), "Failed to list Machine objects for Cluster %s", klog.KRef(input.Namespace, input.ClusterName))
|
||||||
|
|
||||||
|
return machineList
|
||||||
|
}
|
||||||
|
|
||||||
// WaitForControlPlaneAndMachinesReadyInput is the input type for WaitForControlPlaneAndMachinesReady.
|
// WaitForControlPlaneAndMachinesReadyInput is the input type for WaitForControlPlaneAndMachinesReady.
|
||||||
type WaitForControlPlaneAndMachinesReadyInput struct {
|
type WaitForControlPlaneAndMachinesReadyInput struct {
|
||||||
GetLister framework.GetLister
|
GetLister framework.GetLister
|
||||||
|
|
@ -509,6 +534,42 @@ func WaitForClusterToUpgrade(ctx context.Context, input WaitForClusterToUpgradeI
|
||||||
}, intervals...).Should(Succeed())
|
}, intervals...).Should(Succeed())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WaitForClusterReadyInput is the input type for WaitForClusterReady.
|
||||||
|
type WaitForClusterReadyInput struct {
|
||||||
|
Getter framework.Getter
|
||||||
|
Name string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForClusterReady will wait for a Cluster to be Ready.
|
||||||
|
func WaitForClusterReady(ctx context.Context, input WaitForClusterReadyInput, intervals ...interface{}) {
|
||||||
|
By("Waiting for Cluster to be Ready")
|
||||||
|
|
||||||
|
Eventually(func() error {
|
||||||
|
cluster := &clusterv1.Cluster{}
|
||||||
|
key := types.NamespacedName{Name: input.Name, Namespace: input.Namespace}
|
||||||
|
|
||||||
|
if err := input.Getter.Get(ctx, key, cluster); err != nil {
|
||||||
|
return fmt.Errorf("getting Cluster %s/%s: %w", input.Namespace, input.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
readyCondition := conditions.Get(cluster, clusterv1.ReadyCondition)
|
||||||
|
if readyCondition == nil {
|
||||||
|
return fmt.Errorf("Cluster Ready condition is not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch readyCondition.Status {
|
||||||
|
case corev1.ConditionTrue:
|
||||||
|
//Cluster is ready
|
||||||
|
return nil
|
||||||
|
case corev1.ConditionFalse:
|
||||||
|
return fmt.Errorf("Cluster is not Ready")
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Cluster Ready condition is unknown")
|
||||||
|
}
|
||||||
|
}, intervals...).Should(Succeed())
|
||||||
|
}
|
||||||
|
|
||||||
// setDefaults sets the default values for ApplyCustomClusterTemplateAndWaitInput if not set.
|
// setDefaults sets the default values for ApplyCustomClusterTemplateAndWaitInput if not set.
|
||||||
// Currently, we set the default ControlPlaneWaiters here, which are implemented for RKE2ControlPlane.
|
// Currently, we set the default ControlPlaneWaiters here, which are implemented for RKE2ControlPlane.
|
||||||
func setDefaults(input *ApplyCustomClusterTemplateAndWaitInput) {
|
func setDefaults(input *ApplyCustomClusterTemplateAndWaitInput) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue