Skip to content

Commit c1e8c52

Browse files
swarner1033shraddhabang1ms-ms
authored
Add elastic IP annotation to front end NLB (#4330)
* feat(ingress): first draft of adding eip * chore(annotation): spelling * test(ingress): add eip tests * feat(ingress): check that all members of ingress group have the same eip * fix(ingress): count against chosen subnets * fix(ingress): assign subnet if eip is not specified * test(ingress): eip ne subnet * [feat gw api] Add auth cognito action for secure listeners on ALBs * feat: add ssl redirect to IngressClassParams * chore(ingress): remove debug * fix(ingress): works when no subnets are specified * test(ingress): current tests pass * wip(ingress): works but with the current bug where it will pick a private subnet if a public one is not spcified like in #2782 * wip(ingress): sketch out new func * fix(ingress): first draft of subnet discovery rework * refactor(ingress): abstract out annotation validation * test(ingress): exisitng tests pass with subnet discovery * test(ingress): 1st test passes non-conditional * test(ingress): all tests except 1 pass without conditionals * fix(ingress): count wrong way around in error message * chore(ingress): remove debug * fix(ingress): refactor validateAndResolveSubnets * docs(ingress): add docs for new annotation * Revert "fix(ingress): refactor validateAndResolveSubnets" This reverts commit fb0db52. * chore(ingress): remove extra comments * chore(*): fix bad rebase --------- Co-authored-by: shraddha bang <shrabang@amazon.com> Co-authored-by: Michal Szewczyk <vomircom@gmail.com>
1 parent 644c4b3 commit c1e8c52

File tree

4 files changed

+269
-61
lines changed

4 files changed

+269
-61
lines changed

docs/guide/ingress/annotations.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ You can add annotations to kubernetes Ingress and Service objects to customize t
8383
| [alb.ingress.kubernetes.io/frontend-nlb-healthcheck-unhealthy-threshold-count](#frontend-nlb-healthcheck-unhealthy-threshold-count) | integer |3| Ingress | N/A |
8484
| [alb.ingress.kubernetes.io/frontend-nlb-healthcheck-success-codes](#frontend-nlb-healthcheck-success-codes) | string |200| Ingress | N/A |
8585
| [alb.ingress.kubernetes.io/frontend-nlb-tags](#frontend-nlb-tags) | stringMap | N/A | Ingress | Exclusive |
86+
| [alb.ingress.kubernetes.io/frontend-nlb-eip-allocation](#frontend-nlb-eip-allocation) | stringList |200| Ingress | N/A |
8687

8788
## IngressGroup
8889
IngressGroup feature enables you to group multiple Ingress resources together.
@@ -1199,3 +1200,15 @@ When this option is set to true, the controller will automatically provision a N
11991200
```
12001201
alb.ingress.kubernetes.io/frontend-nlb-tags: Environment=prod,Team=platform
12011202
```
1203+
1204+
- <a name="frontend-nlb-eip-allocation">`alb.ingress.kubernetes.io/frontend-nlb-eip-allocation`</a> specifies a list of [elastic IP address](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html) configuration for an internet-facing NLB.
1205+
1206+
!!!note
1207+
- This configuration is optional, and you can use it to assign static IP addresses to your NLB
1208+
- If you include the subnets [annotation](#frontend-nlb-subnets) it must have the same number of subnets as this annotation has EIPs.
1209+
- NLB must be internet-facing
1210+
1211+
!!!example
1212+
```
1213+
alb.ingress.kubernetes.io/frontend-nlb-eip-allocation: eipalloc-xyz, eipalloc-zzz
1214+
```

pkg/annotations/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const (
6565
IngressSuffixFrontendNlbSubnets = "frontend-nlb-subnets"
6666
IngressSuffixFrontendNlbSecurityGroups = "frontend-nlb-security-groups"
6767
IngressSuffixFrontendNlbListenerPortMapping = "frontend-nlb-listener-port-mapping"
68+
IngressSuffixFrontendNlbEipAlloactions = "frontend-nlb-eip-allocations"
6869
IngressSuffixFrontendNlbHealthCheckPort = "frontend-nlb-healthcheck-port"
6970
IngressSuffixFrontendNlbHealthCheckProtocol = "frontend-nlb-healthcheck-protocol"
7071
IngressSuffixFrontendNlbHealthCheckPath = "frontend-nlb-healthcheck-path"

pkg/ingress/model_build_frontend_nlb.go

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -105,45 +105,99 @@ func (t *defaultModelBuildTask) buildFrontendNlbScheme(ctx context.Context, alb
105105
return t.defaultScheme, nil
106106
}
107107

108-
func (t *defaultModelBuildTask) buildFrontendNlbSubnetMappings(ctx context.Context, scheme elbv2model.LoadBalancerScheme) ([]elbv2model.SubnetMapping, error) {
109-
var explicitSubnetNameOrIDsList [][]string
110-
for _, member := range t.ingGroup.Members {
111-
var rawSubnetNameOrIDs []string
112-
if exists := t.annotationParser.ParseStringSliceAnnotation(annotations.IngressSuffixFrontendNlbSubnets, &rawSubnetNameOrIDs, member.Ing.Annotations); !exists {
113-
continue
114-
}
115-
explicitSubnetNameOrIDsList = append(explicitSubnetNameOrIDsList, rawSubnetNameOrIDs)
116-
}
117-
108+
func (t *defaultModelBuildTask) validateAndResolveSubnets(ctx context.Context, explicitSubnetNameOrIDsList [][]string, scheme elbv2model.LoadBalancerScheme) ([]ec2types.Subnet, error) {
118109
if len(explicitSubnetNameOrIDsList) != 0 {
110+
// Check all ingresses have the same subnets
119111
chosenSubnetNameOrIDs := explicitSubnetNameOrIDsList[0]
120112
for _, subnetNameOrIDs := range explicitSubnetNameOrIDsList[1:] {
121113
if !cmp.Equal(chosenSubnetNameOrIDs, subnetNameOrIDs, equality.IgnoreStringSliceOrder()) {
122114
return nil, errors.Errorf("conflicting subnets: %v | %v", chosenSubnetNameOrIDs, subnetNameOrIDs)
123115
}
124116
}
117+
125118
chosenSubnets, err := t.subnetsResolver.ResolveViaNameOrIDSlice(ctx, chosenSubnetNameOrIDs,
126119
networking.WithSubnetsResolveLBType(elbv2model.LoadBalancerTypeNetwork),
127120
networking.WithSubnetsResolveLBScheme(scheme),
128121
)
129122
if err != nil {
130123
return nil, err
131124
}
125+
return chosenSubnets, nil
126+
}
132127

133-
return buildFrontendNlbSubnetMappingsWithSubnets(chosenSubnets), nil
128+
// If no explicit subnets, discover public or private subnets based on scheme
129+
chosenSubnets, err := t.subnetsResolver.ResolveViaDiscovery(ctx,
130+
networking.WithSubnetsResolveLBScheme(scheme),
131+
)
132+
if err != nil {
133+
return nil, err
134134
}
135+
return chosenSubnets, nil
136+
}
135137

136-
return nil, nil
138+
func (t *defaultModelBuildTask) validateEIPAllocations(eipAllocationsList [][]string, scheme elbv2model.LoadBalancerScheme) ([]string, error) {
139+
if len(eipAllocationsList) != 0 {
140+
if scheme != elbv2model.LoadBalancerSchemeInternetFacing {
141+
return nil, errors.Errorf("EIP allocations can only be set for internet facing load balancers")
142+
}
143+
chosenEipAllocations := eipAllocationsList[0]
144+
for _, eipAllocations := range eipAllocationsList[1:] {
145+
if !cmp.Equal(chosenEipAllocations, eipAllocations, equality.IgnoreStringSliceOrder()) {
146+
return nil, errors.Errorf("all EIP allocations for the ingress group must be the same: %v | %v", chosenEipAllocations, eipAllocations)
147+
}
148+
}
149+
return chosenEipAllocations, nil
150+
}
151+
return []string{}, nil
152+
}
137153

154+
func (t *defaultModelBuildTask) buildFrontendNlbSubnetMappings(ctx context.Context, scheme elbv2model.LoadBalancerScheme) ([]elbv2model.SubnetMapping, error) {
155+
var explicitSubnetNameOrIDsList [][]string
156+
var eipAllocationsList [][]string
157+
// Read annotations
158+
for _, member := range t.ingGroup.Members {
159+
var rawSubnetNameOrIDs []string
160+
if exists := t.annotationParser.ParseStringSliceAnnotation(annotations.IngressSuffixFrontendNlbSubnets, &rawSubnetNameOrIDs, member.Ing.Annotations); exists {
161+
explicitSubnetNameOrIDsList = append(explicitSubnetNameOrIDsList, rawSubnetNameOrIDs)
162+
}
163+
var rawEIP []string
164+
if exists := t.annotationParser.ParseStringSliceAnnotation(annotations.IngressSuffixFrontendNlbEipAlloactions, &rawEIP, member.Ing.Annotations); exists {
165+
eipAllocationsList = append(eipAllocationsList, rawEIP)
166+
}
167+
}
168+
169+
chosenSubnets, err := t.validateAndResolveSubnets(ctx, explicitSubnetNameOrIDsList, scheme)
170+
if err != nil {
171+
return nil, err
172+
}
173+
174+
chosenEipAllocations, err := t.validateEIPAllocations(eipAllocationsList, scheme)
175+
if err != nil {
176+
return nil, err
177+
}
178+
179+
// Construct subnet mapping
180+
if len(chosenEipAllocations) != 0 {
181+
if len(chosenEipAllocations) != len(chosenSubnets) {
182+
return nil, errors.Errorf("count of EIP allocations (%d) and subnets (%d) must match", len(chosenEipAllocations), len(chosenSubnets))
183+
}
184+
return buildFrontendNlbSubnetMappingsWithSubnets(chosenSubnets, chosenEipAllocations), nil
185+
}
186+
187+
return buildFrontendNlbSubnetMappingsWithSubnets(chosenSubnets, []string{}), nil
138188
}
139189

140-
func buildFrontendNlbSubnetMappingsWithSubnets(subnets []ec2types.Subnet) []elbv2model.SubnetMapping {
190+
func buildFrontendNlbSubnetMappingsWithSubnets(subnets []ec2types.Subnet, eipAllocation []string) []elbv2model.SubnetMapping {
141191
subnetMappings := make([]elbv2model.SubnetMapping, 0, len(subnets))
142-
for _, subnet := range subnets {
192+
for idx, subnet := range subnets {
143193
subnetMappings = append(subnetMappings, elbv2model.SubnetMapping{
144194
SubnetID: awssdk.ToString(subnet.SubnetId),
145195
})
196+
if len(eipAllocation) > 0 {
197+
subnetMappings[idx].AllocationID = awssdk.String(eipAllocation[idx])
198+
}
146199
}
200+
147201
return subnetMappings
148202
}
149203

@@ -174,11 +228,6 @@ func (t *defaultModelBuildTask) buildFrontendNlbSpec(ctx context.Context, scheme
174228
return elbv2model.LoadBalancerSpec{}, err
175229
}
176230

177-
// use alb subnetMappings if it is not explicitly specified
178-
if subnetMappings == nil {
179-
subnetMappings = alb.Spec.SubnetMappings
180-
}
181-
182231
name, err := t.buildFrontendNlbName(ctx, scheme, alb)
183232
if err != nil {
184233
return elbv2model.LoadBalancerSpec{}, err

0 commit comments

Comments
 (0)