sigs.k8s.io/cluster-api-provider-aws@v1.5.5/controllers/awsmachine_controller.go (about) 1 /* 2 Copyright 2019 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 "encoding/json" 22 "fmt" 23 24 "github.com/aws/aws-sdk-go/aws" 25 ignTypes "github.com/flatcar-linux/ignition/config/v2_3/types" 26 "github.com/go-logr/logr" 27 "github.com/google/go-cmp/cmp" 28 "github.com/pkg/errors" 29 corev1 "k8s.io/api/core/v1" 30 apierrors "k8s.io/apimachinery/pkg/api/errors" 31 "k8s.io/client-go/tools/record" 32 "k8s.io/utils/pointer" 33 ctrl "sigs.k8s.io/controller-runtime" 34 "sigs.k8s.io/controller-runtime/pkg/client" 35 "sigs.k8s.io/controller-runtime/pkg/controller" 36 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 37 "sigs.k8s.io/controller-runtime/pkg/event" 38 "sigs.k8s.io/controller-runtime/pkg/handler" 39 "sigs.k8s.io/controller-runtime/pkg/predicate" 40 "sigs.k8s.io/controller-runtime/pkg/source" 41 42 infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1" 43 ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1beta1" 44 "sigs.k8s.io/cluster-api-provider-aws/feature" 45 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud" 46 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/scope" 47 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services" 48 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/ec2" 49 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/elb" 50 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/instancestate" 51 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/s3" 52 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/secretsmanager" 53 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/ssm" 54 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/userdata" 55 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 56 "sigs.k8s.io/cluster-api/controllers/noderefutil" 57 capierrors "sigs.k8s.io/cluster-api/errors" 58 "sigs.k8s.io/cluster-api/util" 59 "sigs.k8s.io/cluster-api/util/annotations" 60 "sigs.k8s.io/cluster-api/util/conditions" 61 "sigs.k8s.io/cluster-api/util/predicates" 62 ) 63 64 // InstanceIDIndex defines the aws machine controller's instance ID index. 65 const InstanceIDIndex = ".spec.instanceID" 66 67 // AWSMachineReconciler reconciles a AwsMachine object. 68 type AWSMachineReconciler struct { 69 client.Client 70 Log logr.Logger 71 Recorder record.EventRecorder 72 ec2ServiceFactory func(scope.EC2Scope) services.EC2Interface 73 elbServiceFactory func(scope.ELBScope) services.ELBInterface 74 secretsManagerServiceFactory func(cloud.ClusterScoper) services.SecretInterface 75 SSMServiceFactory func(cloud.ClusterScoper) services.SecretInterface 76 objectStoreServiceFactory func(cloud.ClusterScoper) services.ObjectStoreInterface 77 Endpoints []scope.ServiceEndpoint 78 WatchFilterValue string 79 } 80 81 const ( 82 // AWSManagedControlPlaneRefKind is the string value indicating that a cluster is AWS managed. 83 AWSManagedControlPlaneRefKind = "AWSManagedControlPlane" 84 ) 85 86 func (r *AWSMachineReconciler) getEC2Service(scope scope.EC2Scope) services.EC2Interface { 87 if r.ec2ServiceFactory != nil { 88 return r.ec2ServiceFactory(scope) 89 } 90 91 return ec2.NewService(scope) 92 } 93 94 func (r *AWSMachineReconciler) getSecretsManagerService(scope cloud.ClusterScoper) services.SecretInterface { 95 if r.secretsManagerServiceFactory != nil { 96 return r.secretsManagerServiceFactory(scope) 97 } 98 99 return secretsmanager.NewService(scope) 100 } 101 102 func (r *AWSMachineReconciler) getSSMService(scope cloud.ClusterScoper) services.SecretInterface { 103 if r.SSMServiceFactory != nil { 104 return r.SSMServiceFactory(scope) 105 } 106 return ssm.NewService(scope) 107 } 108 109 func (r *AWSMachineReconciler) getSecretService(machineScope *scope.MachineScope, scope cloud.ClusterScoper) (services.SecretInterface, error) { 110 switch machineScope.SecureSecretsBackend() { 111 case infrav1.SecretBackendSSMParameterStore: 112 return r.getSSMService(scope), nil 113 case infrav1.SecretBackendSecretsManager: 114 return r.getSecretsManagerService(scope), nil 115 } 116 return nil, errors.New("invalid secret backend") 117 } 118 119 func (r *AWSMachineReconciler) getELBService(elbScope scope.ELBScope) services.ELBInterface { 120 if r.elbServiceFactory != nil { 121 return r.elbServiceFactory(elbScope) 122 } 123 return elb.NewService(elbScope) 124 } 125 126 func (r *AWSMachineReconciler) getObjectStoreService(scope scope.S3Scope) services.ObjectStoreInterface { 127 if r.objectStoreServiceFactory != nil { 128 return r.objectStoreServiceFactory(scope) 129 } 130 131 return s3.NewService(scope) 132 } 133 134 // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmachines,verbs=get;list;watch;create;update;patch;delete 135 // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsmachines/status,verbs=get;update;patch 136 // +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machines;machines/status,verbs=get;list;watch 137 // +kubebuilder:rbac:groups="",resources=secrets;,verbs=get;list;watch 138 // +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch 139 // +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch 140 141 func (r *AWSMachineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) { 142 log := ctrl.LoggerFrom(ctx) 143 144 // Fetch the AWSMachine instance. 145 awsMachine := &infrav1.AWSMachine{} 146 err := r.Get(ctx, req.NamespacedName, awsMachine) 147 if err != nil { 148 if apierrors.IsNotFound(err) { 149 return ctrl.Result{}, nil 150 } 151 return ctrl.Result{}, err 152 } 153 154 // Fetch the Machine. 155 machine, err := util.GetOwnerMachine(ctx, r.Client, awsMachine.ObjectMeta) 156 if err != nil { 157 return ctrl.Result{}, err 158 } 159 if machine == nil { 160 log.Info("Machine Controller has not yet set OwnerRef") 161 return ctrl.Result{}, nil 162 } 163 164 log = log.WithValues("machine", machine.Name) 165 166 // Fetch the Cluster. 167 cluster, err := util.GetClusterFromMetadata(ctx, r.Client, machine.ObjectMeta) 168 if err != nil { 169 log.Info("Machine is missing cluster label or cluster does not exist") 170 return ctrl.Result{}, nil 171 } 172 173 if annotations.IsPaused(cluster, awsMachine) { 174 log.Info("AWSMachine or linked Cluster is marked as paused. Won't reconcile") 175 return ctrl.Result{}, nil 176 } 177 178 log = log.WithValues("cluster", cluster.Name) 179 180 infraCluster, err := r.getInfraCluster(ctx, log, cluster, awsMachine) 181 if err != nil { 182 return ctrl.Result{}, errors.New("error getting infra provider cluster or control plane object") 183 } 184 if infraCluster == nil { 185 log.Info("AWSCluster or AWSManagedControlPlane is not ready yet") 186 return ctrl.Result{}, nil 187 } 188 189 // Create the machine scope 190 machineScope, err := scope.NewMachineScope(scope.MachineScopeParams{ 191 Client: r.Client, 192 Cluster: cluster, 193 Machine: machine, 194 InfraCluster: infraCluster, 195 AWSMachine: awsMachine, 196 }) 197 if err != nil { 198 log.Error(err, "failed to create scope") 199 return ctrl.Result{}, err 200 } 201 202 // Always close the scope when exiting this function so we can persist any AWSMachine changes. 203 defer func() { 204 if err := machineScope.Close(); err != nil && reterr == nil { 205 reterr = err 206 } 207 }() 208 209 switch infraScope := infraCluster.(type) { 210 case *scope.ManagedControlPlaneScope: 211 if !awsMachine.ObjectMeta.DeletionTimestamp.IsZero() { 212 return r.reconcileDelete(machineScope, infraScope, infraScope, nil, nil) 213 } 214 215 return r.reconcileNormal(ctx, machineScope, infraScope, infraScope, nil, nil) 216 case *scope.ClusterScope: 217 if !awsMachine.ObjectMeta.DeletionTimestamp.IsZero() { 218 return r.reconcileDelete(machineScope, infraScope, infraScope, infraScope, infraScope) 219 } 220 221 return r.reconcileNormal(ctx, machineScope, infraScope, infraScope, infraScope, infraScope) 222 default: 223 return ctrl.Result{}, errors.New("infraCluster has unknown type") 224 } 225 } 226 227 func (r *AWSMachineReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error { 228 log := ctrl.LoggerFrom(ctx) 229 AWSClusterToAWSMachines := r.AWSClusterToAWSMachines(log) 230 231 controller, err := ctrl.NewControllerManagedBy(mgr). 232 WithOptions(options). 233 For(&infrav1.AWSMachine{}). 234 Watches( 235 &source.Kind{Type: &clusterv1.Machine{}}, 236 handler.EnqueueRequestsFromMapFunc(util.MachineToInfrastructureMapFunc(infrav1.GroupVersion.WithKind("AWSMachine"))), 237 ). 238 Watches( 239 &source.Kind{Type: &infrav1.AWSCluster{}}, 240 handler.EnqueueRequestsFromMapFunc(AWSClusterToAWSMachines), 241 ). 242 WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log, r.WatchFilterValue)). 243 WithEventFilter( 244 predicate.Funcs{ 245 // Avoid reconciling if the event triggering the reconciliation is related to incremental status updates 246 // for AWSMachine resources only 247 UpdateFunc: func(e event.UpdateEvent) bool { 248 if e.ObjectOld.GetObjectKind().GroupVersionKind().Kind != "AWSMachine" { 249 return true 250 } 251 252 oldMachine := e.ObjectOld.(*infrav1.AWSMachine).DeepCopy() 253 newMachine := e.ObjectNew.(*infrav1.AWSMachine).DeepCopy() 254 255 oldMachine.Status = infrav1.AWSMachineStatus{} 256 newMachine.Status = infrav1.AWSMachineStatus{} 257 258 oldMachine.ObjectMeta.ResourceVersion = "" 259 newMachine.ObjectMeta.ResourceVersion = "" 260 261 return !cmp.Equal(oldMachine, newMachine) 262 }, 263 }, 264 ). 265 Build(r) 266 if err != nil { 267 return err 268 } 269 270 // Add index to AWSMachine to find by providerID 271 if err := mgr.GetFieldIndexer().IndexField(ctx, &infrav1.AWSMachine{}, 272 InstanceIDIndex, 273 r.indexAWSMachineByInstanceID, 274 ); err != nil { 275 return errors.Wrap(err, "error setting index fields") 276 } 277 278 requeueAWSMachinesForUnpausedCluster := r.requeueAWSMachinesForUnpausedCluster(log) 279 return controller.Watch( 280 &source.Kind{Type: &clusterv1.Cluster{}}, 281 handler.EnqueueRequestsFromMapFunc(requeueAWSMachinesForUnpausedCluster), 282 predicates.ClusterUnpausedAndInfrastructureReady(log), 283 ) 284 } 285 286 func (r *AWSMachineReconciler) reconcileDelete(machineScope *scope.MachineScope, clusterScope cloud.ClusterScoper, ec2Scope scope.EC2Scope, elbScope scope.ELBScope, objectStoreScope scope.S3Scope) (ctrl.Result, error) { 287 machineScope.Info("Handling deleted AWSMachine") 288 289 ec2Service := r.getEC2Service(ec2Scope) 290 291 if err := r.deleteBootstrapData(machineScope, clusterScope, objectStoreScope); err != nil { 292 machineScope.Error(err, "unable to delete machine") 293 return ctrl.Result{}, err 294 } 295 296 instance, err := r.findInstance(machineScope, ec2Service) 297 if err != nil && err != ec2.ErrInstanceNotFoundByID { 298 machineScope.Error(err, "query to find instance failed") 299 return ctrl.Result{}, err 300 } 301 302 if instance == nil { 303 // The machine was never created or was deleted by some other entity 304 // One way to reach this state: 305 // 1. Scale deployment to 0 306 // 2. Rename EC2 machine, and delete ProviderID from spec of both Machine 307 // and AWSMachine 308 // 3. Issue a delete 309 // 4. Scale controller deployment to 1 310 machineScope.V(2).Info("Unable to locate EC2 instance by ID or tags") 311 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "NoInstanceFound", "Unable to find matching EC2 instance") 312 controllerutil.RemoveFinalizer(machineScope.AWSMachine, infrav1.MachineFinalizer) 313 return ctrl.Result{}, nil 314 } 315 316 machineScope.V(3).Info("EC2 instance found matching deleted AWSMachine", "instance-id", instance.ID) 317 318 if err := r.reconcileLBAttachment(machineScope, elbScope, instance); err != nil { 319 // We are tolerating AccessDenied error, so this won't block for users with older version of IAM; 320 // all the other errors are blocking. 321 if !elb.IsAccessDenied(err) && !elb.IsNotFound(err) { 322 conditions.MarkFalse(machineScope.AWSMachine, infrav1.ELBAttachedCondition, "DeletingFailed", clusterv1.ConditionSeverityWarning, err.Error()) 323 return ctrl.Result{}, errors.Errorf("failed to reconcile LB attachment: %+v", err) 324 } 325 } 326 327 if machineScope.IsControlPlane() { 328 conditions.MarkFalse(machineScope.AWSMachine, infrav1.ELBAttachedCondition, clusterv1.DeletedReason, clusterv1.ConditionSeverityInfo, "") 329 } 330 331 if feature.Gates.Enabled(feature.EventBridgeInstanceState) { 332 instancestateSvc := instancestate.NewService(ec2Scope) 333 instancestateSvc.RemoveInstanceFromEventPattern(instance.ID) 334 } 335 336 // Check the instance state. If it's already shutting down or terminated, 337 // do nothing. Otherwise attempt to delete it. 338 // This decision is based on the ec2-instance-lifecycle graph at 339 // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-lifecycle.html 340 switch instance.State { 341 case infrav1.InstanceStateShuttingDown, infrav1.InstanceStateTerminated: 342 machineScope.Info("EC2 instance is shutting down or already terminated", "instance-id", instance.ID) 343 default: 344 machineScope.Info("Terminating EC2 instance", "instance-id", instance.ID) 345 346 // Set the InstanceReadyCondition and patch the object before the blocking operation 347 conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, clusterv1.DeletingReason, clusterv1.ConditionSeverityInfo, "") 348 if err := machineScope.PatchObject(); err != nil { 349 machineScope.Error(err, "failed to patch object") 350 return ctrl.Result{}, err 351 } 352 353 if err := ec2Service.TerminateInstanceAndWait(instance.ID); err != nil { 354 machineScope.Error(err, "failed to terminate instance") 355 conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, "DeletingFailed", clusterv1.ConditionSeverityWarning, err.Error()) 356 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "FailedTerminate", "Failed to terminate instance %q: %v", instance.ID, err) 357 return ctrl.Result{}, err 358 } 359 conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, clusterv1.DeletedReason, clusterv1.ConditionSeverityInfo, "") 360 361 // If the AWSMachine specifies NetworkStatus Interfaces, detach the cluster's core Security Groups from them as part of deletion. 362 if len(machineScope.AWSMachine.Spec.NetworkInterfaces) > 0 { 363 core, err := ec2Service.GetCoreSecurityGroups(machineScope) 364 if err != nil { 365 machineScope.Error(err, "failed to get core security groups to detach from instance's network interfaces") 366 return ctrl.Result{}, err 367 } 368 369 machineScope.V(3).Info( 370 "Detaching security groups from provided network interface", 371 "groups", core, 372 "instanceID", instance.ID, 373 ) 374 375 conditions.MarkFalse(machineScope.AWSMachine, infrav1.SecurityGroupsReadyCondition, clusterv1.DeletingReason, clusterv1.ConditionSeverityInfo, "") 376 if err := machineScope.PatchObject(); err != nil { 377 return ctrl.Result{}, err 378 } 379 380 for _, id := range machineScope.AWSMachine.Spec.NetworkInterfaces { 381 if err := ec2Service.DetachSecurityGroupsFromNetworkInterface(core, id); err != nil { 382 machineScope.Error(err, "failed to detach security groups from instance's network interfaces") 383 conditions.MarkFalse(machineScope.AWSMachine, infrav1.SecurityGroupsReadyCondition, "DeletingFailed", clusterv1.ConditionSeverityWarning, err.Error()) 384 return ctrl.Result{}, err 385 } 386 } 387 conditions.MarkFalse(machineScope.AWSMachine, infrav1.SecurityGroupsReadyCondition, clusterv1.DeletedReason, clusterv1.ConditionSeverityInfo, "") 388 } 389 390 machineScope.Info("EC2 instance successfully terminated", "instance-id", instance.ID) 391 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeNormal, "SuccessfulTerminate", "Terminated instance %q", instance.ID) 392 } 393 394 // Instance is deleted so remove the finalizer. 395 controllerutil.RemoveFinalizer(machineScope.AWSMachine, infrav1.MachineFinalizer) 396 397 return ctrl.Result{}, nil 398 } 399 400 // findInstance queries the EC2 apis and retrieves the instance if it exists. 401 // If providerID is empty, finds instance by tags and if it cannot be found, returns empty instance with nil error. 402 // If providerID is set, either finds the instance by ID or returns error. 403 func (r *AWSMachineReconciler) findInstance(scope *scope.MachineScope, ec2svc services.EC2Interface) (*infrav1.Instance, error) { 404 var instance *infrav1.Instance 405 406 // Parse the ProviderID. 407 pid, err := noderefutil.NewProviderID(scope.GetProviderID()) 408 if err != nil { 409 if !errors.Is(err, noderefutil.ErrEmptyProviderID) { 410 return nil, errors.Wrapf(err, "failed to parse Spec.ProviderID") 411 } 412 // If the ProviderID is empty, try to query the instance using tags. 413 // If an instance cannot be found, GetRunningInstanceByTags returns empty instance with nil error. 414 instance, err = ec2svc.GetRunningInstanceByTags(scope) 415 if err != nil { 416 return nil, errors.Wrapf(err, "failed to query AWSMachine instance by tags") 417 } 418 } else { 419 // If the ProviderID is populated, describe the instance using the ID. 420 // InstanceIfExists() returns error (ErrInstanceNotFoundByID or ErrDescribeInstance) if the instance could not be found. 421 instance, err = ec2svc.InstanceIfExists(pointer.StringPtr(pid.ID())) 422 if err != nil { 423 return nil, err 424 } 425 } 426 427 // The only case where the instance is nil here is when the providerId is empty and instance could not be found by tags. 428 return instance, nil 429 } 430 431 func (r *AWSMachineReconciler) reconcileNormal(_ context.Context, machineScope *scope.MachineScope, clusterScope cloud.ClusterScoper, ec2Scope scope.EC2Scope, elbScope scope.ELBScope, objectStoreScope scope.S3Scope) (ctrl.Result, error) { 432 machineScope.Info("Reconciling AWSMachine") 433 434 // If the AWSMachine is in an error state, return early. 435 if machineScope.HasFailed() { 436 machineScope.Info("Error state detected, skipping reconciliation") 437 438 // If we are in a failed state, delete the secret regardless of instance state. 439 if err := r.deleteBootstrapData(machineScope, clusterScope, objectStoreScope); err != nil { 440 machineScope.Error(err, "unable to reconcile machine") 441 return ctrl.Result{}, err 442 } 443 444 return ctrl.Result{}, nil 445 } 446 447 if !machineScope.Cluster.Status.InfrastructureReady { 448 machineScope.Info("Cluster infrastructure is not ready yet") 449 conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.WaitingForClusterInfrastructureReason, clusterv1.ConditionSeverityInfo, "") 450 return ctrl.Result{}, nil 451 } 452 453 // Make sure bootstrap data is available and populated. 454 if machineScope.Machine.Spec.Bootstrap.DataSecretName == nil { 455 machineScope.Info("Bootstrap data secret reference is not yet available") 456 conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.WaitingForBootstrapDataReason, clusterv1.ConditionSeverityInfo, "") 457 return ctrl.Result{}, nil 458 } 459 460 ec2svc := r.getEC2Service(ec2Scope) 461 462 // Find existing instance 463 instance, err := r.findInstance(machineScope, ec2svc) 464 if err != nil { 465 machineScope.Error(err, "unable to find instance") 466 conditions.MarkUnknown(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceNotFoundReason, err.Error()) 467 return ctrl.Result{}, err 468 } 469 470 // If the AWSMachine doesn't have our finalizer, add it. 471 controllerutil.AddFinalizer(machineScope.AWSMachine, infrav1.MachineFinalizer) 472 // Register the finalizer after first read operation from AWS to avoid orphaning AWS resources on delete 473 if err := machineScope.PatchObject(); err != nil { 474 machineScope.Error(err, "unable to patch object") 475 return ctrl.Result{}, err 476 } 477 478 // Create new instance since providerId is nil and instance could not be found by tags. 479 if instance == nil { 480 // Avoid a flickering condition between InstanceProvisionStarted and InstanceProvisionFailed if there's a persistent failure with createInstance 481 if conditions.GetReason(machineScope.AWSMachine, infrav1.InstanceReadyCondition) != infrav1.InstanceProvisionFailedReason { 482 conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceProvisionStartedReason, clusterv1.ConditionSeverityInfo, "") 483 if patchErr := machineScope.PatchObject(); err != nil { 484 machineScope.Error(patchErr, "failed to patch conditions") 485 return ctrl.Result{}, patchErr 486 } 487 } 488 489 var objectStoreSvc services.ObjectStoreInterface 490 491 if objectStoreScope != nil { 492 objectStoreSvc = r.getObjectStoreService(objectStoreScope) 493 } 494 495 instance, err = r.createInstance(ec2svc, machineScope, clusterScope, objectStoreSvc) 496 if err != nil { 497 machineScope.Error(err, "unable to create instance") 498 conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceProvisionFailedReason, clusterv1.ConditionSeverityError, err.Error()) 499 return ctrl.Result{}, err 500 } 501 } 502 if feature.Gates.Enabled(feature.EventBridgeInstanceState) { 503 instancestateSvc := instancestate.NewService(ec2Scope) 504 if err := instancestateSvc.AddInstanceToEventPattern(instance.ID); err != nil { 505 return ctrl.Result{}, errors.Wrap(err, "failed to add instance to Event Bridge instance state rule") 506 } 507 } 508 509 // Make sure Spec.ProviderID and Spec.InstanceID are always set. 510 machineScope.SetProviderID(instance.ID, instance.AvailabilityZone) 511 machineScope.SetInstanceID(instance.ID) 512 513 // See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-lifecycle.html 514 515 // Sets the AWSMachine status Interruptible, when the SpotMarketOptions is enabled for AWSMachine, Interruptible is set as true. 516 machineScope.SetInterruptible() 517 518 existingInstanceState := machineScope.GetInstanceState() 519 machineScope.SetInstanceState(instance.State) 520 521 // Proceed to reconcile the AWSMachine state. 522 if existingInstanceState == nil || *existingInstanceState != instance.State { 523 machineScope.Info("EC2 instance state changed", "state", instance.State, "instance-id", *machineScope.GetInstanceID()) 524 } 525 526 switch instance.State { 527 case infrav1.InstanceStatePending: 528 machineScope.SetNotReady() 529 conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceNotReadyReason, clusterv1.ConditionSeverityWarning, "") 530 case infrav1.InstanceStateStopping, infrav1.InstanceStateStopped: 531 machineScope.SetNotReady() 532 conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceStoppedReason, clusterv1.ConditionSeverityError, "") 533 case infrav1.InstanceStateRunning: 534 machineScope.SetReady() 535 conditions.MarkTrue(machineScope.AWSMachine, infrav1.InstanceReadyCondition) 536 case infrav1.InstanceStateShuttingDown, infrav1.InstanceStateTerminated: 537 machineScope.SetNotReady() 538 machineScope.Info("Unexpected EC2 instance termination", "state", instance.State, "instance-id", *machineScope.GetInstanceID()) 539 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "InstanceUnexpectedTermination", "Unexpected EC2 instance termination") 540 conditions.MarkFalse(machineScope.AWSMachine, infrav1.InstanceReadyCondition, infrav1.InstanceTerminatedReason, clusterv1.ConditionSeverityError, "") 541 default: 542 machineScope.SetNotReady() 543 machineScope.Info("EC2 instance state is undefined", "state", instance.State, "instance-id", *machineScope.GetInstanceID()) 544 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "InstanceUnhandledState", "EC2 instance state is undefined") 545 machineScope.SetFailureReason(capierrors.UpdateMachineError) 546 machineScope.SetFailureMessage(errors.Errorf("EC2 instance state %q is undefined", instance.State)) 547 conditions.MarkUnknown(machineScope.AWSMachine, infrav1.InstanceReadyCondition, "", "") 548 } 549 550 // reconcile the deletion of the bootstrap data secret now that we have updated instance state 551 if deleteSecretErr := r.deleteBootstrapData(machineScope, clusterScope, objectStoreScope); deleteSecretErr != nil { 552 r.Log.Error(deleteSecretErr, "unable to delete secrets") 553 return ctrl.Result{}, deleteSecretErr 554 } 555 556 if instance.State == infrav1.InstanceStateTerminated { 557 machineScope.SetFailureReason(capierrors.UpdateMachineError) 558 machineScope.SetFailureMessage(errors.Errorf("EC2 instance state %q is unexpected", instance.State)) 559 } 560 561 // tasks that can take place during all known instance states 562 if machineScope.InstanceIsInKnownState() { 563 _, err = r.ensureTags(ec2svc, machineScope.AWSMachine, machineScope.GetInstanceID(), machineScope.AdditionalTags()) 564 if err != nil { 565 machineScope.Error(err, "failed to ensure tags") 566 return ctrl.Result{}, err 567 } 568 569 if instance != nil { 570 r.ensureStorageTags(ec2svc, instance, machineScope.AWSMachine) 571 } 572 573 if err := r.reconcileLBAttachment(machineScope, elbScope, instance); err != nil { 574 machineScope.Error(err, "failed to reconcile LB attachment") 575 return ctrl.Result{}, err 576 } 577 } 578 579 // tasks that can only take place during operational instance states 580 if machineScope.InstanceIsOperational() { 581 machineScope.SetAddresses(instance.Addresses) 582 583 existingSecurityGroups, err := ec2svc.GetInstanceSecurityGroups(*machineScope.GetInstanceID()) 584 if err != nil { 585 machineScope.Error(err, "unable to get instance security groups") 586 return ctrl.Result{}, err 587 } 588 589 // Ensure that the security groups are correct. 590 _, err = r.ensureSecurityGroups(ec2svc, machineScope, machineScope.AWSMachine.Spec.AdditionalSecurityGroups, existingSecurityGroups) 591 if err != nil { 592 conditions.MarkFalse(machineScope.AWSMachine, infrav1.SecurityGroupsReadyCondition, infrav1.SecurityGroupsFailedReason, clusterv1.ConditionSeverityError, err.Error()) 593 machineScope.Error(err, "unable to ensure security groups") 594 return ctrl.Result{}, err 595 } 596 conditions.MarkTrue(machineScope.AWSMachine, infrav1.SecurityGroupsReadyCondition) 597 } 598 599 return ctrl.Result{}, nil 600 } 601 602 func (r *AWSMachineReconciler) deleteEncryptedBootstrapDataSecret(machineScope *scope.MachineScope, clusterScope cloud.ClusterScoper) error { 603 secretSvc, secretBackendErr := r.getSecretService(machineScope, clusterScope) 604 if secretBackendErr != nil { 605 machineScope.Error(secretBackendErr, "unable to get secret service backend") 606 return secretBackendErr 607 } 608 609 // do nothing if there isn't a secret 610 if machineScope.GetSecretPrefix() == "" { 611 return nil 612 } 613 if machineScope.GetSecretCount() == 0 { 614 return errors.New("secretPrefix present, but secretCount is not set") 615 } 616 617 // Do nothing if the AWSMachine is not in a failed state, and is operational from an EC2 perspective, but does not have a node reference 618 if !machineScope.HasFailed() && machineScope.InstanceIsOperational() && machineScope.Machine.Status.NodeRef == nil && !machineScope.AWSMachineIsDeleted() { 619 return nil 620 } 621 machineScope.Info("Deleting unneeded entry from AWS Secret", "secretPrefix", machineScope.GetSecretPrefix()) 622 if err := secretSvc.Delete(machineScope); err != nil { 623 machineScope.Info("Unable to delete entries from AWS Secret containing encrypted userdata", "secretPrefix", machineScope.GetSecretPrefix()) 624 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "FailedDeleteEncryptedBootstrapDataSecrets", "AWS Secret entries containing userdata not deleted") 625 return err 626 } 627 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeNormal, "SuccessfulDeleteEncryptedBootstrapDataSecrets", "AWS Secret entries containing userdata deleted") 628 629 machineScope.DeleteSecretPrefix() 630 machineScope.SetSecretCount(0) 631 632 return nil 633 } 634 635 func (r *AWSMachineReconciler) createInstance(ec2svc services.EC2Interface, machineScope *scope.MachineScope, clusterScope cloud.ClusterScoper, objectStoreSvc services.ObjectStoreInterface) (*infrav1.Instance, error) { 636 machineScope.Info("Creating EC2 instance") 637 638 userData, userDataFormat, userDataErr := r.resolveUserData(machineScope, clusterScope, objectStoreSvc) 639 if userDataErr != nil { 640 return nil, errors.Wrapf(userDataErr, "failed to resolve userdata") 641 } 642 643 instance, err := ec2svc.CreateInstance(machineScope, userData, userDataFormat) 644 if err != nil { 645 return nil, errors.Wrapf(err, "failed to create AWSMachine instance") 646 } 647 648 return instance, nil 649 } 650 651 func (r *AWSMachineReconciler) resolveUserData(machineScope *scope.MachineScope, clusterScope cloud.ClusterScoper, objectStoreSvc services.ObjectStoreInterface) ([]byte, string, error) { 652 userData, userDataFormat, err := machineScope.GetRawBootstrapDataWithFormat() 653 if err != nil { 654 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "FailedGetBootstrapData", err.Error()) 655 return nil, "", err 656 } 657 658 if machineScope.UseSecretsManager(userDataFormat) { 659 userData, err = r.cloudInitUserData(machineScope, clusterScope, userData) 660 } 661 662 if machineScope.UseIgnition(userDataFormat) { 663 userData, err = r.ignitionUserData(machineScope, objectStoreSvc, userData) 664 } 665 666 return userData, userDataFormat, err 667 } 668 669 func (r *AWSMachineReconciler) cloudInitUserData(machineScope *scope.MachineScope, clusterScope cloud.ClusterScoper, userData []byte) ([]byte, error) { 670 secretSvc, secretBackendErr := r.getSecretService(machineScope, clusterScope) 671 if secretBackendErr != nil { 672 machineScope.Error(secretBackendErr, "unable to reconcile machine") 673 return nil, secretBackendErr 674 } 675 676 compressedUserData, compressErr := userdata.GzipBytes(userData) 677 if compressErr != nil { 678 return nil, compressErr 679 } 680 prefix, chunks, serviceErr := secretSvc.Create(machineScope, compressedUserData) 681 // Only persist the AWS Secret Backend entries if there is at least one 682 if chunks > 0 { 683 machineScope.SetSecretPrefix(prefix) 684 machineScope.SetSecretCount(chunks) 685 } 686 // Register the Secret ARN immediately to avoid orphaning whatever AWS resources have been created 687 if err := machineScope.PatchObject(); err != nil { 688 return nil, err 689 } 690 if serviceErr != nil { 691 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "FailedCreateAWSSecrets", serviceErr.Error()) 692 machineScope.Error(serviceErr, "Failed to create AWS Secret entry", "secretPrefix", prefix) 693 return nil, serviceErr 694 } 695 encryptedCloudInit, err := secretSvc.UserData(machineScope.GetSecretPrefix(), machineScope.GetSecretCount(), machineScope.InfraCluster.Region(), r.Endpoints) 696 if err != nil { 697 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "FailedGenerateAWSSecretsCloudInit", err.Error()) 698 return nil, err 699 } 700 return encryptedCloudInit, nil 701 } 702 703 func (r *AWSMachineReconciler) ignitionUserData(scope *scope.MachineScope, objectStoreSvc services.ObjectStoreInterface, userData []byte) ([]byte, error) { 704 if objectStoreSvc == nil { 705 return nil, errors.New("object store service not available") 706 } 707 708 objectURL, err := objectStoreSvc.Create(scope, userData) 709 if err != nil { 710 return nil, errors.Wrap(err, "creating userdata object") 711 } 712 713 ignData := &ignTypes.Config{ 714 Ignition: ignTypes.Ignition{ 715 Version: "2.3.0", 716 Config: ignTypes.IgnitionConfig{ 717 Append: []ignTypes.ConfigReference{ 718 { 719 Source: objectURL, 720 }, 721 }, 722 }, 723 }, 724 } 725 726 ignitionUserData, err := json.Marshal(ignData) 727 if err != nil { 728 r.Recorder.Eventf(scope.AWSMachine, corev1.EventTypeWarning, "FailedGenerateIgnition", err.Error()) 729 return nil, errors.Wrap(err, "serializing generated data") 730 } 731 732 return ignitionUserData, nil 733 } 734 735 func (r *AWSMachineReconciler) deleteBootstrapData(machineScope *scope.MachineScope, clusterScope cloud.ClusterScoper, objectStoreScope scope.S3Scope) error { 736 if !machineScope.AWSMachine.Spec.CloudInit.InsecureSkipSecretsManager { 737 if err := r.deleteEncryptedBootstrapDataSecret(machineScope, clusterScope); err != nil { 738 return err 739 } 740 } 741 742 if objectStoreScope != nil { 743 // Bootstrap data will be removed from S3 if it is already populated. 744 if err := r.deleteIgnitionBootstrapDataFromS3(machineScope, r.getObjectStoreService(objectStoreScope)); err != nil { 745 return err 746 } 747 } 748 749 return nil 750 } 751 752 func (r *AWSMachineReconciler) deleteIgnitionBootstrapDataFromS3(machineScope *scope.MachineScope, objectStoreSvc services.ObjectStoreInterface) error { 753 // Do nothing if the AWSMachine is not in a failed state, and is operational from an EC2 perspective, but does not have a node reference 754 if !machineScope.HasFailed() && machineScope.InstanceIsOperational() && machineScope.Machine.Status.NodeRef == nil && !machineScope.AWSMachineIsDeleted() { 755 return nil 756 } 757 758 // If bootstrap data has not been populated yet, we cannot determine it's format, so there is probably nothing to do. 759 if machineScope.Machine.Spec.Bootstrap.DataSecretName == nil { 760 return nil 761 } 762 763 machineScope.Info("Deleting unneeded entry from AWS S3", "secretPrefix", machineScope.GetSecretPrefix()) 764 765 _, userDataFormat, err := machineScope.GetRawBootstrapDataWithFormat() 766 if err != nil { 767 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "FailedGetBootstrapData", err.Error()) 768 return err 769 } 770 771 if !machineScope.UseIgnition(userDataFormat) { 772 return nil 773 } 774 775 if err := objectStoreSvc.Delete(machineScope); err != nil { 776 return errors.Wrap(err, "deleting bootstrap data object") 777 } 778 779 return nil 780 } 781 782 func (r *AWSMachineReconciler) reconcileLBAttachment(machineScope *scope.MachineScope, elbScope scope.ELBScope, i *infrav1.Instance) error { 783 if !machineScope.IsControlPlane() { 784 return nil 785 } 786 787 elbsvc := r.getELBService(elbScope) 788 789 // In order to prevent sending request to a "not-ready" control plane machines, it is required to remove the machine 790 // from the ELB as soon as the machine gets deleted or when the machine is in a not running state. 791 if !machineScope.AWSMachine.DeletionTimestamp.IsZero() || !machineScope.InstanceIsRunning() { 792 registered, err := elbsvc.IsInstanceRegisteredWithAPIServerELB(i) 793 if err != nil { 794 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "FailedDetachControlPlaneELB", 795 "Failed to deregister control plane instance %q from load balancer: failed to determine registration status: %v", i.ID, err) 796 return errors.Wrapf(err, "could not deregister control plane instance %q from load balancer - error determining registration status", i.ID) 797 } 798 if !registered { 799 // Already deregistered - nothing more to do 800 return nil 801 } 802 803 if err := elbsvc.DeregisterInstanceFromAPIServerELB(i); err != nil { 804 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "FailedDetachControlPlaneELB", 805 "Failed to deregister control plane instance %q from load balancer: %v", i.ID, err) 806 conditions.MarkFalse(machineScope.AWSMachine, infrav1.ELBAttachedCondition, infrav1.ELBDetachFailedReason, clusterv1.ConditionSeverityError, err.Error()) 807 return errors.Wrapf(err, "could not deregister control plane instance %q from load balancer", i.ID) 808 } 809 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeNormal, "SuccessfulDetachControlPlaneELB", 810 "Control plane instance %q is de-registered from load balancer", i.ID) 811 return nil 812 } 813 814 registered, err := elbsvc.IsInstanceRegisteredWithAPIServerELB(i) 815 if err != nil { 816 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "FailedAttachControlPlaneELB", 817 "Failed to register control plane instance %q with load balancer: failed to determine registration status: %v", i.ID, err) 818 return errors.Wrapf(err, "could not register control plane instance %q with load balancer - error determining registration status", i.ID) 819 } 820 if registered { 821 // Already registered - nothing more to do 822 return nil 823 } 824 825 if err := elbsvc.RegisterInstanceWithAPIServerELB(i); err != nil { 826 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "FailedAttachControlPlaneELB", 827 "Failed to register control plane instance %q with load balancer: %v", i.ID, err) 828 conditions.MarkFalse(machineScope.AWSMachine, infrav1.ELBAttachedCondition, infrav1.ELBAttachFailedReason, clusterv1.ConditionSeverityError, err.Error()) 829 return errors.Wrapf(err, "could not register control plane instance %q with load balancer", i.ID) 830 } 831 r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeNormal, "SuccessfulAttachControlPlaneELB", 832 "Control plane instance %q is registered with load balancer", i.ID) 833 conditions.MarkTrue(machineScope.AWSMachine, infrav1.ELBAttachedCondition) 834 return nil 835 } 836 837 // AWSClusterToAWSMachines is a handler.ToRequestsFunc to be used to enqeue requests for reconciliation 838 // of AWSMachines. 839 func (r *AWSMachineReconciler) AWSClusterToAWSMachines(log logr.Logger) handler.MapFunc { 840 return func(o client.Object) []ctrl.Request { 841 c, ok := o.(*infrav1.AWSCluster) 842 if !ok { 843 panic(fmt.Sprintf("Expected a AWSCluster but got a %T", o)) 844 } 845 846 log := log.WithValues("objectMapper", "awsClusterToAWSMachine", "namespace", c.Namespace, "awsCluster", c.Name) 847 848 // Don't handle deleted AWSClusters 849 if !c.ObjectMeta.DeletionTimestamp.IsZero() { 850 log.V(4).Info("AWSCluster has a deletion timestamp, skipping mapping.") 851 return nil 852 } 853 854 cluster, err := util.GetOwnerCluster(context.TODO(), r.Client, c.ObjectMeta) 855 switch { 856 case apierrors.IsNotFound(err) || cluster == nil: 857 log.V(4).Info("Cluster for AWSCluster not found, skipping mapping.") 858 return nil 859 case err != nil: 860 log.Error(err, "Failed to get owning cluster, skipping mapping.") 861 return nil 862 } 863 864 return r.requestsForCluster(log, cluster.Namespace, cluster.Name) 865 } 866 } 867 868 func (r *AWSMachineReconciler) requeueAWSMachinesForUnpausedCluster(log logr.Logger) handler.MapFunc { 869 return func(o client.Object) []ctrl.Request { 870 c, ok := o.(*clusterv1.Cluster) 871 if !ok { 872 panic(fmt.Sprintf("Expected a Cluster but got a %T", o)) 873 } 874 875 log := log.WithValues("objectMapper", "clusterToAWSMachine", "namespace", c.Namespace, "cluster", c.Name) 876 877 // Don't handle deleted clusters 878 if !c.ObjectMeta.DeletionTimestamp.IsZero() { 879 log.V(4).Info("Cluster has a deletion timestamp, skipping mapping.") 880 return nil 881 } 882 883 return r.requestsForCluster(log, c.Namespace, c.Name) 884 } 885 } 886 887 func (r *AWSMachineReconciler) requestsForCluster(log logr.Logger, namespace, name string) []ctrl.Request { 888 labels := map[string]string{clusterv1.ClusterLabelName: name} 889 machineList := &clusterv1.MachineList{} 890 if err := r.Client.List(context.TODO(), machineList, client.InNamespace(namespace), client.MatchingLabels(labels)); err != nil { 891 log.Error(err, "Failed to get owned Machines, skipping mapping.") 892 return nil 893 } 894 895 result := make([]ctrl.Request, 0, len(machineList.Items)) 896 for _, m := range machineList.Items { 897 log.WithValues("machine", m.Name) 898 if m.Spec.InfrastructureRef.GroupVersionKind().Kind != "AWSMachine" { 899 log.V(4).Info("Machine has an InfrastructureRef for a different type, will not add to reconciliation request.") 900 continue 901 } 902 if m.Spec.InfrastructureRef.Name == "" { 903 log.V(4).Info("Machine has an InfrastructureRef with an empty name, will not add to reconciliation request.") 904 continue 905 } 906 log.WithValues("awsMachine", m.Spec.InfrastructureRef.Name) 907 log.V(4).Info("Adding AWSMachine to reconciliation request.") 908 result = append(result, ctrl.Request{NamespacedName: client.ObjectKey{Namespace: m.Namespace, Name: m.Spec.InfrastructureRef.Name}}) 909 } 910 return result 911 } 912 913 func (r *AWSMachineReconciler) getInfraCluster(ctx context.Context, log logr.Logger, cluster *clusterv1.Cluster, awsMachine *infrav1.AWSMachine) (scope.EC2Scope, error) { 914 var clusterScope *scope.ClusterScope 915 var managedControlPlaneScope *scope.ManagedControlPlaneScope 916 var err error 917 918 if cluster.Spec.ControlPlaneRef != nil && cluster.Spec.ControlPlaneRef.Kind == "AWSManagedControlPlane" { 919 controlPlane := &ekscontrolplanev1.AWSManagedControlPlane{} 920 controlPlaneName := client.ObjectKey{ 921 Namespace: awsMachine.Namespace, 922 Name: cluster.Spec.ControlPlaneRef.Name, 923 } 924 925 if err := r.Get(ctx, controlPlaneName, controlPlane); err != nil { 926 // AWSManagedControlPlane is not ready 927 return nil, nil // nolint:nilerr 928 } 929 930 managedControlPlaneScope, err = scope.NewManagedControlPlaneScope(scope.ManagedControlPlaneScopeParams{ 931 Client: r.Client, 932 Logger: &log, 933 Cluster: cluster, 934 ControlPlane: controlPlane, 935 ControllerName: "awsManagedControlPlane", 936 Endpoints: r.Endpoints, 937 }) 938 if err != nil { 939 return nil, err 940 } 941 942 return managedControlPlaneScope, nil 943 } 944 945 awsCluster := &infrav1.AWSCluster{} 946 947 infraClusterName := client.ObjectKey{ 948 Namespace: awsMachine.Namespace, 949 Name: cluster.Spec.InfrastructureRef.Name, 950 } 951 952 if err := r.Client.Get(ctx, infraClusterName, awsCluster); err != nil { 953 // AWSCluster is not ready 954 return nil, nil // nolint:nilerr 955 } 956 957 // Create the cluster scope 958 clusterScope, err = scope.NewClusterScope(scope.ClusterScopeParams{ 959 Client: r.Client, 960 Logger: &log, 961 Cluster: cluster, 962 AWSCluster: awsCluster, 963 ControllerName: "awsmachine", 964 }) 965 if err != nil { 966 return nil, err 967 } 968 969 return clusterScope, nil 970 } 971 972 func (r *AWSMachineReconciler) indexAWSMachineByInstanceID(o client.Object) []string { 973 awsMachine, ok := o.(*infrav1.AWSMachine) 974 if !ok { 975 r.Log.Error(errors.New("incorrect type"), "expected an AWSMachine", "type", fmt.Sprintf("%T", o)) 976 return nil 977 } 978 979 if awsMachine.Spec.InstanceID != nil { 980 return []string{*awsMachine.Spec.InstanceID} 981 } 982 983 return nil 984 } 985 986 func (r *AWSMachineReconciler) ensureStorageTags(ec2svc services.EC2Interface, instance *infrav1.Instance, machine *infrav1.AWSMachine) { 987 annotations, err := r.machineAnnotationJSON(machine, VolumeTagsLastAppliedAnnotation) 988 if err != nil { 989 r.Log.Error(err, "Failed to fetch the annotations for volume tags") 990 } 991 for _, volumeID := range instance.VolumeIDs { 992 if subAnnotation, ok := annotations[volumeID].(map[string]interface{}); ok { 993 newAnnotation, err := r.ensureVolumeTags(ec2svc, aws.String(volumeID), subAnnotation, machine.Spec.AdditionalTags) 994 if err != nil { 995 r.Log.Error(err, "Failed to fetch the changed volume tags in EC2 instance") 996 } 997 annotations[volumeID] = newAnnotation 998 } else { 999 newAnnotation, err := r.ensureVolumeTags(ec2svc, aws.String(volumeID), make(map[string]interface{}), machine.Spec.AdditionalTags) 1000 if err != nil { 1001 r.Log.Error(err, "Failed to fetch the changed volume tags in EC2 instance") 1002 } 1003 annotations[volumeID] = newAnnotation 1004 } 1005 1006 // We also need to update the annotation if anything changed. 1007 err = r.updateMachineAnnotationJSON(machine, VolumeTagsLastAppliedAnnotation, annotations) 1008 if err != nil { 1009 r.Log.Error(err, "Failed to fetch the changed volume tags in EC2 instance") 1010 } 1011 } 1012 }