@@ -20,6 +20,10 @@ import (
20
20
"fmt"
21
21
"sort"
22
22
"time"
23
+
24
+ "github.com/aws/aws-sdk-go/aws"
25
+ "github.com/aws/aws-sdk-go/service/ec2"
26
+ "k8s.io/utils/ptr"
23
27
)
24
28
25
29
const (
@@ -37,6 +41,11 @@ const (
37
41
DefaultAPIServerHealthThresholdCount = 5
38
42
// DefaultAPIServerUnhealthThresholdCount the API server unhealthy check threshold count.
39
43
DefaultAPIServerUnhealthThresholdCount = 3
44
+
45
+ // ZoneTypeAvailabilityZone defines the regular AWS zones in the Region.
46
+ ZoneTypeAvailabilityZone ZoneType = "availability-zone"
47
+ // ZoneTypeLocalZone defines the AWS zone type in Local Zone infrastructure.
48
+ ZoneTypeLocalZone ZoneType = "local-zone"
40
49
)
41
50
42
51
// NetworkStatus encapsulates AWS networking resources.
@@ -508,6 +517,39 @@ type SubnetSpec struct {
508
517
509
518
// Tags is a collection of tags describing the resource.
510
519
Tags Tags `json:"tags,omitempty"`
520
+
521
+ // ZoneType defines the type of the zone where the subnet is created.
522
+ //
523
+ // The valid values are availability-zone, and local-zone.
524
+ //
525
+ // Subnet with zone type availability-zone (regular) is always selected to create cluster
526
+ // resources, like Load Balancers, NAT Gateways, Contol Plane nodes, etc.
527
+ //
528
+ // Subnet with zone type local-zone is not eligible to automatically create
529
+ // regular cluster resources.
530
+ //
531
+ // The public subnet in availability-zone or local-zone is associated with regular public
532
+ // route table with default route entry to a Internet Gateway.
533
+ //
534
+ // The private subnet in the availability-zone is associated with a private route table with
535
+ // the default route entry to a NAT Gateway created in that zone.
536
+ //
537
+ // The private subnet in the local-zone is associated with a private route table with
538
+ // the default route entry re-using the NAT Gateway in the Region (preferred from the
539
+ // parent zone, the zone type availability-zone in the region, or first table available).
540
+ //
541
+ // +kubebuilder:validation:Enum=availability-zone;local-zone
542
+ // +optional
543
+ ZoneType * ZoneType `json:"zoneType,omitempty"`
544
+
545
+ // ParentZoneName is the zone name where the current subnet's zone is tied when
546
+ // the zone is a Local Zone.
547
+ //
548
+ // The subnets in Local Zone locations consume the ParentZoneName to determine the correct
549
+ // private route table to egress traffic to the internet.
550
+ //
551
+ // +optional
552
+ ParentZoneName * string `json:"parentZoneName,omitempty"`
511
553
}
512
554
513
555
// GetResourceID returns the identifier for this subnet,
@@ -524,6 +566,39 @@ func (s *SubnetSpec) String() string {
524
566
return fmt .Sprintf ("id=%s/az=%s/public=%v" , s .GetResourceID (), s .AvailabilityZone , s .IsPublic )
525
567
}
526
568
569
+ // IsEdge returns the true when the subnet is created in the edge zone,
570
+ // Local Zones.
571
+ func (s * SubnetSpec ) IsEdge () bool {
572
+ return s .ZoneType != nil && * s .ZoneType == ZoneTypeLocalZone
573
+ }
574
+
575
+ // SetZoneInfo updates the subnets with zone information.
576
+ func (s * SubnetSpec ) SetZoneInfo (zones []* ec2.AvailabilityZone ) error {
577
+ zoneInfo := func (zoneName string ) * ec2.AvailabilityZone {
578
+ for _ , zone := range zones {
579
+ if aws .StringValue (zone .ZoneName ) == zoneName {
580
+ return zone
581
+ }
582
+ }
583
+ return nil
584
+ }
585
+
586
+ zone := zoneInfo (s .AvailabilityZone )
587
+ if zone == nil {
588
+ if len (s .AvailabilityZone ) > 0 {
589
+ return fmt .Errorf ("unable to update zone information for subnet '%v' and zone '%v'" , s .ID , s .AvailabilityZone )
590
+ }
591
+ return fmt .Errorf ("unable to update zone information for subnet '%v'" , s .ID )
592
+ }
593
+ if zone .ZoneType != nil {
594
+ s .ZoneType = ptr .To (ZoneType (* zone .ZoneType ))
595
+ }
596
+ if zone .ParentZoneName != nil {
597
+ s .ParentZoneName = zone .ParentZoneName
598
+ }
599
+ return nil
600
+ }
601
+
527
602
// Subnets is a slice of Subnet.
528
603
// +listType=map
529
604
// +listMapKey=id
@@ -541,6 +616,22 @@ func (s Subnets) ToMap() map[string]*SubnetSpec {
541
616
542
617
// IDs returns a slice of the subnet ids.
543
618
func (s Subnets ) IDs () []string {
619
+ res := []string {}
620
+ for _ , subnet := range s {
621
+ // Prevent returning edge zones (Local Zone) to regular Subnet IDs.
622
+ // Edge zones should not deploy control plane nodes, and does not support Nat Gateway and
623
+ // Network Load Balancers. Any resource for the core infrastructure should not consume edge
624
+ // zones.
625
+ if subnet .IsEdge () {
626
+ continue
627
+ }
628
+ res = append (res , subnet .GetResourceID ())
629
+ }
630
+ return res
631
+ }
632
+
633
+ // IDsWithEdge returns a slice of the subnet ids.
634
+ func (s Subnets ) IDsWithEdge () []string {
544
635
res := []string {}
545
636
for _ , subnet := range s {
546
637
res = append (res , subnet .GetResourceID ())
@@ -581,21 +672,29 @@ func (s Subnets) FindEqual(spec *SubnetSpec) *SubnetSpec {
581
672
// FilterPrivate returns a slice containing all subnets marked as private.
582
673
func (s Subnets ) FilterPrivate () (res Subnets ) {
583
674
for _ , x := range s {
675
+ // Subnets in AWS Local Zones or Wavelength should not be used by core infrastructure.
676
+ if x .IsEdge () {
677
+ continue
678
+ }
584
679
if ! x .IsPublic {
585
680
res = append (res , x )
586
681
}
587
682
}
588
- return
683
+ return res
589
684
}
590
685
591
686
// FilterPublic returns a slice containing all subnets marked as public.
592
687
func (s Subnets ) FilterPublic () (res Subnets ) {
593
688
for _ , x := range s {
689
+ // Subnets in AWS Local Zones or Wavelength should not be used by core infrastructure.
690
+ if x .IsEdge () {
691
+ continue
692
+ }
594
693
if x .IsPublic {
595
694
res = append (res , x )
596
695
}
597
696
}
598
- return
697
+ return res
599
698
}
600
699
601
700
// FilterByZone returns a slice containing all subnets that live in the availability zone specified.
@@ -605,22 +704,32 @@ func (s Subnets) FilterByZone(zone string) (res Subnets) {
605
704
res = append (res , x )
606
705
}
607
706
}
608
- return
707
+ return res
609
708
}
610
709
611
710
// GetUniqueZones returns a slice containing the unique zones of the subnets.
612
711
func (s Subnets ) GetUniqueZones () []string {
613
712
keys := make (map [string ]bool )
614
713
zones := []string {}
615
714
for _ , x := range s {
616
- if _ , value := keys [x .AvailabilityZone ]; ! value {
715
+ if _ , value := keys [x .AvailabilityZone ]; len ( x . AvailabilityZone ) > 0 && ! value {
617
716
keys [x .AvailabilityZone ] = true
618
717
zones = append (zones , x .AvailabilityZone )
619
718
}
620
719
}
621
720
return zones
622
721
}
623
722
723
+ // SetZoneInfo updates the subnets with zone information.
724
+ func (s Subnets ) SetZoneInfo (zones []* ec2.AvailabilityZone ) error {
725
+ for i := range s {
726
+ if err := s [i ].SetZoneInfo (zones ); err != nil {
727
+ return err
728
+ }
729
+ }
730
+ return nil
731
+ }
732
+
624
733
// CNISpec defines configuration for CNI.
625
734
type CNISpec struct {
626
735
// CNIIngressRules specify rules to apply to control plane and worker node security groups.
@@ -835,3 +944,11 @@ func (i *IngressRule) Equals(o *IngressRule) bool {
835
944
836
945
return true
837
946
}
947
+
948
+ // ZoneType defines listener AWS Availability Zone type.
949
+ type ZoneType string
950
+
951
+ // String returns the string representation for the zone type.
952
+ func (z ZoneType ) String () string {
953
+ return string (z )
954
+ }
0 commit comments