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