Skip to content

Commit b04dbbd

Browse files
authored
Merge pull request #9703 from willie-yao/fix-mp-cc-e2e
🌱 Add MachinePools to Runtime SDK and Rollout tests
2 parents 3b0e99d + c737c7a commit b04dbbd

File tree

5 files changed

+409
-38
lines changed

5 files changed

+409
-38
lines changed

test/e2e/cluster_upgrade.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -208,19 +208,18 @@ func ClusterUpgradeConformanceSpec(ctx context.Context, inputGetter func() Clust
208208
MachineDeployments: clusterResources.MachineDeployments,
209209
WaitForMachinesToBeUpgraded: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
210210
})
211-
}
212-
}
213211

214-
// Only attempt to upgrade MachinePools if they were provided in the template.
215-
if len(clusterResources.MachinePools) > 0 && workerMachineCount > 0 {
216-
By("Upgrading the machinepool instances")
217-
framework.UpgradeMachinePoolAndWait(ctx, framework.UpgradeMachinePoolAndWaitInput{
218-
ClusterProxy: input.BootstrapClusterProxy,
219-
Cluster: clusterResources.Cluster,
220-
UpgradeVersion: input.E2EConfig.GetVariable(KubernetesVersionUpgradeTo),
221-
WaitForMachinePoolToBeUpgraded: input.E2EConfig.GetIntervals(specName, "wait-machine-pool-upgrade"),
222-
MachinePools: clusterResources.MachinePools,
223-
})
212+
if len(clusterResources.MachinePools) > 0 {
213+
By("Upgrading the machinepool instances")
214+
framework.UpgradeMachinePoolAndWait(ctx, framework.UpgradeMachinePoolAndWaitInput{
215+
ClusterProxy: input.BootstrapClusterProxy,
216+
Cluster: clusterResources.Cluster,
217+
UpgradeVersion: input.E2EConfig.GetVariable(KubernetesVersionUpgradeTo),
218+
WaitForMachinePoolToBeUpgraded: input.E2EConfig.GetIntervals(specName, "wait-machine-pool-upgrade"),
219+
MachinePools: clusterResources.MachinePools,
220+
})
221+
}
222+
}
224223
}
225224

226225
By("Waiting until nodes are ready")

test/e2e/cluster_upgrade_runtimesdk.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -247,18 +247,6 @@ func clusterUpgradeWithRuntimeSDKSpec(ctx context.Context, inputGetter func() cl
247247
},
248248
})
249249

250-
// Only attempt to upgrade MachinePools if they were provided in the template.
251-
if len(clusterResources.MachinePools) > 0 && workerMachineCount > 0 {
252-
By("Upgrading the machinepool instances")
253-
framework.UpgradeMachinePoolAndWait(ctx, framework.UpgradeMachinePoolAndWaitInput{
254-
ClusterProxy: input.BootstrapClusterProxy,
255-
Cluster: clusterResources.Cluster,
256-
UpgradeVersion: input.E2EConfig.GetVariable(KubernetesVersionUpgradeTo),
257-
WaitForMachinePoolToBeUpgraded: input.E2EConfig.GetIntervals(specName, "wait-machine-pool-upgrade"),
258-
MachinePools: clusterResources.MachinePools,
259-
})
260-
}
261-
262250
By("Waiting until nodes are ready")
263251
workloadProxy := input.BootstrapClusterProxy.GetWorkloadCluster(ctx, namespace.Name, clusterResources.Cluster.Name)
264252
workloadClient := workloadProxy.GetClient()

test/e2e/clusterclass_changes.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434

3535
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
3636
"sigs.k8s.io/cluster-api/controllers/external"
37+
expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
3738
"sigs.k8s.io/cluster-api/internal/contract"
3839
"sigs.k8s.io/cluster-api/test/e2e/internal/log"
3940
"sigs.k8s.io/cluster-api/test/framework"
@@ -426,6 +427,131 @@ func modifyMachineDeploymentViaClusterClassAndWait(ctx context.Context, input mo
426427
}
427428
}
428429

