Skip to content

Commit a226964

Browse files
fix(controllers): add watches to resolve AWSMachineTemplate e2e race condition
Add MachineDeployment and KubeadmControlPlane watchers to trigger AWSMachineTemplate reconciliation, ensuring nodeInfo is populated before cache sync completes. Related: kubernetes-sigs#5711
1 parent 04a4f62 commit a226964

File tree

3 files changed

+79
-4
lines changed

3 files changed

+79
-4
lines changed

controllers/awsmachinetemplate_controller.go

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
ctrl "sigs.k8s.io/controller-runtime"
3434
"sigs.k8s.io/controller-runtime/pkg/client"
3535
"sigs.k8s.io/controller-runtime/pkg/controller"
36+
"sigs.k8s.io/controller-runtime/pkg/handler"
3637

3738
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
3839
ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/eks/api/v1beta2"
@@ -47,6 +48,11 @@ import (
4748
"sigs.k8s.io/cluster-api/util/predicates"
4849
)
4950

51+
const (
52+
// awsMachineTemplateKind is the Kind name for AWSMachineTemplate resources.
53+
awsMachineTemplateKind = "AWSMachineTemplate"
54+
)
55+
5056
// AWSMachineTemplateReconciler reconciles AWSMachineTemplate objects.
5157
//
5258
// This controller automatically populates capacity information for AWSMachineTemplate resources
@@ -62,11 +68,27 @@ type AWSMachineTemplateReconciler struct {
6268
func (r *AWSMachineTemplateReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
6369
log := logger.FromContext(ctx)
6470

65-
return ctrl.NewControllerManagedBy(mgr).
71+
b := ctrl.NewControllerManagedBy(mgr).
6672
For(&infrav1.AWSMachineTemplate{}).
6773
WithOptions(options).
6874
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(mgr.GetScheme(), log.GetLogger(), r.WatchFilterValue)).
69-
Complete(r)
75+
Watches(
76+
&clusterv1.MachineDeployment{},
77+
handler.EnqueueRequestsFromMapFunc(r.machineDeploymentToAWSMachineTemplate),
78+
)
79+
80+
// Optionally watch KubeadmControlPlane if the CRD exists
81+
if _, err := mgr.GetRESTMapper().RESTMapping(schema.GroupKind{Group: controlplanev1.GroupVersion.Group, Kind: "KubeadmControlPlane"}, controlplanev1.GroupVersion.Version); err == nil {
82+
b = b.Watches(&controlplanev1.KubeadmControlPlane{},
83+
handler.EnqueueRequestsFromMapFunc(r.kubeadmControlPlaneToAWSMachineTemplate))
84+
}
85+
86+
_, err := b.Build(r)
87+
if err != nil {
88+
return errors.Wrap(err, "failed setting up with a controller manager")
89+
}
90+
91+
return nil
7092
}
7193

