Skip to content

Commit a2c8ae7

Browse files
committed
✨ 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 #4900
1 parent 7824286 commit a2c8ae7

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)