430+
// modifyMachinePoolViaClusterClassAndWaitInput is the input type for modifyMachinePoolViaClusterClassAndWait.
431+
type modifyMachinePoolViaClusterClassAndWaitInput struct {
432+
ClusterProxy framework.ClusterProxy
433+
ClusterClass *clusterv1.ClusterClass
434+
Cluster *clusterv1.Cluster
435+
ModifyBootstrapConfigTemplateFields map[string]interface{}
436+
ModifyInfrastructureMachinePoolTemplateFields map[string]interface{}
437+
WaitForMachinePools []interface{}
438+
}
439+
440+
// modifyMachinePoolViaClusterClassAndWait modifies the BootstrapConfigTemplate of MachinePoolClasses of a ClusterClass
441+
// by setting ModifyBootstrapConfigTemplateFields and waits until the changes are rolled out to the MachinePools of the Cluster.
442+
// NOTE: This helper is really specific to this test, so we are keeping this private vs. adding it to the framework.
443+
func modifyMachinePoolViaClusterClassAndWait(ctx context.Context, input modifyMachinePoolViaClusterClassAndWaitInput) {
444+
Expect(ctx).NotTo(BeNil(), "ctx is required for modifyMachinePoolViaClusterClassAndWait")
445+
Expect(input.ClusterProxy).ToNot(BeNil(), "Invalid argument. input.ClusterProxy can't be nil when calling modifyMachinePoolViaClusterClassAndWait")
446+
Expect(input.ClusterClass).ToNot(BeNil(), "Invalid argument. input.ClusterClass can't be nil when calling modifyMachinePoolViaClusterClassAndWait")
447+
Expect(input.Cluster).ToNot(BeNil(), "Invalid argument. input.Cluster can't be nil when calling modifyMachinePoolViaClusterClassAndWait")
448+
449+
mgmtClient := input.ClusterProxy.GetClient()
450+
451+
for _, mpClass := range input.ClusterClass.Spec.Workers.MachinePools {
452+
// Only try to modify the BootstrapConfigTemplate if the MachinePoolClass is using a BootstrapConfigTemplate.
453+
var bootstrapConfigTemplateRef *corev1.ObjectReference
454+
var newBootstrapConfigTemplateName string
455+
if mpClass.Template.Bootstrap.Ref != nil {
456+
log.Logf("Modifying the BootstrapConfigTemplate of MachinePoolClass %q of ClusterClass %s", mpClass.Class, klog.KObj(input.ClusterClass))
457+
458+
// Retrieve BootstrapConfigTemplate object.
459+
bootstrapConfigTemplateRef = mpClass.Template.Bootstrap.Ref
460+
bootstrapConfigTemplate, err := external.Get(ctx, mgmtClient, bootstrapConfigTemplateRef, input.Cluster.Namespace)
461+
Expect(err).ToNot(HaveOccurred())
462+
// Create a new BootstrapConfigTemplate object with a new name and ModifyBootstrapConfigTemplateFields set.
463+
newBootstrapConfigTemplate := bootstrapConfigTemplate.DeepCopy()
464+
newBootstrapConfigTemplateName = fmt.Sprintf("%s-%s", bootstrapConfigTemplateRef.Name, util.RandomString(6))
465+
newBootstrapConfigTemplate.SetName(newBootstrapConfigTemplateName)
466+
newBootstrapConfigTemplate.SetResourceVersion("")
467+
for fieldPath, value := range input.ModifyBootstrapConfigTemplateFields {
468+
Expect(unstructured.SetNestedField(newBootstrapConfigTemplate.Object, value, strings.Split(fieldPath, ".")...)).To(Succeed())
469+
}
470+
Expect(mgmtClient.Create(ctx, newBootstrapConfigTemplate)).To(Succeed())
471+
}
472+
473+
log.Logf("Modifying the InfrastructureMachinePoolTemplate of MachinePoolClass %q of ClusterClass %s", mpClass.Class, klog.KObj(input.ClusterClass))
474+
475+
// Retrieve InfrastructureMachineTemplate object.
476+
infrastructureMachinePoolTemplateRef := mpClass.Template.Infrastructure.Ref
477+
infrastructureMachinePoolTemplate, err := external.Get(ctx, mgmtClient, infrastructureMachinePoolTemplateRef, input.Cluster.Namespace)
478+
Expect(err).ToNot(HaveOccurred())
479+
// Create a new InfrastructureMachinePoolTemplate object with a new name and ModifyInfrastructureMachinePoolTemplateFields set.
480+
newInfrastructureMachinePoolTemplate := infrastructureMachinePoolTemplate.DeepCopy()
481+
newInfrastructureMachinePoolTemplateName := fmt.Sprintf("%s-%s", infrastructureMachinePoolTemplateRef.Name, util.RandomString(6))
482+
newInfrastructureMachinePoolTemplate.SetName(newInfrastructureMachinePoolTemplateName)
483+
newInfrastructureMachinePoolTemplate.SetResourceVersion("")
484+
for fieldPath, value := range input.ModifyInfrastructureMachinePoolTemplateFields {
485+
Expect(unstructured.SetNestedField(newInfrastructureMachinePoolTemplate.Object, value, strings.Split(fieldPath, ".")...)).To(Succeed())
486+
}
487+
Expect(mgmtClient.Create(ctx, newInfrastructureMachinePoolTemplate)).To(Succeed())
488+
489+
// Patch the refs of the MachinePoolClass to reference the new templates.
490+
patchHelper, err := patch.NewHelper(input.ClusterClass, mgmtClient)
491+
Expect(err).ToNot(HaveOccurred())
492+
if mpClass.Template.Bootstrap.Ref != nil {
493+
bootstrapConfigTemplateRef.Name = newBootstrapConfigTemplateName
494+
}
495+
infrastructureMachinePoolTemplateRef.Name = newInfrastructureMachinePoolTemplateName
496+
Expect(patchHelper.Patch(ctx, input.ClusterClass)).To(Succeed())
497+
498+
log.Logf("Waiting for MachinePool rollout for MachinePoolClass %q to complete.", mpClass.Class)
499+
for _, mpTopology := range input.Cluster.Spec.Topology.Workers.MachinePools {
500+
// Continue if the MachinePoolTopology belongs to another MachinePoolClass.
501+
if mpTopology.Class != mpClass.Class {
502+
continue
503+
}
504+
505+
// NOTE: We only wait until the change is rolled out to the MachinePool objects and not to the worker machines
506+
// to speed up the test and focus the test on the ClusterClass feature.
507+
log.Logf("Waiting for MachinePool rollout for MachinePoolTopology %q (class %q) to complete.", mpTopology.Name, mpTopology.Class)
508+
Eventually(func(g Gomega) error {
509+
// Get MachinePool for the current MachinePoolTopology.
510+
mpList := &expv1.MachinePoolList{}
511+
g.Expect(mgmtClient.List(ctx, mpList, client.InNamespace(input.Cluster.Namespace), client.MatchingLabels{
512+
clusterv1.ClusterTopologyMachinePoolNameLabel: mpTopology.Name,
513+
})).To(Succeed())
514+
g.Expect(mpList.Items).To(HaveLen(1), fmt.Sprintf("expected one MachinePool for topology %q, but got %d", mpTopology.Name, len(mpList.Items)))
515+
mp := mpList.Items[0]
516+
517+
// Verify that the fields from Cluster topology are set on the MachinePool.
518+
assertMachinePoolTopologyFields(g, mp, mpTopology)
519+
520+
if mpClass.Template.Bootstrap.Ref != nil {
521+
// Get the corresponding BootstrapConfig object.
522+
bootstrapConfigObjectRef := mp.Spec.Template.Spec.Bootstrap.ConfigRef
523+
bootstrapConfigObject, err := external.Get(ctx, mgmtClient, bootstrapConfigObjectRef, input.Cluster.Namespace)
524+
g.Expect(err).ToNot(HaveOccurred())
525+
526+
// Verify that ModifyBootstrapConfigTemplateFields have been set and propagates to the BootstrapConfig.
527+
for fieldPath, expectedValue := range input.ModifyBootstrapConfigTemplateFields {
528+
// MachinePools have a BootstrapConfig, not a BootstrapConfigTemplate, so we need to convert the fieldPath so it can find it on the object.
529+
fieldPath = strings.TrimPrefix(fieldPath, "spec.template.")
530+
currentValue, ok, err := unstructured.NestedFieldNoCopy(bootstrapConfigObject.Object, strings.Split(fieldPath, ".")...)
531+
g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get field %q", fieldPath))
532+
g.Expect(ok).To(BeTrue(), fmt.Sprintf("failed to get field %q", fieldPath))
533+
g.Expect(currentValue).To(Equal(expectedValue), fmt.Sprintf("field %q should be equal", fieldPath))
534+
}
535+
}
536+
537+
// Get the corresponding InfrastructureMachinePoolTemplate.
538+
infrastructureMachinePoolTemplateRef := mp.Spec.Template.Spec.InfrastructureRef
539+
infrastructureMachinePoolTemplate, err := external.Get(ctx, mgmtClient, &infrastructureMachinePoolTemplateRef, input.Cluster.Namespace)
540+
g.Expect(err).ToNot(HaveOccurred())
541+
542+
// Verify that ModifyInfrastructureMachinePoolTemplateFields have been set.
543+
for fieldPath, expectedValue := range input.ModifyInfrastructureMachinePoolTemplateFields {
544+
currentValue, ok, err := unstructured.NestedFieldNoCopy(infrastructureMachinePoolTemplate.Object, strings.Split(fieldPath, ".")...)
545+
g.Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("failed to get field %q", fieldPath))
546+
g.Expect(ok).To(BeTrue(), fmt.Sprintf("failed to get field %q", fieldPath))
547+
g.Expect(currentValue).To(Equal(expectedValue), fmt.Sprintf("field %q should be equal", fieldPath))
548+
}
549+
return nil
550+
}, input.WaitForMachinePools...).Should(BeNil())
551+
}
552+
}
553+
}
554+
429555
// assertMachineDeploymentTopologyFields asserts that all fields set in the MachineDeploymentTopology have been set on the MachineDeployment.
430556
// Note: We intentionally focus on the fields set in the MachineDeploymentTopology and ignore the ones set through ClusterClass
431557
// as we want to validate that the fields of the MachineDeploymentTopology have been propagated correctly.
@@ -464,6 +590,40 @@ func assertMachineDeploymentTopologyFields(g Gomega, md clusterv1.MachineDeploym
464590
}
465591
}
466592

