diff --git a/docs/releases/1.19-NOTES.md b/docs/releases/1.19-NOTES.md index f5e993c05a2c5..e6dd2af3cb983 100644 --- a/docs/releases/1.19-NOTES.md +++ b/docs/releases/1.19-NOTES.md @@ -74,6 +74,20 @@ has been updated by a newer version of kops unless it is given the `--allow-kops * See note about [Openstack Cinder plugin](#openstack-cinder-plugin) above. +* Terraform users, in order to prevent downtime you will have to remove the state of any existing ELB or TargetGroup attatchments from your Terraform state file. This is due to migrating the attachments to the in-line `aws_autoscaling_group` fields. See the [terraform documentation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group) for more information about the difference. This migration is required due to a bug described in [#9913](https://github.com/kubernetes/kops/issues/9913). + +To prevent downtime, follow these steps with the new version of Kops: + ``` + kops update cluster --target terraform ... + terraform plan + terraform state list | grep aws_autoscaling_attachment | xargs -L1 terraform state rm + terraform plan + # Ensure these resources are no longer being destroyed and recreated + terraform apply + ``` + +* If you are using Terraform with an additional .tf file and using "aws_autoscaling_attachment" to attach additional Load Balancers or ALB/NLB Target Groups you'll need to migrate to [attaching them through the InstanceGroup spec instead](https://kops.sigs.k8s.io/instance_groups/#externalloadbalancers). + # Deprecations * Support for Kubernetes versions 1.11 and 1.12 are deprecated and will be removed in kops 1.20. diff --git a/pkg/model/awsmodel/api_loadbalancer.go b/pkg/model/awsmodel/api_loadbalancer.go index 0f2d1498e6187..b846559b35164 100644 --- a/pkg/model/awsmodel/api_loadbalancer.go +++ b/pkg/model/awsmodel/api_loadbalancer.go @@ -25,7 +25,6 @@ import ( "k8s.io/klog/v2" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/dns" - "k8s.io/kops/pkg/featureflag" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awstasks" ) @@ -273,20 +272,6 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error { elb.ForAPIServer = true } - // When Spotinst Elastigroups are used, there is no need to create - // a separate task for the attachment of the load balancer since this - // is already done as part of the Elastigroup's creation, if needed. - if !featureflag.Spotinst.Enabled() { - for _, ig := range b.MasterInstanceGroups() { - c.AddTask(&awstasks.LoadBalancerAttachment{ - Name: fi.String("api-" + ig.ObjectMeta.Name), - Lifecycle: b.Lifecycle, - AutoscalingGroup: b.LinkToAutoscalingGroup(ig), - LoadBalancer: b.LinkToELB("api"), - }) - } - } - return nil } diff --git a/pkg/model/awsmodel/autoscalinggroup.go b/pkg/model/awsmodel/autoscalinggroup.go index 6be53ada1a7a0..692ff7db21a60 100644 --- a/pkg/model/awsmodel/autoscalinggroup.go +++ b/pkg/model/awsmodel/autoscalinggroup.go @@ -91,10 +91,6 @@ func (b *AutoscalingGroupModelBuilder) Build(c *fi.ModelBuilderContext) error { } c.AddTask(tsk) - // @step: add any external load balancer attachments - if err := b.buildExternalLoadBalancerTasks(c, ig); err != nil { - return err - } } return nil @@ -353,6 +349,40 @@ func (b *AutoscalingGroupModelBuilder) buildAutoScalingGroupTask(c *fi.ModelBuil t.InstanceProtection = ig.Spec.InstanceProtection + // When Spotinst Elastigroups are used, there is no need to create + // a separate task for the attachment of the load balancer since this + // is already done as part of the Elastigroup's creation, if needed. + if !featureflag.Spotinst.Enabled() { + if b.UseLoadBalancerForAPI() && ig.Spec.Role == kops.InstanceGroupRoleMaster { + t.LoadBalancers = append(t.LoadBalancers, b.LinkToELB("api")) + } + + if ig.Spec.Role == kops.InstanceGroupRoleBastion { + t.LoadBalancers = append(t.LoadBalancers, b.LinkToELB("bastion")) + } + } + + for _, extLB := range ig.Spec.ExternalLoadBalancers { + if extLB.LoadBalancerName != nil { + t.LoadBalancers = append(t.LoadBalancers, &awstasks.LoadBalancer{Name: extLB.LoadBalancerName}) + + c.AddTask(&awstasks.LoadBalancer{ + Name: extLB.LoadBalancerName, + Shared: fi.Bool(true), + }) + } + + if extLB.TargetGroupARN != nil { + t.TargetGroups = append(t.TargetGroups, &awstasks.TargetGroup{Name: extLB.TargetGroupARN, ARN: extLB.TargetGroupARN}) + + c.AddTask(&awstasks.TargetGroup{ + Name: extLB.TargetGroupARN, + ARN: extLB.TargetGroupARN, + Shared: fi.Bool(true), + }) + } + } + // @step: are we using a mixed instance policy if ig.Spec.MixedInstancesPolicy != nil { spec := ig.Spec.MixedInstancesPolicy @@ -368,28 +398,3 @@ func (b *AutoscalingGroupModelBuilder) buildAutoScalingGroupTask(c *fi.ModelBuil return t, nil } - -// buildExternlLoadBalancerTasks is responsible for adding any ELB attachment tasks to the model -func (b *AutoscalingGroupModelBuilder) buildExternalLoadBalancerTasks(c *fi.ModelBuilderContext, ig *kops.InstanceGroup) error { - for _, x := range ig.Spec.ExternalLoadBalancers { - if x.LoadBalancerName != nil { - c.AddTask(&awstasks.ExternalLoadBalancerAttachment{ - Name: fi.String("extlb-" + *x.LoadBalancerName + "-" + ig.Name), - Lifecycle: b.Lifecycle, - LoadBalancerName: *x.LoadBalancerName, - AutoscalingGroup: b.LinkToAutoscalingGroup(ig), - }) - } - - if x.TargetGroupARN != nil { - c.AddTask(&awstasks.ExternalTargetGroupAttachment{ - Name: fi.String("exttg-" + *x.TargetGroupARN + "-" + ig.Name), - Lifecycle: b.Lifecycle, - TargetGroupARN: *x.TargetGroupARN, - AutoscalingGroup: b.LinkToAutoscalingGroup(ig), - }) - } - } - - return nil -} diff --git a/pkg/model/bastion.go b/pkg/model/bastion.go index 20c495a674670..c736aa6450636 100644 --- a/pkg/model/bastion.go +++ b/pkg/model/bastion.go @@ -21,7 +21,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/kops/pkg/apis/kops" - "k8s.io/kops/pkg/featureflag" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awstasks" ) @@ -263,25 +262,6 @@ func (b *BastionModelBuilder) Build(c *fi.ModelBuilderContext) error { c.AddTask(elb) } - // When Spotinst Elastigroups are used, there is no need to create - // a separate task for the attachment of the load balancer since this - // is already done as part of the Elastigroup's creation, if needed. - if !featureflag.Spotinst.Enabled() { - for _, ig := range bastionInstanceGroups { - // We build the ASG when we iterate over the instance groups - - // Attach the ELB to the ASG - t := &awstasks.LoadBalancerAttachment{ - Name: s("bastion-elb-attachment"), - Lifecycle: b.Lifecycle, - - LoadBalancer: elb, - AutoscalingGroup: b.LinkToAutoscalingGroup(ig), - } - c.AddTask(t) - } - } - bastionPublicName := "" if b.Cluster.Spec.Topology != nil && b.Cluster.Spec.Topology.Bastion != nil { bastionPublicName = b.Cluster.Spec.Topology.Bastion.BastionPublicName diff --git a/tests/integration/update_cluster/bastionadditional_user-data/kubernetes.tf b/tests/integration/update_cluster/bastionadditional_user-data/kubernetes.tf index 5e5d2c411d5e9..330c7a032ccba 100644 --- a/tests/integration/update_cluster/bastionadditional_user-data/kubernetes.tf +++ b/tests/integration/update_cluster/bastionadditional_user-data/kubernetes.tf @@ -110,22 +110,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-bastionuserdata-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-bastionuserdata-example-com.id - elb = aws_elb.bastion-bastionuserdata-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-bastionuserdata-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-bastionuserdata-example-com.id - elb = aws_elb.api-bastionuserdata-example-com.id -} - resource "aws_autoscaling_group" "bastion-bastionuserdata-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-bastionuserdata-example-com.id version = aws_launch_template.bastion-bastionuserdata-example-com.latest_version } + load_balancers = [aws_elb.bastion-bastionuserdata-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -174,6 +165,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-bastionuserdata-exam id = aws_launch_template.master-us-test-1a-masters-bastionuserdata-example-com.id version = aws_launch_template.master-us-test-1a-masters-bastionuserdata-example-com.latest_version } + load_balancers = [aws_elb.api-bastionuserdata-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/complex/kubernetes.tf b/tests/integration/update_cluster/complex/kubernetes.tf index d7c3f083a486b..53c3c87216247 100644 --- a/tests/integration/update_cluster/complex/kubernetes.tf +++ b/tests/integration/update_cluster/complex/kubernetes.tf @@ -80,17 +80,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-complex-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-complex-example-com.id - elb = aws_elb.api-complex-example-com.id -} - resource "aws_autoscaling_group" "master-us-test-1a-masters-complex-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.master-us-test-1a-masters-complex-example-com.id version = aws_launch_template.master-us-test-1a-masters-complex-example-com.latest_version } + load_balancers = [aws_elb.api-complex-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/existing_sg/kubernetes.tf b/tests/integration/update_cluster/existing_sg/kubernetes.tf index 2d7b9d185cd79..44fb7cb2a1ca3 100644 --- a/tests/integration/update_cluster/existing_sg/kubernetes.tf +++ b/tests/integration/update_cluster/existing_sg/kubernetes.tf @@ -90,27 +90,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-existingsg-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-existingsg-example-com.id - elb = aws_elb.api-existingsg-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1b-masters-existingsg-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1b-masters-existingsg-example-com.id - elb = aws_elb.api-existingsg-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1c-masters-existingsg-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1c-masters-existingsg-example-com.id - elb = aws_elb.api-existingsg-example-com.id -} - resource "aws_autoscaling_group" "master-us-test-1a-masters-existingsg-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.master-us-test-1a-masters-existingsg-example-com.id version = aws_launch_template.master-us-test-1a-masters-existingsg-example-com.latest_version } + load_balancers = [aws_elb.api-existingsg-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -159,6 +145,7 @@ resource "aws_autoscaling_group" "master-us-test-1b-masters-existingsg-example-c id = aws_launch_template.master-us-test-1b-masters-existingsg-example-com.id version = aws_launch_template.master-us-test-1b-masters-existingsg-example-com.latest_version } + load_balancers = [aws_elb.api-existingsg-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -207,6 +194,7 @@ resource "aws_autoscaling_group" "master-us-test-1c-masters-existingsg-example-c id = aws_launch_template.master-us-test-1c-masters-existingsg-example-com.id version = aws_launch_template.master-us-test-1c-masters-existingsg-example-com.latest_version } + load_balancers = [aws_elb.api-existingsg-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/externallb/kubernetes.tf b/tests/integration/update_cluster/externallb/kubernetes.tf index d7103ac252e5c..973b60ecf33ec 100644 --- a/tests/integration/update_cluster/externallb/kubernetes.tf +++ b/tests/integration/update_cluster/externallb/kubernetes.tf @@ -80,27 +80,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "extlb-my-elb-nodes" { - autoscaling_group_name = aws_autoscaling_group.nodes-externallb-example-com.id - elb = "my-elb" -} - -resource "aws_autoscaling_attachment" "extlb-my-other-elb-master-us-test-1a" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-externallb-example-com.id - elb = "my-other-elb" -} - -resource "aws_autoscaling_attachment" "exttg-aws_my-tg--0123456789abcdef-master-us-test-1a" { - alb_target_group_arn = "aws:arn:elasticloadbalancing:us-test-1a:123456789012:targetgroup/my-tg/0123456789abcdef" - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-externallb-example-com.id -} - resource "aws_autoscaling_group" "master-us-test-1a-masters-externallb-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.master-us-test-1a-masters-externallb-example-com.id version = aws_launch_template.master-us-test-1a-masters-externallb-example-com.latest_version } + load_balancers = ["my-other-elb"] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -140,6 +126,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-externallb-example-c propagate_at_launch = true value = "owned" } + target_group_arns = ["aws:arn:elasticloadbalancing:us-test-1a:123456789012:targetgroup/my-tg/0123456789abcdef"] vpc_zone_identifier = [aws_subnet.us-test-1a-externallb-example-com.id] } @@ -149,6 +136,7 @@ resource "aws_autoscaling_group" "nodes-externallb-example-com" { id = aws_launch_template.nodes-externallb-example-com.id version = aws_launch_template.nodes-externallb-example-com.latest_version } + load_balancers = ["my-elb"] max_size = 2 metrics_granularity = "1Minute" min_size = 2 diff --git a/tests/integration/update_cluster/externalpolicies/kubernetes.tf b/tests/integration/update_cluster/externalpolicies/kubernetes.tf index c1816e196af56..d97b5221840b8 100644 --- a/tests/integration/update_cluster/externalpolicies/kubernetes.tf +++ b/tests/integration/update_cluster/externalpolicies/kubernetes.tf @@ -80,17 +80,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-externalpolicies-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-externalpolicies-example-com.id - elb = aws_elb.api-externalpolicies-example-com.id -} - resource "aws_autoscaling_group" "master-us-test-1a-masters-externalpolicies-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.master-us-test-1a-masters-externalpolicies-example-com.id version = aws_launch_template.master-us-test-1a-masters-externalpolicies-example-com.latest_version } + load_balancers = [aws_elb.api-externalpolicies-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/private-shared-subnet/kubernetes.tf b/tests/integration/update_cluster/private-shared-subnet/kubernetes.tf index 2cee5ce7a056c..3b60aff6dfa2e 100644 --- a/tests/integration/update_cluster/private-shared-subnet/kubernetes.tf +++ b/tests/integration/update_cluster/private-shared-subnet/kubernetes.tf @@ -100,22 +100,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-private-shared-subnet-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-private-shared-subnet-example-com.id - elb = aws_elb.bastion-private-shared-subnet-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-private-shared-subnet-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-private-shared-subnet-example-com.id - elb = aws_elb.api-private-shared-subnet-example-com.id -} - resource "aws_autoscaling_group" "bastion-private-shared-subnet-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-private-shared-subnet-example-com.id version = aws_launch_template.bastion-private-shared-subnet-example-com.latest_version } + load_balancers = [aws_elb.bastion-private-shared-subnet-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -164,6 +155,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-private-shared-subne id = aws_launch_template.master-us-test-1a-masters-private-shared-subnet-example-com.id version = aws_launch_template.master-us-test-1a-masters-private-shared-subnet-example-com.latest_version } + load_balancers = [aws_elb.api-private-shared-subnet-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/privatecalico/kubernetes.tf b/tests/integration/update_cluster/privatecalico/kubernetes.tf index a52e07f07af49..c3ccb7ac08c62 100644 --- a/tests/integration/update_cluster/privatecalico/kubernetes.tf +++ b/tests/integration/update_cluster/privatecalico/kubernetes.tf @@ -110,22 +110,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-privatecalico-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-privatecalico-example-com.id - elb = aws_elb.bastion-privatecalico-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-privatecalico-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-privatecalico-example-com.id - elb = aws_elb.api-privatecalico-example-com.id -} - resource "aws_autoscaling_group" "bastion-privatecalico-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-privatecalico-example-com.id version = aws_launch_template.bastion-privatecalico-example-com.latest_version } + load_balancers = [aws_elb.bastion-privatecalico-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -174,6 +165,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-privatecalico-exampl id = aws_launch_template.master-us-test-1a-masters-privatecalico-example-com.id version = aws_launch_template.master-us-test-1a-masters-privatecalico-example-com.latest_version } + load_balancers = [aws_elb.api-privatecalico-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/privatecanal/kubernetes.tf b/tests/integration/update_cluster/privatecanal/kubernetes.tf index 5eb90f1e37937..c29f08492e44a 100644 --- a/tests/integration/update_cluster/privatecanal/kubernetes.tf +++ b/tests/integration/update_cluster/privatecanal/kubernetes.tf @@ -110,22 +110,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-privatecanal-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-privatecanal-example-com.id - elb = aws_elb.bastion-privatecanal-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-privatecanal-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-privatecanal-example-com.id - elb = aws_elb.api-privatecanal-example-com.id -} - resource "aws_autoscaling_group" "bastion-privatecanal-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-privatecanal-example-com.id version = aws_launch_template.bastion-privatecanal-example-com.latest_version } + load_balancers = [aws_elb.bastion-privatecanal-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -174,6 +165,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-privatecanal-example id = aws_launch_template.master-us-test-1a-masters-privatecanal-example-com.id version = aws_launch_template.master-us-test-1a-masters-privatecanal-example-com.latest_version } + load_balancers = [aws_elb.api-privatecanal-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/privatecilium/kubernetes.tf b/tests/integration/update_cluster/privatecilium/kubernetes.tf index 2f560d354a2d9..9a9d5fc2887d0 100644 --- a/tests/integration/update_cluster/privatecilium/kubernetes.tf +++ b/tests/integration/update_cluster/privatecilium/kubernetes.tf @@ -110,22 +110,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-privatecilium-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-privatecilium-example-com.id - elb = aws_elb.bastion-privatecilium-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-privatecilium-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-privatecilium-example-com.id - elb = aws_elb.api-privatecilium-example-com.id -} - resource "aws_autoscaling_group" "bastion-privatecilium-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-privatecilium-example-com.id version = aws_launch_template.bastion-privatecilium-example-com.latest_version } + load_balancers = [aws_elb.bastion-privatecilium-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -174,6 +165,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-privatecilium-exampl id = aws_launch_template.master-us-test-1a-masters-privatecilium-example-com.id version = aws_launch_template.master-us-test-1a-masters-privatecilium-example-com.latest_version } + load_balancers = [aws_elb.api-privatecilium-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/privatecilium2/kubernetes.tf b/tests/integration/update_cluster/privatecilium2/kubernetes.tf index 2f560d354a2d9..9a9d5fc2887d0 100644 --- a/tests/integration/update_cluster/privatecilium2/kubernetes.tf +++ b/tests/integration/update_cluster/privatecilium2/kubernetes.tf @@ -110,22 +110,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-privatecilium-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-privatecilium-example-com.id - elb = aws_elb.bastion-privatecilium-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-privatecilium-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-privatecilium-example-com.id - elb = aws_elb.api-privatecilium-example-com.id -} - resource "aws_autoscaling_group" "bastion-privatecilium-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-privatecilium-example-com.id version = aws_launch_template.bastion-privatecilium-example-com.latest_version } + load_balancers = [aws_elb.bastion-privatecilium-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -174,6 +165,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-privatecilium-exampl id = aws_launch_template.master-us-test-1a-masters-privatecilium-example-com.id version = aws_launch_template.master-us-test-1a-masters-privatecilium-example-com.latest_version } + load_balancers = [aws_elb.api-privatecilium-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/privateciliumadvanced/kubernetes.tf b/tests/integration/update_cluster/privateciliumadvanced/kubernetes.tf index 8d9d6c07978c6..62c2283a1553b 100644 --- a/tests/integration/update_cluster/privateciliumadvanced/kubernetes.tf +++ b/tests/integration/update_cluster/privateciliumadvanced/kubernetes.tf @@ -110,22 +110,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-privateciliumadvanced-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-privateciliumadvanced-example-com.id - elb = aws_elb.bastion-privateciliumadvanced-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-privateciliumadvanced-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-privateciliumadvanced-example-com.id - elb = aws_elb.api-privateciliumadvanced-example-com.id -} - resource "aws_autoscaling_group" "bastion-privateciliumadvanced-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-privateciliumadvanced-example-com.id version = aws_launch_template.bastion-privateciliumadvanced-example-com.latest_version } + load_balancers = [aws_elb.bastion-privateciliumadvanced-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -174,6 +165,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-privateciliumadvance id = aws_launch_template.master-us-test-1a-masters-privateciliumadvanced-example-com.id version = aws_launch_template.master-us-test-1a-masters-privateciliumadvanced-example-com.latest_version } + load_balancers = [aws_elb.api-privateciliumadvanced-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/privatedns1/kubernetes.tf b/tests/integration/update_cluster/privatedns1/kubernetes.tf index d9d3c35c95884..8078f38636d39 100644 --- a/tests/integration/update_cluster/privatedns1/kubernetes.tf +++ b/tests/integration/update_cluster/privatedns1/kubernetes.tf @@ -110,22 +110,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-privatedns1-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-privatedns1-example-com.id - elb = aws_elb.bastion-privatedns1-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-privatedns1-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-privatedns1-example-com.id - elb = aws_elb.api-privatedns1-example-com.id -} - resource "aws_autoscaling_group" "bastion-privatedns1-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-privatedns1-example-com.id version = aws_launch_template.bastion-privatedns1-example-com.latest_version } + load_balancers = [aws_elb.bastion-privatedns1-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -184,6 +175,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-privatedns1-example- id = aws_launch_template.master-us-test-1a-masters-privatedns1-example-com.id version = aws_launch_template.master-us-test-1a-masters-privatedns1-example-com.latest_version } + load_balancers = [aws_elb.api-privatedns1-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/privatedns2/kubernetes.tf b/tests/integration/update_cluster/privatedns2/kubernetes.tf index 44a21e4b57318..3851584616ab9 100644 --- a/tests/integration/update_cluster/privatedns2/kubernetes.tf +++ b/tests/integration/update_cluster/privatedns2/kubernetes.tf @@ -105,22 +105,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-privatedns2-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-privatedns2-example-com.id - elb = aws_elb.bastion-privatedns2-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-privatedns2-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-privatedns2-example-com.id - elb = aws_elb.api-privatedns2-example-com.id -} - resource "aws_autoscaling_group" "bastion-privatedns2-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-privatedns2-example-com.id version = aws_launch_template.bastion-privatedns2-example-com.latest_version } + load_balancers = [aws_elb.bastion-privatedns2-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -169,6 +160,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-privatedns2-example- id = aws_launch_template.master-us-test-1a-masters-privatedns2-example-com.id version = aws_launch_template.master-us-test-1a-masters-privatedns2-example-com.latest_version } + load_balancers = [aws_elb.api-privatedns2-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/privateflannel/kubernetes.tf b/tests/integration/update_cluster/privateflannel/kubernetes.tf index 462c99c24ed63..d236feab7b592 100644 --- a/tests/integration/update_cluster/privateflannel/kubernetes.tf +++ b/tests/integration/update_cluster/privateflannel/kubernetes.tf @@ -110,22 +110,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-privateflannel-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-privateflannel-example-com.id - elb = aws_elb.bastion-privateflannel-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-privateflannel-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-privateflannel-example-com.id - elb = aws_elb.api-privateflannel-example-com.id -} - resource "aws_autoscaling_group" "bastion-privateflannel-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-privateflannel-example-com.id version = aws_launch_template.bastion-privateflannel-example-com.latest_version } + load_balancers = [aws_elb.bastion-privateflannel-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -174,6 +165,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-privateflannel-examp id = aws_launch_template.master-us-test-1a-masters-privateflannel-example-com.id version = aws_launch_template.master-us-test-1a-masters-privateflannel-example-com.latest_version } + load_balancers = [aws_elb.api-privateflannel-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/privatekopeio/kubernetes.tf b/tests/integration/update_cluster/privatekopeio/kubernetes.tf index 439c850121f6b..ee36fd7569bb8 100644 --- a/tests/integration/update_cluster/privatekopeio/kubernetes.tf +++ b/tests/integration/update_cluster/privatekopeio/kubernetes.tf @@ -125,22 +125,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-privatekopeio-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-privatekopeio-example-com.id - elb = aws_elb.bastion-privatekopeio-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-privatekopeio-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-privatekopeio-example-com.id - elb = aws_elb.api-privatekopeio-example-com.id -} - resource "aws_autoscaling_group" "bastion-privatekopeio-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-privatekopeio-example-com.id version = aws_launch_template.bastion-privatekopeio-example-com.latest_version } + load_balancers = [aws_elb.bastion-privatekopeio-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -189,6 +180,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-privatekopeio-exampl id = aws_launch_template.master-us-test-1a-masters-privatekopeio-example-com.id version = aws_launch_template.master-us-test-1a-masters-privatekopeio-example-com.latest_version } + load_balancers = [aws_elb.api-privatekopeio-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/privateweave/kubernetes.tf b/tests/integration/update_cluster/privateweave/kubernetes.tf index 52b85f89fd467..1e5c0d6d45b7c 100644 --- a/tests/integration/update_cluster/privateweave/kubernetes.tf +++ b/tests/integration/update_cluster/privateweave/kubernetes.tf @@ -110,22 +110,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-privateweave-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-privateweave-example-com.id - elb = aws_elb.bastion-privateweave-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-privateweave-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-privateweave-example-com.id - elb = aws_elb.api-privateweave-example-com.id -} - resource "aws_autoscaling_group" "bastion-privateweave-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-privateweave-example-com.id version = aws_launch_template.bastion-privateweave-example-com.latest_version } + load_balancers = [aws_elb.bastion-privateweave-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -174,6 +165,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-privateweave-example id = aws_launch_template.master-us-test-1a-masters-privateweave-example-com.id version = aws_launch_template.master-us-test-1a-masters-privateweave-example-com.latest_version } + load_balancers = [aws_elb.api-privateweave-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/tests/integration/update_cluster/unmanaged/kubernetes.tf b/tests/integration/update_cluster/unmanaged/kubernetes.tf index 9132ef700e670..b6bf50dffe251 100644 --- a/tests/integration/update_cluster/unmanaged/kubernetes.tf +++ b/tests/integration/update_cluster/unmanaged/kubernetes.tf @@ -105,22 +105,13 @@ provider "aws" { region = "us-test-1" } -resource "aws_autoscaling_attachment" "bastion-unmanaged-example-com" { - autoscaling_group_name = aws_autoscaling_group.bastion-unmanaged-example-com.id - elb = aws_elb.bastion-unmanaged-example-com.id -} - -resource "aws_autoscaling_attachment" "master-us-test-1a-masters-unmanaged-example-com" { - autoscaling_group_name = aws_autoscaling_group.master-us-test-1a-masters-unmanaged-example-com.id - elb = aws_elb.api-unmanaged-example-com.id -} - resource "aws_autoscaling_group" "bastion-unmanaged-example-com" { enabled_metrics = ["GroupDesiredCapacity", "GroupInServiceInstances", "GroupMaxSize", "GroupMinSize", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"] launch_template { id = aws_launch_template.bastion-unmanaged-example-com.id version = aws_launch_template.bastion-unmanaged-example-com.latest_version } + load_balancers = [aws_elb.bastion-unmanaged-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 @@ -169,6 +160,7 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-unmanaged-example-co id = aws_launch_template.master-us-test-1a-masters-unmanaged-example-com.id version = aws_launch_template.master-us-test-1a-masters-unmanaged-example-com.latest_version } + load_balancers = [aws_elb.api-unmanaged-example-com.id] max_size = 1 metrics_granularity = "1Minute" min_size = 1 diff --git a/upup/pkg/fi/cloudup/awstasks/BUILD.bazel b/upup/pkg/fi/cloudup/awstasks/BUILD.bazel index 6b1363ac4c645..1384cfdd6ce51 100644 --- a/upup/pkg/fi/cloudup/awstasks/BUILD.bazel +++ b/upup/pkg/fi/cloudup/awstasks/BUILD.bazel @@ -18,10 +18,6 @@ go_library( "ebsvolume_fitask.go", "elastic_ip.go", "elasticip_fitask.go", - "external_load_balancer_attachment.go", - "external_target_group_attachment.go", - "externalloadbalancerattachment_fitask.go", - "externaltargetgroupattachment_fitask.go", "helper.go", "iaminstanceprofile.go", "iaminstanceprofile_fitask.go", @@ -47,11 +43,9 @@ go_library( "launchtemplate_target_cloudformation.go", "launchtemplate_target_terraform.go", "load_balancer.go", - "load_balancer_attachment.go", "loadbalancer_attributes.go", "loadbalancer_fitask.go", "loadbalancer_healthchecks.go", - "loadbalancerattachment_fitask.go", "natgateway.go", "natgateway_fitask.go", "route.go", @@ -69,6 +63,8 @@ go_library( "subnet.go", "subnet_fitask.go", "tags.go", + "targetgroup.go", + "targetgroup_fitask.go", "vpc.go", "vpc_dhcpoptions_association.go", "vpc_fitask.go", diff --git a/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go b/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go index effd3e30afad2..ffadebd7cf2ca 100644 --- a/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go +++ b/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go @@ -54,6 +54,8 @@ type AutoscalingGroup struct { LaunchConfiguration *LaunchConfiguration // LaunchTemplate is the launch template for the asg LaunchTemplate *LaunchTemplate + // LoadBalancers is a list of elastic load balancer names to add to the autoscaling group + LoadBalancers []*LoadBalancer // MaxSize is the max number of nodes in asg MaxSize *int64 // Metrics is a collection of metrics to monitor @@ -85,6 +87,8 @@ type AutoscalingGroup struct { SuspendProcesses *[]string // Tags is a collection of keypairs to apply to the node on launch Tags map[string]string + // TargetGroups is a list of ALB/NLB target group ARNs to add to the autoscaling group + TargetGroups []*TargetGroup } var _ fi.CompareWithID = &AutoscalingGroup{} @@ -112,6 +116,14 @@ func (e *AutoscalingGroup) Find(c *fi.Context) (*AutoscalingGroup, error) { MinSize: g.MinSize, } + for _, lb := range g.LoadBalancerNames { + actual.LoadBalancers = append(actual.LoadBalancers, &LoadBalancer{Name: aws.String(*lb)}) + } + + for _, tg := range g.TargetGroupARNs { + actual.TargetGroups = append(actual.TargetGroups, &TargetGroup{ARN: aws.String(*tg)}) + } + if g.VPCZoneIdentifier != nil { subnets := strings.Split(*g.VPCZoneIdentifier, ",") for _, subnet := range subnets { @@ -264,6 +276,14 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos VPCZoneIdentifier: fi.String(strings.Join(e.AutoscalingGroupSubnets(), ",")), } + for _, k := range e.LoadBalancers { + request.LoadBalancerNames = append(request.LoadBalancerNames, k.GetName()) + } + + for _, tg := range e.TargetGroups { + request.TargetGroupARNs = append(request.TargetGroupARNs, tg.ARN) + } + // @check are we using a launch configuration, mixed instances policy, or launch template if e.LaunchConfiguration != nil { request.LaunchConfigurationName = e.LaunchConfiguration.ID @@ -444,6 +464,22 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos changes.Tags = nil } + var attachLBRequest *autoscaling.AttachLoadBalancersInput + var detachLBRequest *autoscaling.DetachLoadBalancersInput + if changes.LoadBalancers != nil { + attachLBRequest = &autoscaling.AttachLoadBalancersInput{ + AutoScalingGroupName: e.Name, + LoadBalancerNames: e.AutoscalingLoadBalancers(), + } + + if a != nil && len(a.LoadBalancers) > 0 { + detachLBRequest = &autoscaling.DetachLoadBalancersInput{AutoScalingGroupName: e.Name} + detachLBRequest.LoadBalancerNames = e.getLBsToDetach(a.LoadBalancers) + } + + changes.Tags = nil + } + if changes.Metrics != nil || changes.Granularity != nil { // TODO: Support disabling metrics? if len(e.Metrics) != 0 { @@ -513,6 +549,17 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos return fmt.Errorf("error updating AutoscalingGroup tags: %v", err) } } + + if detachLBRequest != nil { + if _, err := t.Cloud.Autoscaling().DetachLoadBalancers(detachLBRequest); err != nil { + return fmt.Errorf("error detatching LoadBalancers: %v", err) + } + } + if attachLBRequest != nil { + if _, err := t.Cloud.Autoscaling().AttachLoadBalancers(attachLBRequest); err != nil { + return fmt.Errorf("error attaching LoadBalancers: %v", err) + } + } } return nil @@ -574,6 +621,17 @@ func (e *AutoscalingGroup) AutoscalingGroupSubnets() []string { return list } +// AutoscalingLoadBalancers returns a list of LBs attatched to the ASG +func (e *AutoscalingGroup) AutoscalingLoadBalancers() []*string { + var list []*string + + for _, v := range e.LoadBalancers { + list = append(list, v.Name) + } + + return list +} + // processCompare returns processes that exist in a but not in b func processCompare(a *[]string, b *[]string) []*string { notInB := []*string{} @@ -611,6 +669,24 @@ func (e *AutoscalingGroup) getASGTagsToDelete(currentTags map[string]string) []* return tagsToDelete } +// getLBsToDetach loops through the currently set LBs and builds a list of +// LBs to be detach from the Autoscaling Group +func (e *AutoscalingGroup) getLBsToDetach(currentLBs []*LoadBalancer) []*string { + lbsToDetach := []*string{} + desiredLBs := map[string]bool{} + + for _, v := range e.LoadBalancers { + desiredLBs[*v.Name] = true + } + + for _, v := range currentLBs { + if _, ok := desiredLBs[*v.Name]; !ok { + lbsToDetach = append(lbsToDetach, v.Name) + } + } + return lbsToDetach +} + type terraformASGTag struct { Key *string `json:"key" cty:"key"` Value *string `json:"value" cty:"value"` @@ -678,6 +754,8 @@ type terraformAutoscalingGroup struct { EnabledMetrics []*string `json:"enabled_metrics,omitempty" cty:"enabled_metrics"` SuspendedProcesses []*string `json:"suspended_processes,omitempty" cty:"suspended_processes"` InstanceProtection *bool `json:"protect_from_scale_in,omitempty" cty:"protect_from_scale_in"` + LoadBalancers []*terraform.Literal `json:"load_balancers,omitempty" cty:"load_balancers"` + TargetGroupARNs []*terraform.Literal `json:"target_group_arns,omitempty" cty:"target_group_arns"` } // RenderTerraform is responsible for rendering the terraform codebase @@ -704,6 +782,16 @@ func (_ *AutoscalingGroup) RenderTerraform(t *terraform.TerraformTarget, a, e, c }) } + for _, k := range e.LoadBalancers { + tf.LoadBalancers = append(tf.LoadBalancers, k.TerraformLink()) + } + terraform.SortLiterals(tf.LoadBalancers) + + for _, tg := range e.TargetGroups { + tf.TargetGroupARNs = append(tf.TargetGroupARNs, tg.TerraformLink()) + } + terraform.SortLiterals(tf.TargetGroupARNs) + if e.LaunchConfiguration != nil { tf.LaunchConfigurationName = e.LaunchConfiguration.TerraformLink() } else if e.UseMixedInstancesPolicy() { @@ -931,6 +1019,14 @@ func (_ *AutoscalingGroup) RenderCloudformation(t *cloudformation.Cloudformation }) } + for _, k := range e.LoadBalancers { + cf.LoadBalancerNames = append(cf.LoadBalancerNames, k.CloudformationLink()) + } + + for _, tg := range e.TargetGroups { + cf.TargetGroupARNs = append(cf.TargetGroupARNs, tg.CloudformationLink()) + } + return t.RenderResource("AWS::AutoScaling::AutoScalingGroup", fi.StringValue(e.Name), cf) } diff --git a/upup/pkg/fi/cloudup/awstasks/external_load_balancer_attachment.go b/upup/pkg/fi/cloudup/awstasks/external_load_balancer_attachment.go deleted file mode 100644 index 2b736ba03a1e9..0000000000000 --- a/upup/pkg/fi/cloudup/awstasks/external_load_balancer_attachment.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package awstasks - -import ( - "fmt" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/autoscaling" - "k8s.io/klog/v2" - "k8s.io/kops/upup/pkg/fi" - "k8s.io/kops/upup/pkg/fi/cloudup/awsup" - "k8s.io/kops/upup/pkg/fi/cloudup/cloudformation" - "k8s.io/kops/upup/pkg/fi/cloudup/terraform" -) - -// +kops:fitask -type ExternalLoadBalancerAttachment struct { - Name *string - Lifecycle *fi.Lifecycle - - LoadBalancerName string - - AutoscalingGroup *AutoscalingGroup -} - -func (e *ExternalLoadBalancerAttachment) Find(c *fi.Context) (*ExternalLoadBalancerAttachment, error) { - cloud := c.Cloud.(awsup.AWSCloud) - - if e.LoadBalancerName == "" { - return nil, fmt.Errorf("InstanceGroup did not have LoadBalancerNames set") - } - - g, err := findAutoscalingGroup(cloud, *e.AutoscalingGroup.Name) - if err != nil { - return nil, err - } - if g == nil { - return nil, nil - } - - for _, name := range g.LoadBalancerNames { - if aws.StringValue(name) != e.LoadBalancerName { - continue - } - - actual := &ExternalLoadBalancerAttachment{} - actual.LoadBalancerName = e.LoadBalancerName - actual.AutoscalingGroup = e.AutoscalingGroup - - // Prevent spurious changes - actual.Name = e.Name // ELB attachments don't have tags - actual.Lifecycle = e.Lifecycle - - return actual, nil - } - - return nil, nil -} - -func (e *ExternalLoadBalancerAttachment) Run(c *fi.Context) error { - return fi.DefaultDeltaRunMethod(e, c) -} - -func (s *ExternalLoadBalancerAttachment) CheckChanges(a, e, changes *ExternalLoadBalancerAttachment) error { - if a == nil { - if e.LoadBalancerName == "" { - return fi.RequiredField("LoadBalancerName") - } - if e.AutoscalingGroup == nil { - return fi.RequiredField("AutoscalingGroup") - } - } - return nil -} - -func (_ *ExternalLoadBalancerAttachment) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *ExternalLoadBalancerAttachment) error { - if e.LoadBalancerName == "" { - return fi.RequiredField("LoadBalancerName") - } - - request := &autoscaling.AttachLoadBalancersInput{} - request.AutoScalingGroupName = e.AutoscalingGroup.Name - request.LoadBalancerNames = aws.StringSlice([]string{e.LoadBalancerName}) - - klog.V(2).Infof("Attaching autoscaling group %q to ELB %q", fi.StringValue(e.AutoscalingGroup.Name), e.LoadBalancerName) - _, err := t.Cloud.Autoscaling().AttachLoadBalancers(request) - if err != nil { - return fmt.Errorf("error attaching autoscaling group to ELB: %v", err) - } - - return nil -} - -type terraformExternalLoadBalancerAttachment struct { - ELB *terraform.Literal `json:"elb" cty:"elb"` - AutoscalingGroup *terraform.Literal `json:"autoscaling_group_name,omitempty" cty:"autoscaling_group_name"` -} - -func (_ *ExternalLoadBalancerAttachment) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *ExternalLoadBalancerAttachment) error { - tf := &terraformExternalLoadBalancerAttachment{ - ELB: terraform.LiteralFromStringValue(e.LoadBalancerName), - AutoscalingGroup: e.AutoscalingGroup.TerraformLink(), - } - - return t.RenderResource("aws_autoscaling_attachment", *e.Name, tf) -} - -func (e *ExternalLoadBalancerAttachment) TerraformLink() *terraform.Literal { - return terraform.LiteralProperty("aws_autoscaling_attachment", e.LoadBalancerName, "id") -} - -func (_ *ExternalLoadBalancerAttachment) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *ExternalLoadBalancerAttachment) error { - cfObj, ok := t.Find(e.AutoscalingGroup.CloudformationLink()) - if !ok { - // topo-sort fail? - return fmt.Errorf("AutoScalingGroup not yet rendered") - } - cf, ok := cfObj.(*cloudformationAutoscalingGroup) - if !ok { - return fmt.Errorf("unexpected type for CF record: %T", cfObj) - } - - cf.LoadBalancerNames = append(cf.LoadBalancerNames, cloudformation.LiteralString(e.LoadBalancerName)) - return nil -} diff --git a/upup/pkg/fi/cloudup/awstasks/external_target_group_attachment.go b/upup/pkg/fi/cloudup/awstasks/external_target_group_attachment.go deleted file mode 100644 index 3f5e8b5568cb3..0000000000000 --- a/upup/pkg/fi/cloudup/awstasks/external_target_group_attachment.go +++ /dev/null @@ -1,146 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package awstasks - -import ( - "fmt" - "regexp" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/autoscaling" - "k8s.io/klog/v2" - "k8s.io/kops/upup/pkg/fi" - "k8s.io/kops/upup/pkg/fi/cloudup/awsup" - "k8s.io/kops/upup/pkg/fi/cloudup/cloudformation" - "k8s.io/kops/upup/pkg/fi/cloudup/terraform" -) - -// +kops:fitask -type ExternalTargetGroupAttachment struct { - Name *string - Lifecycle *fi.Lifecycle - - TargetGroupARN string - - AutoscalingGroup *AutoscalingGroup -} - -func (e *ExternalTargetGroupAttachment) name() string { - re := regexp.MustCompile("arn:.*:targetgroup/") - return re.ReplaceAllString(*e.Name, "") -} - -func (e *ExternalTargetGroupAttachment) Find(c *fi.Context) (*ExternalTargetGroupAttachment, error) { - cloud := c.Cloud.(awsup.AWSCloud) - - if e.TargetGroupARN == "" { - return nil, fmt.Errorf("InstanceGroup did not have TargetGroupARNs set") - } - - g, err := findAutoscalingGroup(cloud, *e.AutoscalingGroup.Name) - if err != nil { - return nil, err - } - if g == nil { - return nil, nil - } - - for _, name := range g.TargetGroupARNs { - if aws.StringValue(name) != e.TargetGroupARN { - continue - } - - actual := &ExternalTargetGroupAttachment{} - actual.TargetGroupARN = e.TargetGroupARN - actual.AutoscalingGroup = e.AutoscalingGroup - - // Prevent spurious changes - actual.Name = e.Name // ELB attachments don't have tags - actual.Lifecycle = e.Lifecycle - - return actual, nil - } - - return nil, nil -} - -func (e *ExternalTargetGroupAttachment) Run(c *fi.Context) error { - return fi.DefaultDeltaRunMethod(e, c) -} - -func (s *ExternalTargetGroupAttachment) CheckChanges(a, e, changes *ExternalTargetGroupAttachment) error { - if a == nil { - if e.TargetGroupARN == "" { - return fi.RequiredField("TargetGroupARN") - } - if e.AutoscalingGroup == nil { - return fi.RequiredField("AutoscalingGroup") - } - } - return nil -} - -func (_ *ExternalTargetGroupAttachment) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *ExternalTargetGroupAttachment) error { - if e.TargetGroupARN == "" { - return fi.RequiredField("TargetGroupARN") - } - - request := &autoscaling.AttachLoadBalancerTargetGroupsInput{} - request.AutoScalingGroupName = e.AutoscalingGroup.Name - request.TargetGroupARNs = aws.StringSlice([]string{e.TargetGroupARN}) - - klog.V(2).Infof("Attaching autoscaling group %q to Target Group %q", fi.StringValue(e.AutoscalingGroup.Name), e.TargetGroupARN) - _, err := t.Cloud.Autoscaling().AttachLoadBalancerTargetGroups(request) - if err != nil { - return fmt.Errorf("error attaching autoscaling group to ELB: %v", err) - } - - return nil -} - -type terraformExternalTargetGroupAttachment struct { - TargetGroupARN *terraform.Literal `json:"alb_target_group_arn,omitempty" cty:"alb_target_group_arn"` - AutoscalingGroup *terraform.Literal `json:"autoscaling_group_name,omitempty" cty:"autoscaling_group_name"` -} - -func (_ *ExternalTargetGroupAttachment) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *ExternalTargetGroupAttachment) error { - tf := &terraformExternalTargetGroupAttachment{ - TargetGroupARN: terraform.LiteralFromStringValue(e.TargetGroupARN), - AutoscalingGroup: e.AutoscalingGroup.TerraformLink(), - } - - return t.RenderResource("aws_autoscaling_attachment", e.name(), tf) -} - -func (e *ExternalTargetGroupAttachment) TerraformLink() *terraform.Literal { - return terraform.LiteralProperty("aws_autoscaling_attachment", e.name(), "id") -} - -func (_ *ExternalTargetGroupAttachment) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *ExternalTargetGroupAttachment) error { - cfObj, ok := t.Find(e.AutoscalingGroup.CloudformationLink()) - if !ok { - // topo-sort fail? - return fmt.Errorf("AutoScalingGroup not yet rendered") - } - cf, ok := cfObj.(*cloudformationAutoscalingGroup) - if !ok { - return fmt.Errorf("unexpected type for CF record: %T", cfObj) - } - - cf.TargetGroupARNs = append(cf.TargetGroupARNs, cloudformation.LiteralString(e.TargetGroupARN)) - return nil -} diff --git a/upup/pkg/fi/cloudup/awstasks/externalloadbalancerattachment_fitask.go b/upup/pkg/fi/cloudup/awstasks/externalloadbalancerattachment_fitask.go deleted file mode 100644 index f34c9bf026fca..0000000000000 --- a/upup/pkg/fi/cloudup/awstasks/externalloadbalancerattachment_fitask.go +++ /dev/null @@ -1,51 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 2020 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by fitask. DO NOT EDIT. - -package awstasks - -import ( - "k8s.io/kops/upup/pkg/fi" -) - -// ExternalLoadBalancerAttachment - -var _ fi.HasLifecycle = &ExternalLoadBalancerAttachment{} - -// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle -func (o *ExternalLoadBalancerAttachment) GetLifecycle() *fi.Lifecycle { - return o.Lifecycle -} - -// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle -func (o *ExternalLoadBalancerAttachment) SetLifecycle(lifecycle fi.Lifecycle) { - o.Lifecycle = &lifecycle -} - -var _ fi.HasName = &ExternalLoadBalancerAttachment{} - -// GetName returns the Name of the object, implementing fi.HasName -func (o *ExternalLoadBalancerAttachment) GetName() *string { - return o.Name -} - -// String is the stringer function for the task, producing readable output using fi.TaskAsString -func (o *ExternalLoadBalancerAttachment) String() string { - return fi.TaskAsString(o) -} diff --git a/upup/pkg/fi/cloudup/awstasks/externaltargetgroupattachment_fitask.go b/upup/pkg/fi/cloudup/awstasks/externaltargetgroupattachment_fitask.go deleted file mode 100644 index 562964c354bb5..0000000000000 --- a/upup/pkg/fi/cloudup/awstasks/externaltargetgroupattachment_fitask.go +++ /dev/null @@ -1,51 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 2020 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by fitask. DO NOT EDIT. - -package awstasks - -import ( - "k8s.io/kops/upup/pkg/fi" -) - -// ExternalTargetGroupAttachment - -var _ fi.HasLifecycle = &ExternalTargetGroupAttachment{} - -// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle -func (o *ExternalTargetGroupAttachment) GetLifecycle() *fi.Lifecycle { - return o.Lifecycle -} - -// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle -func (o *ExternalTargetGroupAttachment) SetLifecycle(lifecycle fi.Lifecycle) { - o.Lifecycle = &lifecycle -} - -var _ fi.HasName = &ExternalTargetGroupAttachment{} - -// GetName returns the Name of the object, implementing fi.HasName -func (o *ExternalTargetGroupAttachment) GetName() *string { - return o.Name -} - -// String is the stringer function for the task, producing readable output using fi.TaskAsString -func (o *ExternalTargetGroupAttachment) String() string { - return fi.TaskAsString(o) -} diff --git a/upup/pkg/fi/cloudup/awstasks/load_balancer.go b/upup/pkg/fi/cloudup/awstasks/load_balancer.go index 892a767f454ad..b80761d25f2db 100644 --- a/upup/pkg/fi/cloudup/awstasks/load_balancer.go +++ b/upup/pkg/fi/cloudup/awstasks/load_balancer.go @@ -67,6 +67,9 @@ type LoadBalancer struct { Tags map[string]string ForAPIServer bool + + // Shared is set if this is an external LB (one we don't create or own) + Shared *bool } var _ fi.CompareWithID = &LoadBalancer{} @@ -438,6 +441,13 @@ func (e *LoadBalancer) Run(c *fi.Context) error { return fi.DefaultDeltaRunMethod(e, c) } +func (_ *LoadBalancer) ShouldCreate(a, e, changes *LoadBalancer) (bool, error) { + if fi.BoolValue(e.Shared) { + return false, nil + } + return true, nil +} + func (e *LoadBalancer) Normalize() { // We need to sort our arrays consistently, so we don't get spurious changes sort.Stable(OrderSubnetsById(e.Subnets)) @@ -449,11 +459,15 @@ func (s *LoadBalancer) CheckChanges(a, e, changes *LoadBalancer) error { if fi.StringValue(e.Name) == "" { return fi.RequiredField("Name") } - if len(e.SecurityGroups) == 0 { - return fi.RequiredField("SecurityGroups") - } - if len(e.Subnets) == 0 { - return fi.RequiredField("Subnets") + + shared := fi.BoolValue(e.Shared) + if !shared { + if len(e.SecurityGroups) == 0 { + return fi.RequiredField("SecurityGroups") + } + if len(e.Subnets) == 0 { + return fi.RequiredField("Subnets") + } } if e.AccessLog != nil { @@ -483,6 +497,11 @@ func (s *LoadBalancer) CheckChanges(a, e, changes *LoadBalancer) error { } func (_ *LoadBalancer) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *LoadBalancer) error { + shared := fi.BoolValue(e.Shared) + if shared { + return nil + } + var loadBalancerName string if a == nil { if e.LoadBalancerName == nil { @@ -692,6 +711,11 @@ type terraformLoadBalancerHealthCheck struct { } func (_ *LoadBalancer) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *LoadBalancer) error { + shared := fi.BoolValue(e.Shared) + if shared { + return nil + } + cloud := t.Cloud.(awsup.AWSCloud) if e.LoadBalancerName == nil { @@ -782,6 +806,16 @@ func (_ *LoadBalancer) RenderTerraform(t *terraform.TerraformTarget, a, e, chang } func (e *LoadBalancer) TerraformLink(params ...string) *terraform.Literal { + shared := fi.BoolValue(e.Shared) + if shared { + if e.Name == nil { + klog.Fatalf("Name must be set, if LB is shared: %s", e) + } + + klog.V(4).Infof("reusing existing LB with name %q", *e.Name) + return terraform.LiteralFromStringValue(*e.Name) + } + prop := "id" if len(params) > 0 { prop = params[0] @@ -836,6 +870,11 @@ func (_ *LoadBalancer) RenderCloudformation(t *cloudformation.CloudformationTarg // If this resource has a public IP address and is also in a VPC that is defined in the same template, // you must use the DependsOn attribute to declare a dependency on the VPC-gateway attachment. + shared := fi.BoolValue(e.Shared) + if shared { + return nil + } + cloud := t.Cloud.(awsup.AWSCloud) if e.LoadBalancerName == nil { @@ -912,6 +951,16 @@ func (_ *LoadBalancer) RenderCloudformation(t *cloudformation.CloudformationTarg } func (e *LoadBalancer) CloudformationLink() *cloudformation.Literal { + shared := fi.BoolValue(e.Shared) + if shared { + if e.Name == nil { + klog.Fatalf("Name must be set, if LB is shared: %s", e) + } + + klog.V(4).Infof("reusing existing LB with name %q", *e.Name) + return cloudformation.LiteralString(*e.Name) + } + return cloudformation.Ref("AWS::ElasticLoadBalancing::LoadBalancer", *e.Name) } diff --git a/upup/pkg/fi/cloudup/awstasks/load_balancer_attachment.go b/upup/pkg/fi/cloudup/awstasks/load_balancer_attachment.go deleted file mode 100644 index 46c840f54a823..0000000000000 --- a/upup/pkg/fi/cloudup/awstasks/load_balancer_attachment.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package awstasks - -import ( - "fmt" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/autoscaling" - "github.com/aws/aws-sdk-go/service/elb" - "k8s.io/klog/v2" - "k8s.io/kops/upup/pkg/fi" - "k8s.io/kops/upup/pkg/fi/cloudup/awsup" - "k8s.io/kops/upup/pkg/fi/cloudup/cloudformation" - "k8s.io/kops/upup/pkg/fi/cloudup/terraform" -) - -// +kops:fitask -type LoadBalancerAttachment struct { - Name *string - Lifecycle *fi.Lifecycle - - LoadBalancer *LoadBalancer - - // LoadBalancerAttachments now support ASGs or direct instances - AutoscalingGroup *AutoscalingGroup - Subnet *Subnet - - // Here be dragons.. - // This will *NOT* unmarshal.. for some reason this pointer is initiated as nil - // instead of a pointer to Instance with nil members.. - Instance *Instance -} - -func (e *LoadBalancerAttachment) Find(c *fi.Context) (*LoadBalancerAttachment, error) { - cloud := c.Cloud.(awsup.AWSCloud) - - // Instance only - if e.Instance != nil && e.AutoscalingGroup == nil { - i, err := e.Instance.Find(c) - if err != nil { - return nil, fmt.Errorf("unable to find instance: %v", err) - } - actual := &LoadBalancerAttachment{} - actual.LoadBalancer = e.LoadBalancer - actual.Instance = i - return actual, nil - // ASG only - } else if e.AutoscalingGroup != nil && e.Instance == nil { - if aws.StringValue(e.LoadBalancer.LoadBalancerName) == "" { - return nil, fmt.Errorf("LoadBalancer did not have LoadBalancerName set") - } - - g, err := findAutoscalingGroup(cloud, *e.AutoscalingGroup.Name) - if err != nil { - return nil, err - } - if g == nil { - return nil, nil - } - - for _, name := range g.LoadBalancerNames { - if aws.StringValue(name) != *e.LoadBalancer.LoadBalancerName { - continue - } - - actual := &LoadBalancerAttachment{} - actual.LoadBalancer = e.LoadBalancer - actual.AutoscalingGroup = e.AutoscalingGroup - - // Prevent spurious changes - actual.Name = e.Name // ELB attachments don't have tags - actual.Lifecycle = e.Lifecycle - - return actual, nil - } - } else { - // Invalid request - return nil, fmt.Errorf("Must specify either an instance or an ASG") - } - - return nil, nil -} - -func (e *LoadBalancerAttachment) Run(c *fi.Context) error { - return fi.DefaultDeltaRunMethod(e, c) -} - -func (s *LoadBalancerAttachment) CheckChanges(a, e, changes *LoadBalancerAttachment) error { - if a == nil { - if e.LoadBalancer == nil { - return fi.RequiredField("LoadBalancer") - } - if e.AutoscalingGroup == nil { - return fi.RequiredField("AutoscalingGroup") - } - } - return nil -} - -func (_ *LoadBalancerAttachment) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *LoadBalancerAttachment) error { - if e.LoadBalancer == nil { - return fi.RequiredField("LoadBalancer") - } - loadBalancerName := fi.StringValue(e.LoadBalancer.LoadBalancerName) - if loadBalancerName == "" { - return fi.RequiredField("LoadBalancer.LoadBalancerName") - } - - if e.AutoscalingGroup != nil && e.Instance == nil { - request := &autoscaling.AttachLoadBalancersInput{} - request.AutoScalingGroupName = e.AutoscalingGroup.Name - request.LoadBalancerNames = aws.StringSlice([]string{loadBalancerName}) - - klog.V(2).Infof("Attaching autoscaling group %q to ELB %q", fi.StringValue(e.AutoscalingGroup.Name), loadBalancerName) - _, err := t.Cloud.Autoscaling().AttachLoadBalancers(request) - if err != nil { - return fmt.Errorf("error attaching autoscaling group to ELB: %v", err) - } - } else if e.AutoscalingGroup == nil && e.Instance != nil { - request := &elb.RegisterInstancesWithLoadBalancerInput{} - request.Instances = append(request.Instances, &elb.Instance{InstanceId: e.Instance.ID}) - request.LoadBalancerName = aws.String(loadBalancerName) - - klog.V(2).Infof("Attaching instance %q to ELB %q", fi.StringValue(e.Instance.ID), loadBalancerName) - _, err := t.Cloud.ELB().RegisterInstancesWithLoadBalancer(request) - if err != nil { - return fmt.Errorf("error attaching instance to ELB: %v", err) - } - } - return nil -} - -type terraformLoadBalancerAttachment struct { - ELB *terraform.Literal `json:"elb" cty:"elb"` - Instance *terraform.Literal `json:"instance,omitempty" cty:"instance"` - AutoscalingGroup *terraform.Literal `json:"autoscaling_group_name,omitempty" cty:"autoscaling_group_name"` -} - -func (_ *LoadBalancerAttachment) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *LoadBalancerAttachment) error { - tf := &terraformLoadBalancerAttachment{ - ELB: e.LoadBalancer.TerraformLink(), - } - - if e.AutoscalingGroup != nil && e.Instance == nil { - tf.AutoscalingGroup = e.AutoscalingGroup.TerraformLink() - return t.RenderResource("aws_autoscaling_attachment", *e.AutoscalingGroup.Name, tf) - } else if e.AutoscalingGroup == nil && e.Instance != nil { - tf.Instance = e.Instance.TerraformLink() - return t.RenderResource("aws_elb_attachment", *e.LoadBalancer.Name, tf) - } - return nil -} - -func (e *LoadBalancerAttachment) TerraformLink() *terraform.Literal { - if e.AutoscalingGroup != nil && e.Instance == nil { - return terraform.LiteralProperty("aws_autoscaling_attachment", *e.AutoscalingGroup.Name, "id") - } else if e.AutoscalingGroup == nil && e.Instance != nil { - return terraform.LiteralProperty("aws_elb_attachment", *e.LoadBalancer.Name, "id") - } - return nil -} - -func (_ *LoadBalancerAttachment) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *LoadBalancerAttachment) error { - if e.AutoscalingGroup != nil { - cfObj, ok := t.Find(e.AutoscalingGroup.CloudformationLink()) - if !ok { - // topo-sort fail? - return fmt.Errorf("AutoScalingGroup not yet rendered") - } - cf, ok := cfObj.(*cloudformationAutoscalingGroup) - if !ok { - return fmt.Errorf("unexpected type for CF record: %T", cfObj) - } - - cf.LoadBalancerNames = append(cf.LoadBalancerNames, e.LoadBalancer.CloudformationLink()) - } - if e.Instance != nil { - return fmt.Errorf("expected Instance to be nil") - } - return nil -} diff --git a/upup/pkg/fi/cloudup/awstasks/targetgroup.go b/upup/pkg/fi/cloudup/awstasks/targetgroup.go new file mode 100644 index 0000000000000..0a17ba31a03d2 --- /dev/null +++ b/upup/pkg/fi/cloudup/awstasks/targetgroup.go @@ -0,0 +1,136 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package awstasks + +import ( + "fmt" + + "k8s.io/klog/v2" + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/awsup" + "k8s.io/kops/upup/pkg/fi/cloudup/cloudformation" + "k8s.io/kops/upup/pkg/fi/cloudup/terraform" +) + +// TargetGroup manages an targetgroup used for an ALB/NLB. +// +kops:fitask +type TargetGroup struct { + Name *string + Lifecycle *fi.Lifecycle + + // ARN is the Amazon Resource Name for the Target Group + ARN *string + + // Shared is set if this is an external LB (one we don't create or own) + Shared *bool +} + +var _ fi.CompareWithID = &TargetGroup{} + +func (e *TargetGroup) CompareWithID() *string { + return e.ARN +} + +func (e *TargetGroup) Find(c *fi.Context) (*TargetGroup, error) { + if e.ARN == nil { + return nil, fmt.Errorf("ARN must be set for TargetGroup") + } + + actual := &TargetGroup{} + actual.ARN = e.ARN + + // Prevent spurious changes + actual.Name = e.Name + actual.Lifecycle = e.Lifecycle + + return actual, nil +} + +func (e *TargetGroup) Run(c *fi.Context) error { + return fi.DefaultDeltaRunMethod(e, c) +} + +func (_ *TargetGroup) ShouldCreate(a, e, changes *TargetGroup) (bool, error) { + if fi.BoolValue(e.Shared) { + return false, nil + } + return true, nil +} + +func (s *TargetGroup) CheckChanges(a, e, changes *TargetGroup) error { + if a == nil { + if e.ARN == nil { + return fi.RequiredField("ARN") + } + } + return nil +} + +func (_ *TargetGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *TargetGroup) error { + shared := fi.BoolValue(e.Shared) + if shared { + return nil + } + + return fmt.Errorf("non shared Target Groups is not yet supported") +} + +func (_ *TargetGroup) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *TargetGroup) error { + shared := fi.BoolValue(e.Shared) + if shared { + return nil + } + + return fmt.Errorf("non shared Target Groups is not yet supported") +} + +func (e *TargetGroup) TerraformLink(params ...string) *terraform.Literal { + shared := fi.BoolValue(e.Shared) + if shared { + if e.ARN == nil { + klog.Fatalf("ARN must be set for shared Target Group: %s", e) + } + + klog.V(4).Infof("reusing existing Target Group with ARN %q", *e.ARN) + return terraform.LiteralFromStringValue(*e.ARN) + } + + return nil +} + +func (_ *TargetGroup) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *TargetGroup) error { + shared := fi.BoolValue(e.Shared) + if shared { + return nil + } + + return fmt.Errorf("non shared Target Groups is not yet supported") +} + +func (e *TargetGroup) CloudformationLink() *cloudformation.Literal { + shared := fi.BoolValue(e.Shared) + if shared { + if e.ARN == nil { + klog.Fatalf("ARN must be set for shared Target Group: %s", e) + } + + klog.V(4).Infof("reusing existing Target Group with ARN %q", *e.ARN) + return cloudformation.LiteralString(*e.ARN) + } + + return nil +} diff --git a/upup/pkg/fi/cloudup/awstasks/loadbalancerattachment_fitask.go b/upup/pkg/fi/cloudup/awstasks/targetgroup_fitask.go similarity index 75% rename from upup/pkg/fi/cloudup/awstasks/loadbalancerattachment_fitask.go rename to upup/pkg/fi/cloudup/awstasks/targetgroup_fitask.go index 48e03eabb412b..ddf0bc9d8fd99 100644 --- a/upup/pkg/fi/cloudup/awstasks/loadbalancerattachment_fitask.go +++ b/upup/pkg/fi/cloudup/awstasks/targetgroup_fitask.go @@ -24,28 +24,28 @@ import ( "k8s.io/kops/upup/pkg/fi" ) -// LoadBalancerAttachment +// TargetGroup -var _ fi.HasLifecycle = &LoadBalancerAttachment{} +var _ fi.HasLifecycle = &TargetGroup{} // GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle -func (o *LoadBalancerAttachment) GetLifecycle() *fi.Lifecycle { +func (o *TargetGroup) GetLifecycle() *fi.Lifecycle { return o.Lifecycle } // SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle -func (o *LoadBalancerAttachment) SetLifecycle(lifecycle fi.Lifecycle) { +func (o *TargetGroup) SetLifecycle(lifecycle fi.Lifecycle) { o.Lifecycle = &lifecycle } -var _ fi.HasName = &LoadBalancerAttachment{} +var _ fi.HasName = &TargetGroup{} // GetName returns the Name of the object, implementing fi.HasName -func (o *LoadBalancerAttachment) GetName() *string { +func (o *TargetGroup) GetName() *string { return o.Name } // String is the stringer function for the task, producing readable output using fi.TaskAsString -func (o *LoadBalancerAttachment) String() string { +func (o *TargetGroup) String() string { return fi.TaskAsString(o) }