Skip to content

Commit 151d33e

Browse files
committed
autoscaling: try to gather the version information from MachineSets and e2e test improvements
1 parent cf0fed0 commit 151d33e

File tree

3 files changed

+81
-31
lines changed

3 files changed

+81
-31
lines changed

config/rbac/role.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ rules:
8181
- clusters/status
8282
- machinedeployments
8383
- machines/status
84+
- machinesets
8485
verbs:
8586
- get
8687
- list

controllers/awsmachinetemplate_controller.go

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ import (
3131
"k8s.io/apimachinery/pkg/runtime/schema"
3232
"k8s.io/utils/ptr"
3333
ctrl "sigs.k8s.io/controller-runtime"
34+
"sigs.k8s.io/controller-runtime/pkg/builder"
3435
"sigs.k8s.io/controller-runtime/pkg/client"
3536
"sigs.k8s.io/controller-runtime/pkg/controller"
37+
"sigs.k8s.io/controller-runtime/pkg/event"
3638
"sigs.k8s.io/controller-runtime/pkg/handler"
39+
"sigs.k8s.io/controller-runtime/pkg/predicate"
3740

3841
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
3942
ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/eks/api/v1beta2"
@@ -75,12 +78,38 @@ func (r *AWSMachineTemplateReconciler) SetupWithManager(ctx context.Context, mgr
7578
Watches(
7679
&clusterv1.MachineDeployment{},
7780
handler.EnqueueRequestsFromMapFunc(r.machineDeploymentToAWSMachineTemplate),
81+
// Only emit events for creation to reconcile in case the MachineDeployment got created after the AWSMachineTemplate was reconciled.
82+
builder.WithPredicates(predicate.Funcs{
83+
CreateFunc: func(e event.CreateEvent) bool { return true },
84+
UpdateFunc: func(e event.UpdateEvent) bool { return false },
85+
DeleteFunc: func(e event.DeleteEvent) bool { return false },
86+
GenericFunc: func(e event.GenericEvent) bool { return true },
87+
}),
88+
).
89+
Watches(
90+
&clusterv1.MachineSet{},
91+
handler.EnqueueRequestsFromMapFunc(r.machineSetToAWSMachineTemplate),
92+
// Only emit events for creation to reconcile in case the MachineDeployment got created after the AWSMachineTemplate was reconciled.
93+
builder.WithPredicates(predicate.Funcs{
94+
CreateFunc: func(e event.CreateEvent) bool { return true },
95+
UpdateFunc: func(e event.UpdateEvent) bool { return false },
96+
DeleteFunc: func(e event.DeleteEvent) bool { return false },
97+
GenericFunc: func(e event.GenericEvent) bool { return true },
98+
}),
7899
)
79100

80-
// Optionally watch KubeadmControlPlane if the CRD exists
101+
// Watch KubeadmControlPlane if they exist.
81102
if _, err := mgr.GetRESTMapper().RESTMapping(schema.GroupKind{Group: controlplanev1.GroupVersion.Group, Kind: "KubeadmControlPlane"}, controlplanev1.GroupVersion.Version); err == nil {
82103
b = b.Watches(&controlplanev1.KubeadmControlPlane{},
83-
handler.EnqueueRequestsFromMapFunc(r.kubeadmControlPlaneToAWSMachineTemplate))
104+
handler.EnqueueRequestsFromMapFunc(r.kubeadmControlPlaneToAWSMachineTemplate),
105+
// Only emit events for creation to reconcile in case the KubeadmControlPlane got created after the AWSMachineTemplate was reconciled.
106+
builder.WithPredicates(predicate.Funcs{
107+
CreateFunc: func(e event.CreateEvent) bool { return true },
108+
UpdateFunc: func(e event.UpdateEvent) bool { return false },
109+
DeleteFunc: func(e event.DeleteEvent) bool { return false },
110+
GenericFunc: func(e event.GenericEvent) bool { return true },
111+
}),
112+
)
84113
}
85114

86115
_, err := b.Build(r)
@@ -95,7 +124,7 @@ func (r *AWSMachineTemplateReconciler) SetupWithManager(ctx context.Context, mgr
95124
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmachinetemplates/status,verbs=get;update;patch
96125
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsclusters,verbs=get;list;watch
97126
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters,verbs=get;list;watch
98-
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinedeployments,verbs=get;list;watch
127+
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinedeployments;machinesets,verbs=get;list;watch
99128
// +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch
100129

101130
// Reconcile populates capacity information for AWSMachineTemplate.
@@ -387,7 +416,22 @@ func (r *AWSMachineTemplateReconciler) getKubernetesVersion(ctx context.Context,
387416
return "", errors.Wrap(err, "failed to get parent list options")
388417
}
389418

390-
// Try to find version from MachineDeployment first
419+
// Try to find version from MachineSet first
420+
machineSetList := &clusterv1.MachineSetList{}
421+
if err := r.List(ctx, machineSetList, listOpts...); err != nil {
422+
return "", errors.Wrap(err, "failed to list MachineSets")
423+
}
424+
425+
// Find MachineDeployments that reference this AWSMachineTemplate
426+
for _, ms := range machineSetList.Items {
427+
if ms.Spec.Template.Spec.InfrastructureRef.Kind == awsMachineTemplateKind &&
428+
ms.Spec.Template.Spec.InfrastructureRef.Name == template.Name &&
429+
ms.Spec.Template.Spec.Version != "" {
430+
return ms.Spec.Template.Spec.Version, nil
431+
}
432+
}
433+
434+
// If not found, try MachineDeployment.
391435
machineDeploymentList := &clusterv1.MachineDeploymentList{}
392436
if err := r.List(ctx, machineDeploymentList, listOpts...); err != nil {
393437
return "", errors.Wrap(err, "failed to list MachineDeployments")
@@ -402,7 +446,7 @@ func (r *AWSMachineTemplateReconciler) getKubernetesVersion(ctx context.Context,
402446
}
403447
}
404448

405-
// If not found in MachineDeployment, try KubeadmControlPlane
449+
// If not found, try KubeadmControlPlane
406450
kcpList := &controlplanev1.KubeadmControlPlaneList{}
407451
if err := r.List(ctx, kcpList, listOpts...); err != nil {
408452
return "", errors.Wrap(err, "failed to list KubeadmControlPlanes")
@@ -492,3 +536,28 @@ func (r *AWSMachineTemplateReconciler) machineDeploymentToAWSMachineTemplate(ctx
492536
},
493537
}
494538
}
539+
540+
// machineSetToAWSMachineTemplate maps MachineSet to AWSMachineTemplate reconcile requests.
541+
// This enables the controller to reconcile AWSMachineTemplate when its owner MachineSet is created or updated,
542+
// ensuring that nodeInfo can be populated even if the cache hasn't synced yet.
543+
func (r *AWSMachineTemplateReconciler) machineSetToAWSMachineTemplate(ctx context.Context, o client.Object) []ctrl.Request {
544+
md, ok := o.(*clusterv1.MachineSet)
545+
if !ok {
546+
return nil
547+
}
548+
549+
// Check if it references an AWSMachineTemplate
550+
if md.Spec.Template.Spec.InfrastructureRef.Kind != awsMachineTemplateKind {
551+
return nil
552+
}
553+
554+
// Return reconcile request for the referenced AWSMachineTemplate
555+
return []ctrl.Request{
556+
{
557+
NamespacedName: client.ObjectKey{
558+
Namespace: md.Namespace,
559+
Name: md.Spec.Template.Spec.InfrastructureRef.Name,
560+
},
561+
},
562+
}
563+
}

test/e2e/suites/unmanaged/unmanaged_functional_test.go

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ var _ = ginkgo.Context("[unmanaged] [functional]", func() {
354354
Expect(len(workerMachines)).To(Equal(1))
355355
Expect(len(controlPlaneMachines)).To(Equal(1))
356356

357-
ginkgo.By("Verifying AWSMachineTemplate capacity is populated for autoscaling from zero")
357+
ginkgo.By("Verifying AWSMachineTemplate capacity and nodeInfo is populated for autoscaling from zero")
358358
Eventually(func(g Gomega) {
359359
awsMachineTemplateList := &infrav1.AWSMachineTemplateList{}
360360
g.Expect(e2eCtx.Environment.BootstrapClusterProxy.GetClient().List(ctx, awsMachineTemplateList, client.InNamespace(namespace.Name))).To(Succeed())
@@ -364,32 +364,12 @@ var _ = ginkgo.Context("[unmanaged] [functional]", func() {
364364
capacity := template.Status.Capacity
365365
_, hasCPU := capacity[corev1.ResourceCPU]
366366
_, hasMemory := capacity[corev1.ResourceMemory]
367-
if hasCPU && hasMemory {
368-
ginkgo.By(fmt.Sprintf("AWSMachineTemplate %s has capacity populated: %v", template.Name, capacity))
369-
return
370-
}
367+
g.Expect(hasCPU).To(BeTrue(), "Expected AWSMachineTemplate %s to have .status.capacity for CPU set", template.Name)
368+
g.Expect(hasMemory).To(BeTrue(), "Expected AWSMachineTemplate %s to have .status.capacity for memory set", template.Name)
369+
g.Expect(template.Status.NodeInfo).ToNot(BeNil(), "Expected AWSMachineTemplate %s to have .status.nodeInfo set", template.Name)
370+
g.Expect(template.Status.NodeInfo.Architecture).ToNot(BeEmpty(), "Expected AWSMachineTemplate %s to have .status.nodeInfo.architecture set", template.Name)
371+
g.Expect(template.Status.NodeInfo.OperatingSystem).ToNot(BeEmpty(), "Expected AWSMachineTemplate %s to have .status.nodeInfo.operatingSystem set", template.Name)
371372
}
372-
g.Expect(false).To(BeTrue(), "Expected at least one AWSMachineTemplate to have capacity with CPU and Memory")
373-
}, e2eCtx.E2EConfig.GetIntervals(specName, "wait-deployment")...).Should(Succeed())
374-
375-
ginkgo.By("Verifying AWSMachineTemplate nodeInfo is populated")
376-
Eventually(func(g Gomega) {
377-
awsMachineTemplateList := &infrav1.AWSMachineTemplateList{}
378-
g.Expect(e2eCtx.Environment.BootstrapClusterProxy.GetClient().List(ctx, awsMachineTemplateList, client.InNamespace(namespace.Name))).To(Succeed())
379-
g.Expect(awsMachineTemplateList.Items).ToNot(BeEmpty())
380-
381-
for _, template := range awsMachineTemplateList.Items {
382-
nodeInfo := template.Status.NodeInfo
383-
if nodeInfo == nil {
384-
continue
385-
}
386-
arch := string(nodeInfo.Architecture)
387-
if (arch == "amd64" || arch == "arm64") && nodeInfo.OperatingSystem != "" {
388-
ginkgo.By(fmt.Sprintf("AWSMachineTemplate %s has nodeInfo populated: %v", template.Name, nodeInfo))
389-
return
390-
}
391-
}
392-
g.Expect(false).To(BeTrue(), "Expected at least one AWSMachineTemplate to have valid nodeInfo")
393373
}, e2eCtx.E2EConfig.GetIntervals(specName, "wait-deployment")...).Should(Succeed())
394374
})
395375
})

0 commit comments

Comments
 (0)