593+
// assertMachinePoolTopologyFields asserts that all fields set in the MachinePoolTopology have been set on the MachinePool.
594+
// Note: We intentionally focus on the fields set in the MachinePoolTopology and ignore the ones set through ClusterClass
595+
// as we want to validate that the fields of the MachinePoolTopology have been propagated correctly.
596+
func assertMachinePoolTopologyFields(g Gomega, mp expv1.MachinePool, mpTopology clusterv1.MachinePoolTopology) {
597+
// Note: We only verify that all labels and annotations from the Cluster topology exist to keep it simple here.
598+
// This is fully covered by the ClusterClass rollout test.
599+
for k, v := range mpTopology.Metadata.Labels {
600+
g.Expect(mp.Labels).To(HaveKeyWithValue(k, v))
601+
}
602+
for k, v := range mpTopology.Metadata.Annotations {
603+
g.Expect(mp.Annotations).To(HaveKeyWithValue(k, v))
604+
}
605+
606+
if mpTopology.NodeDrainTimeout != nil {
607+
g.Expect(mp.Spec.Template.Spec.NodeDrainTimeout).To(Equal(mpTopology.NodeDrainTimeout))
608+
}
609+
610+
if mpTopology.NodeDeletionTimeout != nil {
611+
g.Expect(mp.Spec.Template.Spec.NodeDeletionTimeout).To(Equal(mpTopology.NodeDeletionTimeout))
612+
}
613+
614+
if mpTopology.NodeVolumeDetachTimeout != nil {
615+
g.Expect(mp.Spec.Template.Spec.NodeVolumeDetachTimeout).To(Equal(mpTopology.NodeVolumeDetachTimeout))
616+
}
617+
618+
if mpTopology.MinReadySeconds != nil {
619+
g.Expect(mp.Spec.MinReadySeconds).To(Equal(mpTopology.MinReadySeconds))
620+
}
621+
622+
if mpTopology.FailureDomains != nil && mp.Spec.Template.Spec.FailureDomain != nil {
623+
g.Expect(mpTopology.FailureDomains).To(ContainElement(mp.Spec.Template.Spec.FailureDomain))
624+
}
625+
}
626+
467627
// rebaseClusterClassAndWaitInput is the input type for rebaseClusterClassAndWait.
468628
type rebaseClusterClassAndWaitInput struct {
469629
ClusterProxy framework.ClusterProxy

0 commit comments

Comments
 (0)