Skip to content

Commit 06f3c98

Browse files
committed
autoscaling: try to gather the version information from MachineSets and e2e test improvements
1 parent 45c7a2e commit 06f3c98

File tree

3 files changed

+73
-31
lines changed

3 files changed

+73
-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: 66 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"
@@ -64,6 +67,13 @@ type AWSMachineTemplateReconciler struct {
6467
WatchFilterValue string
6568
}
6669

70+
var resourceCreatedPredicate = predicate.Funcs{
71+
CreateFunc: func(e event.CreateEvent) bool { return true },
72+
UpdateFunc: func(e event.UpdateEvent) bool { return false },
73+
DeleteFunc: func(e event.DeleteEvent) bool { return false },
74+
GenericFunc: func(e event.GenericEvent) bool { return true },
75+
}
76+
6777
// SetupWithManager sets up the controller with the Manager.
6878
func (r *AWSMachineTemplateReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
6979
log := logger.FromContext(ctx)
@@ -75,12 +85,23 @@ func (r *AWSMachineTemplateReconciler) SetupWithManager(ctx context.Context, mgr
7585
Watches(
7686
&clusterv1.MachineDeployment{},
7787
handler.EnqueueRequestsFromMapFunc(r.machineDeploymentToAWSMachineTemplate),
88+
// Only emit events for creation to reconcile in case the MachineDeployment got created after the AWSMachineTemplate was reconciled.
89+
builder.WithPredicates(resourceCreatedPredicate),
90+
).
91+
Watches(
92+
&clusterv1.MachineSet{},
93+
handler.EnqueueRequestsFromMapFunc(r.machineSetToAWSMachineTemplate),
94+
// Only emit events for creation to reconcile in case the MachineSet got created after the AWSMachineTemplate was reconciled.
95+
builder.WithPredicates(resourceCreatedPredicate),
7896
)
7997

80-
// Optionally watch KubeadmControlPlane if the CRD exists
98+
// Watch KubeadmControlPlane if they exist.
8199
if _, err := mgr.GetRESTMapper().RESTMapping(schema.GroupKind{Group: controlplanev1.GroupVersion.Group, Kind: "KubeadmControlPlane"}, controlplanev1.GroupVersion.Version); err == nil {
82100
b = b.Watches(&controlplanev1.KubeadmControlPlane{},
83-
handler.EnqueueRequestsFromMapFunc(r.kubeadmControlPlaneToAWSMachineTemplate))
101+
handler.EnqueueRequestsFromMapFunc(r.kubeadmControlPlaneToAWSMachineTemplate),
102+
// Only emit events for creation to reconcile in case the KubeadmControlPlane got created after the AWSMachineTemplate was reconciled.
103+
builder.WithPredicates(resourceCreatedPredicate),
104+
)
84105
}
85106

86107
_, err := b.Build(r)
@@ -95,7 +116,7 @@ func (r *AWSMachineTemplateReconciler) SetupWithManager(ctx context.Context, mgr
95116
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmachinetemplates/status,verbs=get;update;patch
96117
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsclusters,verbs=get;list;watch
97118
// +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
119+
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinedeployments;machinesets,verbs=get;list;watch
99120
// +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch
100121

101122
// Reconcile populates capacity information for AWSMachineTemplate.
@@ -387,7 +408,22 @@ func (r *AWSMachineTemplateReconciler) getKubernetesVersion(ctx context.Context,
387408
return "", errors.Wrap(err, "failed to get parent list options")
388409
}
389410

390-
// Try to find version from MachineDeployment first
411+
// Try to find version from MachineSet first
412+
machineSetList := &clusterv1.MachineSetList{}
413+
if err := r.List(ctx, machineSetList, listOpts...); err != nil {
414+
return "", errors.Wrap(err, "failed to list MachineSets")
415+
}
416+
417+
// Find MachineSets that reference this AWSMachineTemplate
418+
for _, ms := range machineSetList.Items {
419+
if ms.Spec.Template.Spec.InfrastructureRef.Kind == awsMachineTemplateKind &&
420+
ms.Spec.Template.Spec.InfrastructureRef.Name == template.Name &&
421+
ms.Spec.Template.Spec.Version != "" {
422+
return ms.Spec.Template.Spec.Version, nil
423+
}
424+
}
425+
426+
// If not found, try MachineDeployment.
391427
machineDeploymentList := &clusterv1.MachineDeploymentList{}
392428
if err := r.List(ctx, machineDeploymentList, listOpts...); err != nil {
393429
return "", errors.Wrap(err, "failed to list MachineDeployments")
@@ -402,7 +438,7 @@ func (r *AWSMachineTemplateReconciler) getKubernetesVersion(ctx context.Context,
402438
}
403439
}
404440

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

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)