Skip to content

Commit ba448d4

Browse files
M00nF1shTimothy-Dougherty
authored andcommitted
add event handler for ingressClass&ingressClassParams events (kubernetes-sigs#1991)
* add event handler for ingressClass&ingressClassParams events * update crd in test cases * make ingressClass optional * force deployment
1 parent 138442c commit ba448d4

File tree

9 files changed

+420
-10
lines changed

9 files changed

+420
-10
lines changed
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+
networking "k8s.io/api/networking/v1beta1"
7+
"k8s.io/apimachinery/pkg/api/equality"
8+
"k8s.io/apimachinery/pkg/api/meta"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/client-go/tools/record"
11+
"k8s.io/client-go/util/workqueue"
12+
"sigs.k8s.io/aws-load-balancer-controller/pkg/ingress"
13+
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
14+
"sigs.k8s.io/controller-runtime/pkg/client"
15+
"sigs.k8s.io/controller-runtime/pkg/event"
16+
"sigs.k8s.io/controller-runtime/pkg/handler"
17+
)
18+
19+
// NewEnqueueRequestsForIngressClassEvent constructs new enqueueRequestsForIngressClassEvent.
20+
func NewEnqueueRequestsForIngressClassEvent(ingEventChan chan<- event.GenericEvent,
21+
k8sClient client.Client, eventRecorder record.EventRecorder, logger logr.Logger) *enqueueRequestsForIngressClassEvent {
22+
return &enqueueRequestsForIngressClassEvent{
23+
ingEventChan: ingEventChan,
24+
k8sClient: k8sClient,
25+
eventRecorder: eventRecorder,
26+
logger: logger,
27+
}
28+
}
29+
30+
var _ handler.EventHandler = (*enqueueRequestsForIngressClassEvent)(nil)
31+
32+
type enqueueRequestsForIngressClassEvent struct {
33+
ingEventChan chan<- event.GenericEvent
34+
k8sClient client.Client
35+
eventRecorder record.EventRecorder
36+
logger logr.Logger
37+
}
38+
39+
func (h *enqueueRequestsForIngressClassEvent) Create(e event.CreateEvent, _ workqueue.RateLimitingInterface) {
40+
h.enqueueImpactedIngresses(e.Meta)
41+
}
42+
43+
func (h *enqueueRequestsForIngressClassEvent) Update(e event.UpdateEvent, _ workqueue.RateLimitingInterface) {
44+
ingClassOld := e.ObjectOld.(*networking.IngressClass)
45+
ingClassNew := e.ObjectNew.(*networking.IngressClass)
46+
47+
// we only care below update event:
48+
// 2. IngressClass spec updates
49+
// 3. IngressClass deletions
50+
if equality.Semantic.DeepEqual(ingClassOld.Spec, ingClassNew.Spec) &&
51+
equality.Semantic.DeepEqual(ingClassOld.DeletionTimestamp.IsZero(), ingClassNew.DeletionTimestamp.IsZero()) {
52+
return
53+
}
54+
55+
h.enqueueImpactedIngresses(e.MetaNew)
56+
}
57+
58+
func (h *enqueueRequestsForIngressClassEvent) Delete(e event.DeleteEvent, _ workqueue.RateLimitingInterface) {
59+
h.enqueueImpactedIngresses(e.Meta)
60+
}
61+
62+
func (h *enqueueRequestsForIngressClassEvent) Generic(e event.GenericEvent, _ workqueue.RateLimitingInterface) {
63+
h.enqueueImpactedIngresses(e.Meta)
64+
}
65+
66+
func (h *enqueueRequestsForIngressClassEvent) enqueueImpactedIngresses(ingClass metav1.Object) {
67+
ingList := &networking.IngressList{}
68+
if err := h.k8sClient.List(context.Background(), ingList,
69+
client.MatchingFields{ingress.IndexKeyIngressClassRefName: ingClass.GetName()}); err != nil {
70+
h.logger.Error(err, "failed to fetch ingresses")
71+
return
72+
}
73+
74+
for index := range ingList.Items {
75+
ing := &ingList.Items[index]
76+
meta, _ := meta.Accessor(ing)
77+
78+
h.logger.V(1).Info("enqueue ingress for ingressClass event",
79+
"ingressClass", ingClass.GetName(),
80+
"ingress", k8s.NamespacedName(ing))
81+
h.ingEventChan <- event.GenericEvent{
82+
Meta: meta,
83+
Object: ing,
84+
}
85+
}
86+
}
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+
networking "k8s.io/api/networking/v1beta1"
7+
"k8s.io/apimachinery/pkg/api/equality"
8+
"k8s.io/apimachinery/pkg/api/meta"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/client-go/tools/record"
11+
"k8s.io/client-go/util/workqueue"
12+
elbv2api "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1beta1"
13+
"sigs.k8s.io/aws-load-balancer-controller/pkg/ingress"
14+
"sigs.k8s.io/controller-runtime/pkg/client"
15+
"sigs.k8s.io/controller-runtime/pkg/event"
16+
"sigs.k8s.io/controller-runtime/pkg/handler"
17+
)
18+
19+
// NewEnqueueRequestsForIngressClassParamsEvent constructs new enqueueRequestsForIngressClassParamsEvent.
20+
func NewEnqueueRequestsForIngressClassParamsEvent(ingClassEventChan chan<- event.GenericEvent,
21+
k8sClient client.Client, eventRecorder record.EventRecorder, logger logr.Logger) *enqueueRequestsForIngressClassParamsEvent {
22+
return &enqueueRequestsForIngressClassParamsEvent{
23+
ingClassEventChan: ingClassEventChan,
24+
k8sClient: k8sClient,
25+
eventRecorder: eventRecorder,
26+
logger: logger,
27+
}
28+
}
29+
30+
var _ handler.EventHandler = (*enqueueRequestsForIngressClassParamsEvent)(nil)
31+
32+
type enqueueRequestsForIngressClassParamsEvent struct {
33+
ingClassEventChan chan<- event.GenericEvent
34+
k8sClient client.Client
35+
eventRecorder record.EventRecorder
36+
logger logr.Logger
37+
}
38+
39+
func (h *enqueueRequestsForIngressClassParamsEvent) Create(e event.CreateEvent, _ workqueue.RateLimitingInterface) {
40+
h.enqueueImpactedIngressClasses(e.Meta)
41+
}
42+
43+
func (h *enqueueRequestsForIngressClassParamsEvent) Update(e event.UpdateEvent, _ workqueue.RateLimitingInterface) {
44+
ingClassParamsOld := e.ObjectOld.(*elbv2api.IngressClassParams)
45+
ingClassParamsNew := e.ObjectNew.(*elbv2api.IngressClassParams)
46+
47+
// we only care below update event:
48+
// 2. IngressClassParams spec updates
49+
// 3. IngressClassParams deletion
50+
if equality.Semantic.DeepEqual(ingClassParamsOld.Spec, ingClassParamsNew.Spec) &&
51+
equality.Semantic.DeepEqual(ingClassParamsOld.DeletionTimestamp.IsZero(), ingClassParamsNew.DeletionTimestamp.IsZero()) {
52+
return
53+
}
54+
55+
h.enqueueImpactedIngressClasses(e.MetaNew)
56+
}
57+
58+
func (h *enqueueRequestsForIngressClassParamsEvent) Delete(e event.DeleteEvent, _ workqueue.RateLimitingInterface) {
59+
h.enqueueImpactedIngressClasses(e.Meta)
60+
}
61+
62+
func (h *enqueueRequestsForIngressClassParamsEvent) Generic(e event.GenericEvent, _ workqueue.RateLimitingInterface) {
63+
// we don't have any generic event for secrets.
64+
}
65+
66+
//
67+
func (h *enqueueRequestsForIngressClassParamsEvent) enqueueImpactedIngressClasses(ingClassParams metav1.Object) {
68+
ingClassList := &networking.IngressClassList{}
69+
if err := h.k8sClient.List(context.Background(), ingClassList,
70+
client.MatchingFields{ingress.IndexKeyIngressClassParamsRefName: ingClassParams.GetName()}); err != nil {
71+
h.logger.Error(err, "failed to fetch ingressClasses")
72+
return
73+
}
74+
for index := range ingClassList.Items {
75+
ingClass := &ingClassList.Items[index]
76+
meta, _ := meta.Accessor(ingClass)
77+
78+
h.logger.V(1).Info("enqueue ingressClass for ingressClassParams event",
79+
"ingressClassParams", ingClassParams.GetName(),
80+
"ingressClass", ingClass.GetName())
81+
h.ingClassEventChan <- event.GenericEvent{
82+
Meta: meta,
83+
Object: ingClass,
84+
}
85+
}
86+
}

