Skip to content

Commit 6ab38e9

Browse files
authored
Set Gateway Endpoint IP family (#1611)
* Enhance VPC endpoint creation with IP address type and refactor IPv6 checks * Modify endpoint ip type upon change in shoot ip stack * Enhance comments
1 parent ea5e5d1 commit 6ab38e9

5 files changed

Lines changed: 98 additions & 37 deletions

File tree

pkg/aws/client/client.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,19 +1400,22 @@ func (c *Client) DeleteEgressOnlyInternetGateway(ctx context.Context, id string)
14001400
// CreateVpcEndpoint creates an EC2 VPC endpoint resource.
14011401
func (c *Client) CreateVpcEndpoint(ctx context.Context, endpoint *VpcEndpoint) (*VpcEndpoint, error) {
14021402
input := &ec2.CreateVpcEndpointInput{
1403-
ServiceName: aws.String(endpoint.ServiceName),
1404-
// TagSpecifications: endpoint.ToTagSpecifications(ec2.ResourceTypeClientVpnEndpoint),
1405-
VpcId: endpoint.VpcId,
1403+
ServiceName: aws.String(endpoint.ServiceName),
1404+
TagSpecifications: endpoint.ToTagSpecifications(ec2types.ResourceTypeVpcEndpoint),
1405+
VpcId: endpoint.VpcId,
1406+
IpAddressType: ec2types.IpAddressType(endpoint.IpAddressType),
1407+
// default VpcEndpointType is Gateway
14061408
}
14071409
output, err := c.EC2.CreateVpcEndpoint(ctx, input)
14081410
if err != nil {
14091411
return nil, err
14101412
}
14111413
return &VpcEndpoint{
1412-
// Tags: FromTags(output.VpcEndpoint.Tags),
1414+
Tags: FromTags(output.VpcEndpoint.Tags),
14131415
VpcEndpointId: aws.ToString(output.VpcEndpoint.VpcEndpointId),
14141416
VpcId: output.VpcEndpoint.VpcId,
14151417
ServiceName: aws.ToString(output.VpcEndpoint.ServiceName),
1418+
IpAddressType: string(output.VpcEndpoint.IpAddressType),
14161419
}, nil
14171420
}
14181421

@@ -1441,6 +1444,7 @@ func (c *Client) describeVpcEndpoints(ctx context.Context, input *ec2.DescribeVp
14411444
VpcEndpointId: aws.ToString(item.VpcEndpointId),
14421445
VpcId: item.VpcId,
14431446
ServiceName: aws.ToString(item.ServiceName),
1447+
IpAddressType: string(item.IpAddressType),
14441448
}
14451449
endpoints = append(endpoints, endpoint)
14461450
}
@@ -1457,6 +1461,17 @@ func (c *Client) DeleteVpcEndpoint(ctx context.Context, id string) error {
14571461
return ignoreNotFound(err)
14581462
}
14591463

