Skip to content

Commit 356694f

Browse files
authored
feat: support ingress class (#113)
Signed-off-by: ashing <[email protected]>
1 parent f346ae3 commit 356694f

File tree

8 files changed

+363
-22
lines changed

8 files changed

+363
-22
lines changed

internal/controller/indexer/indexer.go

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ func SetupIndexer(mgr ctrl.Manager) error {
4242
if err := setupBackendTrafficPolicyIndexer(mgr); err != nil {
4343
return err
4444
}
45+
if err := setupIngressClassIndexer(mgr); err != nil {
46+
return err
47+
}
48+
if err := setupGatewayProxyIndexer(mgr); err != nil {
49+
return err
50+
}
4551
return nil
4652
}
4753

@@ -149,53 +155,90 @@ func setupHTTPRouteIndexer(mgr ctrl.Manager) error {
149155
return nil
150156
}
151157

152-
func setupIngressIndexer(mgr ctrl.Manager) error {
158+
func setupIngressClassIndexer(mgr ctrl.Manager) error {
153159
// create IngressClass index
154160
if err := mgr.GetFieldIndexer().IndexField(
155161
context.Background(),
156-
&networkingv1.Ingress{},
157-
IngressClassRef,
158-
IngressClassRefIndexFunc,
162+
&networkingv1.IngressClass{},
163+
IngressClass,
164+
IngressClassIndexFunc,
159165
); err != nil {
160166
return err
161167
}
162168

163-
// create Service index for quick lookup of Ingresses using specific services
169+
// create IngressClassParametersRef index
164170
if err := mgr.GetFieldIndexer().IndexField(
165171
context.Background(),
166-
&networkingv1.Ingress{},
167-
ServiceIndexRef,
168-
IngressServiceIndexFunc,
172+
&networkingv1.IngressClass{},
173+
IngressClassParametersRef,
174+
IngressClassParametersRefIndexFunc,
169175
); err != nil {
170176
return err
171177
}
172178

173-
// create secret index for TLS
179+
return nil
180+
}
181+
182+
func setupGatewayProxyIndexer(mgr ctrl.Manager) error {
174183
if err := mgr.GetFieldIndexer().IndexField(
175184
context.Background(),
176-
&networkingv1.Ingress{},
185+
&v1alpha1.GatewayProxy{},
177186
SecretIndexRef,
178-
IngressSecretIndexFunc,
187+
GatewayProxySecretIndexFunc,
179188
); err != nil {
180189
return err
181190
}
191+
return nil
192+
}
182193

194+
func GatewayProxySecretIndexFunc(rawObj client.Object) []string {
195+
gatewayProxy := rawObj.(*v1alpha1.GatewayProxy)
196+
secretKeys := make([]string, 0)
197+
198+
// Check if provider is ControlPlane type and has AdminKey auth
199+
if gatewayProxy.Spec.Provider != nil &&
200+
gatewayProxy.Spec.Provider.Type == v1alpha1.ProviderTypeControlPlane &&
201+
gatewayProxy.Spec.Provider.ControlPlane != nil &&
202+
gatewayProxy.Spec.Provider.ControlPlane.Auth.Type == v1alpha1.AuthTypeAdminKey &&
203+
gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey != nil &&
204+
gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom != nil &&
205+
gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef != nil {
206+
207+
ref := gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef
208+
ns := gatewayProxy.GetNamespace()
209+
key := GenIndexKey(ns, ref.Name)
210+
secretKeys = append(secretKeys, key)
211+
}
212+
return secretKeys
213+
}
214+
215+
func setupIngressIndexer(mgr ctrl.Manager) error {
183216
// create IngressClass index
184217
if err := mgr.GetFieldIndexer().IndexField(
185218
context.Background(),
186-
&networkingv1.IngressClass{},
187-
IngressClass,
188-
IngressClassIndexFunc,
219+
&networkingv1.Ingress{},
220+
IngressClassRef,
221+
IngressClassRefIndexFunc,
189222
); err != nil {
190223
return err
191224
}
192225

193-
// create IngressClassParametersRef index
226+
// create Service index for quick lookup of Ingresses using specific services
194227
if err := mgr.GetFieldIndexer().IndexField(
195228
context.Background(),
196-
&networkingv1.IngressClass{},
197-
IngressClassParametersRef,
198-
IngressClassParametersRefIndexFunc,
229+
&networkingv1.Ingress{},
230+
ServiceIndexRef,
231+
IngressServiceIndexFunc,
232+
); err != nil {
233+
return err
234+
}
235+
236+
// create secret index for TLS
237+
if err := mgr.GetFieldIndexer().IndexField(
238+
context.Background(),
239+
&networkingv1.Ingress{},
240+
SecretIndexRef,
241+
IngressSecretIndexFunc,
199242
); err != nil {
200243
return err
201244
}

internal/controller/ingress_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ func (r *IngressReconciler) processIngressClassParameters(ctx context.Context, t
651651

652652
parameters := ingressClass.Spec.Parameters
653653
// check if the parameters reference GatewayProxy
654-
if parameters.APIGroup != nil && *parameters.APIGroup == v1alpha1.GroupVersion.Group && parameters.Kind == "GatewayProxy" {
654+
if parameters.APIGroup != nil && *parameters.APIGroup == v1alpha1.GroupVersion.Group && parameters.Kind == KindGatewayProxy {
655655
ns := ingress.GetNamespace()
656656
if parameters.Namespace != nil {
657657
ns = *parameters.Namespace
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package controller
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/api7/gopkg/pkg/log"
8+
"github.com/go-logr/logr"
9+
corev1 "k8s.io/api/core/v1"
10+
networkingv1 "k8s.io/api/networking/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/runtime"
13+
ctrl "sigs.k8s.io/controller-runtime"
14+
"sigs.k8s.io/controller-runtime/pkg/builder"
15+
"sigs.k8s.io/controller-runtime/pkg/client"
16+
"sigs.k8s.io/controller-runtime/pkg/handler"
17+
"sigs.k8s.io/controller-runtime/pkg/predicate"
18+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
19+
20+
"github.com/api7/api7-ingress-controller/api/v1alpha1"
21+
"github.com/api7/api7-ingress-controller/internal/controller/indexer"
22+
"github.com/api7/api7-ingress-controller/internal/provider"
23+
)
24+
25+
// IngressClassReconciler reconciles a IngressClass object.
26+
type IngressClassReconciler struct {
27+
client.Client
28+
Scheme *runtime.Scheme
29+
Log logr.Logger
30+
31+
Provider provider.Provider
32+
}
33+
34+
// SetupWithManager sets up the controller with the Manager.
35+
func (r *IngressClassReconciler) SetupWithManager(mgr ctrl.Manager) error {
36+
return ctrl.NewControllerManagedBy(mgr).
37+
For(
38+
&networkingv1.IngressClass{},
39+
builder.WithPredicates(
40+
predicate.NewPredicateFuncs(r.matchesController),
41+
),
42+
).
43+
WithEventFilter(predicate.GenerationChangedPredicate{}).
44+
Watches(
45+
&v1alpha1.GatewayProxy{},
46+
handler.EnqueueRequestsFromMapFunc(r.listIngressClassesForGatewayProxy),
47+
).
48+
Watches(
49+
&corev1.Secret{},
50+
handler.EnqueueRequestsFromMapFunc(r.listIngressClassesForSecret),
51+
).
52+
Complete(r)
53+
}
54+
55+
func (r *IngressClassReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
56+
ingressClass := new(networkingv1.IngressClass)
57+
if err := r.Get(ctx, req.NamespacedName, ingressClass); err != nil {
58+
if client.IgnoreNotFound(err) == nil {
59+
ingressClass.Name = req.Name
60+
61+
ingressClass.TypeMeta = metav1.TypeMeta{
62+
Kind: KindIngressClass,
63+
APIVersion: networkingv1.SchemeGroupVersion.String(),
64+
}
65+
66+
if err := r.Provider.Delete(ctx, ingressClass); err != nil {
67+
return ctrl.Result{}, err
68+
}
69+
return ctrl.Result{}, nil
70+
}
71+
return ctrl.Result{}, err
72+
}
73+
74+
// Create a translate context
75+
tctx := provider.NewDefaultTranslateContext(ctx)
76+
77+
if err := r.processInfrastructure(tctx, ingressClass); err != nil {
78+
r.Log.Error(err, "failed to process infrastructure for ingressclass", "ingressclass", ingressClass.GetName())
79+
return ctrl.Result{}, err
80+
}
81+
82+
if err := r.Provider.Update(ctx, tctx, ingressClass); err != nil {
83+
r.Log.Error(err, "failed to update ingressclass", "ingressclass", ingressClass.GetName())
84+
return ctrl.Result{}, err
85+
}
86+
87+
return ctrl.Result{}, nil
88+
}
89+
90+
func (r *IngressClassReconciler) matchesController(obj client.Object) bool {
91+
ingressClass, ok := obj.(*networkingv1.IngressClass)
92+
if !ok {
93+
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to IngressClass")
94+
return false
95+
}
96+
return matchesController(ingressClass.Spec.Controller)
97+
}
98+
99+
func (r *IngressClassReconciler) listIngressClassesForGatewayProxy(ctx context.Context, obj client.Object) []reconcile.Request {
100+
gatewayProxy, ok := obj.(*v1alpha1.GatewayProxy)
101+
if !ok {
102+
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to GatewayProxy")
103+
return nil
104+
}
105+
namespace := gatewayProxy.GetNamespace()
106+
name := gatewayProxy.GetName()
107+
108+
ingressClassList := &networkingv1.IngressClassList{}
109+
if err := r.List(ctx, ingressClassList, client.MatchingFields{
110+
indexer.IngressClassParametersRef: indexer.GenIndexKey(namespace, name),
111+
}); err != nil {
112+
r.Log.Error(err, "failed to list ingress classes for gateway proxy", "gatewayproxy", gatewayProxy.GetName())
113+
return nil
114+
}
115+
116+
recs := make([]reconcile.Request, 0, len(ingressClassList.Items))
117+
for _, ingressClass := range ingressClassList.Items {
118+
if !r.matchesController(&ingressClass) {
119+
continue
120+
}
121+
recs = append(recs, reconcile.Request{
122+
NamespacedName: client.ObjectKey{
123+
Name: ingressClass.GetName(),
124+
},
125+
})
126+
}
127+
return recs
128+
}
129+
130+
func (r *IngressClassReconciler) listIngressClassesForSecret(ctx context.Context, obj client.Object) []reconcile.Request {
131+
secret, ok := obj.(*corev1.Secret)
132+
if !ok {
133+
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to Secret")
134+
return nil
135+
}
136+
137+
// 1. list gateway proxies by secret
138+
gatewayProxyList := &v1alpha1.GatewayProxyList{}
139+
if err := r.List(ctx, gatewayProxyList, client.MatchingFields{
140+
indexer.SecretIndexRef: indexer.GenIndexKey(secret.GetNamespace(), secret.GetName()),
141+
}); err != nil {
142+
r.Log.Error(err, "failed to list gateway proxies by secret", "secret", secret.GetName())
143+
return nil
144+
}
145+
146+
// 2. list ingress classes by gateway proxies
147+
requests := make([]reconcile.Request, 0)
148+
for _, gatewayProxy := range gatewayProxyList.Items {
149+
ingressClassList := &networkingv1.IngressClassList{}
150+
if err := r.List(ctx, ingressClassList, client.MatchingFields{
151+
indexer.IngressClassParametersRef: indexer.GenIndexKey(gatewayProxy.GetNamespace(), gatewayProxy.GetName()),
152+
}); err != nil {
153+
r.Log.Error(err, "failed to list ingress classes by secret", "secret", secret.GetName())
154+
return nil
155+
}
156+
for _, ingressClass := range ingressClassList.Items {
157+
if !r.matchesController(&ingressClass) {
158+
continue
159+
}
160+
requests = append(requests, reconcile.Request{
161+
NamespacedName: client.ObjectKey{
162+
Name: ingressClass.GetName(),
163+
Namespace: ingressClass.GetNamespace(),
164+
},
165+
})
166+
}
167+
}
168+
169+
requests = distinctRequests(requests)
170+
171+
return requests
172+
}
173+
174+
func (r *IngressClassReconciler) processInfrastructure(tctx *provider.TranslateContext, ingressClass *networkingv1.IngressClass) error {
175+
if ingressClass.Spec.Parameters == nil {
176+
return nil
177+
}
178+
179+
if ingressClass.Spec.Parameters.APIGroup == nil ||
180+
*ingressClass.Spec.Parameters.APIGroup != v1alpha1.GroupVersion.Group ||
181+
ingressClass.Spec.Parameters.Kind != KindGatewayProxy {
182+
return nil
183+
}
184+
185+
namespace := ingressClass.Namespace
186+
if ingressClass.Spec.Parameters.Namespace != nil {
187+
namespace = *ingressClass.Spec.Parameters.Namespace
188+
}
189+
190+
gatewayProxy := new(v1alpha1.GatewayProxy)
191+
if err := r.Get(context.Background(), client.ObjectKey{
192+
Namespace: namespace,
193+
Name: ingressClass.Spec.Parameters.Name,
194+
}, gatewayProxy); err != nil {
195+
return fmt.Errorf("failed to get gateway proxy: %w", err)
196+
}
197+
198+
rk := provider.ResourceKind{
199+
Kind: ingressClass.Kind,
200+
Namespace: ingressClass.Namespace,
201+
Name: ingressClass.Name,
202+
}
203+
204+
tctx.GatewayProxies[rk] = *gatewayProxy
205+
tctx.ResourceParentRefs[rk] = append(tctx.ResourceParentRefs[rk], rk)
206+
207+
// Load secrets if needed
208+
if gatewayProxy.Spec.Provider != nil && gatewayProxy.Spec.Provider.ControlPlane != nil {
209+
auth := gatewayProxy.Spec.Provider.ControlPlane.Auth
210+
if auth.Type == v1alpha1.AuthTypeAdminKey && auth.AdminKey != nil && auth.AdminKey.ValueFrom != nil {
211+
if auth.AdminKey.ValueFrom.SecretKeyRef != nil {
212+
secretRef := auth.AdminKey.ValueFrom.SecretKeyRef
213+
secret := &corev1.Secret{}
214+
if err := r.Get(context.Background(), client.ObjectKey{
215+
Namespace: namespace,
216+
Name: secretRef.Name,
217+
}, secret); err != nil {
218+
log.Error(err, "failed to get secret for gateway proxy", "namespace", namespace, "name", secretRef.Name)
219+
return err
220+
}
221+
tctx.Secrets[client.ObjectKey{
222+
Namespace: namespace,
223+
Name: secretRef.Name,
224+
}] = secret
225+
}
226+
}
227+
}
228+
229+
_, ok := tctx.GatewayProxies[rk]
230+
if !ok {
231+
return fmt.Errorf("no gateway proxy found for ingress class")
232+
}
233+
234+
return nil
235+
}

internal/controller/utils.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ const (
2727
KindHTTPRoute = "HTTPRoute"
2828
KindGatewayClass = "GatewayClass"
2929
KindIngress = "Ingress"
30+
KindIngressClass = "IngressClass"
31+
KindGatewayProxy = "GatewayProxy"
3032
)
3133

3234
const defaultIngressClassAnnotation = "ingressclass.kubernetes.io/is-default-class"
@@ -783,7 +785,7 @@ func ProcessGatewayProxy(r client.Client, tctx *provider.TranslateContext, gatew
783785

784786
ns := gateway.GetNamespace()
785787
paramRef := infra.ParametersRef
786-
if string(paramRef.Group) == v1alpha1.GroupVersion.Group && string(paramRef.Kind) == "GatewayProxy" {
788+
if string(paramRef.Group) == v1alpha1.GroupVersion.Group && string(paramRef.Kind) == KindGatewayProxy {
787789
gatewayProxy := &v1alpha1.GatewayProxy{}
788790
if err := r.Get(context.Background(), client.ObjectKey{
789791
Namespace: ns,

internal/manager/controllers.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,11 @@ func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Pro
8080
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("Consumer"),
8181
Provider: pro,
8282
},
83+
&controller.IngressClassReconciler{
84+
Client: mgr.GetClient(),
85+
Scheme: mgr.GetScheme(),
86+
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("IngressClass"),
87+
Provider: pro,
88+
},
8389
}, nil
8490
}

0 commit comments

Comments
 (0)