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

Commit

Permalink
Merge pull request #4742 from hashicorp/i-ecs-target-group-protocol
Browse files Browse the repository at this point in the history
ECS bug fixes and improvements
  • Loading branch information
paladin-devops committed May 31, 2023
2 parents 05f46b9 + 48e79f7 commit 3d59666
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 14 deletions.
21 changes: 21 additions & 0 deletions .changelog/4742.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
```release-note:improvement
plugin/aws-ecs: Add config options for the target group protocol and protocol
version.
```
```release-note:bug
plugin/aws-ecs: Set the protocol of a health check correctly.
```
```release-note:bug
plugin/aws-ecs: Fix panic when settings `grpc_code` or `http_code` for a health
check.
```
```release-note:bug
plugin/aws-ecs: Fix failure when destroying the target group during a release
destroy operation.
```
```release-note:bug
plugin/aws-ecs: Destroy the ALB only if it is managed by Waypoint.
```
```release-note:bug
runnerinstall/aws-ecs: Add missing permission to on-demand runner IAM policy.
```
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,21 @@ parameter {
default_value = "public subnets in the default VPC"
}

parameter {
key = "target_group_protocol"
description = "The protocol to use for routing traffic to the targets.\nThe protocol to use for routing traffic to the targets. For Application Load Balancers, the supported protocols are HTTP and HTTPS. For Network Load Balancers, the supported protocols are TCP, TLS, UDP, or TCP_UDP. For Gateway Load Balancers, the supported protocol is GENEVE. A TCP_UDP listener must be associated with a TCP_UDP target group. If the target is a Lambda function, this parameter does not apply."
type = "string"
required = false
default_value = "HTTP"
}

parameter {
key = "target_group_protocol_version"
description = "The version of the protocol to use for routing traffic to the targets.\n[HTTP/HTTPS protocol] The protocol version. Specify GRPC to send requests to targets using gRPC. Specify HTTP2 to send requests to targets using HTTP/2. The default is HTTP1, which sends requests to targets using HTTP/1.1."
type = "string"
required = false
}

