Skip to content

Commit df6d2da

Browse files
authored
add support for external-managed-tags & prefer defaultTags (#1970)
1 parent eb2c151 commit df6d2da

25 files changed

+564
-102
lines changed

controllers/ingress/group_controller.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func NewGroupReconciler(cloud aws.Cloud, k8sClient client.Client, eventRecorder
4646
cloud.EC2(), cloud.ACM(),
4747
annotationParser, subnetsResolver,
4848
authConfigBuilder, enhancedBackendBuilder,
49-
cloud.VpcID(), config.ClusterName, config.DefaultTags,
49+
cloud.VpcID(), config.ClusterName, config.DefaultTags, config.ExternalManagedTags,
5050
config.DefaultSSLPolicy, logger)
5151
stackMarshaller := deploy.NewDefaultStackMarshaller()
5252
stackDeployer := deploy.NewDefaultStackDeployer(cloud, k8sClient, networkingSGManager, networkingSGReconciler,

controllers/service/service_controller.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func NewServiceReconciler(cloud aws.Cloud, k8sClient client.Client, eventRecorde
4242
trackingProvider := tracking.NewDefaultProvider(serviceTagPrefix, config.ClusterName)
4343
elbv2TaggingManager := elbv2.NewDefaultTaggingManager(cloud.ELBV2(), logger)
4444
modelBuilder := service.NewDefaultModelBuilder(annotationParser, subnetsResolver, vpcResolver, trackingProvider,
45-
elbv2TaggingManager, config.ClusterName, config.DefaultTags, config.DefaultSSLPolicy)
45+
elbv2TaggingManager, config.ClusterName, config.DefaultTags, config.ExternalManagedTags, config.DefaultSSLPolicy)
4646
stackMarshaller := deploy.NewDefaultStackMarshaller()
4747
stackDeployer := deploy.NewDefaultStackDeployer(cloud, k8sClient, networkingSGManager, networkingSGReconciler, config, serviceTagPrefix, logger)
4848
return &serviceReconciler{

pkg/config/controller_config.go

+55
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package config
33
import (
44
"github.com/pkg/errors"
55
"github.com/spf13/pflag"
6+
"k8s.io/apimachinery/pkg/util/sets"
67
"sigs.k8s.io/aws-load-balancer-controller/pkg/aws"
78
"sigs.k8s.io/aws-load-balancer-controller/pkg/inject"
89
)
@@ -11,6 +12,7 @@ const (
1112
flagLogLevel = "log-level"
1213
flagK8sClusterName = "cluster-name"
1314
flagDefaultTags = "default-tags"
15+
flagExternalManagedTags = "external-managed-tags"
1416
flagServiceMaxConcurrentReconciles = "service-max-concurrent-reconciles"
1517
flagTargetGroupBindingMaxConcurrentReconciles = "targetgroupbinding-max-concurrent-reconciles"
1618
flagDefaultSSLPolicy = "default-ssl-policy"
@@ -19,6 +21,16 @@ const (
1921
defaultSSLPolicy = "ELBSecurityPolicy-2016-08"
2022
)
2123

24+
var (
25+
trackingTagKeys = sets.NewString(
26+
"elbv2.k8s.aws/cluster",
27+
"ingress.k8s.aws/stack",
28+
"ingress.k8s.aws/resource",
29+
"service.k8s.aws/stack",
30+
"service.k8s.aws/resource",
31+
)
32+
)
33+
2234
// ControllerConfig contains the controller configuration
2335
type ControllerConfig struct {
2436
// Log level for the controller logs
@@ -39,6 +51,9 @@ type ControllerConfig struct {
3951
// Default AWS Tags that will be applied to all AWS resources managed by this controller.
4052
DefaultTags map[string]string
4153

54+
// List of Tag keys on AWS resources that will be managed externally.
55+
ExternalManagedTags []string
56+
4257
// Default SSL Policy that will be applied to all ingresses or services that do not have
4358
// the SSL Policy annotation.
4459
DefaultSSLPolicy string
@@ -56,6 +71,8 @@ func (cfg *ControllerConfig) BindFlags(fs *pflag.FlagSet) {
5671
fs.StringVar(&cfg.ClusterName, flagK8sClusterName, "", "Kubernetes cluster name")
5772
fs.StringToStringVar(&cfg.DefaultTags, flagDefaultTags, nil,
5873
"Default AWS Tags that will be applied to all AWS resources managed by this controller")
74+
fs.StringSliceVar(&cfg.ExternalManagedTags, flagExternalManagedTags, nil,
75+
"List of Tag keys on AWS resources that will be managed externally")
5976
fs.IntVar(&cfg.ServiceMaxConcurrentReconciles, flagServiceMaxConcurrentReconciles, defaultMaxConcurrentReconciles,
6077
"Maximum number of concurrently running reconcile loops for service")
6178
fs.IntVar(&cfg.TargetGroupBindingMaxConcurrentReconciles, flagTargetGroupBindingMaxConcurrentReconciles, defaultMaxConcurrentReconciles,
@@ -76,5 +93,43 @@ func (cfg *ControllerConfig) Validate() error {
7693
if len(cfg.ClusterName) == 0 {
7794
return errors.New("kubernetes cluster name must be specified")
7895
}
96+
97+
if err := cfg.validateDefaultTagsCollisionWithTrackingTags(); err != nil {
98+
return err
99+
}
100+
if err := cfg.validateExternalManagedTagsCollisionWithTrackingTags(); err != nil {
101+
return err
102+
}
103+
if err := cfg.validateExternalManagedTagsCollisionWithDefaultTags(); err != nil {
104+
return err
105+
}
106+
return nil
107+
}
108+
109+
func (cfg *ControllerConfig) validateDefaultTagsCollisionWithTrackingTags() error {
110+
for tagKey := range cfg.DefaultTags {
111+
if trackingTagKeys.Has(tagKey) {
112+
return errors.Errorf("tag key %v cannot be specified in %v flag", tagKey, flagDefaultTags)
113+
}
114+
}
115+
return nil
116+
}
117+
118+
func (cfg *ControllerConfig) validateExternalManagedTagsCollisionWithTrackingTags() error {
119+
for _, tagKey := range cfg.ExternalManagedTags {
120+
if trackingTagKeys.Has(tagKey) {
121+
return errors.Errorf("tag key %v cannot be specified in %v flag", tagKey, flagExternalManagedTags)
122+
}
123+
}
124+
return nil
125+
}
126+
127+
func (cfg *ControllerConfig) validateExternalManagedTagsCollisionWithDefaultTags() error {
128+
for _, tagKey := range cfg.ExternalManagedTags {
129+
if _, ok := cfg.DefaultTags[tagKey]; ok {
130+
return errors.Errorf("tag key %v cannot be specified in both %v and %v flag",
131+
tagKey, flagDefaultTags, flagExternalManagedTags)
132+
}
133+
}
79134
return nil
80135
}

pkg/config/controller_config_test.go

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package config
2+
3+
import (
4+
"github.com/pkg/errors"
5+
"github.com/stretchr/testify/assert"
6+
"testing"
7+
)
8+
9+
func TestControllerConfig_validateDefaultTagsCollisionWithTrackingTags(t *testing.T) {
10+
type fields struct {
11+
DefaultTags map[string]string
12+
}
13+
tests := []struct {
14+
name string
15+
fields fields
16+
wantErr error
17+
}{
18+
{
19+
name: "default tags and tracking tags have no collision",
20+
fields: fields{
21+
DefaultTags: map[string]string{
22+
"tag-a": "value-a",
23+
},
24+
},
25+
wantErr: nil,
26+
},
27+
{
28+
name: "default tags and tracking tags have collision",
29+
fields: fields{
30+
DefaultTags: map[string]string{
31+
"elbv2.k8s.aws/cluster": "value-a",
32+
},
33+
},
34+
wantErr: errors.New("tag key elbv2.k8s.aws/cluster cannot be specified in default-tags flag"),
35+
},
36+
{
37+
name: "default tags is empty",
38+
fields: fields{
39+
DefaultTags: nil,
40+
},
41+
wantErr: nil,
42+
},
43+
}
44+
for _, tt := range tests {
45+
t.Run(tt.name, func(t *testing.T) {
46+
cfg := &ControllerConfig{
47+
DefaultTags: tt.fields.DefaultTags,
48+
}
49+
err := cfg.validateDefaultTagsCollisionWithTrackingTags()
50+
if tt.wantErr != nil {
51+
assert.EqualError(t, err, tt.wantErr.Error())
52+
} else {
53+
assert.NoError(t, err)
54+
}
55+
})
56+
}
57+
}
58+
59+
func TestControllerConfig_validateExternalManagedTagsCollisionWithTrackingTags(t *testing.T) {
60+
type fields struct {
61+
ExternalManagedTags []string
62+
}
63+
tests := []struct {
64+
name string
65+
fields fields
66+
wantErr error
67+
}{
68+
{
69+
name: "external managed tags and tracking tags have no collision",
70+
fields: fields{
71+
ExternalManagedTags: []string{"tag-a"},
72+
},
73+
wantErr: nil,
74+
},
75+
{
76+
name: "external managed tags and tracking tags have collision",
77+
fields: fields{
78+
ExternalManagedTags: []string{"elbv2.k8s.aws/cluster"},
79+
},
80+
wantErr: errors.New("tag key elbv2.k8s.aws/cluster cannot be specified in external-managed-tags flag"),
81+
},
82+
{
83+
name: "external managed tags is empty",
84+
fields: fields{
85+
ExternalManagedTags: nil,
86+
},
87+
wantErr: nil,
88+
},
89+
}
90+
for _, tt := range tests {
91+
t.Run(tt.name, func(t *testing.T) {
92+
cfg := &ControllerConfig{
93+
ExternalManagedTags: tt.fields.ExternalManagedTags,
94+
}
95+
err := cfg.validateExternalManagedTagsCollisionWithTrackingTags()
96+
if tt.wantErr != nil {
97+
assert.EqualError(t, err, tt.wantErr.Error())
98+
} else {
99+
assert.NoError(t, err)
100+
}
101+
})
102+
}
103+
}
104+
105+
func TestControllerConfig_validateExternalManagedTagsCollisionWithDefaultTags(t *testing.T) {
106+
type fields struct {
107+
DefaultTags map[string]string
108+
ExternalManagedTags []string
109+
}
110+
tests := []struct {
111+
name string
112+
fields fields
113+
wantErr error
114+
}{
115+
{
116+
name: "default tags and external managed tags have no collision",
117+
fields: fields{
118+
DefaultTags: map[string]string{
119+
"tag-a": "value-a",
120+
},
121+
ExternalManagedTags: []string{"tag-b"},
122+
},
123+
wantErr: nil,
124+
},
125+
{
126+
name: "default tags and external managed tags have collision",
127+
fields: fields{
128+
DefaultTags: map[string]string{
129+
"tag-a": "value-a",
130+
},
131+
ExternalManagedTags: []string{"tag-a"},
132+
},
133+
wantErr: errors.New("tag key tag-a cannot be specified in both default-tags and external-managed-tags flag"),
134+
},
135+
{
136+
name: "empty default tags and non-empty external managed tags",
137+
fields: fields{
138+
DefaultTags: nil,
139+
ExternalManagedTags: []string{"tag-a"},
140+
},
141+
wantErr: nil,
142+
},
143+
{
144+
name: "empty default tags and empty external managed tags",
145+
fields: fields{
146+
DefaultTags: nil,
147+
ExternalManagedTags: nil,
148+
},
149+
wantErr: nil,
150+
},
151+
}
152+
for _, tt := range tests {
153+
t.Run(tt.name, func(t *testing.T) {
154+
cfg := &ControllerConfig{
155+
DefaultTags: tt.fields.DefaultTags,
156+
ExternalManagedTags: tt.fields.ExternalManagedTags,
157+
}
158+
err := cfg.validateExternalManagedTagsCollisionWithDefaultTags()
159+
if tt.wantErr != nil {
160+
assert.EqualError(t, err, tt.wantErr.Error())
161+
} else {
162+
assert.NoError(t, err)
163+
}
164+
})
165+
}
166+
}

pkg/deploy/ec2/security_group_manager.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ type SecurityGroupManager interface {
3131

3232
// NewDefaultSecurityGroupManager constructs new defaultSecurityGroupManager.
3333
func NewDefaultSecurityGroupManager(ec2Client services.EC2, trackingProvider tracking.Provider, taggingManager TaggingManager,
34-
networkingSGReconciler networking.SecurityGroupReconciler, vpcID string, logger logr.Logger) *defaultSecurityGroupManager {
34+
networkingSGReconciler networking.SecurityGroupReconciler, vpcID string, externalManagedTags []string, logger logr.Logger) *defaultSecurityGroupManager {
3535
return &defaultSecurityGroupManager{
3636
ec2Client: ec2Client,
3737
trackingProvider: trackingProvider,
3838
taggingManager: taggingManager,
3939
networkingSGReconciler: networkingSGReconciler,
4040
vpcID: vpcID,
41+
externalManagedTags: externalManagedTags,
4142
logger: logger,
4243

4344
waitSGDeletionPollInterval: defaultWaitSGDeletionPollInterval,
@@ -52,6 +53,7 @@ type defaultSecurityGroupManager struct {
5253
taggingManager TaggingManager
5354
networkingSGReconciler networking.SecurityGroupReconciler
5455
vpcID string
56+
externalManagedTags []string
5557
logger logr.Logger
5658

5759
waitSGDeletionPollInterval time.Duration
@@ -136,7 +138,8 @@ func (m *defaultSecurityGroupManager) updateSDKSecurityGroupGroupWithTags(ctx co
136138
desiredSGTags := m.trackingProvider.ResourceTags(resSG.Stack(), resSG, resSG.Spec.Tags)
137139
return m.taggingManager.ReconcileTags(ctx, sdkSG.SecurityGroupID, desiredSGTags,
138140
WithCurrentTags(sdkSG.Tags),
139-
WithIgnoredTagKeys(m.trackingProvider.LegacyTagKeys()))
141+
WithIgnoredTagKeys(m.trackingProvider.LegacyTagKeys()),
142+
WithIgnoredTagKeys(m.externalManagedTags))
140143
}
141144

142145
func buildIPPermissionInfos(permissions []ec2model.IPPermission) ([]networking.IPPermissionInfo, error) {

pkg/deploy/ec2/tagging_manager.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func WithCurrentTags(tags map[string]string) ReconcileTagsOption {
4343
// WithIgnoredTagKeys is a reconcile option that configures IgnoredTagKeys.
4444
func WithIgnoredTagKeys(ignoredTagKeys []string) ReconcileTagsOption {
4545
return func(opts *ReconcileTagsOptions) {
46-
opts.IgnoredTagKeys = ignoredTagKeys
46+
opts.IgnoredTagKeys = append(opts.IgnoredTagKeys, ignoredTagKeys...)
4747
}
4848
}
4949

pkg/deploy/elbv2/listener_manager.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ type ListenerManager interface {
2727
}
2828

2929
func NewDefaultListenerManager(elbv2Client services.ELBV2, trackingProvider tracking.Provider,
30-
taggingManager TaggingManager, logger logr.Logger) *defaultListenerManager {
30+
taggingManager TaggingManager, externalManagedTags []string, logger logr.Logger) *defaultListenerManager {
3131
return &defaultListenerManager{
3232
elbv2Client: elbv2Client,
3333
trackingProvider: trackingProvider,
3434
taggingManager: taggingManager,
35+
externalManagedTags: externalManagedTags,
3536
logger: logger,
3637
waitLSExistencePollInterval: defaultWaitLSExistencePollInterval,
3738
waitLSExistenceTimeout: defaultWaitLSExistenceTimeout,
@@ -42,10 +43,11 @@ var _ ListenerManager = &defaultListenerManager{}
4243

4344
// default implementation for ListenerManager
4445
type defaultListenerManager struct {
45-
elbv2Client services.ELBV2
46-
trackingProvider tracking.Provider
47-
taggingManager TaggingManager
48-
logger logr.Logger
46+
elbv2Client services.ELBV2
47+
trackingProvider tracking.Provider
48+
taggingManager TaggingManager
49+
externalManagedTags []string
50+
logger logr.Logger
4951

5052
waitLSExistencePollInterval time.Duration
5153
waitLSExistenceTimeout time.Duration
@@ -113,7 +115,8 @@ func (m *defaultListenerManager) Delete(ctx context.Context, sdkLS ListenerWithT
113115
func (m *defaultListenerManager) updateSDKListenerWithTags(ctx context.Context, resLS *elbv2model.Listener, sdkLS ListenerWithTags) error {
114116
desiredLSTags := m.trackingProvider.ResourceTags(resLS.Stack(), resLS, resLS.Spec.Tags)
115117
return m.taggingManager.ReconcileTags(ctx, awssdk.StringValue(sdkLS.Listener.ListenerArn), desiredLSTags,
116-
WithCurrentTags(sdkLS.Tags))
118+
WithCurrentTags(sdkLS.Tags),
119+
WithIgnoredTagKeys(m.externalManagedTags))
117120
}
118121

119122
func (m *defaultListenerManager) updateSDKListenerWithSettings(ctx context.Context, resLS *elbv2model.Listener, sdkLS ListenerWithTags) error {

0 commit comments

Comments
 (0)