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