Skip to content

Commit b74fd2e

Browse files
mtuliofad3t
authored andcommitted
✨ edge subnets/gateway: carrier gateway routing for Wavelength subnets
✨ edge subnets/cagw: carrier gateway for public subnets in Wavelength Introduce Carrier Gateway resource reconciliator in the network service. Carrier Gateway is the gateway responsible to route ingress and egress traffic **in/out the Wavelength Zone**, located in the Carrier Infrastructure - communications service providers’ (CSP) 5G networks. Carrier Gateway is similar Internet Gatewat resource, responsible for the network border groups in the Region and Local Zones for public subnets. ✨ edge subnets/routes: supporting custom routes for Wavelength For private and public subnets in edge zones, the following changes is introduced according to each rule: General: - IPv6 subnets is not be supported in AWS Local Zones and Wavelength zone, consequently no ip6 routes will be created - nat gateways is not supported, default gateway's route for private subnets will use nat gateways from the zones in the Region (availability-zone's zone type) - one route table by zone's role by zone (standard flow) Private tables for Local Zones and Wavelength: - default route's gateways is assigned using nat gateway created in the region (availability-zones). Public tables for Wavelength zones: - default route's gateways is assigned using Carrier Gateway, resource introduced in the edge zone's feature. The changes in the standard flow (without edge subnets' support) was isolated in the PR kubernetes-sigs#4900
1 parent 84dd494 commit b74fd2e

File tree

8 files changed

+571
-0
lines changed

8 files changed

+571
-0
lines changed

api/v1beta2/conditions_consts.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ const (
6969
EgressOnlyInternetGatewayFailedReason = "EgressOnlyInternetGatewayFailed"
7070
)
7171

72+
const (
73+
// CarrierGatewayReadyCondition reports on the successful reconciliation of carrier gateways.
74+
// Only applicable to managed clusters.
75+
CarrierGatewayReadyCondition clusterv1.ConditionType = "CarrierGatewayReady"
76+
// CarrierGatewayFailedReason used when errors occur during carrier gateway reconciliation.
77+
CarrierGatewayFailedReason = "CarrierGatewayFailed"
78+
)
79+
7280
const (
7381
// NatGatewaysReadyCondition reports successful reconciliation of NAT gateways.
7482
// Only applicable to managed clusters.

pkg/cloud/awserrors/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const (
3333
GatewayNotFound = "InvalidGatewayID.NotFound"
3434
GroupNotFound = "InvalidGroup.NotFound"
3535
InternetGatewayNotFound = "InvalidInternetGatewayID.NotFound"
36+
InvalidCarrierGatewayNotFound = "InvalidCarrierGatewayID.NotFound"
3637
EgressOnlyInternetGatewayNotFound = "InvalidEgressOnlyInternetGatewayID.NotFound"
3738
InUseIPAddress = "InvalidIPAddress.InUse"
3839
InvalidAccessKeyID = "InvalidAccessKeyId"
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package network
18+
19+
import (
20+
"context"
21+
"fmt"
22+
23+
"github.com/aws/aws-sdk-go/aws"
24+
"github.com/aws/aws-sdk-go/service/ec2"
25+
"github.com/pkg/errors"
26+
27+
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
28+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/awserrors"
29+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/converters"
30+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/filter"
31+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services"
32+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/wait"
33+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/tags"
34+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/record"
35+
"sigs.k8s.io/cluster-api/util/conditions"
36+
)
37+
38+
func (s *Service) reconcileCarrierGateway() error {
39+
if s.scope.VPC().IsUnmanaged(s.scope.Name()) {
40+
s.scope.Trace("Skipping carrier gateway reconcile in unmanaged mode")
41+
return nil
42+
}
43+
44+
if !s.scope.Subnets().HasPublicSubnetWavelength() {
45+
s.scope.Trace("Skipping carrier gateway reconcile in VPC without subnets in zone type wavelength-zone")
46+
return nil
47+
}
48+
49+
s.scope.Debug("Reconciling carrier gateway")
50+
51+
cagw, err := s.describeVpcCarrierGateway()
52+
if awserrors.IsNotFound(err) {
53+
if s.scope.VPC().IsUnmanaged(s.scope.Name()) {
54+
return errors.Errorf("failed to validate network: no carrier gateway found in VPC %q", s.scope.VPC().ID)
55+
}
56+
57+
cg, err := s.createCarrierGateway()
58+
if err != nil {
59+
return err
60+
}
61+
cagw = cg
62+
} else if err != nil {
63+
return err
64+
}
65+
66+
s.scope.VPC().CarrierGatewayID = cagw.CarrierGatewayId
67+
68+
// Make sure tags are up-to-date.
69+
if err := wait.WaitForWithRetryable(wait.NewBackoff(), func() (bool, error) {
70+
buildParams := s.getGatewayTagParams(*cagw.CarrierGatewayId)
71+
tagsBuilder := tags.New(&buildParams, tags.WithEC2(s.EC2Client))
72+
if err := tagsBuilder.Ensure(converters.TagsToMap(cagw.Tags)); err != nil {
73+
return false, err
74+
}
75+
return true, nil
76+
}, awserrors.InvalidCarrierGatewayNotFound); err != nil {
77+
record.Warnf(s.scope.InfraCluster(), "FailedTagCarrierGateway", "Failed to tag managed Carrier Gateway %q: %v", cagw.CarrierGatewayId, err)
78+
return errors.Wrapf(err, "failed to tag carrier gateway %q", *cagw.CarrierGatewayId)
79+
}
80+
conditions.MarkTrue(s.scope.InfraCluster(), infrav1.CarrierGatewayReadyCondition)
81+
return nil
82+
}
83+
84+
func (s *Service) deleteCarrierGateway() error {
85+
if s.scope.VPC().IsUnmanaged(s.scope.Name()) {
86+
s.scope.Trace("Skipping carrier gateway deletion in unmanaged mode")
87+
return nil
88+
}
89+
90+
cagw, err := s.describeVpcCarrierGateway()
91+
if awserrors.IsNotFound(err) {
92+
return nil
93+
} else if err != nil {
94+
return err
95+
}
96+
97+
deleteReq := &ec2.DeleteCarrierGatewayInput{
98+
CarrierGatewayId: cagw.CarrierGatewayId,
99+
}
100+
101+
if _, err = s.EC2Client.DeleteCarrierGatewayWithContext(context.TODO(), deleteReq); err != nil {
102+
record.Warnf(s.scope.InfraCluster(), "FailedDeleteCarrierGateway", "Failed to delete Carrier Gateway %q previously attached to VPC %q: %v", *cagw.CarrierGatewayId, s.scope.VPC().ID, err)
103+
return errors.Wrapf(err, "failed to delete carrier gateway %q", *cagw.CarrierGatewayId)
104+
}
105+
106+
record.Eventf(s.scope.InfraCluster(), "SuccessfulDeleteCarrierGateway", "Deleted Carrier Gateway %q previously attached to VPC %q", *cagw.CarrierGatewayId, s.scope.VPC().ID)
107+
s.scope.Info("Deleted Carrier Gateway in VPC", "carrier-gateway-id", *cagw.CarrierGatewayId, "vpc-id", s.scope.VPC().ID)
108+
109+
return nil
110+
}
111+
112+
func (s *Service) createCarrierGateway() (*ec2.CarrierGateway, error) {
113+
ig, err := s.EC2Client.CreateCarrierGatewayWithContext(context.TODO(), &ec2.CreateCarrierGatewayInput{
114+
VpcId: aws.String(s.scope.VPC().ID),
115+
TagSpecifications: []*ec2.TagSpecification{
116+
tags.BuildParamsToTagSpecification(ec2.ResourceTypeCarrierGateway, s.getGatewayTagParams(services.TemporaryResourceID)),
117+
},
118+
})
119+
if err != nil {
120+
record.Warnf(s.scope.InfraCluster(), "FailedCreateCarrierGateway", "Failed to create new managed Internet Gateway: %v", err)
121+
return nil, errors.Wrap(err, "failed to create carrier gateway")
122+
}
123+
record.Eventf(s.scope.InfraCluster(), "SuccessfulCreateCarrierGateway", "Created new managed Internet Gateway %q", *ig.CarrierGateway.CarrierGatewayId)
124+
s.scope.Info("Created Internet gateway for VPC", "internet-gateway-id", *ig.CarrierGateway.CarrierGatewayId, "vpc-id", s.scope.VPC().ID)
125+
126+
return ig.CarrierGateway, nil
127+
}
128+
129+
func (s *Service) describeVpcCarrierGateway() (*ec2.CarrierGateway, error) {
130+
out, err := s.EC2Client.DescribeCarrierGatewaysWithContext(context.TODO(), &ec2.DescribeCarrierGatewaysInput{
131+
Filters: []*ec2.Filter{
132+
filter.EC2.VPC(s.scope.VPC().ID),
133+
},
134+
})
135+
if err != nil {
136+
record.Eventf(s.scope.InfraCluster(), "FailedDescribeCarrierGateway", "Failed to describe carrier gateways in vpc %q: %v", s.scope.VPC().ID, err)
137+
return nil, errors.Wrapf(err, "failed to describe carrier gateways in vpc %q", s.scope.VPC().ID)
138+
}
139+
140+
if len(out.CarrierGateways) == 0 {
141+
return nil, awserrors.NewNotFound(fmt.Sprintf("no carrier gateways found in vpc %q", s.scope.VPC().ID))
142+
}
143+
144+
return out.CarrierGateways[0], nil
145+
}

0 commit comments

Comments
 (0)