parameter {
key = "task_role_name"
description = "the name of the task IAM role to assign.\nIf no role exists and a one or more task role policies are requested, a role with this name will be created."
Expand Down
85 changes: 78 additions & 7 deletions builtin/aws/ecs/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -1315,14 +1315,24 @@ func (p *Platform) resourceTargetGroupCreate(
HealthCheckEnabled: aws.Bool(true),
Name: &targetGroupName,
Port: &state.Port,
Protocol: aws.String("HTTP"),
TargetType: aws.String("ip"),
VpcId: &subnets.Subnets.VpcId,
Matcher: &elbv2.Matcher{},
}

// default to HTTP
createTargetGroupInput.Protocol = aws.String("HTTP")
if p.config.Protocol != "" {
createTargetGroupInput.Protocol = aws.String(p.config.Protocol)
}

if p.config.ProtocolVersion != "" {
createTargetGroupInput.ProtocolVersion = aws.String(p.config.ProtocolVersion)
}

if p.config.HealthCheck != nil {
if p.config.HealthCheck.Protocol != "" {
createTargetGroupInput.Protocol = aws.String(p.config.HealthCheck.Protocol)
createTargetGroupInput.HealthCheckProtocol = aws.String(p.config.HealthCheck.Protocol)
}

if p.config.HealthCheck.Path != "" {
Expand Down Expand Up @@ -1398,7 +1408,7 @@ func (p *Platform) resourceTargetGroupDestroy(
})
if err != nil {
return status.Errorf(codes.Internal, "failed to describe target group %s (ARN: %q): %s", state.Name, state.Arn, err)
} else if len(groups.TargetGroups) > 0 {
} else if len(groups.TargetGroups) > 1 {
return status.Errorf(codes.FailedPrecondition, "only one target group should be returned for ARN %q, but found %d matching target groups", state.Arn, len(groups.TargetGroups))
}

Expand Down Expand Up @@ -1426,10 +1436,13 @@ func (p *Platform) resourceTargetGroupDestroy(
}

listeners, err := elbsrv.DescribeListeners(describeListenersInput)

log.Debug("inspecting listeners", "alb_arn", lb)
if err != nil {
return status.Errorf(codes.Internal, "failed to describe listeners for ALB (ARN: %q): %s", *lb, err)
}
for _, listener := range listeners.Listeners {
log.Debug("inspecting listener", "listener_arn", listener.ListenerArn)
for _, defaultAction := range listener.DefaultActions {
if *defaultAction.TargetGroupArn == state.Arn {
s.Update("Found active ALB listener with ARN " + *listener.ListenerArn)
Expand Down Expand Up @@ -1869,15 +1882,47 @@ func (p *Platform) resourceAlbDestroy(
return nil
}

s := sg.Add("Initializing ALB deletion")
s := sg.Add("Checking if ALB is managed by Waypoint")
defer s.Abort()

s.Update("Deleting ALB %s", state.DnsName)

elbsrv := elbv2.New(sess)
input := elbv2.DeleteLoadBalancerInput{LoadBalancerArn: &state.Arn}

_, err := elbsrv.DeleteLoadBalancer(&input)
loadBalancers, err := elbsrv.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{
LoadBalancerArns: []*string{&state.Arn},
})
if err != nil {
log.Error("error getting ALB details", "err", err.Error())
return status.Errorf(codes.Internal, "failed to get ALB details: %s", err)
}
for _, loadBalancer := range loadBalancers.LoadBalancers {
tdo, err := elbsrv.DescribeTags(&elbv2.DescribeTagsInput{ResourceArns: aws.StringSlice([]string{
*loadBalancer.LoadBalancerArn,
})})
if err != nil {
return status.Errorf(codes.Internal, "failed to get ALB with ARN %s tags: %s", *loadBalancer.LoadBalancerArn, err)
}

for _, tagDescription := range tdo.TagDescriptions {
for _, tag := range tagDescription.Tags {
if *tag.Key == "waypoint_managed" && *tag.Value == "true" {
goto DELETE_ALB
}
}
}
// If we reach this point, we've checked all of the tags on the ALB,
// and none of them indicate the ALB is managed by Waypoint
log.Debug("ALB not created by Waypoint - skipping ALB deletion")
s.Update("ALB is not managed by Waypoint - skipping ALB deletion")
s.Done()
return nil
}
DELETE_ALB:
s.Update("ALB is managed by Waypoint - proceeding with deletion")
s.Done()

s = sg.Add("Deleting ALB %s", state.DnsName)
_, err = elbsrv.DeleteLoadBalancer(&input)
if err != nil {
return status.Errorf(codes.Internal, "failed to remove ALB %s: %s", state.DnsName, err)
}
Expand Down Expand Up @@ -2893,7 +2938,14 @@ type Config struct {

Logging *Logging `hcl:"logging,block"`

// Health check configurations for the target group
HealthCheck *AppHealthCheck `hcl:"health_check,block"`

// The protocol to use for routing traffic to the targets
Protocol string `hcl:"target_group_protocol,optional"`

// The version of the protocol to use for routing traffic to the targets
ProtocolVersion string `hcl:"target_group_protocol_version,optional"`
}

type AppHealthCheck struct {
Expand Down Expand Up @@ -3303,6 +3355,25 @@ deploy {
"the instruction set CPU architecture that the Amazon ECS supports. Valid values are: \"x86_64\", \"arm64\"",
)

doc.SetField(
"target_group_protocol",
"The protocol to use for routing traffic to the targets.",
docs.Default("HTTP"),
docs.Summary("The protocol to use for routing traffic to the targets. "+
"For Application Load Balancers, the supported protocols are HTTP "+
"and HTTPS. For Network Load Balancers, the supported protocols are"+
" TCP, TLS, UDP, or TCP_UDP. For Gateway Load Balancers, the supported"+
" protocol is GENEVE. A TCP_UDP listener must be associated with a "+
"TCP_UDP target group. If the target is a Lambda function, this "+
"parameter does not apply."))

doc.SetField("target_group_protocol_version",
"The version of the protocol to use for routing traffic to the targets.",
docs.Summary("[HTTP/HTTPS protocol] The protocol version. Specify GRPC "+
"to send requests to targets using gRPC. Specify HTTP2 to send requests"+
" to targets using HTTP/2. The default is HTTP1, which sends requests "+
"to targets using HTTP/1.1."))

return doc, nil
}

Expand Down
16 changes: 11 additions & 5 deletions builtin/aws/ecs/releaser.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ func (r *Releaser) Release(
s.Update("Deployment did not define an ALB listener - skipping release.")
s.Done()
return &Release{}, nil

}

s.Update("Release initialized")
s.Done()

sess, err := utils.GetSession(&utils.SessionConfig{
Region: r.p.config.Region,
Logger: log,
Expand Down Expand Up @@ -134,22 +136,24 @@ func (r *Releaser) Release(
}

s = sg.Add("Checking that all targets are healthy...")
targetHealth, err := elbsrv.DescribeTargetHealthWithContext(ctx, &elbv2.DescribeTargetHealthInput{
targetHealth, err := elbsrv.DescribeTargetHealth(&elbv2.DescribeTargetHealthInput{
TargetGroupArn: aws.String(target.TargetGroupArn),
})
if err != nil {
log.Error("error getting target health", "err", err.Error())
return nil, errors.Wrapf(err, "failed to describe health of target group with ARN %q", target.TargetGroupArn)
}

// Check each target to see if any one of them isn't healthy, before we
// route 100% of traffic to it!
for _, targetHealth := range targetHealth.TargetHealthDescriptions {
for _, targetHealthDescription := range targetHealth.TargetHealthDescriptions {
log.Debug("checking target health", "target", targetHealthDescription.Target.Id)
// Possible states are: initial, healthy, unhealthy, unused, draining,
// and unavailable.
if *targetHealth.TargetHealth.State != "healthy" {
if *targetHealthDescription.TargetHealth.State != "healthy" {
return nil, errors.Errorf("target (id: %s) is not healthy - will "+
"only release when all targets in group (ARN: %q) are healthy",
*targetHealth.Target.Id, target.TargetGroupArn)
*targetHealthDescription.Target.Id, target.TargetGroupArn)
}
}
s.Update("All targets are healthy!")
Expand All @@ -176,6 +180,8 @@ func (r *Releaser) Release(
s.Done()

return &Release{
// TODO: Determine what the prefix to the hostname should be instead of
// assuming HTTP
Url: "http://" + hostname,
LoadBalancerArn: target.LoadBalancerArn,
}, nil
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/DataDog/opencensus-go-exporter-datadog v0.0.0-20210527074920-9baf37265e83
github.com/adrg/xdg v0.2.1
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
github.com/aws/aws-sdk-go v1.44.122
github.com/aws/aws-sdk-go v1.44.269
github.com/bmatcuk/doublestar v1.1.5
github.com/buildpacks/pack v0.20.0
github.com/cenkalti/backoff/v4 v4.1.1
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,9 @@ github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU
github.com/aws/aws-sdk-go v1.33.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/aws/aws-sdk-go v1.44.122 h1:p6mw01WBaNpbdP2xrisz5tIkcNwzj/HysobNoaAHjgo=
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.269 h1:NUNq++KMjhWUVVUIx7HYLgBpX16bWfTY1EdQRraLALo=
github.com/aws/aws-sdk-go v1.44.269/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
Expand Down
1 change: 1 addition & 0 deletions internal/runnerinstall/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const odrRolePolicy = `{
"elasticloadbalancing:DescribeRules",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:ModifyListener",
"elasticloadbalancing:DescribeTargetHealth",
"iam:AttachRolePolicy",
"iam:CreateRole",
"iam:GetRole",
Expand Down

0 comments on commit 3d59666

Please sign in to comment.