sigs.k8s.io/cluster-api-provider-aws@v1.5.5/exp/controllers/awsmanagedmachinepool_controller.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package controllers 18 19 import ( 20 "context" 21 "fmt" 22 23 "github.com/go-logr/logr" 24 "github.com/pkg/errors" 25 apierrors "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/runtime/schema" 28 "k8s.io/client-go/tools/record" 29 ctrl "sigs.k8s.io/controller-runtime" 30 "sigs.k8s.io/controller-runtime/pkg/client" 31 "sigs.k8s.io/controller-runtime/pkg/client/apiutil" 32 "sigs.k8s.io/controller-runtime/pkg/controller" 33 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 34 "sigs.k8s.io/controller-runtime/pkg/handler" 35 "sigs.k8s.io/controller-runtime/pkg/reconcile" 36 "sigs.k8s.io/controller-runtime/pkg/source" 37 38 ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1beta1" 39 expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/exp/api/v1beta1" 40 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope" 41 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/eks" 42 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 43 expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" 44 "sigs.k8s.io/cluster-api/util" 45 "sigs.k8s.io/cluster-api/util/annotations" 46 "sigs.k8s.io/cluster-api/util/conditions" 47 "sigs.k8s.io/cluster-api/util/predicates" 48 ) 49 50 // AWSManagedMachinePoolReconciler reconciles a AWSManagedMachinePool object. 51 type AWSManagedMachinePoolReconciler struct { 52 client.Client 53 Recorder record.EventRecorder 54 Endpoints []scope.ServiceEndpoint 55 EnableIAM bool 56 AllowAdditionalRoles bool 57 WatchFilterValue string 58 } 59 60 // SetupWithManager is used to setup the controller. 61 func (r *AWSManagedMachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { 62 log := ctrl.LoggerFrom(ctx) 63 64 gvk, err := apiutil.GVKForObject(new(expinfrav1.AWSManagedMachinePool), mgr.GetScheme()) 65 if err != nil { 66 return errors.Wrapf(err, "failed to find GVK for AWSManagedMachinePool") 67 } 68 managedControlPlaneToManagedMachinePoolMap := managedControlPlaneToManagedMachinePoolMapFunc(r.Client, gvk, log) 69 return ctrl.NewControllerManagedBy(mgr). 70 For(&expinfrav1.AWSManagedMachinePool{}). 71 WithOptions(options). 72 WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log, r.WatchFilterValue)). 73 Watches( 74 &source.Kind{Type: &expclusterv1.MachinePool{}}, 75 handler.EnqueueRequestsFromMapFunc(machinePoolToInfrastructureMapFunc(gvk)), 76 ). 77 Watches( 78 &source.Kind{Type: &ekscontrolplanev1.AWSManagedControlPlane{}}, 79 handler.EnqueueRequestsFromMapFunc(managedControlPlaneToManagedMachinePoolMap), 80 ). 81 Complete(r) 82 } 83 84 // +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinepools;machinepools/status,verbs=get;list;watch 85 // +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;patch 86 // +kubebuilder:rbac:groups=controlplane.cluster.x-k8s.io,resources=awsmanagedcontrolplanes;awsmanagedcontrolplanes/status,verbs=get;list;watch 87 // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmanagedmachinepools,verbs=get;list;watch;create;update;patch;delete 88 // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmanagedmachinepools/status,verbs=get;update;patch 89 90 // Reconcile reconciles AWSManagedMachinePools. 91 func (r *AWSManagedMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) { 92 log := ctrl.LoggerFrom(ctx) 93 94 awsPool := &expinfrav1.AWSManagedMachinePool{} 95 if err := r.Get(ctx, req.NamespacedName, awsPool); err != nil { 96 if apierrors.IsNotFound(err) { 97 return ctrl.Result{}, nil 98 } 99 return ctrl.Result{Requeue: true}, nil 100 } 101 102 machinePool, err := getOwnerMachinePool(ctx, r.Client, awsPool.ObjectMeta) 103 if err != nil { 104 log.Error(err, "Failed to retrieve owner MachinePool from the API Server") 105 return ctrl.Result{}, err 106 } 107 if machinePool == nil { 108 log.Info("MachinePool Controller has not yet set OwnerRef") 109 return ctrl.Result{}, nil 110 } 111 112 log = log.WithValues("MachinePool", machinePool.Name) 113 114 cluster, err := util.GetClusterFromMetadata(ctx, r.Client, machinePool.ObjectMeta) 115 if err != nil { 116 log.Info("Failed to retrieve Cluster from MachinePool") 117 return reconcile.Result{}, nil 118 } 119 120 if annotations.IsPaused(cluster, awsPool) { 121 log.Info("Reconciliation is paused for this object") 122 return ctrl.Result{}, nil 123 } 124 125 log = log.WithValues("Cluster", cluster.Name) 126 127 controlPlaneKey := client.ObjectKey{ 128 Namespace: awsPool.Namespace, 129 Name: cluster.Spec.ControlPlaneRef.Name, 130 } 131 controlPlane := &ekscontrolplanev1.AWSManagedControlPlane{} 132 if err := r.Client.Get(ctx, controlPlaneKey, controlPlane); err != nil { 133 log.Info("Failed to retrieve ControlPlane from MachinePool") 134 return reconcile.Result{}, nil 135 } 136 137 if !controlPlane.Status.Ready { 138 log.Info("Control plane is not ready yet") 139 conditions.MarkFalse(awsPool, expinfrav1.EKSNodegroupReadyCondition, expinfrav1.WaitingForEKSControlPlaneReason, clusterv1.ConditionSeverityInfo, "") 140 return ctrl.Result{}, nil 141 } 142 143 machinePoolScope, err := scope.NewManagedMachinePoolScope(scope.ManagedMachinePoolScopeParams{ 144 Client: r.Client, 145 ControllerName: "awsmanagedmachinepool", 146 Cluster: cluster, 147 ControlPlane: controlPlane, 148 MachinePool: machinePool, 149 ManagedMachinePool: awsPool, 150 EnableIAM: r.EnableIAM, 151 AllowAdditionalRoles: r.AllowAdditionalRoles, 152 Endpoints: r.Endpoints, 153 }) 154 if err != nil { 155 return ctrl.Result{}, errors.Wrap(err, "failed to create scope") 156 } 157 158 defer func() { 159 applicableConditions := []clusterv1.ConditionType{ 160 expinfrav1.EKSNodegroupReadyCondition, 161 expinfrav1.IAMNodegroupRolesReadyCondition, 162 } 163 164 conditions.SetSummary(machinePoolScope.ManagedMachinePool, conditions.WithConditions(applicableConditions...), conditions.WithStepCounter()) 165 166 if err := machinePoolScope.Close(); err != nil && reterr == nil { 167 reterr = err 168 } 169 }() 170 171 if !awsPool.ObjectMeta.DeletionTimestamp.IsZero() { 172 return r.reconcileDelete(ctx, machinePoolScope) 173 } 174 175 return r.reconcileNormal(ctx, machinePoolScope) 176 } 177 178 func (r *AWSManagedMachinePoolReconciler) reconcileNormal( 179 _ context.Context, 180 machinePoolScope *scope.ManagedMachinePoolScope, 181 ) (ctrl.Result, error) { 182 machinePoolScope.Info("Reconciling AWSManagedMachinePool") 183 184 controllerutil.AddFinalizer(machinePoolScope.ManagedMachinePool, expinfrav1.ManagedMachinePoolFinalizer) 185 if err := machinePoolScope.PatchObject(); err != nil { 186 return ctrl.Result{}, err 187 } 188 189 ekssvc := eks.NewNodegroupService(machinePoolScope) 190 191 if err := ekssvc.ReconcilePool(); err != nil { 192 return reconcile.Result{}, errors.Wrapf(err, "failed to reconcile machine pool for AWSManagedMachinePool %s/%s", machinePoolScope.ManagedMachinePool.Namespace, machinePoolScope.ManagedMachinePool.Name) 193 } 194 195 return ctrl.Result{}, nil 196 } 197 198 func (r *AWSManagedMachinePoolReconciler) reconcileDelete( 199 _ context.Context, 200 machinePoolScope *scope.ManagedMachinePoolScope, 201 ) (ctrl.Result, error) { 202 machinePoolScope.Info("Reconciling deletion of AWSManagedMachinePool") 203 204 ekssvc := eks.NewNodegroupService(machinePoolScope) 205 206 if err := ekssvc.ReconcilePoolDelete(); err != nil { 207 return reconcile.Result{}, errors.Wrapf(err, "failed to reconcile machine pool deletion for AWSManagedMachinePool %s/%s", machinePoolScope.ManagedMachinePool.Namespace, machinePoolScope.ManagedMachinePool.Name) 208 } 209 210 controllerutil.RemoveFinalizer(machinePoolScope.ManagedMachinePool, expinfrav1.ManagedMachinePoolFinalizer) 211 212 return reconcile.Result{}, nil 213 } 214 215 // GetOwnerClusterKey returns only the Cluster name and namespace. 216 func GetOwnerClusterKey(obj metav1.ObjectMeta) (*client.ObjectKey, error) { 217 for _, ref := range obj.OwnerReferences { 218 if ref.Kind != "Cluster" { 219 continue 220 } 221 gv, err := schema.ParseGroupVersion(ref.APIVersion) 222 if err != nil { 223 return nil, errors.WithStack(err) 224 } 225 if gv.Group == clusterv1.GroupVersion.Group { 226 return &client.ObjectKey{ 227 Namespace: obj.Namespace, 228 Name: ref.Name, 229 }, nil 230 } 231 } 232 return nil, nil 233 } 234 235 func managedControlPlaneToManagedMachinePoolMapFunc(c client.Client, gvk schema.GroupVersionKind, log logr.Logger) handler.MapFunc { 236 return func(o client.Object) []reconcile.Request { 237 ctx := context.Background() 238 awsControlPlane, ok := o.(*ekscontrolplanev1.AWSManagedControlPlane) 239 if !ok { 240 panic(fmt.Sprintf("Expected a AWSManagedControlPlane but got a %T", o)) 241 } 242 243 if !awsControlPlane.ObjectMeta.DeletionTimestamp.IsZero() { 244 return nil 245 } 246 247 clusterKey, err := GetOwnerClusterKey(awsControlPlane.ObjectMeta) 248 if err != nil { 249 log.Error(err, "couldn't get AWS control plane owner ObjectKey") 250 return nil 251 } 252 if clusterKey == nil { 253 return nil 254 } 255 256 managedPoolForClusterList := expclusterv1.MachinePoolList{} 257 if err := c.List( 258 ctx, &managedPoolForClusterList, client.InNamespace(clusterKey.Namespace), client.MatchingLabels{clusterv1.ClusterLabelName: clusterKey.Name}, 259 ); err != nil { 260 log.Error(err, "couldn't list pools for cluster") 261 return nil 262 } 263 264 mapFunc := machinePoolToInfrastructureMapFunc(gvk) 265 266 var results []ctrl.Request 267 for i := range managedPoolForClusterList.Items { 268 managedPool := mapFunc(&managedPoolForClusterList.Items[i]) 269 results = append(results, managedPool...) 270 } 271 272 return results 273 } 274 }