Skip to content

Commit 0386cd3

Browse files
committed
add svc event handler for targetGroupBinding
1 parent dbc8f00 commit 0386cd3

File tree

6 files changed

+524
-2
lines changed

6 files changed

+524
-2
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package eventhandlers
2+
3+
import (
4+
"context"
5+
"github.com/golang/mock/gomock"
6+
"github.com/google/go-cmp/cmp"
7+
"github.com/stretchr/testify/assert"
8+
corev1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/apimachinery/pkg/types"
11+
"k8s.io/client-go/util/workqueue"
12+
elbv2api "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1beta1"
13+
mock_client "sigs.k8s.io/aws-load-balancer-controller/mocks/controller-runtime/client"
14+
"sigs.k8s.io/aws-load-balancer-controller/pkg/testutils"
15+
ctrl "sigs.k8s.io/controller-runtime"
16+
"sigs.k8s.io/controller-runtime/pkg/client"
17+
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
18+
"sigs.k8s.io/controller-runtime/pkg/log"
19+
"testing"
20+
)
21+
22+
func Test_enqueueRequestsForEndpointsEvent_enqueueImpactedTargetGroupBindings(t *testing.T) {
23+
instanceTargetType := elbv2api.TargetTypeInstance
24+
ipTargetType := elbv2api.TargetTypeIP
25+
26+
type tgbListCall struct {
27+
opts []client.ListOption
28+
tgbs []*elbv2api.TargetGroupBinding
29+
err error
30+
}
31+
type fields struct {
32+
tgbListCalls []tgbListCall
33+
}
34+
type args struct {
35+
eps *corev1.Endpoints
36+
}
37+
tests := []struct {
38+
name string
39+
fields fields
40+
args args
41+
wantRequests []ctrl.Request
42+
}{
43+
{
44+
name: "service event should enqueue impacted ip TargetType TGBs",
45+
fields: fields{
46+
tgbListCalls: []tgbListCall{
47+
{
48+
opts: []client.ListOption{
49+
client.InNamespace("awesome-ns"),
50+
client.MatchingFields{"spec.serviceRef.name": "awesome-svc"},
51+
},
52+
tgbs: []*elbv2api.TargetGroupBinding{
53+
{
54+
ObjectMeta: metav1.ObjectMeta{
55+
Namespace: "awesome-ns",
56+
Name: "tgb-1",
57+
},
58+
Spec: elbv2api.TargetGroupBindingSpec{
59+
TargetType: &ipTargetType,
60+
},
61+
},
62+
{
63+
ObjectMeta: metav1.ObjectMeta{
64+
Namespace: "awesome-ns",
65+
Name: "tgb-2",
66+
},
67+
Spec: elbv2api.TargetGroupBindingSpec{
68+
TargetType: &instanceTargetType,
69+
},
70+
},
71+
{
72+
ObjectMeta: metav1.ObjectMeta{
73+
Namespace: "awesome-ns",
74+
Name: "tgb-3",
75+
},
76+
Spec: elbv2api.TargetGroupBindingSpec{
77+
TargetType: &ipTargetType,
78+
},
79+
},
80+
},
81+
},
82+
},
83+
},
84+
args: args{
85+
eps: &corev1.Endpoints{
86+
ObjectMeta: metav1.ObjectMeta{
87+
Namespace: "awesome-ns",
88+
Name: "awesome-svc",
89+
},
90+
},
91+
},
92+
wantRequests: []ctrl.Request{
93+
{
94+
NamespacedName: types.NamespacedName{Namespace: "awesome-ns", Name: "tgb-1"},
95+
},
96+
{
97+
NamespacedName: types.NamespacedName{Namespace: "awesome-ns", Name: "tgb-3"},
98+
},
99+
},
100+
},
101+
{
102+
name: "service event should enqueue impacted ip TargetType TGBs - ignore nil TargetType",
103+
fields: fields{
104+
tgbListCalls: []tgbListCall{
105+
{
106+
opts: []client.ListOption{
107+
client.InNamespace("awesome-ns"),
108+
client.MatchingFields{"spec.serviceRef.name": "awesome-svc"},
109+
},
110+
tgbs: []*elbv2api.TargetGroupBinding{
111+
{
112+
ObjectMeta: metav1.ObjectMeta{
113+
Namespace: "awesome-ns",
114+
Name: "tgb-1",
115+
},
116+
Spec: elbv2api.TargetGroupBindingSpec{
117+
TargetType: &ipTargetType,
118+
},
119+
},
120+
{
121+
ObjectMeta: metav1.ObjectMeta{
122+
Namespace: "awesome-ns",
123+
Name: "tgb-2",
124+
},
125+
Spec: elbv2api.TargetGroupBindingSpec{
126+
TargetType: nil,
127+
},
128+
},
129+
},
130+
},
131+
},
132+
},
133+
args: args{
134+
eps: &corev1.Endpoints{
135+
ObjectMeta: metav1.ObjectMeta{
136+
Namespace: "awesome-ns",
137+
Name: "awesome-svc",
138+
},
139+
},
140+
},
141+
wantRequests: []ctrl.Request{
142+
{
143+
NamespacedName: types.NamespacedName{Namespace: "awesome-ns", Name: "tgb-1"},
144+
},
145+
},
146+
},
147+
}
148+
for _, tt := range tests {
149+
t.Run(tt.name, func(t *testing.T) {
150+
ctrl := gomock.NewController(t)
151+
defer ctrl.Finish()
152+
153+
k8sClient := mock_client.NewMockClient(ctrl)
154+
for _, call := range tt.fields.tgbListCalls {
155+
var extraMatchers []interface{}
156+
for _, opt := range call.opts {
157+
extraMatchers = append(extraMatchers, testutils.NewListOptionEquals(opt))
158+
}
159+
k8sClient.EXPECT().List(gomock.Any(), gomock.Any(), extraMatchers...).DoAndReturn(
160+
func(ctx context.Context, tgbList *elbv2api.TargetGroupBindingList, opts ...client.ListOption) error {
161+
for _, tgb := range call.tgbs {
162+
tgbList.Items = append(tgbList.Items, *(tgb.DeepCopy()))
163+
}
164+
return call.err
165+
},
166+
)
167+
}
168+
169+
h := &enqueueRequestsForEndpointsEvent{
170+
k8sClient: k8sClient,
171+
logger: &log.NullLogger{},
172+
}
173+
queue := controllertest.Queue{Interface: workqueue.New()}
174+
h.enqueueImpactedTargetGroupBindings(queue, tt.args.eps)
175+
gotRequests := testutils.ExtractCTRLRequestsFromQueue(queue)
176+
assert.True(t, cmp.Equal(tt.wantRequests, gotRequests),
177+
"diff", cmp.Diff(tt.wantRequests, gotRequests))
178+
})
179+
}
180+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package eventhandlers
2+
3+
import (
4+
"context"
5+
"github.com/go-logr/logr"
6+
corev1 "k8s.io/api/core/v1"
7+
"k8s.io/apimachinery/pkg/api/equality"
8+
"k8s.io/apimachinery/pkg/types"
9+
"k8s.io/client-go/util/workqueue"
10+
elbv2api "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1beta1"
11+
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
12+
"sigs.k8s.io/aws-load-balancer-controller/pkg/targetgroupbinding"
13+
"sigs.k8s.io/controller-runtime/pkg/client"
14+
"sigs.k8s.io/controller-runtime/pkg/event"
15+
"sigs.k8s.io/controller-runtime/pkg/handler"
16+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
17+
)
18+
19+
// NewEnqueueRequestsForServiceEvent constructs new enqueueRequestsForServiceEvent.
20+
func NewEnqueueRequestsForServiceEvent(k8sClient client.Client, logger logr.Logger) handler.EventHandler {
21+
return &enqueueRequestsForServiceEvent{
22+
k8sClient: k8sClient,
23+
logger: logger,
24+
}
25+
}
26+
27+
type enqueueRequestsForServiceEvent struct {
28+
k8sClient client.Client
29+
logger logr.Logger
30+
}
31+
32+
// Create is called in response to an create event - e.g. Pod Creation.
33+
func (h *enqueueRequestsForServiceEvent) Create(e event.CreateEvent, queue workqueue.RateLimitingInterface) {
34+
svcNew := e.Object.(*corev1.Service)
35+
h.enqueueImpactedTargetGroupBindings(queue, svcNew)
36+
}
37+
38+
// Update is called in response to an update event - e.g. Pod Updated.
39+
func (h *enqueueRequestsForServiceEvent) Update(e event.UpdateEvent, queue workqueue.RateLimitingInterface) {
40+
svcOld := e.ObjectOld.(*corev1.Service)
41+
svcNew := e.ObjectNew.(*corev1.Service)
42+
if !equality.Semantic.DeepEqual(svcOld.Spec.Ports, svcNew.Spec.Ports) {
43+
h.enqueueImpactedTargetGroupBindings(queue, svcNew)
44+
}
45+
}
46+
47+
// Delete is called in response to a delete event - e.g. Pod Deleted.
48+
func (h *enqueueRequestsForServiceEvent) Delete(e event.DeleteEvent, queue workqueue.RateLimitingInterface) {
49+
svcOld := e.Object.(*corev1.Service)
50+
h.enqueueImpactedTargetGroupBindings(queue, svcOld)
51+
}
52+
53+
// Generic is called in response to an event of an unknown type or a synthetic event triggered as a cron or
54+
// external trigger request - e.g. reconcile AutoScaling, or a WebHook.
55+
func (h *enqueueRequestsForServiceEvent) Generic(e event.GenericEvent, queue workqueue.RateLimitingInterface) {
56+
// nothing to do here
57+
}
58+
59+
// enqueueImpactedEndpointBindings will enqueue all impacted TargetGroupBindings for service events.
60+
func (h *enqueueRequestsForServiceEvent) enqueueImpactedTargetGroupBindings(queue workqueue.RateLimitingInterface, svc *corev1.Service) {
61+
tgbList := &elbv2api.TargetGroupBindingList{}
62+
if err := h.k8sClient.List(context.Background(), tgbList,
63+
client.InNamespace(svc.Namespace),
64+
client.MatchingFields{targetgroupbinding.IndexKeyServiceRefName: svc.Name}); err != nil {
65+
h.logger.Error(err, "failed to fetch targetGroupBindings")
66+
return
67+
}
68+
69+
svcKey := k8s.NamespacedName(svc)
70+
for _, tgb := range tgbList.Items {
71+
if tgb.Spec.TargetType == nil || (*tgb.Spec.TargetType) != elbv2api.TargetTypeInstance {
72+
continue
73+
}
74+
75+
h.logger.V(1).Info("enqueue targetGroupBinding for service event",
76+
"service", svcKey,
77+
"targetGroupBinding", k8s.NamespacedName(&tgb),
78+
)
79+
queue.Add(reconcile.Request{
80+
NamespacedName: types.NamespacedName{
81+
Namespace: tgb.Namespace,
82+
Name: tgb.Name,
83+
},
84+
})
85+
}
86+
}

0 commit comments

Comments
 (0)