Skip to content

Commit ea748d0

Browse files
authoredMay 20, 2024··
Fetch VPC ID from runtime using VPC tags provided via controller flags (#3656)
* Fetch vpcid from runtime using vpc flags * added tests related to changes using mockgen * fixed the filter error * change vpc tag key name to more generic * add logs about the vpc values fetched by the controller * improve the doc changes * improve the doc changes * improve the doc changes * fixed the logger issue * update unit test case
1 parent 9b4999b commit ea748d0

File tree

8 files changed

+141
-62
lines changed

8 files changed

+141
-62
lines changed
 

‎docs/deploy/configurations.md

+47-45
Large diffs are not rendered by default.

‎docs/deploy/installation.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ You can set the IMDSv2 as follows:
3737
aws ec2 modify-instance-metadata-options --http-put-response-hop-limit 2 --http-tokens required --region <region> --instance-id <instance-id>
3838
```
3939

40-
Instead of depending on IMDSv2, you can specify the AWS Region and the VPC via the controller flags `--aws-region` and `--aws-vpc-id`.
40+
Instead of depending on IMDSv2, you can specify the AWS Region via the controller flag `--aws-region`, and the AWS VPC via controller flag `--aws-vpc-id` or by specifying vpc tags via the flag `--aws-vpc-tags` and an optional flag `--aws-vpc-tag-key` if you have a different key for the tag other than "Name". When both flags `--aws-vpc-id` and `--aws-vpc-tags` are specified, the controller prioritizes `--aws-vpc-id`and ignores the other flag.
4141

4242
## Configure IAM
4343

‎main.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package main
1818

1919
import (
2020
"os"
21+
2122
elbv2deploy "sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/elbv2"
2223

2324
"github.com/go-logr/logr"
@@ -76,7 +77,7 @@ func main() {
7677
}
7778
ctrl.SetLogger(getLoggerWithLogLevel(controllerCFG.LogLevel))
7879

79-
cloud, err := aws.NewCloud(controllerCFG.AWSConfig, metrics.Registry)
80+
cloud, err := aws.NewCloud(controllerCFG.AWSConfig, metrics.Registry, ctrl.Log)
8081
if err != nil {
8182
setupLog.Error(err, "unable to initialize AWS cloud")
8283
os.Exit(1)

‎pkg/aws/cloud.go

+43-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package aws
22

33
import (
4+
"context"
45
"fmt"
56
"net"
67
"os"
@@ -10,6 +11,7 @@ import (
1011
"github.com/aws/aws-sdk-go/aws/endpoints"
1112
"github.com/aws/aws-sdk-go/aws/session"
1213
"github.com/aws/aws-sdk-go/service/ec2"
14+
"github.com/go-logr/logr"
1315
"github.com/pkg/errors"
1416
"github.com/prometheus/client_golang/prometheus"
1517
amerrors "k8s.io/apimachinery/pkg/util/errors"
@@ -49,7 +51,7 @@ type Cloud interface {
4951
}
5052

5153
// NewCloud constructs new Cloud implementation.
52-
func NewCloud(cfg CloudConfig, metricsRegisterer prometheus.Registerer) (Cloud, error) {
54+
func NewCloud(cfg CloudConfig, metricsRegisterer prometheus.Registerer, logger logr.Logger) (Cloud, error) {
5355
hasIPv4 := true
5456
addrs, err := net.InterfaceAddrs()
5557
if err == nil {
@@ -112,14 +114,11 @@ func NewCloud(cfg CloudConfig, metricsRegisterer prometheus.Registerer) (Cloud,
112114

113115
ec2Service := services.NewEC2(sess)
114116

115-
if len(cfg.VpcID) == 0 {
116-
vpcID, err := inferVPCID(metadata, ec2Service)
117-
if err != nil {
118-
return nil, errors.Wrap(err, "failed to introspect vpcID from EC2Metadata or Node name, specify --aws-vpc-id instead if EC2Metadata is unavailable")
119-
}
120-
cfg.VpcID = vpcID
117+
vpcID, err := getVpcID(cfg, ec2Service, metadata, logger)
118+
if err != nil {
119+
return nil, errors.Wrap(err, "failed to get VPC ID")
121120
}
122-
121+
cfg.VpcID = vpcID
123122
return &defaultCloud{
124123
cfg: cfg,
125124
ec2: ec2Service,
@@ -132,6 +131,20 @@ func NewCloud(cfg CloudConfig, metricsRegisterer prometheus.Registerer) (Cloud,
132131
}, nil
133132
}
134133

134+
func getVpcID(cfg CloudConfig, ec2Service services.EC2, metadata services.EC2Metadata, logger logr.Logger) (string, error) {
135+
136+
if cfg.VpcID != "" {
137+
logger.V(1).Info("vpcid is specified using flag --aws-vpc-id, controller will use the value", "vpc: ", cfg.VpcID)
138+
return cfg.VpcID, nil
139+
}
140+
141+
if cfg.VpcTags != nil {
142+
return inferVPCIDFromTags(ec2Service, cfg.VpcNameTagKey, cfg.VpcTags[cfg.VpcNameTagKey])
143+
}
144+
145+
return inferVPCID(metadata, ec2Service)
146+
}
147+
135148
func inferVPCID(metadata services.EC2Metadata, ec2Service services.EC2) (string, error) {
136149
var errList []error
137150
vpcId, err := metadata.VpcID()
@@ -168,6 +181,28 @@ func inferVPCID(metadata services.EC2Metadata, ec2Service services.EC2) (string,
168181
return "", amerrors.NewAggregate(errList)
169182
}
170183

184+
func inferVPCIDFromTags(ec2Service services.EC2, VpcNameTagKey string, VpcNameTagValue string) (string, error) {
185+
vpcs, err := ec2Service.DescribeVPCsAsList(context.Background(), &ec2.DescribeVpcsInput{
186+
Filters: []*ec2.Filter{
187+
{
188+
Name: aws.String("tag:" + VpcNameTagKey),
189+
Values: []*string{aws.String(VpcNameTagValue)},
190+
},
191+
},
192+
})
193+
if err != nil {
194+
return "", fmt.Errorf("failed to fetch VPC ID with tag: %w", err)
195+
}
196+
if len(vpcs) == 0 {
197+
return "", fmt.Errorf("no VPC exists with tag: %w", err)
198+
}
199+
if len(vpcs) > 1 {
200+
return "", fmt.Errorf("multiple VPCs exists with tag: %w", err)
201+
}
202+
203+
return *vpcs[0].VpcId, nil
204+
}
205+
171206
var _ Cloud = &defaultCloud{}
172207

173208
type defaultCloud struct {

‎pkg/aws/cloud_config.go

+11
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ const (
1212
flagAWSAPIEndpoints = "aws-api-endpoints"
1313
flagAWSAPIThrottle = "aws-api-throttle"
1414
flagAWSVpcID = "aws-vpc-id"
15+
flagAWSVpcTags = "aws-vpc-tags"
1516
flagAWSVpcCacheTTL = "aws-vpc-cache-ttl"
1617
flagAWSMaxRetries = "aws-max-retries"
18+
flagAWSVpcNameTagKey = "aws-vpc-tag-key"
1719
defaultVpcID = ""
20+
defaultVpcNameTagKey = "Name"
1821
defaultRegion = ""
1922
defaultAPIMaxRetries = 10
2023
)
@@ -29,6 +32,12 @@ type CloudConfig struct {
2932
// VpcID for the LoadBalancer resources.
3033
VpcID string
3134

35+
// VPC tags List
36+
VpcTags map[string]string
37+
38+
// VPC Name Tag Key, default "Name"
39+
VpcNameTagKey string
40+
3241
// VPC cache TTL in minutes
3342
VpcCacheTTL time.Duration
3443

@@ -43,6 +52,8 @@ func (cfg *CloudConfig) BindFlags(fs *pflag.FlagSet) {
4352
fs.StringVar(&cfg.Region, flagAWSRegion, defaultRegion, "AWS Region for the kubernetes cluster")
4453
fs.Var(cfg.ThrottleConfig, flagAWSAPIThrottle, "throttle settings for AWS APIs, format: serviceID1:operationRegex1=rate:burst,serviceID2:operationRegex2=rate:burst")
4554
fs.StringVar(&cfg.VpcID, flagAWSVpcID, defaultVpcID, "AWS VpcID for the LoadBalancer resources")
55+
fs.StringToStringVar(&cfg.VpcTags, flagAWSVpcTags, nil, "AWS VPC tags List,format: tagkey1=tagvalue1,tagkey2=tagvalue2")
56+
fs.StringVar(&cfg.VpcNameTagKey, flagAWSVpcNameTagKey, defaultVpcNameTagKey, "AWS tag key for identifying the VPC")
4657
fs.IntVar(&cfg.MaxRetries, flagAWSMaxRetries, defaultAPIMaxRetries, "Maximum retries for AWS APIs")
4758
fs.StringToStringVar(&cfg.AWSEndpoints, flagAWSAPIEndpoints, nil, "Custom AWS endpoint configuration, format: serviceID1=URL1,serviceID2=URL2")
4859
}

‎pkg/aws/services/ec2.go

+19-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package services
22

33
import (
44
"context"
5+
56
"github.com/aws/aws-sdk-go/aws/session"
67
"github.com/aws/aws-sdk-go/service/ec2"
78
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
@@ -10,17 +11,20 @@ import (
1011
type EC2 interface {
1112
ec2iface.EC2API
1213

13-
// wrapper to DescribeInstancesPagesWithContext API, which aggregates paged results into list.
14+
// DescribeInstancesAsList wraps the DescribeInstancesPagesWithContext API, which aggregates paged results into list.
1415
DescribeInstancesAsList(ctx context.Context, input *ec2.DescribeInstancesInput) ([]*ec2.Instance, error)
1516

16-
// wrapper to DescribeNetworkInterfacesPagesWithContext API, which aggregates paged results into list.
17+
// DescribeNetworkInterfacesAsList wraps the DescribeNetworkInterfacesPagesWithContext API, which aggregates paged results into list.
1718
DescribeNetworkInterfacesAsList(ctx context.Context, input *ec2.DescribeNetworkInterfacesInput) ([]*ec2.NetworkInterface, error)
1819

19-
// wrapper to DescribeSecurityGroupsPagesWithContext API, which aggregates paged results into list.
20+
// DescribeSecurityGroupsAsList wraps the DescribeSecurityGroupsPagesWithContext API, which aggregates paged results into list.
2021
DescribeSecurityGroupsAsList(ctx context.Context, input *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error)
2122

22-
// wrapper to DescribeSubnetsPagesWithContext API, which aggregates paged results into list.
23+
// DescribeSubnetsAsList wraps the DescribeSubnetsPagesWithContext API, which aggregates paged results into list.
2324
DescribeSubnetsAsList(ctx context.Context, input *ec2.DescribeSubnetsInput) ([]*ec2.Subnet, error)
25+
26+
// DescribeVPCsAsList wraps the DescribeVpcsPagesWithContext API, which aggregates paged results into list.
27+
DescribeVPCsAsList(ctx context.Context, input *ec2.DescribeVpcsInput) ([]*ec2.Vpc, error)
2428
}
2529

2630
// NewEC2 constructs new EC2 implementation.
@@ -79,3 +83,14 @@ func (c *defaultEC2) DescribeSubnetsAsList(ctx context.Context, input *ec2.Descr
7983
}
8084
return result, nil
8185
}
86+
87+
func (c *defaultEC2) DescribeVPCsAsList(ctx context.Context, input *ec2.DescribeVpcsInput) ([]*ec2.Vpc, error) {
88+
var result []*ec2.Vpc
89+
if err := c.DescribeVpcsPagesWithContext(ctx, input, func(output *ec2.DescribeVpcsOutput, _ bool) bool {
90+
result = append(result, output.Vpcs...)
91+
return true
92+
}); err != nil {
93+
return nil, err
94+
}
95+
return result, nil
96+
}

‎pkg/aws/services/ec2_mocks.go

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎test/framework/framework.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,18 @@ func InitFramework() (*Framework, error) {
5555
return nil, err
5656
}
5757

58+
logger, loggerReporter := utils.NewGinkgoLogger()
59+
5860
cloud, err := aws.NewCloud(aws.CloudConfig{
5961
Region: globalOptions.AWSRegion,
6062
VpcID: globalOptions.AWSVPCID,
6163
MaxRetries: 3,
6264
ThrottleConfig: throttle.NewDefaultServiceOperationsThrottleConfig(),
63-
}, nil)
65+
}, nil, logger)
6466
if err != nil {
6567
return nil, err
6668
}
6769

68-
logger, loggerReporter := utils.NewGinkgoLogger()
69-
7070
f := &Framework{
7171
Options: globalOptions,
7272
RestCfg: restCfg,

0 commit comments

Comments
 (0)
Please sign in to comment.