Skip to content

Commit

Permalink
[#3543] Classic to Application Load Balancer migration
Browse files Browse the repository at this point in the history
Implementation for migrating the rack API Classic Load Balancer to an Application Load Balancer.

- cloudformation changes
- new cli command `convox rack sync` to retrieve the new rack API URL

It's important to note that with the ELB migration, the rack API hostname will change.
The switch between hostname will be transparent for console managed racks. For users running racks outside of the console, we provide the `convox rack sync --name rack-name` command that should be executed after the update. This will print the new hostname to stdout.
  • Loading branch information
heronrs committed Nov 28, 2022
1 parent 227ad41 commit f7308e4
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 105 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ profile.out
cmd/convox/pkg
provider/aws/lambda/autoscale/handler
provider/aws/lambda/autoscale/lambda.zip
provider/aws/lambda/autoscale/handler
provider/aws/lambda/autoscale/lambda.zip
.vscode/
4 changes: 4 additions & 0 deletions pkg/cli/rack.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ func init() {
Validate: stdcli.Args(2),
})

register("rack sync", "sync v2 rack API url", RackSync, stdcli.CommandOptions{
Flags: []stdcli.Flag{flagRack, stdcli.StringFlag("name", "n", "rack name. Use it for non console managed racks")},
})

register("rack update", "update the rack", RackUpdate, stdcli.CommandOptions{
Flags: []stdcli.Flag{flagRack, flagWait},
Validate: stdcli.ArgsMax(1),
Expand Down
2 changes: 0 additions & 2 deletions pkg/structs/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ type Provider interface {
SystemUninstall(name string, w io.Writer, opts SystemUninstallOptions) error
SystemUpdate(opts SystemUpdateOptions) error
Sync(string) error

WithContext(ctx context.Context) Provider

Workers() error
}

Expand Down
229 changes: 128 additions & 101 deletions provider/aws/formation/rack.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Conditions": {
"ApiRouterALB": { "Fn::Equals": [ { "Ref": "ApiRouter" }, "ALB" ] },
"ApiRouterELB": { "Fn::Equals": [ { "Ref": "ApiRouter" }, "ELB" ] },
"Autoscale": { "Fn::Equals": [ { "Ref": "Autoscale" }, "Yes" ] },
"BlankAmi": { "Fn::Equals": [ { "Ref": "Ami" }, "" ] },
"BlankAvailabilityZones": { "Fn::Equals": [ { "Fn::Join": [ "", { "Ref": "AvailabilityZones" } ] }, "" ] },
Expand All @@ -25,6 +23,10 @@
"BlankRouterSecurityGroup": { "Fn::Equals": [ { "Fn::Join": [ ",", { "Ref": "RouterSecurityGroup" } ] }, "" ] },
"BlankRouterInternalSecurityGroup": { "Fn::Equals": [ { "Fn::Join": [ ",", { "Ref": "RouterInternalSecurityGroup" } ] }, "" ] },
"BlankSslPolicy": { "Fn::Equals": [ { "Ref": "SslPolicy" }, "" ] },
"CreateThirdPrivateSubnet": {"Fn::Or": [
{"Condition": "PrivateApiAndThirdAvailabilityZoneAndHighAvailability"},
{"Condition": "PrivateAndThirdAvailabilityZoneAndHighAvailability"}
]},
"DedicatedBuilder": { "Fn::Not": [ { "Fn::Equals": [ { "Ref": "BuildInstance" }, "" ] } ] },
"Development": { "Fn::Equals": [ { "Ref": "Development" }, "Yes" ] },
"EnableCloudWatch": { "Fn::Equals": [ { "Ref": "LogDriver" }, "CloudWatch" ] },
Expand Down Expand Up @@ -117,6 +119,9 @@
"Fn::And": [ { "Condition": "Private" }, { "Condition": "ThirdAvailabilityZone" }, { "Condition": "HighAvailability" } ]
},
"PrivateApi": { "Fn::Equals": [ { "Ref": "PrivateApi" }, "Yes" ] },
"PrivateApiAndThirdAvailabilityZoneAndHighAvailability": {
"Fn::And": [ { "Condition": "PrivateApi" }, { "Condition": "ThirdAvailabilityZone" }, { "Condition": "HighAvailability" } ]
},
"PrivateBuild": { "Fn::Or": [ { "Fn::Equals": [ { "Ref": "Private" }, "Yes" ] }, { "Fn::Equals": [ { "Ref": "PrivateBuild" }, "Yes" ] } ] },
"PrivateInstances": { "Fn::Equals": [ { "Ref": "Private" }, "Yes" ] },
"PublicRouter": { "Fn::Equals": [ { "Ref": "InternalOnly" }, "No" ] },
Expand Down Expand Up @@ -229,15 +234,12 @@
"Value": { "Ref": "CMKPolicy" }
},
"Dashboard": {
"Value": { "Fn::If": [ "ApiRouterALB",
{ "Fn::Join": [ ".", [
"rack",
{ "Fn::Select": [ 0, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "Router", "DNSName" ] } ] } ] },
{ "Fn::Select": [ 1, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "Router", "DNSName" ] } ] } ] },
"convox.site"
] ] },
{ "Fn::GetAtt": [ "Balancer", "DNSName" ] }
] }
"Value": { "Fn::Join": [ ".", [
"rack",
{ "Fn::Select": [ 0, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "ApiBalancer", "DNSName" ] } ] } ] },
{ "Fn::Select": [ 1, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "ApiBalancer", "DNSName" ] } ] } ] },
"convox.site"
] ] }
},
"Domain": {
"Condition": "PublicRouter",
Expand Down Expand Up @@ -493,7 +495,7 @@
"Value": { "Ref": "SubnetPrivate1" }
},
"SubnetPrivate2": {
"Condition": "PrivateAndThirdAvailabilityZoneAndHighAvailability",
"Condition": "CreateThirdPrivateSubnet",
"Export": { "Name": { "Fn::Sub": "${AWS::StackName}:SubnetPrivate2" } },
"Value": { "Ref": "SubnetPrivate2" }
},
Expand Down Expand Up @@ -1223,7 +1225,7 @@
}
},
"SubnetPrivate2": {
"Condition": "PrivateAndThirdAvailabilityZoneAndHighAvailability",
"Condition": "CreateThirdPrivateSubnet",
"Type": "AWS::EC2::Subnet",
"Properties": {
"Tags": [ { "Key": "Name", "Value": { "Fn::Join": [ " ", [ { "Ref": "AWS::StackName" }, "private", "2" ] ] } } ],
Expand Down Expand Up @@ -1403,15 +1405,12 @@
"Type": "CNAME",
"TTL": "3600",
"ResourceRecords": [
{ "Fn::If": [ "ApiRouterALB",
{ "Fn::Join": [ ".", [
"rack",
{ "Fn::Select": [ 0, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "Router", "DNSName" ] } ] } ] },
{ "Fn::Select": [ 1, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "Router", "DNSName" ] } ] } ] },
"convox.site"
] ] },
{ "Fn::GetAtt": [ "Balancer", "DNSName" ] }
] }
{ "Fn::Join": [ ".", [
"rack",
{ "Fn::Select": [ 0, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "ApiBalancer", "DNSName" ] } ] } ] },
{ "Fn::Select": [ 1, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "ApiBalancer", "DNSName" ] } ] } ] },
"convox.site"
] ] }
]
}
},
Expand Down Expand Up @@ -2620,42 +2619,6 @@
]
}
},
"RouterApiTargetGroup": {
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
"Condition": "ApiRouterALB",
"Properties": {
"HealthCheckIntervalSeconds": "5",
"HealthCheckTimeoutSeconds": "3",
"HealthyThresholdCount": "2",
"UnhealthyThresholdCount": "2",
"HealthCheckPath": "/check",
"Matcher": { "HttpCode": "200" },
"Port": "5443",
"Protocol": "HTTPS",
"TargetGroupAttributes": [
{ "Key": "deregistration_delay.timeout_seconds", "Value": "30" }
],
"TargetType": "instance",
"VpcId": { "Fn::If": [ "BlankExistingVpc", { "Ref": "Vpc" }, { "Ref": "ExistingVpc" } ] }
}
},
"RouterApiListenerRule": {
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
"Condition": "ApiRouterALB",
"Properties": {
"Actions": [ { "Type": "forward", "TargetGroupArn": { "Ref": "RouterApiTargetGroup" } } ],
"Conditions": [
{ "Field": "host-header", "Values": [ { "Fn::Join": [ ".", [
"rack",
{ "Fn::Select": [ 0, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "Router", "DNSName" ] } ] } ] },
{ "Fn::Select": [ 1, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "Router", "DNSName" ] } ] } ] },
"convox.site"
] ] } ] }
],
"ListenerArn": { "Ref": "RouterListener443" },
"Priority": "1"
}
},
"RouterInternal": {
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Condition": "Internal",
Expand Down Expand Up @@ -2746,47 +2709,117 @@
]
}
},
"Balancer": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Condition": "ApiRouterELB",
"ApiBalancer": {
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
"DependsOn": [ "ApiRole", "LogsPolicy" ],
"Properties": {
"ConnectionDrainingPolicy": { "Enabled": true, "Timeout": 60 },
"ConnectionSettings": { "IdleTimeout": 3600 },
"CrossZone": true,
"HealthCheck": {
"HealthyThreshold": "2",
"Interval": 5,
"Target": { "Fn::If": [ "PrivateApi", "HTTPS:3100/check", "HTTPS:3000/check" ] },
"Timeout": 3,
"UnhealthyThreshold": "2"
},
"LBCookieStickinessPolicy": [ { "PolicyName": "affinity" } ],
"Listeners": { "Fn::If": [ "PrivateApi",
[
{ "Protocol": "TCP", "LoadBalancerPort": "443", "InstanceProtocol": "TCP", "InstancePort": "3100" }
],
[
{ "Protocol": "TCP", "LoadBalancerPort": "443", "InstanceProtocol": "TCP", "InstancePort": "3000" }
]
] },
"LoadBalancerName": { "Fn::If": [ "PrivateApi", { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, "i" ] ] }, { "Ref": "AWS::StackName" } ] },
"Scheme": { "Fn::If": [ "PrivateApi", "internal", { "Ref": "AWS::NoValue" } ] },
"SecurityGroups": [ { "Ref": "BalancerSecurity" } ],
"Name": { "Fn::If": [ "PrivateApi", { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, "i" ] ] }, { "Ref": "AWS::StackName" } ] },
"LoadBalancerAttributes": [
{ "Key": "access_logs.s3.enabled", "Value": "true" },
{ "Key": "access_logs.s3.bucket", "Value": { "Fn::If": [ "BlankLogBucket", { "Ref": "ELBLogs" }, { "Ref": "LogBucket" } ] } },
{ "Key": "access_logs.s3.prefix", "Value": { "Fn::Sub": "convox/logs/${AWS::StackName}/alb" } },
{ "Key": "idle_timeout.timeout_seconds", "Value": { "Ref": "LoadBalancerIdleTimeout" } },
{ "Key": "routing.http.desync_mitigation_mode", "Value": { "Ref": "RouterMitigationMode" } },
{ "Key": "routing.http.drop_invalid_header_fields.enabled", "Value": "true" }
],
"Scheme": { "Fn::If": [ "PrivateApi", "internal", "internet-facing" ] },
"Subnets": {
"Fn::If": [ "PrivateApi",
[ { "Ref": "SubnetPrivate0" }, { "Ref": "SubnetPrivate1" }, { "Fn::If": [ "ThirdAvailabilityZoneAndHighAvailability", { "Ref": "SubnetPrivate2" }, { "Ref": "AWS::NoValue" } ] } ],
[ { "Ref": "SubnetPrivate0" }, { "Ref": "SubnetPrivate1" }, { "Fn::If": [ "CreateThirdPrivateSubnet", { "Ref": "SubnetPrivate2" }, { "Ref": "AWS::NoValue" } ] } ],
[ { "Ref": "Subnet0" }, { "Ref": "Subnet1" }, { "Fn::If": [ "ThirdAvailabilityZoneAndHighAvailability", { "Ref": "Subnet2" }, { "Ref": "AWS::NoValue" } ] } ]
]
},
"SecurityGroups": [ { "Ref": "ApiBalancerSecurity" } ],
"Tags": [
{ "Key": "GatewayAttachment", "Value": { "Fn::If": [ "ExistingVpc", "existing", { "Ref": "GatewayAttachment" } ] } },
{ "Key": "Name", "Value": { "Ref": "AWS::StackName" } }
{ "Key": "Rack", "Value": { "Ref": "AWS::StackName" } }
]
}
},
"BalancerSecurity": {
"ApiBalancerCertificate": {
"Type": "AWS::CertificateManager::Certificate",
"Properties": {
"DomainName": { "Fn::Join": [ ".", [
"*",
{ "Fn::Select": [ 0, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "ApiBalancer", "DNSName" ] } ] } ] },
{ "Fn::Select": [ 1, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "ApiBalancer", "DNSName" ] } ] } ] },
"convox.site"
] ] },
"DomainValidationOptions": [
{
"DomainName": { "Fn::Join": [ ".", [
"*",
{ "Fn::Select": [ 0, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "ApiBalancer", "DNSName" ] } ] } ] },
{ "Fn::Select": [ 1, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "ApiBalancer", "DNSName" ] } ] } ] },
"convox.site"
] ] },
"ValidationDomain": "convox.site"
}
]
}
},
"ApiBalancerListener443": {
"Type": "AWS::ElasticLoadBalancingV2::Listener",
"Properties": {
"Certificates": [ { "CertificateArn": { "Ref": "ApiBalancerCertificate" } } ],
"DefaultActions": [ { "Type": "fixed-response", "FixedResponseConfig": { "StatusCode": "404", "MessageBody": "unknown host" } } ],
"LoadBalancerArn": { "Ref" : "ApiBalancer" },
"Port": "443",
"Protocol": "HTTPS",
"SslPolicy": { "Fn::If": [ "BlankSslPolicy", { "Ref": "AWS::NoValue" }, { "Ref": "SslPolicy" } ] }
}
},
"ApiBalancerTargetGroup": {
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
"DependsOn": "ApiBalancerListener443",
"Properties": {
"HealthCheckIntervalSeconds": "5",
"HealthCheckTimeoutSeconds": "3",
"HealthyThresholdCount": "2",
"UnhealthyThresholdCount": "2",
"HealthCheckPath": "/check",
"Matcher": { "HttpCode": "200" },
"Port": { "Fn::If": ["PrivateApi", "4100", "4000"] },
"Protocol": "HTTPS",
"TargetGroupAttributes": [
{ "Key": "deregistration_delay.timeout_seconds", "Value": "30" }
],
"TargetType": "instance",
"VpcId": { "Fn::If": [ "BlankExistingVpc", { "Ref": "Vpc" }, { "Ref": "ExistingVpc" } ] }
}
},
"ApiBalancerListenerRule": {
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
"Properties": {
"Actions": [ { "Type": "forward", "TargetGroupArn": { "Ref": "ApiBalancerTargetGroup" } } ],
"Conditions": [
{ "Field": "host-header", "Values": [ { "Fn::Join": [ ".", [
"rack",
{ "Fn::Select": [ 0, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "ApiBalancer", "DNSName" ] } ] } ] },
{ "Fn::Select": [ 1, { "Fn::Split": [ ".", { "Fn::GetAtt": [ "ApiBalancer", "DNSName" ] } ] } ] },
"convox.site"
] ] } ] }
],
"ListenerArn": { "Ref": "ApiBalancerListener443" },
"Priority": "1"
}
},
"ApiBalancerListenerRuleInternal": {
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
"Properties": {
"Actions": [ { "Type": "forward", "TargetGroupArn": { "Ref": "ApiBalancerTargetGroup" } } ],
"Conditions": [
{ "Field": "host-header", "Values": [ { "Fn::Join": [ ".", [
"rack",
{"Ref": "AWS::StackName"},
"convox"
] ] } ] }
],
"ListenerArn": { "Ref": "ApiBalancerListener443" },
"Priority": "2"
}
},
"ApiBalancerSecurity": {
"Type": "AWS::EC2::SecurityGroup",
"Condition": "ApiRouterELB",
"Properties": {
"GroupDescription": { "Fn::Sub": "${AWS::StackName} balancer" },
"SecurityGroupIngress": [
Expand Down Expand Up @@ -2906,7 +2939,7 @@
]
}
},
"ApiMonitorService": {
"ServiceMonitorApi": {
"Type": "AWS::ECS::Service",
"DependsOn": [ "Instances" ],
"Properties": {
Expand All @@ -2917,17 +2950,14 @@
"TaskDefinition": { "Ref": "ApiMonitorTasks" }
}
},
"ApiWebService": {
"ServiceWebApi": {
"Type": "AWS::ECS::Service",
"DependsOn": [ "Instances" ],
"DependsOn": [ "Instances", "ApiBalancerListenerRule" ],
"Properties": {
"Cluster": { "Ref": "Cluster" },
"DeploymentConfiguration": { "MinimumHealthyPercent": { "Fn::If": [ "HighAvailability", "50", "0" ] }, "MaximumPercent": "200" },
"DesiredCount": { "Fn::If": [ "HighAvailability", { "Ref": "ApiCount" }, 1 ] },
"LoadBalancers": [ { "Fn::If": [ "ApiRouterALB",
{ "ContainerName": "web", "ContainerPort": "5443", "TargetGroupArn": { "Ref": "RouterApiTargetGroup" } },
{ "ContainerName": "web", "ContainerPort": "5443", "LoadBalancerName": { "Ref": "Balancer" } }
] } ],
"LoadBalancers": [ { "ContainerName": "web", "ContainerPort": "5443", "TargetGroupArn": { "Ref": "ApiBalancerTargetGroup" } }],
"Role": { "Fn::GetAtt": [ "ServiceRole", "Arn" ] },
"TaskDefinition": { "Ref": "ApiWebTasks" }
}
Expand Down Expand Up @@ -3263,12 +3293,9 @@
"MemoryReservation": { "Ref": "ApiWebMemory" },
"MountPoints": [ { "SourceVolume": "docker", "ContainerPath": "/var/run/docker.sock" } ],
"Name": "web",
"PortMappings": { "Fn::If": [ "ApiRouterALB",
[ { "ContainerPort": "5443", "Protocol": "tcp" } ],
{ "Fn::If": [ "PrivateApi",
[ { "HostPort": "3100", "ContainerPort": "5443", "Protocol": "tcp" } ],
[ { "HostPort": "3000", "ContainerPort": "5443", "Protocol": "tcp" } ]
] }
"PortMappings": { "Fn::If": [ "PrivateApi",
[ { "HostPort": "4100", "ContainerPort": "5443", "Protocol": "tcp" } ],
[ { "HostPort": "4000", "ContainerPort": "5443", "Protocol": "tcp" } ]
] }
}
],
Expand Down

0 comments on commit f7308e4

Please sign in to comment.