Skip to content

Commit 9beab56

Browse files
committed
feat: add --default-subnets option
1 parent 57472c4 commit 9beab56

13 files changed

+430
-11
lines changed

controllers/ingress/group_controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ingress
33
import (
44
"context"
55
"fmt"
6+
67
"sigs.k8s.io/controller-runtime/pkg/reconcile"
78

89
"github.com/go-logr/logr"
@@ -59,7 +60,7 @@ func NewGroupReconciler(cloud services.Cloud, k8sClient client.Client, eventReco
5960
cloud.EC2(), cloud.ELBV2(), cloud.ACM(),
6061
annotationParser, subnetsResolver,
6162
authConfigBuilder, enhancedBackendBuilder, trackingProvider, elbv2TaggingManager, controllerConfig.FeatureGates,
62-
cloud.VpcID(), controllerConfig.ClusterName, controllerConfig.DefaultTags, controllerConfig.ExternalManagedTags,
63+
cloud.VpcID(), controllerConfig.ClusterName, controllerConfig.DefaultSubnets, controllerConfig.DefaultTags, controllerConfig.ExternalManagedTags,
6364
controllerConfig.DefaultSSLPolicy, controllerConfig.DefaultTargetType, controllerConfig.DefaultLoadBalancerScheme, backendSGProvider, sgResolver,
6465
controllerConfig.EnableBackendSecurityGroup, controllerConfig.DisableRestrictedSGRules, controllerConfig.IngressConfig.AllowedCertificateAuthorityARNs, controllerConfig.FeatureGates.Enabled(config.EnableIPTargetType), logger)
6566
stackMarshaller := deploy.NewDefaultStackMarshaller()

controllers/service/service_controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package service
33
import (
44
"context"
55
"fmt"
6+
67
"sigs.k8s.io/controller-runtime/pkg/reconcile"
78

89
"github.com/go-logr/logr"
@@ -45,7 +46,7 @@ func NewServiceReconciler(cloud services.Cloud, k8sClient client.Client, eventRe
4546
trackingProvider := tracking.NewDefaultProvider(serviceTagPrefix, controllerConfig.ClusterName)
4647
serviceUtils := service.NewServiceUtils(annotationParser, serviceFinalizer, controllerConfig.ServiceConfig.LoadBalancerClass, controllerConfig.FeatureGates)
4748
modelBuilder := service.NewDefaultModelBuilder(annotationParser, subnetsResolver, vpcInfoProvider, cloud.VpcID(), trackingProvider,
48-
elbv2TaggingManager, cloud.EC2(), controllerConfig.FeatureGates, controllerConfig.ClusterName, controllerConfig.DefaultTags, controllerConfig.ExternalManagedTags,
49+
elbv2TaggingManager, cloud.EC2(), controllerConfig.FeatureGates, controllerConfig.ClusterName, controllerConfig.DefaultSubnets, controllerConfig.DefaultTags, controllerConfig.ExternalManagedTags,
4950
controllerConfig.DefaultSSLPolicy, controllerConfig.DefaultTargetType, controllerConfig.DefaultLoadBalancerScheme, controllerConfig.FeatureGates.Enabled(config.EnableIPTargetType), serviceUtils,
5051
backendSGProvider, sgResolver, controllerConfig.EnableBackendSecurityGroup, controllerConfig.DisableRestrictedSGRules, logger)
5152
stackMarshaller := deploy.NewDefaultStackMarshaller()

docs/deploy/configurations.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Currently, you can set only 1 namespace to watch in this flag. See [this Kuberne
7777
| backend-security-group | string | | Backend security group id to use for the ingress rules on the worker node SG |
7878
| cluster-name | string | | Kubernetes cluster name |
7979
| default-ssl-policy | string | ELBSecurityPolicy-2016-08 | Default SSL Policy that will be applied to all Ingresses or Services that do not have the SSL Policy annotation |
80+
| default-subnets | stringList | [] | Default subnets to be selected when not explicitly specified through annotations or other methods |
8081
| default-tags | stringMap | | AWS Tags that will be applied to all AWS resources managed by this controller. Specified Tags takes highest priority |
8182
| default-target-type | string | instance | Default target type for Ingresses and Services - ip, instance |
8283
| default-load-balancer-scheme | string | internal | Default scheme for ELBs - internal, internet-facing |

docs/deploy/subnet_discovery.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ set [Feature Gate ALBSingleSubnet](https://kubernetes-sigs.github.io/aws-load-ba
44
The subnets must be tagged appropriately for auto-discovery to work. The controller chooses one subnet from each Availability Zone. During auto-discovery, the controller
55
considers subnets with at least eight available IP addresses. In the case of multiple qualified tagged subnets in an Availability Zone, the controller chooses the first one in lexicographical
66
order by the subnet IDs.
7+
However, if subnets are specified using the --default-subnets flag, those will take precedence over the auto-discovered subnets.
78
For more information about the subnets for the LBC, see [Application Load Balancers](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html)
89
and [Network Load Balancers](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html).
910
If you used `eksctl` or an Amazon EKS AWS CloudFormation template to create your VPC after March 26, 2020, then the subnets are tagged appropriately when they're created. For

pkg/config/controller_config.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
const (
1616
flagLogLevel = "log-level"
1717
flagK8sClusterName = "cluster-name"
18+
flagDefaultSubnets = "default-subnets"
1819
flagDefaultTags = "default-tags"
1920
flagDefaultTargetType = "default-target-type"
2021
flagDefaultLoadBalancerScheme = "default-load-balancer-scheme"
@@ -69,6 +70,9 @@ type ControllerConfig struct {
6970
// Configurations for the Service controller
7071
ServiceConfig ServiceConfig
7172

73+
// Default subnets that will be used for all AWS resources managed by the networking controller.
74+
DefaultSubnets []string
75+
7276
// Default AWS Tags that will be applied to all AWS resources managed by this controller.
7377
DefaultTags map[string]string
7478

@@ -119,6 +123,8 @@ func (cfg *ControllerConfig) BindFlags(fs *pflag.FlagSet) {
119123
fs.StringVar(&cfg.LogLevel, flagLogLevel, defaultLogLevel,
120124
"Set the controller log level - info(default), debug")
121125
fs.StringVar(&cfg.ClusterName, flagK8sClusterName, "", "Kubernetes cluster name")
126+
fs.StringSliceVar(&cfg.DefaultSubnets, flagDefaultSubnets, nil,
127+
"Default subnets that will be used for all AWS resources managed by the networking controller")
122128
fs.StringToStringVar(&cfg.DefaultTags, flagDefaultTags, nil,
123129
"Default AWS Tags that will be applied to all AWS resources managed by this controller")
124130
fs.StringVar(&cfg.DefaultTargetType, flagDefaultTargetType, string(elbv2.TargetTypeInstance),
@@ -162,7 +168,9 @@ func (cfg *ControllerConfig) Validate() error {
162168
if len(cfg.ClusterName) == 0 {
163169
return errors.New("kubernetes cluster name must be specified")
164170
}
165-
171+
if err := cfg.validateDefaultSubnets(); err != nil {
172+
return err
173+
}
166174
if err := cfg.validateDefaultTagsCollisionWithTrackingTags(); err != nil {
167175
return err
168176
}
@@ -184,6 +192,27 @@ func (cfg *ControllerConfig) Validate() error {
184192
return nil
185193
}
186194

195+
func (cfg *ControllerConfig) validateDefaultSubnets() error {
196+
if len(cfg.DefaultSubnets) == 0 {
197+
return nil
198+
}
199+
for _, subnetID := range cfg.DefaultSubnets {
200+
if !strings.HasPrefix(subnetID, "subnet-") {
201+
return errors.Errorf("invalid value %v for default subnet id", subnetID)
202+
}
203+
}
204+
205+
//validate duplicate subnet ids
206+
seen := make(map[string]bool)
207+
for _, str := range cfg.DefaultSubnets {
208+
if seen[str] {
209+
return errors.Errorf("duplicate subnet id %v is specified in the --default-subnets flag", str)
210+
}
211+
seen[str] = true
212+
}
213+
return nil
214+
}
215+
187216
func (cfg *ControllerConfig) validateDefaultTagsCollisionWithTrackingTags() error {
188217
for tagKey := range cfg.DefaultTags {
189218
if trackingTagKeys.Has(tagKey) {

pkg/config/controller_config_test.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package config
22

33
import (
4+
"testing"
5+
46
"github.com/pkg/errors"
57
"github.com/stretchr/testify/assert"
6-
"testing"
78
)
89

910
func TestControllerConfig_validateDefaultTagsCollisionWithTrackingTags(t *testing.T) {
@@ -164,3 +165,49 @@ func TestControllerConfig_validateExternalManagedTagsCollisionWithDefaultTags(t
164165
})
165166
}
166167
}
168+
169+
func TestControllerConfig_validateDefaultSubnets(t *testing.T) {
170+
type fields struct {
171+
DefaultSubnets []string
172+
}
173+
tests := []struct {
174+
name string
175+
fields fields
176+
wantErr error
177+
}{
178+
{
179+
name: "default subnets is empty",
180+
fields: fields{
181+
DefaultSubnets: nil,
182+
},
183+
wantErr: nil,
184+
},
185+
{
186+
name: "default subnets is not empty",
187+
fields: fields{
188+
DefaultSubnets: []string{"subnet-1", "subnet-2"},
189+
},
190+
wantErr: nil,
191+
},
192+
{
193+
name: "default subnets is not empty and duplicate subnets are specified",
194+
fields: fields{
195+
DefaultSubnets: []string{"subnet-1", "subnet-2", "subnet-1"},
196+
},
197+
wantErr: errors.New("duplicate subnet id subnet-1 is specified in the --default-subnets flag"),
198+
},
199+
}
200+
for _, tt := range tests {
201+
t.Run(tt.name, func(t *testing.T) {
202+
cfg := &ControllerConfig{
203+
DefaultSubnets: tt.fields.DefaultSubnets,
204+
}
205+
err := cfg.validateDefaultSubnets()
206+
if tt.wantErr != nil {
207+
assert.EqualError(t, err, tt.wantErr.Error())
208+
} else {
209+
assert.NoError(t, err)
210+
}
211+
})
212+
}
213+
}

pkg/ingress/model_build_load_balancer.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import (
55
"crypto/sha256"
66
"encoding/hex"
77
"fmt"
8+
"regexp"
9+
810
awssdk "github.com/aws/aws-sdk-go-v2/aws"
911
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
1012
"github.com/google/go-cmp/cmp"
1113
"github.com/pkg/errors"
1214
"k8s.io/apimachinery/pkg/util/sets"
13-
"regexp"
1415
"sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1beta1"
1516
"sigs.k8s.io/aws-load-balancer-controller/pkg/algorithm"
1617
"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
@@ -260,6 +261,7 @@ func (t *defaultModelBuildTask) buildLoadBalancerSubnetMappings(ctx context.Cont
260261
networking.WithSubnetsResolveLBScheme(scheme),
261262
networking.WithSubnetsResolveAvailableIPAddressCount(minimalAvailableIPAddressCount),
262263
networking.WithSubnetsClusterTagCheck(t.featureGates.Enabled(config.SubnetsClusterTagCheck)),
264+
networking.WithDefaultSubnets(t.defaultSubnets),
263265
)
264266
if err != nil {
265267
return nil, errors.Wrap(err, "couldn't auto-discover subnets")

pkg/ingress/model_builder.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package ingress
22

33
import (
44
"context"
5-
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
65
"reflect"
76
"strconv"
87

8+
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
9+
910
awssdk "github.com/aws/aws-sdk-go-v2/aws"
1011
"github.com/go-logr/logr"
1112
"github.com/pkg/errors"
@@ -42,7 +43,7 @@ func NewDefaultModelBuilder(k8sClient client.Client, eventRecorder record.EventR
4243
annotationParser annotations.Parser, subnetsResolver networkingpkg.SubnetsResolver,
4344
authConfigBuilder AuthConfigBuilder, enhancedBackendBuilder EnhancedBackendBuilder,
4445
trackingProvider tracking.Provider, elbv2TaggingManager elbv2deploy.TaggingManager, featureGates config.FeatureGates,
45-
vpcID string, clusterName string, defaultTags map[string]string, externalManagedTags []string, defaultSSLPolicy string, defaultTargetType string, defaultLoadBalancerScheme string,
46+
vpcID string, clusterName string, defaultSubnets []string, defaultTags map[string]string, externalManagedTags []string, defaultSSLPolicy string, defaultTargetType string, defaultLoadBalancerScheme string,
4647
backendSGProvider networkingpkg.BackendSGProvider, sgResolver networkingpkg.SecurityGroupResolver,
4748
enableBackendSG bool, disableRestrictedSGRules bool, allowedCAARNs []string, enableIPTargetType bool, logger logr.Logger) *defaultModelBuilder {
4849
certDiscovery := NewACMCertDiscovery(acmClient, allowedCAARNs, logger)
@@ -65,6 +66,7 @@ func NewDefaultModelBuilder(k8sClient client.Client, eventRecorder record.EventR
6566
trackingProvider: trackingProvider,
6667
elbv2TaggingManager: elbv2TaggingManager,
6768
featureGates: featureGates,
69+
defaultSubnets: defaultSubnets,
6870
defaultTags: defaultTags,
6971
externalManagedTags: sets.NewString(externalManagedTags...),
7072
defaultSSLPolicy: defaultSSLPolicy,
@@ -100,6 +102,7 @@ type defaultModelBuilder struct {
100102
trackingProvider tracking.Provider
101103
elbv2TaggingManager elbv2deploy.TaggingManager
102104
featureGates config.FeatureGates
105+
defaultSubnets []string
103106
defaultTags map[string]string
104107
externalManagedTags sets.String
105108
defaultSSLPolicy string
@@ -141,6 +144,7 @@ func (b *defaultModelBuilder) Build(ctx context.Context, ingGroup Group) (core.S
141144
ingGroup: ingGroup,
142145
stack: stack,
143146

147+
defaultSubnets: b.defaultSubnets,
144148
defaultTags: b.defaultTags,
145149
externalManagedTags: b.externalManagedTags,
146150
defaultIPAddressType: elbv2model.IPAddressTypeIPV4,
@@ -198,6 +202,7 @@ type defaultModelBuildTask struct {
198202
disableRestrictedSGRules bool
199203
enableIPTargetType bool
200204

205+
defaultSubnets []string
201206
defaultTags map[string]string
202207
externalManagedTags sets.String
203208
defaultIPAddressType elbv2model.IPAddressType

pkg/networking/subnet_resolver.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ type SubnetsResolveOptions struct {
5252
SubnetsClusterTagCheck bool
5353
// whether to allow using only 1 subnet for provisioning ALB, default to false
5454
ALBSingleSubnet bool
55+
// Subnets specified with --default-subnets
56+
DefaultSubnets []string
5557
}
5658

5759
// ApplyOptions applies slice of SubnetsResolveOption.
@@ -106,6 +108,13 @@ func WithALBSingleSubnet(ALBSingleSubnet bool) SubnetsResolveOption {
106108
}
107109
}
108110

111+
// WithDefaultSubnets generates an option that configures DefaultSubnets.
112+
func WithDefaultSubnets(defaultSubnets []string) SubnetsResolveOption {
113+
return func(opts *SubnetsResolveOptions) {
114+
opts.DefaultSubnets = defaultSubnets
115+
}
116+
}
117+
109118
// SubnetsResolver is responsible for resolve EC2 Subnets for Load Balancers.
110119
type SubnetsResolver interface {
111120
// ResolveViaDiscovery resolve subnets by auto discover matching subnets.
@@ -234,6 +243,15 @@ func (r *defaultSubnetsResolver) ResolveViaSelector(ctx context.Context, selecto
234243
}
235244
subnetsByAZ := mapSDKSubnetsByAZ(filteredSubnets)
236245
chosenSubnets = make([]ec2types.Subnet, 0, len(subnetsByAZ))
246+
247+
prioritySubnetMap := make(map[string]int)
248+
249+
if len(resolveOpts.DefaultSubnets) > 0 {
250+
for i, subnetID := range resolveOpts.DefaultSubnets {
251+
prioritySubnetMap[subnetID] = i
252+
}
253+
}
254+
237255
for az, subnets := range subnetsByAZ {
238256
if len(subnets) == 1 {
239257
chosenSubnets = append(chosenSubnets, subnets[0])
@@ -247,6 +265,21 @@ func (r *defaultSubnetsResolver) ResolveViaSelector(ctx context.Context, selecto
247265
}
248266
return false
249267
}
268+
269+
// When subnets are specified in --default-subnets, the subnets list will be sorted according to this order.
270+
// Any subnets not specified in --default-subnets will be sorted in lexicographical order and placed after the prioritized subnets.
271+
iVal, iExists := prioritySubnetMap[awssdk.ToString(subnets[i].SubnetId)]
272+
jVal, jExists := prioritySubnetMap[awssdk.ToString(subnets[j].SubnetId)]
273+
274+
if iExists && jExists {
275+
return iVal < jVal
276+
}
277+
if iExists {
278+
return true
279+
}
280+
if jExists {
281+
return false
282+
}
250283
return awssdk.ToString(subnets[i].SubnetId) < awssdk.ToString(subnets[j].SubnetId)
251284
})
252285
r.logger.Info("multiple subnet in the same AvailabilityZone", "AvailabilityZone", az,

0 commit comments

Comments
 (0)