forked from kubernetes-sigs/aws-load-balancer-controller
-
Notifications
You must be signed in to change notification settings - Fork 0
/
target_group_synthesizer.go
205 lines (186 loc) · 7.55 KB
/
target_group_synthesizer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package elbv2
import (
"context"
awssdk "github.com/aws/aws-sdk-go/aws"
"github.com/go-logr/logr"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/sets"
"github.com/sonal-chauhan/aws-load-balancer-controller/pkg/aws/services"
"github.com/sonal-chauhan/aws-load-balancer-controller/pkg/config"
"github.com/sonal-chauhan/aws-load-balancer-controller/pkg/deploy/tracking"
"github.com/sonal-chauhan/aws-load-balancer-controller/pkg/model/core"
elbv2model "github.com/sonal-chauhan/aws-load-balancer-controller/pkg/model/elbv2"
)
// NewTargetGroupSynthesizer constructs targetGroupSynthesizer
func NewTargetGroupSynthesizer(elbv2Client services.ELBV2, trackingProvider tracking.Provider, taggingManager TaggingManager,
tgManager TargetGroupManager, logger logr.Logger, featureGates config.FeatureGates, stack core.Stack) *targetGroupSynthesizer {
return &targetGroupSynthesizer{
elbv2Client: elbv2Client,
trackingProvider: trackingProvider,
taggingManager: taggingManager,
tgManager: tgManager,
featureGates: featureGates,
logger: logger,
stack: stack,
unmatchedSDKTGs: nil,
}
}
// targetGroupSynthesizer is responsible for synthesize TargetGroup resources types for certain stack.
type targetGroupSynthesizer struct {
elbv2Client services.ELBV2
trackingProvider tracking.Provider
taggingManager TaggingManager
tgManager TargetGroupManager
featureGates config.FeatureGates
logger logr.Logger
stack core.Stack
unmatchedSDKTGs []TargetGroupWithTags
}
func (s *targetGroupSynthesizer) Synthesize(ctx context.Context) error {
var resTGs []*elbv2model.TargetGroup
s.stack.ListResources(&resTGs)
sdkTGs, err := s.findSDKTargetGroups(ctx)
if err != nil {
return err
}
matchedResAndSDKTGs, unmatchedResTGs, unmatchedSDKTGs, err := matchResAndSDKTargetGroups(resTGs, sdkTGs,
s.trackingProvider.ResourceIDTagKey(), s.featureGates)
if err != nil {
return err
}
// For TargetGroups, we delete unmatched ones during post synthesize given below facts:
// * unmatched targetGroups might still be use by a listener rule.
s.unmatchedSDKTGs = unmatchedSDKTGs
for _, resTG := range unmatchedResTGs {
tgStatus, err := s.tgManager.Create(ctx, resTG)
if err != nil {
return err
}
resTG.SetStatus(tgStatus)
}
for _, resAndSDKTG := range matchedResAndSDKTGs {
tgStatus, err := s.tgManager.Update(ctx, resAndSDKTG.resTG, resAndSDKTG.sdkTG)
if err != nil {
return err
}
resAndSDKTG.resTG.SetStatus(tgStatus)
}
return nil
}
func (s *targetGroupSynthesizer) PostSynthesize(ctx context.Context) error {
for _, sdkTG := range s.unmatchedSDKTGs {
if err := s.tgManager.Delete(ctx, sdkTG); err != nil {
return err
}
}
return nil
}
// findSDKTargetGroups will find all AWS TargetGroups created for stack.
func (s *targetGroupSynthesizer) findSDKTargetGroups(ctx context.Context) ([]TargetGroupWithTags, error) {
stackTags := s.trackingProvider.StackTags(s.stack)
stackTagsLegacy := s.trackingProvider.StackTagsLegacy(s.stack)
return s.taggingManager.ListTargetGroups(ctx,
tracking.TagsAsTagFilter(stackTags),
tracking.TagsAsTagFilter(stackTagsLegacy))
}
type resAndSDKTargetGroupPair struct {
resTG *elbv2model.TargetGroup
sdkTG TargetGroupWithTags
}
func matchResAndSDKTargetGroups(resTGs []*elbv2model.TargetGroup, sdkTGs []TargetGroupWithTags,
resourceIDTagKey string, featureGates config.FeatureGates) ([]resAndSDKTargetGroupPair, []*elbv2model.TargetGroup, []TargetGroupWithTags, error) {
var matchedResAndSDKTGs []resAndSDKTargetGroupPair
var unmatchedResTGs []*elbv2model.TargetGroup
var unmatchedSDKTGs []TargetGroupWithTags
resTGsByID := mapResTargetGroupByResourceID(resTGs)
sdkTGsByID, err := mapSDKTargetGroupByResourceID(sdkTGs, resourceIDTagKey)
if err != nil {
return nil, nil, nil, err
}
resTGIDs := sets.StringKeySet(resTGsByID)
sdkTGIDs := sets.StringKeySet(sdkTGsByID)
for _, resID := range resTGIDs.Intersection(sdkTGIDs).List() {
resTG := resTGsByID[resID]
sdkTGs := sdkTGsByID[resID]
foundMatch := false
for _, sdkTG := range sdkTGs {
if isSDKTargetGroupRequiresReplacement(sdkTG, resTG, featureGates) {
unmatchedSDKTGs = append(unmatchedSDKTGs, sdkTG)
continue
}
matchedResAndSDKTGs = append(matchedResAndSDKTGs, resAndSDKTargetGroupPair{
resTG: resTG,
sdkTG: sdkTG,
})
foundMatch = true
}
if !foundMatch {
unmatchedResTGs = append(unmatchedResTGs, resTG)
}
}
for _, resID := range resTGIDs.Difference(sdkTGIDs).List() {
unmatchedResTGs = append(unmatchedResTGs, resTGsByID[resID])
}
for _, resID := range sdkTGIDs.Difference(resTGIDs).List() {
unmatchedSDKTGs = append(unmatchedSDKTGs, sdkTGsByID[resID]...)
}
return matchedResAndSDKTGs, unmatchedResTGs, unmatchedSDKTGs, nil
}
func mapResTargetGroupByResourceID(resTGs []*elbv2model.TargetGroup) map[string]*elbv2model.TargetGroup {
resTGsByID := make(map[string]*elbv2model.TargetGroup, len(resTGs))
for _, resTG := range resTGs {
resTGsByID[resTG.ID()] = resTG
}
return resTGsByID
}
func mapSDKTargetGroupByResourceID(sdkTGs []TargetGroupWithTags, resourceIDTagKey string) (map[string][]TargetGroupWithTags, error) {
sdkTGsByID := make(map[string][]TargetGroupWithTags, len(sdkTGs))
for _, sdkTG := range sdkTGs {
resourceID, ok := sdkTG.Tags[resourceIDTagKey]
if !ok {
return nil, errors.Errorf("unexpected targetGroup with no resourceID: %v", awssdk.StringValue(sdkTG.TargetGroup.TargetGroupArn))
}
sdkTGsByID[resourceID] = append(sdkTGsByID[resourceID], sdkTG)
}
return sdkTGsByID, nil
}
// isSDKTargetGroupRequiresReplacement checks whether a sdk TargetGroup requires replacement to fulfill a TargetGroup resource.
func isSDKTargetGroupRequiresReplacement(sdkTG TargetGroupWithTags, resTG *elbv2model.TargetGroup, featureGates config.FeatureGates) bool {
if string(resTG.Spec.TargetType) != awssdk.StringValue(sdkTG.TargetGroup.TargetType) {
return true
}
if string(resTG.Spec.Protocol) != awssdk.StringValue(sdkTG.TargetGroup.Protocol) {
return true
}
if resTG.Spec.ProtocolVersion != nil {
if string(*resTG.Spec.ProtocolVersion) != awssdk.StringValue(sdkTG.TargetGroup.ProtocolVersion) {
return true
}
}
return isSDKTargetGroupRequiresReplacementDueToNLBHealthCheck(sdkTG, resTG, featureGates)
}
// most of the healthCheck settings for NLB targetGroups cannot be changed for now.
func isSDKTargetGroupRequiresReplacementDueToNLBHealthCheck(sdkTG TargetGroupWithTags, resTG *elbv2model.TargetGroup, featureGates config.FeatureGates) bool {
if resTG.Spec.HealthCheckConfig == nil || featureGates.Enabled(config.NLBHealthCheckAdvancedConfig) {
return false
}
if resTG.Spec.Protocol != elbv2model.ProtocolTCP && resTG.Spec.Protocol != elbv2model.ProtocolUDP &&
resTG.Spec.Protocol != elbv2model.ProtocolTCP_UDP && resTG.Spec.Protocol != elbv2model.ProtocolTLS {
return false
}
sdkObj := sdkTG.TargetGroup
hcConfig := *resTG.Spec.HealthCheckConfig
if hcConfig.Protocol != nil && string(*hcConfig.Protocol) != awssdk.StringValue(sdkObj.HealthCheckProtocol) {
return true
}
if hcConfig.Matcher != nil && (sdkObj.Matcher == nil || awssdk.StringValue(hcConfig.Matcher.GRPCCode) != awssdk.StringValue(sdkObj.Matcher.GrpcCode) || awssdk.StringValue(hcConfig.Matcher.HTTPCode) != awssdk.StringValue(sdkObj.Matcher.HttpCode)) {
return true
}
if hcConfig.IntervalSeconds != nil && awssdk.Int64Value(hcConfig.IntervalSeconds) != awssdk.Int64Value(sdkObj.HealthCheckIntervalSeconds) {
return true
}
if hcConfig.TimeoutSeconds != nil && awssdk.Int64Value(hcConfig.TimeoutSeconds) != awssdk.Int64Value(sdkObj.HealthCheckTimeoutSeconds) {
return true
}
return false
}