controllers/ingress/group_controller.go

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ import (
77
"github.com/pkg/errors"
88
corev1 "k8s.io/api/core/v1"
99
networking "k8s.io/api/networking/v1beta1"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1011
k8sruntime "k8s.io/apimachinery/pkg/runtime"
12+
"k8s.io/client-go/kubernetes"
1113
"k8s.io/client-go/tools/record"
14+
elbv2api "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1beta1"
1215
"sigs.k8s.io/aws-load-balancer-controller/controllers/ingress/eventhandlers"
1316
"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
1417
"sigs.k8s.io/aws-load-balancer-controller/pkg/aws"
@@ -30,6 +33,10 @@ import (
3033
const (
3134
ingressTagPrefix = "ingress.k8s.aws"
3235
controllerName = "ingress"
36+
37+
// the groupVersion of used Ingress & IngressClass resource.
38+
ingressResourcesGroupVersion = "networking.k8s.io/v1beta1"
39+
ingressClassKind = "IngressClass"
3340
)
3441

3542
// NewGroupReconciler constructs new GroupReconciler
@@ -197,24 +204,30 @@ func (r *groupReconciler) updateIngressStatus(ctx context.Context, lbDNS string,
197204
return nil
198205
}
199206

200-
func (r *groupReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
207+
func (r *groupReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, clientSet *kubernetes.Clientset) error {
201208
c, err := controller.New(controllerName, mgr, controller.Options{
202209
MaxConcurrentReconciles: r.maxConcurrentReconciles,
203210
Reconciler: r,
204211
})
205212
if err != nil {
206213
return err
207214
}
208-
if err := r.setupIndexes(ctx, mgr.GetFieldIndexer()); err != nil {
215+
216+
resList, err := clientSet.ServerResourcesForGroupVersion(ingressResourcesGroupVersion)
217+
if err != nil {
209218
return err
210219
}
211-
if err := r.setupWatches(ctx, c); err != nil {
220+
ingressClassResourceAvailable := isResourceKindAvailable(resList, ingressClassKind)
221+
if err := r.setupIndexes(ctx, mgr.GetFieldIndexer(), ingressClassResourceAvailable); err != nil {
222+
return err
223+
}
224+
if err := r.setupWatches(ctx, c, ingressClassResourceAvailable); err != nil {
212225
return err
213226
}
214227
return nil
215228
}
216229

217-
func (r *groupReconciler) setupIndexes(ctx context.Context, fieldIndexer client.FieldIndexer) error {
230+
func (r *groupReconciler) setupIndexes(ctx context.Context, fieldIndexer client.FieldIndexer, ingressClassResourceAvailable bool) error {
218231
if err := fieldIndexer.IndexField(ctx, &networking.Ingress{}, ingress.IndexKeyServiceRefName,
219232
func(obj k8sruntime.Object) []string {
220233
return r.referenceIndexer.BuildServiceRefIndexes(context.Background(), obj.(*networking.Ingress))
@@ -236,10 +249,26 @@ func (r *groupReconciler) setupIndexes(ctx context.Context, fieldIndexer client.
236249
); err != nil {
237250
return err
238251
}
252+
if ingressClassResourceAvailable {
253+
if err := fieldIndexer.IndexField(ctx, &networking.IngressClass{}, ingress.IndexKeyIngressClassParamsRefName,
254+
func(obj k8sruntime.Object) []string {
255+
return r.referenceIndexer.BuildIngressClassParamsRefIndexes(ctx, obj.(*networking.IngressClass))
256+
},
257+
); err != nil {
258+
return err
259+
}
260+
if err := fieldIndexer.IndexField(ctx, &networking.Ingress{}, ingress.IndexKeyIngressClassRefName,
261+
func(obj k8sruntime.Object) []string {
262+
return r.referenceIndexer.BuildIngressClassRefIndexes(ctx, obj.(*networking.Ingress))
263+
},
264+
); err != nil {
265+
return err
266+
}
267+
}
239268
return nil
240269
}
241270

242-
func (r *groupReconciler) setupWatches(_ context.Context, c controller.Controller) error {
271+
func (r *groupReconciler) setupWatches(_ context.Context, c controller.Controller, ingressClassResourceAvailable bool) error {
243272
ingEventChan := make(chan event.GenericEvent)
244273
svcEventChan := make(chan event.GenericEvent)
245274
ingEventHandler := eventhandlers.NewEnqueueRequestsForIngressEvent(r.groupLoader, r.eventRecorder,
@@ -248,7 +277,6 @@ func (r *groupReconciler) setupWatches(_ context.Context, c controller.Controlle
248277
r.logger.WithName("eventHandlers").WithName("service"))
249278
secretEventHandler := eventhandlers.NewEnqueueRequestsForSecretEvent(ingEventChan, svcEventChan, r.k8sClient, r.eventRecorder,
250279
r.logger.WithName("eventHandlers").WithName("secret"))
251-
252280
if err := c.Watch(&source.Channel{Source: ingEventChan}, ingEventHandler); err != nil {
253281
return err
254282
}
@@ -264,5 +292,32 @@ func (r *groupReconciler) setupWatches(_ context.Context, c controller.Controlle
264292
if err := c.Watch(&source.Kind{Type: &corev1.Secret{}}, secretEventHandler); err != nil {
265293
return err
266294
}
295+
296+
if ingressClassResourceAvailable {
297+
ingClassEventChan := make(chan event.GenericEvent)
298+
ingClassParamsEventHandler := eventhandlers.NewEnqueueRequestsForIngressClassParamsEvent(ingClassEventChan, r.k8sClient, r.eventRecorder,
299+
r.logger.WithName("eventHandlers").WithName("ingressClassParams"))
300+
ingClassEventHandler := eventhandlers.NewEnqueueRequestsForIngressClassEvent(ingEventChan, r.k8sClient, r.eventRecorder,
301+
r.logger.WithName("eventHandlers").WithName("ingressClass"))
302+
if err := c.Watch(&source.Channel{Source: ingClassEventChan}, ingClassEventHandler); err != nil {
303+
return err
304+
}
305+
if err := c.Watch(&source.Kind{Type: &elbv2api.IngressClassParams{}}, ingClassParamsEventHandler); err != nil {
306+
return err
307+
}
308+
if err := c.Watch(&source.Kind{Type: &networking.IngressClass{}}, ingClassEventHandler); err != nil {
309+
return err
310+
}
311+
}
267312
return nil
268313
}
314+
315+
// isResourceKindAvailable checks whether specific kind is available.
316+
func isResourceKindAvailable(resList *metav1.APIResourceList, kind string) bool {
317+
for _, res := range resList.APIResources {
318+
if res.Kind == kind {
319+
return true
320+
}
321+
}
322+
return false
323+
}

main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func main() {
9797
setupLog.Error(err, "unable to obtain clientSet")
9898
os.Exit(1)
9999
}
100+
100101
podInfoRepo := k8s.NewDefaultPodInfoRepo(clientSet.CoreV1().RESTClient(), rtOpts.Namespace, ctrl.Log)
101102
finalizerManager := k8s.NewDefaultFinalizerManager(mgr.GetClient(), ctrl.Log)
102103
podENIResolver := networking.NewDefaultPodENIInfoResolver(cloud.EC2(), cloud.VpcID(), ctrl.Log)
@@ -118,7 +119,7 @@ func main() {
118119
finalizerManager, tgbResManager,
119120
controllerCFG, ctrl.Log.WithName("controllers").WithName("targetGroupBinding"))
120121
ctx := context.Background()
121-
if err = ingGroupReconciler.SetupWithManager(ctx, mgr); err != nil {
122+
if err = ingGroupReconciler.SetupWithManager(ctx, mgr, clientSet); err != nil {
122123
setupLog.Error(err, "unable to create controller", "controller", "Ingress")
123124
os.Exit(1)
124125
}

pkg/ingress/reference_indexer.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,35 @@ package ingress
22

33
import (
44
"context"
5+
awssdk "github.com/aws/aws-sdk-go/aws"
56
"github.com/go-logr/logr"
67
networking "k8s.io/api/networking/v1beta1"
78
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
89
"k8s.io/apimachinery/pkg/util/sets"
10+
elbv2api "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1beta1"
911
)
1012

1113
const (
12-
// IndexKey for services referenced by Ingress.
14+
// IndexKeyServiceRefName is index key for services referenced by Ingress.
1315
IndexKeyServiceRefName = "ingress.serviceRef.name"
14-
// IndexKey for secrets referenced by Ingress or Service.
16+
// IndexKeySecretRefName is index key for secrets referenced by Ingress or Service.
1517
IndexKeySecretRefName = "ingress.secretRef.name"
18+
// IndexKeyIngressClassRefName is index key for ingressClass referenced by Ingress.
19+
IndexKeyIngressClassRefName = "ingress.ingressClassRef.name"
20+
// IndexKeyIngressClassParamsRefName is index key for ingressClassParams referenced by IngressClass.
21+
IndexKeyIngressClassParamsRefName = "ingressClass.ingressClassParamsRef.name"
1622
)
1723

1824
// ReferenceIndexer has the ability to index Ingresses with referenced objects.
1925
type ReferenceIndexer interface {
26+
// BuildServiceRefIndexes returns the name of related Service objects.
2027
BuildServiceRefIndexes(ctx context.Context, ing *networking.Ingress) []string
28+
// BuildSecretRefIndexes returns the name of related Secret objects.
2129
BuildSecretRefIndexes(ctx context.Context, ingOrSvc metav1.Object) []string
30+
// BuildIngressClassRefIndexes returns the name of related IngressClass objects.
31+
BuildIngressClassRefIndexes(ctx context.Context, ing *networking.Ingress) []string
32+
// BuildIngressClassParamsRefIndexes returns the name of related IngressClassParams objects.
33+
BuildIngressClassParamsRefIndexes(ctx context.Context, ingClass *networking.IngressClass) []string
2234
}
2335

2436
// NewDefaultReferenceIndexer constructs new defaultReferenceIndexer.
@@ -80,6 +92,28 @@ func (i *defaultReferenceIndexer) BuildSecretRefIndexes(ctx context.Context, ing
8092
return extractSecretNamesFromAuthConfig(authCfg)
8193
}
8294

95+
func (i *defaultReferenceIndexer) BuildIngressClassRefIndexes(_ context.Context, ing *networking.Ingress) []string {
96+
if ing.Spec.IngressClassName == nil {
97+
return nil
98+
}
99+
100+
ingClassName := awssdk.StringValue(ing.Spec.IngressClassName)
101+
return []string{ingClassName}
102+
}
103+
104+
func (i *defaultReferenceIndexer) BuildIngressClassParamsRefIndexes(_ context.Context, ingClass *networking.IngressClass) []string {
105+
if ingClass.Spec.Controller != ingressClassControllerALB || ingClass.Spec.Parameters == nil {
106+
return nil
107+
}
108+
if ingClass.Spec.Parameters.APIGroup == nil ||
109+
(*ingClass.Spec.Parameters.APIGroup) != elbv2api.GroupVersion.Group ||
110+
ingClass.Spec.Parameters.Kind != ingressClassParamsKind {
111+
return nil
112+
}
113+
ingClassParamsName := ingClass.Spec.Parameters.Name
114+
return []string{ingClassParamsName}
115+
}
116+
83117
func extractServiceNamesFromAction(action Action) []string {
84118
if action.Type != ActionTypeForward || action.ForwardConfig == nil {
85119
return nil

0 commit comments

Comments
 (0)