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