@@ -36,6 +36,7 @@ import (
3636 "sigs.k8s.io/controller-runtime/pkg/client"
3737
3838 infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
39+ expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
3940 "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
4041 "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
4142 "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services"
@@ -428,6 +429,105 @@ func TestAWSMachineReconcilerIntegrationTests(t *testing.T) {
428429 })
429430 g .Expect (ms .AWSMachine .Finalizers ).ShouldNot (ContainElement (infrav1 .MachineFinalizer ))
430431 })
432+ t .Run ("Should successfully continue AWSMachinePool machine deletion if spec.cloudInit=={}" , func (t * testing.T ) {
433+ g := NewWithT (t )
434+ mockCtrl = gomock .NewController (t )
435+ ec2Mock := mocks .NewMockEC2API (mockCtrl )
436+
437+ // Simulate terminated instance
438+ ec2Mock .EXPECT ().DescribeInstances (context .TODO (), gomock .Eq (& ec2.DescribeInstancesInput {
439+ InstanceIds : []string {"myMachine" },
440+ })).Return (& ec2.DescribeInstancesOutput {
441+ Reservations : []ec2types.Reservation {{Instances : []ec2types.Instance {{Placement : & ec2types.Placement {AvailabilityZone : aws .String ("us-east-1a" )}, InstanceId : aws .String ("i-mymachine" ), State : & ec2types.InstanceState {Name : ec2types .InstanceStateNameTerminated , Code : aws .Int32 (48 )}}}}},
442+ }, nil )
443+
444+ ns , err := testEnv .CreateNamespace (ctx , fmt .Sprintf ("integ-test-%s" , util .RandomString (5 )))
445+ g .Expect (err ).To (BeNil ())
446+
447+ setup (t , g )
448+ awsMachine := & infrav1.AWSMachine {
449+ ObjectMeta : metav1.ObjectMeta {
450+ Namespace : ns .Name ,
451+ GenerateName : "mypool-" ,
452+ Labels : map [string ]string {
453+ clusterv1 .MachinePoolNameLabel : "mypool" ,
454+ clusterv1 .ClusterNameLabel : "test-cluster" ,
455+ },
456+ OwnerReferences : []metav1.OwnerReference {
457+ {
458+ APIVersion : expinfrav1 .GroupVersion .String (),
459+ Kind : "AWSMachinePool" ,
460+ Name : "mypool" ,
461+ BlockOwnerDeletion : ptr .To (true ),
462+ UID : "6d1e6238-045d-4297-8c7e-73df7a5cc998" ,
463+ },
464+ },
465+ },
466+ Spec : infrav1.AWSMachineSpec {
467+ ProviderID : aws .String (providerID ),
468+ InstanceID : aws .String ("i-mymachine" ),
469+ AMI : infrav1.AMIReference {
470+ ID : aws .String ("ami-alsodoesntmatter" ),
471+ },
472+ InstanceType : "foo" ,
473+ PublicIP : aws .Bool (false ),
474+ SSHKeyName : aws .String ("foo" ),
475+ InstanceMetadataOptions : & infrav1.InstanceMetadataOptions {
476+ // ...
477+ },
478+ IAMInstanceProfile : "foo" ,
479+ AdditionalSecurityGroups : nil ,
480+ Subnet : & infrav1.AWSResourceReference {ID : aws .String ("sub-doesntmatter" )},
481+ RootVolume : & infrav1.Volume {
482+ Size : 8 ,
483+ // ...
484+ },
485+ NonRootVolumes : nil ,
486+ NetworkInterfaces : []string {"eni-foobar" },
487+ CloudInit : infrav1.CloudInit {},
488+ SpotMarketOptions : nil ,
489+ Tenancy : "host" ,
490+ },
491+ }
492+ createAWSMachine (g , awsMachine )
493+
494+ defer teardown (g )
495+ defer t .Cleanup (func () {
496+ g .Expect (testEnv .Cleanup (ctx , awsMachine , ns )).To (Succeed ())
497+ })
498+
499+ cs , err := getClusterScope (infrav1.AWSCluster {ObjectMeta : metav1.ObjectMeta {Name : "test" }})
500+ g .Expect (err ).To (BeNil ())
501+ cs .Cluster = & clusterv1.Cluster {ObjectMeta : metav1.ObjectMeta {Name : "test-cluster" }}
502+ ms , err := getMachineScope (cs , awsMachine )
503+ g .Expect (err ).To (BeNil ())
504+
505+ // This case happened in a live object. It didn't get defaulted and actually was
506+ // a machine pool AWSMachine managed via Ignition. The AWSMachine controller must
507+ // not try to use this field or delete bootstrap data, as the object is managed
508+ // by the AWSMachinePool controller.
509+ ms .AWSMachine .Spec .CloudInit .SecureSecretsBackend = ""
510+ now := metav1 .Now ()
511+ ms .AWSMachine .DeletionTimestamp = & now
512+ ms .AWSMachine .Status .InstanceState = & infrav1 .InstanceStateTerminated
513+
514+ // Machine pool controlled Machine/AWSMachine
515+ if ms .Machine .Labels == nil {
516+ ms .Machine .Labels = map [string ]string {}
517+ }
518+ ms .Machine .Labels [clusterv1 .MachinePoolNameLabel ] = ms .AWSMachine .Labels [clusterv1 .MachinePoolNameLabel ]
519+ ms .Machine .Labels [clusterv1 .ClusterNameLabel ] = ms .AWSMachine .Labels [clusterv1 .ClusterNameLabel ]
520+
521+ ec2Svc := ec2Service .NewService (cs )
522+ ec2Svc .EC2Client = ec2Mock
523+ reconciler .ec2ServiceFactory = func (scope scope.EC2Scope ) services.EC2Interface {
524+ return ec2Svc
525+ }
526+
527+ _ , err = reconciler .reconcileDelete (context .TODO (), ms , cs , cs , cs , cs )
528+ g .Expect (err ).To (BeNil ())
529+ g .Expect (ms .AWSMachine .Finalizers ).ShouldNot (ContainElement (infrav1 .MachineFinalizer ))
530+ })
431531}
432532
433533func getMachineScope (cs * scope.ClusterScope , awsMachine * infrav1.AWSMachine ) (* scope.MachineScope , error ) {
0 commit comments