7294
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmachinetemplates,verbs=get;list;watch
@@ -373,7 +395,7 @@ func (r *AWSMachineTemplateReconciler) getKubernetesVersion(ctx context.Context,
373395

374396
// Find MachineDeployments that reference this AWSMachineTemplate
375397
for _, md := range machineDeploymentList.Items {
376-
if md.Spec.Template.Spec.InfrastructureRef.Kind == "AWSMachineTemplate" &&
398+
if md.Spec.Template.Spec.InfrastructureRef.Kind == awsMachineTemplateKind &&
377399
md.Spec.Template.Spec.InfrastructureRef.Name == template.Name &&
378400
md.Spec.Template.Spec.Version != "" {
379401
return md.Spec.Template.Spec.Version, nil
@@ -388,7 +410,7 @@ func (r *AWSMachineTemplateReconciler) getKubernetesVersion(ctx context.Context,
388410

389411
// Find KubeadmControlPlanes that reference this AWSMachineTemplate
390412
for _, kcp := range kcpList.Items {
391-
if kcp.Spec.MachineTemplate.Spec.InfrastructureRef.Kind == "AWSMachineTemplate" &&
413+
if kcp.Spec.MachineTemplate.Spec.InfrastructureRef.Kind == awsMachineTemplateKind &&
392414
kcp.Spec.MachineTemplate.Spec.InfrastructureRef.Name == template.Name &&
393415
kcp.Spec.Version != "" {
394416
return kcp.Spec.Version, nil
@@ -420,3 +442,53 @@ func getParentListOptions(obj metav1.ObjectMeta) ([]client.ListOption, error) {
420442
}
421443
return listOpts, nil
422444
}
445+
446+
// kubeadmControlPlaneToAWSMachineTemplate maps KubeadmControlPlane to AWSMachineTemplate reconcile requests.
447+
// This enables the controller to reconcile AWSMachineTemplate when its owner KubeadmControlPlane is created or updated,
448+
// ensuring that nodeInfo can be populated even if the cache hasn't synced yet.
449+
func (r *AWSMachineTemplateReconciler) kubeadmControlPlaneToAWSMachineTemplate(ctx context.Context, o client.Object) []ctrl.Request {
450+
kcp, ok := o.(*controlplanev1.KubeadmControlPlane)
451+
if !ok {
452+
return nil
453+
}
454+
455+
// Check if it references an AWSMachineTemplate
456+
if kcp.Spec.MachineTemplate.Spec.InfrastructureRef.Kind != awsMachineTemplateKind {
457+
return nil
458+
}
459+
460+
// Return reconcile request for the referenced AWSMachineTemplate
461+
return []ctrl.Request{
462+
{
463+
NamespacedName: client.ObjectKey{
464+
Namespace: kcp.Namespace,
465+
Name: kcp.Spec.MachineTemplate.Spec.InfrastructureRef.Name,
466+
},
467+
},
468+
}
469+
}
470+
471+
// machineDeploymentToAWSMachineTemplate maps MachineDeployment to AWSMachineTemplate reconcile requests.
472+
// This enables the controller to reconcile AWSMachineTemplate when its owner MachineDeployment is created or updated,
473+
// ensuring that nodeInfo can be populated even if the cache hasn't synced yet.
474+
func (r *AWSMachineTemplateReconciler) machineDeploymentToAWSMachineTemplate(ctx context.Context, o client.Object) []ctrl.Request {
475+
md, ok := o.(*clusterv1.MachineDeployment)
476+
if !ok {
477+
return nil
478+
}
479+
480+
// Check if it references an AWSMachineTemplate
481+
if md.Spec.Template.Spec.InfrastructureRef.Kind != awsMachineTemplateKind {
482+
return nil
483+
}
484+
485+
// Return reconcile request for the referenced AWSMachineTemplate
486+
return []ctrl.Request{
487+
{
488+
NamespacedName: client.ObjectKey{
489+
Namespace: md.Namespace,
490+
Name: md.Spec.Template.Spec.InfrastructureRef.Name,
491+
},
492+
},
493+
}
494+
}

main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import (
6565
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
6666
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/record"
6767
"sigs.k8s.io/cluster-api-provider-aws/v2/version"
68+
KubeadmControlPlanev1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2"
6869
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
6970
"sigs.k8s.io/cluster-api/util/flags"
7071
)
@@ -79,6 +80,7 @@ func init() {
7980
_ = eksbootstrapv1beta1.AddToScheme(scheme)
8081
_ = cgscheme.AddToScheme(scheme)
8182
_ = clusterv1.AddToScheme(scheme)
83+
_ = KubeadmControlPlanev1.AddToScheme(scheme)
8284
_ = ekscontrolplanev1.AddToScheme(scheme)
8385
_ = ekscontrolplanev1beta1.AddToScheme(scheme)
8486
_ = rosacontrolplanev1.AddToScheme(scheme)

test/e2e/suites/unmanaged/unmanaged_functional_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ var _ = ginkgo.Context("[unmanaged] [functional]", func() {
363363
foundTemplateWithCapacity := false
364364
foundTemplateWithNodeInfo := false
365365
for _, template := range awsMachineTemplateList.Items {
366+
366367
if len(template.Status.Capacity) > 0 {
367368
foundTemplateWithCapacity = true
368369
ginkgo.By(fmt.Sprintf("AWSMachineTemplate %s has capacity populated: %v", template.Name, template.Status.Capacity))

0 commit comments

Comments
 (0)