Skip to content
This repository has been archived by the owner on Jan 8, 2024. It is now read-only.

Fix bring-your-own-alb for plugin/ecs #4457

Merged
merged 13 commits into from Feb 1, 2023
Merged

Fix bring-your-own-alb for plugin/ecs #4457

merged 13 commits into from Feb 1, 2023

Conversation

izaaklauer
Copy link
Contributor

@izaaklauer izaaklauer commented Jan 27, 2023

Fixes #4456
Replaces #4457

What changed

  • Introduces a new field load_balancer_arn to the ecs plugin's deploy config. If specified, waypoint will use this alb rather than creating it's own.
  • Removes listener_arn field from same config

Motivation

Prior to this, to use an existing alb (and prevent waypoint from creating a new one), you would specify a listener_arn in the ecs deployment config. In practice, that was broken (see the above issue)

This approach wan't ideal anyway. Waypoint needs to modify the listener on every deployment, so if someone were to create the listener through terraform and wire the output into waypoint, that terraform would also try to change the default listener on the rule away from waypoint's deployment on every run.

Waypoint should own the listener, but can inherit an existing ALB.

Backwards-compat

While removing a field is generally a breaking change, because the field was broken anyway (see issue), I can't see how anyone could have been depending on it.

How do I test this?

I'd recommend starting with https://github.com/hashicorp/waypoint-examples/tree/main/aws/aws-ecs/nodejs. Run a few waypoint deploy's while observing the aws alb, and notice how the default rule moves traffic to the most recent target group on every release.

To test bring-your-own-alb, you can copy the arn and subnets of the alb waypoint created in the above testing into your deploy config

Example:

...
  deploy {
    use "aws-ecs" {
...
      alb {
        load_balancer_arn = "arn:aws:elasticloadbalancing:us-east-1:863953081260:loadbalancer/app/waypoint-ecs-hello-izaak-2/9d50bd2cdea67345"
        subnets      = ["subnet-0bd08267365065f8e","subnet-04ff6753f3ba35e49","subnet-02c9aaa7732de7eb7","subnet-047d5841370712b24","subnet-086d533750a535ea5","subnet-0f8dd2e1826266538"]
      }
    }
  }

Run a few more releases, and observe the same successful behavior.

Other fixes in this PR

  • Can now manage an ALB through terraform, and have waypoint use it
  • Can now allow waypoint to use an ALB with other listeners configured, without waypoint modifying the wrong listener.

Prior to this, our docs claimed we allowed users to bring their own alb listeners: https://developer.hashicorp.com/waypoint/plugins/aws-ecs#alb-listener_arn

But in practice, they would get this error:

```
$ waypoint deploy

» Deploying hello-app...
✓ Running deploy v22
✓ Deployment resources created
✓ Discovered service subnets
✓ Discovered alb subnets
✓ Using external security group hello-app-inbound
✓ Using internal security group hello-app-inbound-internal
✓ Using existing log group waypoint-logs
✓ Using existing execution IAM role "ecr-hello-app"
✓ Registered Task definition: waypoint-hello-app
✓ Using existing ECS cluster waypoint
✓ Created target group hello-app-01GQNGJSKZ557FNN7JPG7
✓ Modified ALB Listener to introduce target group
✓ Created ECS Service hello-app-01GQNGJSKZ557FNN7JPG7

» Performing operation locally
✓ Finished building report for ecs deployment
✓ Determining status of ecs service hello-app-01GQNGJSKZ557FNN7JPG7
✓ Found existing ECS cluster: waypoint
⚠️ 1 cluster READY, 1 service READY, 1 task MISSING
⚠️ Waypoint detected that the current deployment is not ready, however your application
might be available or still starting up.

» Releasing...

» Performing operation locally
✓ Running release v17
! ValidationError: A load balancer ARN must be specified
  	status code: 400, request id: f6328bdb-0616-484e-8654-140b8c2c1ca9
```

