Skip to content

Commit d6a306e

Browse files
committed
Add support to expose both in internal and external loadbalancer (#3764)
Add support to expose both in internally and externally
1 parent 92afa66 commit d6a306e

File tree

4 files changed

+194
-26
lines changed

4 files changed

+194
-26
lines changed

pkg/manifest/manifest.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,10 @@ func (m *Manifest) ApplyDefaults() error {
331331
if !m.AttributeSet(fmt.Sprintf("services.%s.termination.grace", s.Name)) {
332332
m.Services[i].Termination.Grace = 30
333333
}
334+
335+
if s.InternalAndExternal {
336+
m.Services[i].Internal = true
337+
}
334338
}
335339

336340
return nil

pkg/manifest/service.go

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,30 @@ import (
1010
type Service struct {
1111
Name string `yaml:"-"`
1212

13-
Agent ServiceAgent `yaml:"agent,omitempty"`
14-
Build ServiceBuild `yaml:"build,omitempty"`
15-
Command ServiceCommand `yaml:"command,omitempty"`
16-
Deployment ServiceDeployment `yaml:"deployment,omitempty"`
17-
Domains ServiceDomains `yaml:"domain,omitempty"`
18-
Drain int `yaml:"drain,omitempty"`
19-
Environment Environment `yaml:"environment,omitempty"`
20-
Health ServiceHealth `yaml:"health,omitempty"`
21-
Image string `yaml:"image,omitempty"`
22-
Init bool `yaml:"init,omitempty"`
23-
Internal bool `yaml:"internal,omitempty"`
24-
Links []string `yaml:"links,omitempty"`
25-
Policies []string `yaml:"policies,omitempty"`
26-
Port ServicePort `yaml:"port,omitempty"`
27-
Privileged bool `yaml:"privileged,omitempty"`
28-
Resources []string `yaml:"resources,omitempty"`
29-
Scale ServiceScale `yaml:"scale,omitempty"`
30-
Singleton bool `yaml:"singleton,omitempty"`
31-
Sticky bool `yaml:"sticky,omitempty"`
32-
Tags map[string]string `yaml:"tags,omitempty"`
33-
Termination ServiceTermination `yaml:"termination,omitempty"`
34-
Test string `yaml:"test,omitempty"`
35-
Volumes []string `yaml:"volumes,omitempty"`
13+
Agent ServiceAgent `yaml:"agent,omitempty"`
14+
Build ServiceBuild `yaml:"build,omitempty"`
15+
Command ServiceCommand `yaml:"command,omitempty"`
16+
Deployment ServiceDeployment `yaml:"deployment,omitempty"`
17+
Domains ServiceDomains `yaml:"domain,omitempty"`
18+
Drain int `yaml:"drain,omitempty"`
19+
Environment Environment `yaml:"environment,omitempty"`
20+
Health ServiceHealth `yaml:"health,omitempty"`
21+
Image string `yaml:"image,omitempty"`
22+
Init bool `yaml:"init,omitempty"`
23+
Internal bool `yaml:"internal,omitempty"`
24+
InternalAndExternal bool `yaml:"internalAndExternal,omitempty"`
25+
Links []string `yaml:"links,omitempty"`
26+
Policies []string `yaml:"policies,omitempty"`
27+
Port ServicePort `yaml:"port,omitempty"`
28+
Privileged bool `yaml:"privileged,omitempty"`
29+
Resources []string `yaml:"resources,omitempty"`
30+
Scale ServiceScale `yaml:"scale,omitempty"`
31+
Singleton bool `yaml:"singleton,omitempty"`
32+
Sticky bool `yaml:"sticky,omitempty"`
33+
Tags map[string]string `yaml:"tags,omitempty"`
34+
Termination ServiceTermination `yaml:"termination,omitempty"`
35+
Test string `yaml:"test,omitempty"`
36+
Volumes []string `yaml:"volumes,omitempty"`
3637
}
3738

3839
type Services []Service

provider/aws/formation/service.json.tmpl

Lines changed: 162 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@
3232
{{ if .Domain }} "{{.Domain}}" {{ else }} { "Fn::Join": [ ".", [ "{{$.App}}-{{.Name}}", { "Fn::ImportValue": { "Fn::Sub": "${Rack}:{{ router .Name $.Manifest }}Host" } } ] ] } {{ end }}
3333
] }
3434
},
35+
{{ if .InternalAndExternal }}
36+
"EndpointExternal": {
37+
"Value": { "Fn::If": [ "InternalDomains",
38+
{ "Fn::Join": [ ".", [ "{{$.App}}-{{.Name}}", { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterHost" } } ] ] },
39+
{{ if .Domain }} "{{.Domain}}" {{ else }} { "Fn::Join": [ ".", [ "{{$.App}}-{{.Name}}", { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterHost" } } ] ] } {{ end }}
40+
] }
41+
},
42+
"TargetGroupExternal": {
43+
"Value": { "Ref": "BalancerTargetGroupExternal" }
44+
},
45+
{{ end }}
3546
"TargetGroup": {
3647
"Value": { "Ref": "BalancerTargetGroup{{ if .Internal }}Internal{{ end }}" }
3748
},
@@ -319,6 +330,7 @@
319330
"VpcId": { "Fn::ImportValue": { "Fn::Sub": "${Rack}:Vpc" } }
320331
}
321332
},
333+
322334
"BalancerListenerRule80": {
323335
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
324336
{{ if .Domain }}
@@ -349,6 +361,77 @@
349361
"Priority": "{{ priority $.App .Name "default" -1 }}"
350362
}
351363
},
364+
{{ if .InternalAndExternal }}
365+
"BalancerTargetGroupExternal": {
366+
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
367+
"Properties": {
368+
"HealthCheckIntervalSeconds": "{{.Health.Interval}}",
369+
"HealthCheckTimeoutSeconds": "{{.Health.Timeout}}",
370+
"HealthyThresholdCount": "2",
371+
"UnhealthyThresholdCount": "2",
372+
"HealthCheckPath": "{{.Health.Path}}",
373+
"Matcher": {
374+
{{ if or (eq .Port.Scheme "grpc") (eq .Port.Scheme "secure-grpc") }}
375+
"GrpcCode": { "Ref": "LoadBalancerGrpcSuccessCodes" }
376+
{{ else }}
377+
"HttpCode": { "Ref": "LoadBalancerSuccessCodes" }
378+
{{ end }}
379+
},
380+
"Port": "{{.Port.Port}}",
381+
{{ if eq .Port.Scheme "grpc" }}
382+
"Protocol": "HTTP",
383+
"ProtocolVersion": "GRPC",
384+
{{ else if eq .Port.Scheme "secure-grpc" }}
385+
"Protocol": "HTTPS",
386+
"ProtocolVersion": "GRPC",
387+
{{ else }}
388+
"Protocol": "{{ upcase .Port.Scheme }}",
389+
{{ end }}
390+
"TargetGroupAttributes": [
391+
{ "Key": "deregistration_delay.timeout_seconds", "Value": "{{.Drain}}" },
392+
{ "Key": "load_balancing.algorithm.type", "Value": { "Ref": "LoadBalancerAlgorithm" } },
393+
{ "Key": "slow_start.duration_seconds", "Value": { "Ref": "SlowStartDuration" } },
394+
{ "Key": "stickiness.enabled", "Value": "{{.Sticky}}" }
395+
],
396+
"Tags": [
397+
{ "Key": "App", "Value": "{{$.App}}" },
398+
{ "Key": "Service", "Value": "{{.Name}}" }
399+
],
400+
"TargetType": { "Fn::If": [ "IsolateServices", "ip", "instance" ] },
401+
"VpcId": { "Fn::ImportValue": { "Fn::Sub": "${Rack}:Vpc" } }
402+
}
403+
},
404+
"BalancerListenerRule80External": {
405+
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
406+
{{ if .Domain }}
407+
"Condition": "InternalDomainsAndRouteHttp",
408+
{{ else }}
409+
"Condition": "RouteHttp",
410+
{{ end }}
411+
"Properties": {
412+
"Actions": [ { "Type": "forward", "TargetGroupArn": { "Ref": "BalancerTargetGroupExternal" } } ],
413+
"Conditions": [ { "Field": "host-header", "Values": [ { "Fn::Join": [ ".", [ "{{$.App}}-{{.Name}}", { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterHost" } } ] ] } ] } ],
414+
"ListenerArn": { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterListener80" } },
415+
"Priority": "{{ priority $.App .Name "default-external" -1 }}"
416+
}
417+
},
418+
"BalancerListenerRule443External": {
419+
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
420+
{{ if .Domain }}
421+
"Condition": "InternalDomains",
422+
{{ end }}
423+
"Properties": {
424+
"Actions": [ { "Type": "forward", "TargetGroupArn": { "Ref": "BalancerTargetGroupExternal" } } ],
425+
"Conditions": [ { "Field": "host-header", "Values": [
426+
{ "Fn::Join": [ ".", [ "{{$.App}}-{{.Name}}", { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterHost" } } ] ] } {{- if $.WildcardDomain }},
427+
{ "Fn::Join": [".", [ "*", "{{$.App}}-{{.Name}}", { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterHost" } } ] ] }
428+
{{ end }}
429+
] } ],
430+
"ListenerArn": { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterListener443" } },
431+
"Priority": "{{ priority $.App .Name "default-external" -1 }}"
432+
}
433+
},
434+
{{ end }}
352435
{{ if $.WildcardDomain }}
353436
"WildCardCertificate": {
354437
"Type": "AWS::CertificateManager::Certificate",
@@ -380,6 +463,38 @@
380463
"ListenerArn" : { "Fn::ImportValue": { "Fn::Sub": "${Rack}:{{ router .Name $.Manifest }}Listener443" } }
381464
}
382465
},
466+
{{ if .InternalAndExternal }}
467+
"WildCardCertificateExternal": {
468+
"Type": "AWS::CertificateManager::Certificate",
469+
{{ if .Domain }}
470+
"Condition": "InternalDomains",
471+
{{ end }}
472+
"Properties": {
473+
"DomainName": { "Fn::Join": [ ".", [ "*", { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterHost" } } ] ] },
474+
"DomainValidationOptions": [
475+
{
476+
"DomainName": { "Fn::Join": [ ".", [ "*", { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterHost" } } ] ] },
477+
"ValidationDomain": "convox.site"
478+
},
479+
{
480+
"DomainName": { "Fn::Join": [ ".", [ "*", "{{$.App}}-{{.Name}}", { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterHost" } } ] ] },
481+
"ValidationDomain": "convox.site"
482+
}
483+
],
484+
"SubjectAlternativeNames": [ { "Fn::Join": [ ".", [ "*", "{{$.App}}-{{.Name}}", { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterHost" } } ] ] } ]
485+
}
486+
},
487+
"AddListenerCertificatesExternal": {
488+
"Type" : "AWS::ElasticLoadBalancingV2::ListenerCertificate",
489+
{{ if .Domain }}
490+
"Condition": "InternalDomains",
491+
{{ end }}
492+
"Properties" : {
493+
"Certificates" : [ { "CertificateArn" : { "Ref": "WildCardCertificate" } } ],
494+
"ListenerArn" : { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterListener443" } }
495+
}
496+
},
497+
{{ end }}
383498
{{ end }}
384499
"RecordSetInternalDomain": {
385500
"Type": "AWS::Route53::RecordSet",
@@ -420,6 +535,15 @@
420535
"ListenerArn": { "Fn::ImportValue": { "Fn::Sub": "${Rack}:{{ router .Name $.Manifest }}Listener443" } }
421536
}
422537
},
538+
{{ if .InternalAndExternal }}
539+
"BalancerListenerCertificateExternal": {
540+
"Type": "AWS::ElasticLoadBalancingV2::ListenerCertificate",
541+
"Properties": {
542+
"Certificates": [ { "CertificateArn": { "Ref": "Certificate" } } ],
543+
"ListenerArn": { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterListener443" } }
544+
}
545+
},
546+
{{ end }}
423547
{{ range $i, $domain := .Domains }}
424548
"BalancerListenerRule80Domain{{$i}}": {
425549
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
@@ -446,6 +570,33 @@
446570
"Priority": "{{ priority $.App $.Service.Name $domain $i }}"
447571
}
448572
},
573+
{{ if .InternalAndExternal }}
574+
"BalancerListenerRule80Domain{{$i}}External": {
575+
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
576+
"Condition": "RouteHttp",
577+
{{ if gt $i 0 }}
578+
"DependsOn": "BalancerListenerRule80Domain{{ dec $i }}External",
579+
{{ end }}
580+
"Properties": {
581+
"Actions": [ { "Type": "forward", "TargetGroupArn": { "Ref": "BalancerTargetGroupExternal" } } ],
582+
"Conditions": [ { "Field": "host-header", "Values": [ "{{$domain}}" ] } ],
583+
"ListenerArn": { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterListener80" } },
584+
"Priority": "{{ priority $.App $.Service.Name (printf "%s-external" $domain) $i }}"
585+
}
586+
},
587+
"BalancerListenerRule443Domain{{$i}}External": {
588+
"Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
589+
{{ if gt $i 0 }}
590+
"DependsOn": "BalancerListenerRule443Domain{{ dec $i }}External",
591+
{{ end }}
592+
"Properties": {
593+
"Actions": [ { "Type": "forward", "TargetGroupArn": { "Ref": "BalancerTargetGroupExternal" } } ],
594+
"Conditions": [ { "Field": "host-header", "Values": [ "{{$domain}}" ] } ],
595+
"ListenerArn": { "Fn::ImportValue": { "Fn::Sub": "${Rack}:RouterListener443" } },
596+
"Priority": "{{ priority $.App $.Service.Name (printf "%s-external" $domain) $i }}"
597+
}
598+
},
599+
{{ end }}
449600
{{ end }}
450601
{{ end }}
451602
{{ end }}
@@ -560,7 +711,11 @@
560711
"Service": {
561712
"Type": "AWS::ECS::Service",
562713
{{ if .Port.Port }}
563-
"DependsOn": "BalancerListenerRule443{{ if .Domain }}Domain0{{ end }}",
714+
"DependsOn": [
715+
"BalancerListenerRule443{{ if .Domain }}Domain0{{ end }}" {{ if .InternalAndExternal }},
716+
"BalancerListenerRule443{{ if .Domain }}Domain0{{ end }}External"
717+
{{ end }}
718+
],
564719
{{ end }}
565720
"Properties": {
566721
"CapacityProviderStrategy": { "Fn::If": [ "FargateBase",
@@ -616,8 +771,13 @@
616771
] },
617772
{{ if .Port.Port }}
618773
"HealthCheckGracePeriodSeconds": "{{.Health.Grace}}",
619-
"LoadBalancers": [ { "ContainerName": "{{.Name}}", "ContainerPort": "{{.Port.Port}}", "TargetGroupArn": { "Ref": "BalancerTargetGroup{{ if .Internal }}Internal{{ end }}" } } ],
774+
"LoadBalancers": [ { "ContainerName": "{{.Name}}", "ContainerPort": "{{.Port.Port}}", "TargetGroupArn": { "Ref": "BalancerTargetGroup{{ if .Internal }}Internal{{ end }}" } } {{ if .InternalAndExternal }},
775+
{ "ContainerName": "{{.Name}}", "ContainerPort": "{{.Port.Port}}", "TargetGroupArn": { "Ref": "BalancerTargetGroupExternal" } }
776+
{{ end }}
777+
],
778+
{{ if not .InternalAndExternal }}
620779
"Role": { "Fn::If": [ "IsolateServices", { "Ref": "AWS::NoValue" }, { "Fn::ImportValue": { "Fn::Sub": "${Rack}:ServiceRole" } } ] },
780+
{{ end }}
621781
{{ end }}
622782
"TaskDefinition": { "Ref": "Tasks" }
623783
}

provider/aws/service.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (p *Provider) ServiceList(app string) (structs.Services, error) {
5757
endpoint := a.Outputs[fmt.Sprintf("Service%sEndpoint", upperName(ms.Name))]
5858
cert := a.Outputs[fmt.Sprintf("Service%sCertificate", upperName(ms.Name))]
5959

60-
if endpoint == "" {
60+
if endpoint == "" || ms.InternalAndExternal {
6161
sr, err := p.stackResource(p.rackStack(app), fmt.Sprintf("Service%s", upperName(ms.Name)))
6262
if err != nil && !strings.HasPrefix(err.Error(), "resource not found") {
6363
return nil, err
@@ -73,6 +73,9 @@ func (p *Provider) ServiceList(app string) (structs.Services, error) {
7373

7474
cert = outputs["Certificate"]
7575
endpoint = outputs["Endpoint"]
76+
if ms.InternalAndExternal {
77+
endpoint = fmt.Sprintf("%s(internal), %s(external)", outputs["Endpoint"], outputs["EndpointExternal"])
78+
}
7679
}
7780
}
7881

0 commit comments

Comments
 (0)