Skip to content

Commit c4a8baa

Browse files
committed
feat: add --default-subnets option
1 parent 49a4f10 commit c4a8baa

13 files changed

+428
-8
lines changed

controllers/ingress/group_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func NewGroupReconciler(cloud services.Cloud, k8sClient client.Client, eventReco
6363
cloud.EC2(), cloud.ELBV2(), cloud.ACM(),
6464
annotationParser, subnetsResolver,
6565
authConfigBuilder, enhancedBackendBuilder, trackingProvider, elbv2TaggingManager, controllerConfig.FeatureGates,
66-
cloud.VpcID(), controllerConfig.ClusterName, controllerConfig.DefaultTags, controllerConfig.ExternalManagedTags,
66+
cloud.VpcID(), controllerConfig.ClusterName, controllerConfig.DefaultSubnets, controllerConfig.DefaultTags, controllerConfig.ExternalManagedTags,
6767
controllerConfig.DefaultSSLPolicy, controllerConfig.DefaultTargetType, controllerConfig.DefaultLoadBalancerScheme, backendSGProvider, sgResolver,
6868
controllerConfig.EnableBackendSecurityGroup, controllerConfig.DisableRestrictedSGRules, controllerConfig.IngressConfig.AllowedCertificateAuthorityARNs, controllerConfig.FeatureGates.Enabled(config.EnableIPTargetType), logger, metricsCollector)
6969
stackMarshaller := deploy.NewDefaultStackMarshaller()

controllers/service/service_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func NewServiceReconciler(cloud services.Cloud, k8sClient client.Client, eventRe
4949
trackingProvider := tracking.NewDefaultProvider(serviceTagPrefix, controllerConfig.ClusterName)
5050
serviceUtils := service.NewServiceUtils(annotationParser, serviceFinalizer, controllerConfig.ServiceConfig.LoadBalancerClass, controllerConfig.FeatureGates)
5151
modelBuilder := service.NewDefaultModelBuilder(annotationParser, subnetsResolver, vpcInfoProvider, cloud.VpcID(), trackingProvider,
52-
elbv2TaggingManager, cloud.EC2(), controllerConfig.FeatureGates, controllerConfig.ClusterName, controllerConfig.DefaultTags, controllerConfig.ExternalManagedTags,
52+
elbv2TaggingManager, cloud.EC2(), controllerConfig.FeatureGates, controllerConfig.ClusterName, controllerConfig.DefaultSubnets, controllerConfig.DefaultTags, controllerConfig.ExternalManagedTags,
5353
controllerConfig.DefaultSSLPolicy, controllerConfig.DefaultTargetType, controllerConfig.DefaultLoadBalancerScheme, controllerConfig.FeatureGates.Enabled(config.EnableIPTargetType), serviceUtils,
5454
backendSGProvider, sgResolver, controllerConfig.EnableBackendSecurityGroup, controllerConfig.DisableRestrictedSGRules, logger, metricsCollector)
5555
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"
@@ -266,6 +267,7 @@ func (t *defaultModelBuildTask) buildLoadBalancerSubnetMappings(ctx context.Cont
266267
networking.WithSubnetsResolveLBScheme(scheme),
267268
networking.WithSubnetsResolveAvailableIPAddressCount(minimalAvailableIPAddressCount),
268269
networking.WithSubnetsClusterTagCheck(t.featureGates.Enabled(config.SubnetsClusterTagCheck)),
270+
networking.WithDefaultSubnets(t.defaultSubnets),
269271
)
270272
if err != nil {
271273
return nil, errors.Wrap(err, "couldn't auto-discover subnets")

pkg/ingress/model_builder.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func NewDefaultModelBuilder(k8sClient client.Client, eventRecorder record.EventR
4646
annotationParser annotations.Parser, subnetsResolver networkingpkg.SubnetsResolver,
4747
authConfigBuilder AuthConfigBuilder, enhancedBackendBuilder EnhancedBackendBuilder,
4848
trackingProvider tracking.Provider, elbv2TaggingManager elbv2deploy.TaggingManager, featureGates config.FeatureGates,
49-
vpcID string, clusterName string, defaultTags map[string]string, externalManagedTags []string, defaultSSLPolicy string, defaultTargetType string, defaultLoadBalancerScheme string,
49+
vpcID string, clusterName string, defaultSubnets []string, defaultTags map[string]string, externalManagedTags []string, defaultSSLPolicy string, defaultTargetType string, defaultLoadBalancerScheme string,
5050
backendSGProvider networkingpkg.BackendSGProvider, sgResolver networkingpkg.SecurityGroupResolver,
5151
enableBackendSG bool, disableRestrictedSGRules bool, allowedCAARNs []string, enableIPTargetType bool, logger logr.Logger, metricsCollector lbcmetrics.MetricCollector) *defaultModelBuilder {
5252
certDiscovery := NewACMCertDiscovery(acmClient, allowedCAARNs, logger)
@@ -69,6 +69,7 @@ func NewDefaultModelBuilder(k8sClient client.Client, eventRecorder record.EventR
6969
trackingProvider: trackingProvider,
7070
elbv2TaggingManager: elbv2TaggingManager,
7171
featureGates: featureGates,
72+
defaultSubnets: defaultSubnets,
7273
defaultTags: defaultTags,
7374
externalManagedTags: sets.NewString(externalManagedTags...),
7475
defaultSSLPolicy: defaultSSLPolicy,
@@ -105,6 +106,7 @@ type defaultModelBuilder struct {
105106
trackingProvider tracking.Provider
106107
elbv2TaggingManager elbv2deploy.TaggingManager
107108
featureGates config.FeatureGates
109+
defaultSubnets []string
108110
defaultTags map[string]string
109111
externalManagedTags sets.String
110112
defaultSSLPolicy string
@@ -148,6 +150,7 @@ func (b *defaultModelBuilder) Build(ctx context.Context, ingGroup Group, metrics
148150
ingGroup: ingGroup,
149151
stack: stack,
150152

153+
defaultSubnets: b.defaultSubnets,
151154
defaultTags: b.defaultTags,
152155
externalManagedTags: b.externalManagedTags,
153156
defaultIPAddressType: elbv2model.IPAddressTypeIPV4,
@@ -205,6 +208,7 @@ type defaultModelBuildTask struct {
205208
disableRestrictedSGRules bool
206209
enableIPTargetType bool
207210

211+
defaultSubnets []string
208212
defaultTags map[string]string
209213
externalManagedTags sets.String
210214
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)