sigs.k8s.io/cluster-api-provider-azure@v1.14.3/controllers/azurejson_machinepool_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/pkg/errors" 24 corev1 "k8s.io/api/core/v1" 25 apierrors "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/client-go/tools/record" 28 "k8s.io/utils/ptr" 29 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 30 "sigs.k8s.io/cluster-api-provider-azure/azure/services/identities" 31 infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1beta1" 32 azureutil "sigs.k8s.io/cluster-api-provider-azure/util/azure" 33 "sigs.k8s.io/cluster-api-provider-azure/util/reconciler" 34 "sigs.k8s.io/cluster-api-provider-azure/util/tele" 35 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 36 "sigs.k8s.io/cluster-api/util" 37 "sigs.k8s.io/cluster-api/util/predicates" 38 ctrl "sigs.k8s.io/controller-runtime" 39 "sigs.k8s.io/controller-runtime/pkg/client" 40 "sigs.k8s.io/controller-runtime/pkg/controller" 41 "sigs.k8s.io/controller-runtime/pkg/handler" 42 "sigs.k8s.io/controller-runtime/pkg/reconcile" 43 "sigs.k8s.io/controller-runtime/pkg/source" 44 ) 45 46 // AzureJSONMachinePoolReconciler reconciles Azure json secrets for AzureMachinePool objects. 47 type AzureJSONMachinePoolReconciler struct { 48 client.Client 49 Recorder record.EventRecorder 50 Timeouts reconciler.Timeouts 51 WatchFilterValue string 52 } 53 54 // SetupWithManager initializes this controller with a manager. 55 func (r *AzureJSONMachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { 56 _, log, done := tele.StartSpanWithLogger(ctx, 57 "controllers.AzureJSONMachinePoolReconciler.SetupWithManager", 58 ) 59 defer done() 60 61 azureMachinePoolMapper, err := util.ClusterToTypedObjectsMapper(r.Client, &infrav1exp.AzureMachinePoolList{}, mgr.GetScheme()) 62 if err != nil { 63 return errors.Wrap(err, "failed to create mapper for Cluster to AzureMachinePools") 64 } 65 66 c, err := ctrl.NewControllerManagedBy(mgr). 67 WithOptions(options). 68 For(&infrav1exp.AzureMachinePool{}). 69 WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log, r.WatchFilterValue)). 70 Owns(&corev1.Secret{}). 71 Build(r) 72 73 if err != nil { 74 return errors.Wrap(err, "failed to create controller") 75 } 76 77 // Add a watch on Clusters to requeue when the infraRef is set. This is needed because the infraRef is not initially 78 // set in Clusters created from a ClusterClass. 79 if err := c.Watch( 80 source.Kind(mgr.GetCache(), &clusterv1.Cluster{}), 81 handler.EnqueueRequestsFromMapFunc(azureMachinePoolMapper), 82 predicates.ClusterUnpausedAndInfrastructureReady(log), 83 predicates.ResourceNotPausedAndHasFilterLabel(log, r.WatchFilterValue), 84 ); err != nil { 85 return errors.Wrap(err, "failed adding a watch for Clusters") 86 } 87 88 return nil 89 } 90 91 // Reconcile reconciles the Azure json for AzureMachinePool objects. 92 func (r *AzureJSONMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) { 93 ctx, cancel := context.WithTimeout(ctx, r.Timeouts.DefaultedLoopTimeout()) 94 defer cancel() 95 96 ctx, log, done := tele.StartSpanWithLogger( 97 ctx, 98 "controllers.AzureJSONMachinePoolReconciler.Reconcile", 99 tele.KVP("namespace", req.Namespace), 100 tele.KVP("name", req.Name), 101 tele.KVP("kind", infrav1.AzureMachinePoolKind), 102 ) 103 defer done() 104 105 log = log.WithValues("namespace", req.Namespace, "azureMachinePool", req.Name) 106 107 // Fetch the AzureMachine instance 108 azureMachinePool := &infrav1exp.AzureMachinePool{} 109 err := r.Get(ctx, req.NamespacedName, azureMachinePool) 110 if err != nil { 111 if apierrors.IsNotFound(err) { 112 log.Info("object was not found") 113 return reconcile.Result{}, nil 114 } 115 return reconcile.Result{}, err 116 } 117 118 // Fetch the CAPI MachinePool. 119 machinePool, err := GetOwnerMachinePool(ctx, r.Client, azureMachinePool.ObjectMeta) 120 if err != nil { 121 return reconcile.Result{}, err 122 } 123 if machinePool == nil { 124 log.Info("MachinePool Controller has not yet set OwnerRef") 125 return reconcile.Result{}, nil 126 } 127 128 log = log.WithValues("machinePool", machinePool.Name) 129 130 // Fetch the Cluster. 131 cluster, err := util.GetClusterFromMetadata(ctx, r.Client, machinePool.ObjectMeta) 132 if err != nil { 133 log.Info("MachinePool is missing cluster label or cluster does not exist") 134 return reconcile.Result{}, nil 135 } 136 137 log = log.WithValues("cluster", cluster.Name) 138 139 if cluster.Spec.InfrastructureRef == nil { 140 log.Info("infra ref is nil") 141 return ctrl.Result{}, nil 142 } 143 144 clusterScope, err := GetClusterScoper(ctx, log, r.Client, cluster, r.Timeouts) 145 if err != nil { 146 return reconcile.Result{}, errors.Wrapf(err, "failed to create cluster scope for cluster %s/%s", cluster.Namespace, cluster.Name) 147 } 148 149 // Construct secret for this machine 150 userAssignedIdentityIfExists := "" 151 if len(azureMachinePool.Spec.UserAssignedIdentities) > 0 { 152 var identitiesClient identities.Client 153 identitiesClient, err := getClient(clusterScope) 154 if err != nil { 155 return reconcile.Result{}, errors.Wrap(err, "failed to create identities client") 156 } 157 parsed, err := azureutil.ParseResourceID(azureMachinePool.Spec.UserAssignedIdentities[0].ProviderID) 158 if err != nil { 159 return reconcile.Result{}, errors.Wrapf(err, "failed to parse ProviderID %s", azureMachinePool.Spec.UserAssignedIdentities[0].ProviderID) 160 } 161 if parsed.SubscriptionID != clusterScope.SubscriptionID() { 162 identitiesClient, err = identities.NewClientBySub(clusterScope, parsed.SubscriptionID) 163 if err != nil { 164 return reconcile.Result{}, errors.Wrapf(err, "failed to create identities client from subscription ID %s", parsed.SubscriptionID) 165 } 166 } 167 userAssignedIdentityIfExists, err = identitiesClient.GetClientID( 168 ctx, azureMachinePool.Spec.UserAssignedIdentities[0].ProviderID) 169 if err != nil { 170 return reconcile.Result{}, errors.Wrap(err, "failed to get user-assigned identity ClientID") 171 } 172 } 173 174 apiVersion, kind := infrav1.GroupVersion.WithKind(infrav1.AzureMachinePoolKind).ToAPIVersionAndKind() 175 owner := metav1.OwnerReference{ 176 APIVersion: apiVersion, 177 Kind: kind, 178 Name: azureMachinePool.GetName(), 179 UID: azureMachinePool.GetUID(), 180 Controller: ptr.To(true), 181 } 182 183 if azureMachinePool.Spec.Identity == infrav1.VMIdentityNone { 184 log.Info(fmt.Sprintf("WARNING, %s", spIdentityWarning)) 185 r.Recorder.Eventf(azureMachinePool, corev1.EventTypeWarning, "VMIdentityNone", spIdentityWarning) 186 } 187 188 newSecret, err := GetCloudProviderSecret( 189 clusterScope, 190 azureMachinePool.Namespace, 191 azureMachinePool.Name, 192 owner, 193 azureMachinePool.Spec.Identity, 194 userAssignedIdentityIfExists, 195 ) 196 197 if err != nil { 198 return ctrl.Result{}, errors.Wrap(err, "failed to create cloud provider config") 199 } 200 201 if err := reconcileAzureSecret(ctx, r.Client, owner, newSecret, clusterScope.ClusterName()); err != nil { 202 r.Recorder.Eventf(azureMachinePool, corev1.EventTypeWarning, "Error reconciling cloud provider secret for AzureMachinePool", err.Error()) 203 return ctrl.Result{}, errors.Wrap(err, "failed to reconcile azure secret") 204 } 205 206 return ctrl.Result{}, nil 207 } 208 209 var getClient = identities.NewClient