diff --git a/pkg/model/awsmodel/api_loadbalancer.go b/pkg/model/awsmodel/api_loadbalancer.go index 3828843e0385..532b46fd51e7 100644 --- a/pkg/model/awsmodel/api_loadbalancer.go +++ b/pkg/model/awsmodel/api_loadbalancer.go @@ -174,6 +174,7 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error { Lifecycle: b.Lifecycle, LoadBalancerName: fi.String(loadBalancerName), + CLBName: fi.String("api." + b.ClusterName()), SubnetMappings: nlbSubnetMappings, Listeners: nlbListeners, TargetGroups: make([]*awstasks.TargetGroup, 0), diff --git a/pkg/model/awsmodel/bastion.go b/pkg/model/awsmodel/bastion.go index c6a28a358f5b..73fdf533463c 100644 --- a/pkg/model/awsmodel/bastion.go +++ b/pkg/model/awsmodel/bastion.go @@ -260,6 +260,7 @@ func (b *BastionModelBuilder) Build(c *fi.ModelBuilderContext) error { Lifecycle: b.Lifecycle, LoadBalancerName: fi.String(loadBalancerName), + CLBName: fi.String("bastion." + b.ClusterName()), SubnetMappings: nlbSubnetMappings, Listeners: nlbListeners, TargetGroups: make([]*awstasks.TargetGroup, 0), diff --git a/upup/pkg/fi/cloudup/awstasks/network_load_balancer.go b/upup/pkg/fi/cloudup/awstasks/network_load_balancer.go index 1f21fefca5ce..ab3379b03d2f 100644 --- a/upup/pkg/fi/cloudup/awstasks/network_load_balancer.go +++ b/upup/pkg/fi/cloudup/awstasks/network_load_balancer.go @@ -24,6 +24,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/elb" "github.com/aws/aws-sdk-go/service/elbv2" "github.com/aws/aws-sdk-go/service/route53" "k8s.io/klog/v2" @@ -48,6 +49,7 @@ type NetworkLoadBalancer struct { // (NLB is restricted as to names, so we have limited choices!) // We use the Name tag to find the existing NLB. LoadBalancerName *string + CLBName *string DNSName *string HostedZoneId *string @@ -74,6 +76,7 @@ type NetworkLoadBalancer struct { var _ fi.CompareWithID = &NetworkLoadBalancer{} var _ fi.TaskNormalize = &NetworkLoadBalancer{} +var _ fi.ProducesDeletions = &NetworkLoadBalancer{} func (e *NetworkLoadBalancer) CompareWithID() *string { return e.Name @@ -254,6 +257,7 @@ func (e *NetworkLoadBalancer) Find(c *fi.Context) (*NetworkLoadBalancer, error) actual := &NetworkLoadBalancer{} actual.Name = e.Name + actual.CLBName = e.CLBName actual.LoadBalancerName = lb.LoadBalancerName actual.DNSName = lb.DNSName actual.HostedZoneId = lb.CanonicalHostedZoneId // CanonicalHostedZoneNameID @@ -937,3 +941,64 @@ func (e *NetworkLoadBalancer) CloudformationAttrCanonicalHostedZoneNameID() *clo func (e *NetworkLoadBalancer) CloudformationAttrDNSName() *cloudformation.Literal { return cloudformation.GetAtt("AWS::ElasticLoadBalancingV2::LoadBalancer", *e.Name, "DNSName") } + +// FindDeletions schedules deletion of the corresponding legacy classic load balancer when it no longer has targets. +func (e *NetworkLoadBalancer) FindDeletions(context *fi.Context) ([]fi.Deletion, error) { + if e.CLBName == nil { + return nil, nil + } + + cloud := context.Cloud.(awsup.AWSCloud) + + lb, err := cloud.FindELBByNameTag(fi.StringValue(e.CLBName)) + if err != nil { + return nil, err + } + if lb == nil { + return nil, nil + } + + // Testing shows that the instances are deregistered immediately after the apply_cluster. + // TODO: Figure out how to delay deregistration until instances are terminated. + //if len(lb.Instances) > 0 { + // klog.V(2).Infof("CLB %s has targets; not scheduling deletion", *lb.LoadBalancerName) + // return nil, nil + //} + + actual := &deleteClassicLoadBalancer{} + actual.LoadBalancerName = lb.LoadBalancerName + + klog.V(4).Infof("Found CLB %+v", actual) + + return []fi.Deletion{actual}, nil +} + +type deleteClassicLoadBalancer struct { + // LoadBalancerName is the name in ELB, possibly different from our name + // (ELB is restricted as to names, so we have limited choices!) + LoadBalancerName *string +} + +func (d deleteClassicLoadBalancer) Delete(t fi.Target) error { + awsTarget, ok := t.(*awsup.AWSAPITarget) + if !ok { + return fmt.Errorf("unexpected target type for deletion: %T", t) + } + + _, err := awsTarget.Cloud.ELB().DeleteLoadBalancer(&elb.DeleteLoadBalancerInput{ + LoadBalancerName: d.LoadBalancerName, + }) + if err != nil { + return fmt.Errorf("deleting classic LoadBalancer: %w", err) + } + + return nil +} + +func (d deleteClassicLoadBalancer) TaskName() string { + return "ClassicLoadBalancer" +} + +func (d deleteClassicLoadBalancer) Item() string { + return *d.LoadBalancerName +}