Waypoint hcl contents:
```
project = "hello-app"

app "hello-app" {
  build {
    use "pack" {}
    registry {
      use "aws-ecr" {
        region     = "us-east-1"
        repository = "waypoint-example"
        tag        = "latest"
      }
    }
  }

  deploy {
    use "aws-ecs" {
      region = "us-east-1"
      memory = "512"
      alb {
        listener_arn = "arn:aws:elasticloadbalancing:us-east-1:863953081260:listener/app/waypoint-ecs-hello-app/3cdb806e2340b21e/1c2034ee6148da78"
        subnets      = ["subnet-0bd08267365065f8e","subnet-04ff6753f3ba35e49","subnet-02c9aaa7732de7eb7","subnet-047d5841370712b24","subnet-086d533750a535ea5","subnet-0f8dd2e1826266538"]
      }

    }
  }
}

```

This change brings us in line with our docs.
var listener *elbv2.Listener
for _, l := range listeners.Listeners {
if *l.Port == int64(externalIngressPort) {
listener = l
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, we were always plucking the first listener from the ALB. Now, we make sure to modify the one that corresponds to our port of interest.

This is important if someone brings an ALB that already has a listener on another port that's unrelated to this waypoint app.


if !newListener {
if listener != nil {
s.Update("Modifying existing ALB Listener to introduce target group")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was no UI output previously! Now it reports progress to users.

@@ -51,107 +62,90 @@ func (r *Releaser) Release(
}
elbsrv := elbv2.New(sess)

dlb, err := elbsrv.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a part of the old bug - if the user specified a listener in the deploy config, we would always try to describe the load balancer here. The deploy plugin didn't set the load balancer arn, so target.LoadBalancerArn would be empty string and we'd get a 400.

} else {
log.Info("load-balancer defined", "dns-name", *lb.DNSName)

lo, err := elbsrv.CreateListener(&elbv2.CreateListenerInput{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this would ever happen - the deploy plugin was already creating a listener if necessary. Now we're relying on that, and only modifying the listener here.

@izaaklauer izaaklauer changed the title Ecs no listener Fix bring-your-own-alb for plugin/ecs Jan 27, 2023
I'm honestly not sure about this use-case, but it's existing behavior and i'm not going to fight it here.
Copy link
Contributor

@paladin-devops paladin-devops left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great - I think users will definitely take advantage of using an existing ALB (managed with Terraform)! I appreciate the well-commented changes - it helped me better understand what the platform and releaser plugins are intended to do in the first place.

if err != nil {
return status.Errorf(codes.Internal, "failed to create listener: %s", err)
}
if alb == nil || alb.Arn == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ I see that this if block already existed, but how is it enforced that the ALB is created before the listener is created? In the resource manager I can see that the ALB listener is sequentially after the ALB itself - is that what ensures that it is done first?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argmapper magic! This resourceAlbListenerCreate function takes an *Resource_Alb as input, and goes into the argmapper soup along with the resourceAlbCreate function that provides it. Argmapper figures out the right order to invoke the whole string of resource functions.

builtin/aws/ecs/platform.go Outdated Show resolved Hide resolved

_, err := elbsrv.ModifyListenerWithContext(ctx, &elbv2.ModifyListenerInput{
ListenerArn: listener.ListenerArn,
// Setting these on every deployment so that we pick up any potential changes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Co-authored-by: Joe <83741749+paladin-devops@users.noreply.github.com>
And properly using the load_balancer_arn field
The cert is a property of the listener, not the ALB, so it's OK for both of these to be set.
@@ -1054,7 +1054,7 @@ func (p *Platform) resourceAlbListenerCreate(
}
log.Info("load-balancer defined", "dns-name", alb.DnsName)

s.Update("Looking for listeners for ALB %q", alb.Name)
s.Update("Looking for listeners for ALB %q", alb.Arn)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name might not be defined if the user specified an arn.

@izaaklauer izaaklauer merged commit d9c5378 into main Feb 1, 2023
@izaaklauer izaaklauer deleted the ecs-no-listener branch February 1, 2023 17:43
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot use existing load balancer with ecs
3 participants