1464+
// UpdateVpcEndpointIpAddressType updates the IPAddressType of a VPC endpoint.
1465+
// Returns nil if resource is not found.
1466+
func (c *Client) UpdateVpcEndpointIpAddressType(ctx context.Context, id string, ipAddressType string) error {
1467+
input := &ec2.ModifyVpcEndpointInput{
1468+
VpcEndpointId: ptr.To(id),
1469+
IpAddressType: ec2types.IpAddressType(ipAddressType),
1470+
}
1471+
_, err := c.EC2.ModifyVpcEndpoint(ctx, input)
1472+
return ignoreNotFound(err)
1473+
}
1474+
14601475
// CreateVpcEndpointRouteTableAssociation creates a route for a VPC endpoint.
14611476
// Itempotent, i.e. does nothing if the route is already existing.
14621477
func (c *Client) CreateVpcEndpointRouteTableAssociation(ctx context.Context, routeTableId, vpcEndpointId string) error {

pkg/aws/client/mock/mocks.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/aws/client/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ type Interface interface {
118118
GetVpcEndpoints(ctx context.Context, ids []string) ([]*VpcEndpoint, error)
119119
FindVpcEndpoints(ctx context.Context, filters []ec2types.Filter) ([]*VpcEndpoint, error)
120120
DeleteVpcEndpoint(ctx context.Context, id string) error
121+
UpdateVpcEndpointIpAddressType(ctx context.Context, id string, ipAddressType string) error
121122

122123
// VPC Endpoints Route table associations
123124
CreateVpcEndpointRouteTableAssociation(ctx context.Context, routeTableId, vpcEndpointId string) error
@@ -463,6 +464,7 @@ type VpcEndpoint struct {
463464
VpcEndpointId string
464465
VpcId *string
465466
ServiceName string
467+
IpAddressType string
466468
}
467469

468470
// RouteTable contains the relevant fields for an EC2 route table resource.

pkg/controller/infrastructure/infraflow/reconcile.go

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ func (c *FlowContext) ensureManagedVpc(ctx context.Context) error {
206206
InstanceTenancy: instanceTenancy,
207207
}
208208

209-
if (c.config.DualStack != nil && c.config.DualStack.Enabled) || isIPv6(c.getIpFamilies()) {
209+
if (c.config.DualStack != nil && c.config.DualStack.Enabled) || containsIPv6(c.getIpFamilies()) {
210210
if c.config.Networks.VPC.Ipv6IpamPool != nil && c.config.Networks.VPC.Ipv6IpamPool.ID != nil {
211211
desired.AssignGeneratedIPv6CidrBlock = false
212212
desired.Ipv6IpamPoolId = c.config.Networks.VPC.Ipv6IpamPool.ID
@@ -253,7 +253,7 @@ func (c *FlowContext) ensureManagedVpc(ctx context.Context) error {
253253
}
254254

255255
func (c *FlowContext) ensureVpcIPv6CidrBlock(ctx context.Context) error {
256-
if (c.config.DualStack != nil && c.config.DualStack.Enabled) || isIPv6(c.getIpFamilies()) {
256+
if (c.config.DualStack != nil && c.config.DualStack.Enabled) || containsIPv6(c.getIpFamilies()) {
257257
vpcID := *c.state.Get(IdentifierVPC) // guaranteed to be set because of ensureVPC dependency
258258
ipv6CidrBlock, err := c.client.WaitForIPv6Cidr(ctx, vpcID)
259259
if err != nil {
@@ -285,7 +285,7 @@ func (c *FlowContext) ensureExistingVpc(ctx context.Context) error {
285285
}
286286
c.state.Set(IdentifierInternetGateway, gw.InternetGatewayId)
287287

288-
if isIPv6(c.getIpFamilies()) {
288+
if containsIPv6(c.getIpFamilies()) {
289289
eogw, err := c.client.FindEgressOnlyInternetGatewayByVPC(ctx, vpcID)
290290
if err != nil || eogw == nil {
291291
return fmt.Errorf("Egress-Only Internet Gateway not found for VPC %s", vpcID)
@@ -319,7 +319,7 @@ func (c *FlowContext) validateVpc(ctx context.Context, item *awsclient.VPC) erro
319319
k, strings.Join(v, ","), strings.Join(options.DhcpConfigurations[k], ","))
320320
}
321321
}
322-
if (isIPv6(c.getIpFamilies()) || (c.config.DualStack != nil && c.config.DualStack.Enabled)) && item.IPv6CidrBlock == "" {
322+
if (containsIPv6(c.getIpFamilies()) || (c.config.DualStack != nil && c.config.DualStack.Enabled)) && item.IPv6CidrBlock == "" {
323323
return fmt.Errorf("VPC has no ipv6 CIDR")
324324
}
325325
return nil
@@ -383,12 +383,14 @@ func (c *FlowContext) ensureInternetGateway(ctx context.Context) error {
383383
func (c *FlowContext) ensureGatewayEndpoints(ctx context.Context) error {
384384
log := LogFromContext(ctx)
385385
child := c.state.GetChild(ChildIdVPCEndpoints)
386+
386387
var desired []*awsclient.VpcEndpoint
387388
for _, endpoint := range c.config.Networks.VPC.GatewayEndpoints {
388389
desired = append(desired, &awsclient.VpcEndpoint{
389-
Tags: c.commonTagsWithSuffix(fmt.Sprintf("gw-%s", endpoint)),
390-
VpcId: c.state.Get(IdentifierVPC),
391-
ServiceName: c.vpcEndpointServiceNamePrefix() + endpoint,
390+
Tags: c.commonTagsWithSuffix(fmt.Sprintf("gw-%s", endpoint)),
391+
VpcId: c.state.Get(IdentifierVPC),
392+
ServiceName: c.vpcEndpointServiceNamePrefix() + endpoint,
393+
IpAddressType: string(toEc2IpAddressType(c.getIpFamilies())),
392394
})
393395
}
394396
current, err := c.collectExistingVPCEndpoints(ctx)
@@ -397,6 +399,8 @@ func (c *FlowContext) ensureGatewayEndpoints(ctx context.Context) error {
397399
}
398400

399401
toBeDeleted, toBeCreated, toBeChecked := diffByID(desired, current, c.extractVpcEndpointName)
402+
403+
// Delete removed endpoints and their associations
400404
for _, item := range toBeDeleted {
401405
vpcEndpointName := c.extractVpcEndpointName(item)
402406
for _, zoneKey := range child.GetChildrenKeys() {
@@ -412,23 +416,33 @@ func (c *FlowContext) ensureGatewayEndpoints(ctx context.Context) error {
412416
}
413417
child.SetPtr(vpcEndpointName, nil)
414418
}
419+
420+
// Create new endpoints
415421
for _, item := range toBeCreated {
416422
log.Info("creating...", "serviceName", item.ServiceName)
417423
created, err := c.client.CreateVpcEndpoint(ctx, item)
418424
if err != nil {
419425
return err
420426
}
421427
child.Set(c.extractVpcEndpointName(item), created.VpcEndpointId)
422-
if _, err := c.updater.UpdateEC2Tags(ctx, created.VpcEndpointId, item.Tags, created.Tags); err != nil {
423-
return err
424-
}
425428
}
429+
426430
for _, pair := range toBeChecked {
427431
child.Set(c.extractVpcEndpointName(pair.current), pair.current.VpcEndpointId)
432+
// Ensure tags on existing endpoints
428433
if _, err := c.updater.UpdateEC2Tags(ctx, pair.current.VpcEndpointId, pair.desired.Tags, pair.current.Tags); err != nil {
429434
return err
430435
}
436+
// Ensure IpAddressType on existing endpoints
437+
if pair.current.IpAddressType != pair.desired.IpAddressType {
438+
log.Info("updating ip address type...", "serviceName", pair.current.ServiceName)
439+
err = c.client.UpdateVpcEndpointIpAddressType(ctx, pair.current.VpcEndpointId, pair.desired.IpAddressType)
440+
if err != nil {
441+
return err
442+
}
443+
}
431444
}
445+
432446
return nil
433447
}
434448

@@ -535,13 +549,13 @@ func (c *FlowContext) ensureNodesSecurityGroup(ctx context.Context) error {
535549
ToPort: ptr.To[int32](32767),
536550
Protocol: "tcp",
537551
CidrBlocks: func() []string {
538-
if isIPv4(c.getIpFamilies()) {
552+
if containsIPv4(c.getIpFamilies()) {
539553
return []string{allIPv4}
540554
}
541555
return nil
542556
}(),
543557
CidrBlocksv6: func() []string {
544-
if isIPv6(c.getIpFamilies()) {
558+
if containsIPv6(c.getIpFamilies()) {
545559
return []string{allIPv6}
546560
}
547561
return nil
@@ -553,13 +567,13 @@ func (c *FlowContext) ensureNodesSecurityGroup(ctx context.Context) error {
553567
ToPort: ptr.To[int32](32767),
554568
Protocol: "udp",
555569
CidrBlocks: func() []string {
556-
if isIPv4(c.getIpFamilies()) {
570+
if containsIPv4(c.getIpFamilies()) {
557571
return []string{allIPv4}
558572
}
559573
return nil
560574
}(),
561575
CidrBlocksv6: func() []string {
562-
if isIPv6(c.getIpFamilies()) {
576+
if containsIPv6(c.getIpFamilies()) {
563577
return []string{allIPv6}
564578
}
565579
return nil
@@ -569,13 +583,13 @@ func (c *FlowContext) ensureNodesSecurityGroup(ctx context.Context) error {
569583
Type: awsclient.SecurityGroupRuleTypeEgress,
570584
Protocol: "-1",
571585
CidrBlocks: func() []string {
572-
if isIPv4(c.getIpFamilies()) {
586+
if containsIPv4(c.getIpFamilies()) {
573587
return []string{allIPv4}
574588
}
575589
return nil
576590
}(),
577591
CidrBlocksv6: func() []string {
578-
if isIPv6(c.getIpFamilies()) {
592+
if containsIPv6(c.getIpFamilies()) {
579593
return []string{allIPv6}
580594
}
581595
return nil
@@ -627,15 +641,15 @@ func (c *FlowContext) ensureNodesSecurityGroup(ctx context.Context) error {
627641
Protocol: "tcp",
628642
}
629643

630-
if isIPv4(c.getIpFamilies()) {
644+
if containsIPv4(c.getIpFamilies()) {
631645
ruleNodesInternalTCP.CidrBlocks = []string{zone.Internal}
632646
ruleNodesInternalUDP.CidrBlocks = []string{zone.Internal}
633647
ruleEfsInboundNFS.CidrBlocks = []string{zone.Internal}
634648
ruleNodesPublicTCP.CidrBlocks = []string{zone.Public}
635649
ruleNodesPublicUDP.CidrBlocks = []string{zone.Public}
636650
}
637651

638-
if isIPv6(c.getIpFamilies()) {
652+
if containsIPv6(c.getIpFamilies()) {
639653
ipv6CidrBlock := c.state.Get(IdentifierVpcIPv6CidrBlock)
640654
if ipv6CidrBlock != nil {
641655
subnetPrefixLength := 64
@@ -742,34 +756,34 @@ func (c *FlowContext) ensureZones(ctx context.Context) error {
742756
tagsPrivate := c.commonTagsWithSuffix(helper.GetSuffixSubnetPrivate())
743757
tagsPrivate[TagKeyRolePrivateELB] = TagValueELB
744758
workersCIDR := zone.Workers
745-
if !isIPv4(c.getIpFamilies()) {
759+
if !containsIPv4(c.getIpFamilies()) {
746760
workersCIDR = ""
747761
}
748762
desired = append(desired,
749763
&awsclient.Subnet{
750764
Tags: tagsWorkers,
751765
VpcId: c.state.Get(IdentifierVPC),
752766
AvailabilityZone: zone.Name,
753-
AssignIpv6AddressOnCreation: ptr.To(isIPv6(c.getIpFamilies())),
767+
AssignIpv6AddressOnCreation: ptr.To(containsIPv6(c.getIpFamilies())),
754768
CidrBlock: workersCIDR,
755-
Ipv6Native: ptr.To(!isIPv4(c.getIpFamilies())),
756-
EnableResourceNameDnsAAAARecordOnLaunch: ptr.To(!isIPv4(c.getIpFamilies())),
757-
EnableDns64: ptr.To(!isIPv4(c.getIpFamilies())),
769+
Ipv6Native: ptr.To(!containsIPv4(c.getIpFamilies())),
770+
EnableResourceNameDnsAAAARecordOnLaunch: ptr.To(!containsIPv4(c.getIpFamilies())),
771+
EnableDns64: ptr.To(!containsIPv4(c.getIpFamilies())),
758772
},
759773
// Load balancers can only be deployed to subnets that have an IPv4 CIDR.
760774
// Therefore, internal and public subnets must not be IPv6 native.
761775
&awsclient.Subnet{
762776
Tags: tagsPrivate,
763777
VpcId: c.state.Get(IdentifierVPC),
764778
AvailabilityZone: zone.Name,
765-
AssignIpv6AddressOnCreation: ptr.To(isIPv6(c.getIpFamilies())),
779+
AssignIpv6AddressOnCreation: ptr.To(containsIPv6(c.getIpFamilies())),
766780
CidrBlock: zone.Internal,
767781
},
768782
&awsclient.Subnet{
769783
Tags: tagsPublic,
770784
VpcId: c.state.Get(IdentifierVPC),
771785
AvailabilityZone: zone.Name,
772-
AssignIpv6AddressOnCreation: ptr.To(isIPv6(c.getIpFamilies())),
786+
AssignIpv6AddressOnCreation: ptr.To(containsIPv6(c.getIpFamilies())),
773787
CidrBlock: zone.Public,
774788
},
775789
)
@@ -1069,7 +1083,7 @@ func (c *FlowContext) ensureSubnetIPv6(subnetKey string, desired, current *awscl
10691083
}
10701084

10711085
func (c *FlowContext) ensureSubnetCidrReservation(ctx context.Context) error {
1072-
if !isIPv6(c.getIpFamilies()) {
1086+
if !containsIPv6(c.getIpFamilies()) {
10731087
return nil
10741088
}
10751089

@@ -1341,7 +1355,7 @@ func (c *FlowContext) deleteNATGateway(zoneName string) flow.TaskFn {
13411355
}
13421356

13431357
func (c *FlowContext) ensureEgressOnlyInternetGateway(ctx context.Context) error {
1344-
if !isIPv6(c.getIpFamilies()) {
1358+
if !containsIPv6(c.getIpFamilies()) {
13451359
return nil
13461360
}
13471361

@@ -1388,7 +1402,7 @@ func (c *FlowContext) ensurePrivateRoutingTable(zoneName string) flow.TaskFn {
13881402
NatGatewayId: child.Get(IdentifierZoneNATGateway),
13891403
})
13901404

1391-
if isIPv6(c.getIpFamilies()) {
1405+
if containsIPv6(c.getIpFamilies()) {
13921406
routes = append(routes, &awsclient.Route{
13931407
DestinationIpv6CidrBlock: ptr.To(allIPv6),
13941408
EgressOnlyInternetGatewayId: c.state.Get(IdentifierEgressOnlyInternetGateway),
@@ -1989,7 +2003,7 @@ func (c *FlowContext) getSubnetKey(item *awsclient.Subnet) (string, string, erro
19892003
zone := c.getZone(item)
19902004
// With IPv6 we don't have configuration for zone.Workers and zone.Internal.
19912005
// In that case, we get the subnetKey comparing the name tag.
1992-
if zone == nil || !isIPv4(c.getIpFamilies()) {
2006+
if zone == nil || !containsIPv4(c.getIpFamilies()) {
19932007
// zone may have been deleted from spec, need to find subnetKey on other ways
19942008
zoneName := item.AvailabilityZone
19952009
if item.SubnetId != "" {

pkg/controller/infrastructure/infraflow/utils.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,28 @@ func deref[T any](ts []*T) []T {
183183
return res
184184
}
185185

186-
func isIPv6(ipfamilies []gardencorev1beta1.IPFamily) bool {
187-
return slices.Contains(ipfamilies, gardencorev1beta1.IPFamilyIPv6)
186+
func containsIPv6(ipFamilies []gardencorev1beta1.IPFamily) bool {
187+
return slices.Contains(ipFamilies, gardencorev1beta1.IPFamilyIPv6)
188188
}
189189

190-
func isIPv4(ipfamilies []gardencorev1beta1.IPFamily) bool {
191-
return slices.Contains(ipfamilies, gardencorev1beta1.IPFamilyIPv4)
190+
func containsIPv4(ipFamilies []gardencorev1beta1.IPFamily) bool {
191+
return slices.Contains(ipFamilies, gardencorev1beta1.IPFamilyIPv4)
192+
}
193+
194+
func toEc2IpAddressType(ipFamilies []gardencorev1beta1.IPFamily) ec2types.IpAddressType {
195+
if gardencorev1beta1.IsIPv4SingleStack(ipFamilies) {
196+
return ec2types.IpAddressTypeIpv4
197+
}
198+
if gardencorev1beta1.IsIPv6SingleStack(ipFamilies) {
199+
return ec2types.IpAddressTypeIpv6
200+
}
201+
// TODO: make use of helper function from g/g once they support dual-stack
202+
if slices.Contains(ipFamilies, gardencorev1beta1.IPFamilyIPv4) && slices.Contains(ipFamilies, gardencorev1beta1.IPFamilyIPv6) {
203+
return ec2types.IpAddressTypeDualstack
204+
}
205+
206+
// fallback to IPv4
207+
return ec2types.IpAddressTypeIpv4
192208
}
193209

194210
// a failed NAT will automatically be deleted by AWS

0 commit comments

Comments
 (0)