Skip to content

Commit 714e8d1

Browse files
committed
chore: Add SecretReconciler to handle Gateway TLS secrets
Introduce a SecretReconciler to watch and reconcile secrets referenced by Gateway TLS configurations. This enables dynamic updates to Gateway SSL certificates when secrets change, improving the controller's ability to handle secret modifications efficiently. The change also includes necessary RBAC updates and test adjustments to ensure proper functionality.
1 parent 356694f commit 714e8d1

File tree

7 files changed

+228
-29
lines changed

7 files changed

+228
-29
lines changed

config/rbac/role.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ rules:
1010
- events
1111
verbs:
1212
- create
13+
- get
14+
- list
1315
- patch
16+
- update
17+
- watch
1418
- apiGroups:
1519
- ""
1620
resources:
@@ -26,6 +30,7 @@ rules:
2630
verbs:
2731
- get
2832
- list
33+
- update
2934
- watch
3035
- apiGroups:
3136
- ""

internal/controller/gateway_controller.go

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,46 @@ import (
55
"fmt"
66
"reflect"
77

8-
"github.com/api7/gopkg/pkg/log"
98
"github.com/go-logr/logr"
9+
"github.com/pkg/errors"
1010
corev1 "k8s.io/api/core/v1"
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1212
"k8s.io/apimachinery/pkg/runtime"
1313
"k8s.io/apimachinery/pkg/types"
1414
ctrl "sigs.k8s.io/controller-runtime"
1515
"sigs.k8s.io/controller-runtime/pkg/builder"
1616
"sigs.k8s.io/controller-runtime/pkg/client"
17+
"sigs.k8s.io/controller-runtime/pkg/event"
1718
"sigs.k8s.io/controller-runtime/pkg/handler"
1819
"sigs.k8s.io/controller-runtime/pkg/predicate"
1920
"sigs.k8s.io/controller-runtime/pkg/reconcile"
21+
"sigs.k8s.io/controller-runtime/pkg/source"
2022
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
2123

24+
"github.com/api7/gopkg/pkg/log"
25+
2226
"github.com/api7/api7-ingress-controller/api/v1alpha1"
2327
"github.com/api7/api7-ingress-controller/internal/controller/indexer"
2428
"github.com/api7/api7-ingress-controller/internal/provider"
2529
)
2630

31+
// +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;update
32+
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;update
33+
2734
// GatewayReconciler reconciles a Gateway object.
2835
type GatewayReconciler struct { //nolint:revive
2936
client.Client
3037
Scheme *runtime.Scheme
3138
Log logr.Logger
3239

3340
Provider provider.Provider
41+
42+
GenericEvent chan event.GenericEvent
3443
}
3544

3645
// SetupWithManager sets up the controller with the Manager.
3746
func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error {
47+
r.GenericEvent = GatewaySecretChan
3848
return ctrl.NewControllerManagedBy(mgr).
3949
For(
4050
&gatewayv1.Gateway{},
@@ -58,10 +68,24 @@ func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error {
5868
&v1alpha1.GatewayProxy{},
5969
handler.EnqueueRequestsFromMapFunc(r.listGatewaysForGatewayProxy),
6070
).
71+
WatchesRawSource(
72+
source.Channel(
73+
r.GenericEvent,
74+
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, object client.Object) []reconcile.Request {
75+
switch object.(type) {
76+
case *corev1.Secret:
77+
return r.listGatewaysForSecret(ctx, object)
78+
default:
79+
return nil
80+
}
81+
})),
82+
).
6183
Complete(r)
6284
}
6385

6486
func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
87+
r.Log.Info("request Reconcile")
88+
6589
gateway := new(gatewayv1.Gateway)
6690
if err := r.Get(ctx, req.NamespacedName, gateway); err != nil {
6791
if client.IgnoreNotFound(err) == nil {
@@ -295,6 +319,36 @@ func (r *GatewayReconciler) listGatewaysForHTTPRoute(ctx context.Context, obj cl
295319
return recs
296320
}
297321

322+
func (r *GatewayReconciler) listGatewaysForSecret(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
323+
secret, ok := obj.(*corev1.Secret)
324+
if !ok {
325+
r.Log.Error(
326+
errors.New("unexpected object type"),
327+
"Secret watch predicate received unexpected object type",
328+
"expected", FullTypeName(new(corev1.Secret)), "found", FullTypeName(obj),
329+
)
330+
return nil
331+
}
332+
r.Log.Info("listGatewaysForSecret, secret", "namespace", secret.GetNamespace(), "name", secret.GetName())
333+
var gatewayList gatewayv1.GatewayList
334+
if err := r.List(ctx, &gatewayList, client.MatchingFields{
335+
indexer.SecretIndexRef: indexer.GenIndexKey(secret.GetNamespace(), secret.GetName()),
336+
}); err != nil {
337+
r.Log.Error(err, "failed to list gateways")
338+
return nil
339+
}
340+
for _, gateway := range gatewayList.Items {
341+
requests = append(requests, reconcile.Request{
342+
NamespacedName: types.NamespacedName{
343+
Namespace: gateway.GetNamespace(),
344+
Name: gateway.GetName(),
345+
},
346+
})
347+
}
348+
r.Log.Info("listGatewaysForSecret", "requests", requests)
349+
return requests
350+
}
351+
298352
func (r *GatewayReconciler) processInfrastructure(tctx *provider.TranslateContext, gateway *gatewayv1.Gateway) error {
299353
rk := provider.ResourceKind{
300354
Kind: gateway.Kind,
@@ -316,12 +370,12 @@ func (r *GatewayReconciler) processListenerConfig(tctx *provider.TranslateContex
316370
if ref.Namespace != nil {
317371
ns = string(*ref.Namespace)
318372
}
319-
if ref.Kind != nil && *ref.Kind == gatewayv1.Kind("Secret") {
373+
if ref.Kind != nil && *ref.Kind == "Secret" {
320374
if err := r.Get(context.Background(), client.ObjectKey{
321375
Namespace: ns,
322376
Name: string(ref.Name),
323377
}, &secret); err != nil {
324-
log.Error(err, "failed to get secret", "namespace", ns, "name", string(ref.Name))
378+
log.Error(err, "failed to get secret", "namespace", ns, "name", ref.Name)
325379
SetGatewayListenerConditionProgrammed(gateway, string(listener.Name), false, err.Error())
326380
SetGatewayListenerConditionResolvedRefs(gateway, string(listener.Name), false, err.Error())
327381
break

internal/controller/indexer/indexer.go

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package indexer
22

33
import (
44
"context"
5+
"log"
56

67
networkingv1 "k8s.io/api/networking/v1"
78
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -27,26 +28,19 @@ const (
2728
)
2829

2930
func SetupIndexer(mgr ctrl.Manager) error {
30-
if err := setupGatewayIndexer(mgr); err != nil {
31-
return err
32-
}
33-
if err := setupHTTPRouteIndexer(mgr); err != nil {
34-
return err
35-
}
36-
if err := setupIngressIndexer(mgr); err != nil {
37-
return err
38-
}
39-
if err := setupConsumerIndexer(mgr); err != nil {
40-
return err
41-
}
42-
if err := setupBackendTrafficPolicyIndexer(mgr); err != nil {
43-
return err
44-
}
45-
if err := setupIngressClassIndexer(mgr); err != nil {
46-
return err
47-
}
48-
if err := setupGatewayProxyIndexer(mgr); err != nil {
49-
return err
31+
for _, setup := range []func(ctrl.Manager) error{
32+
setupGatewayIndexer,
33+
setupHTTPRouteIndexer,
34+
setupIngressIndexer,
35+
setupConsumerIndexer,
36+
setupBackendTrafficPolicyIndexer,
37+
setupIngressClassIndexer,
38+
setupGatewayProxyIndexer,
39+
setupGatewaySecretIndex,
40+
} {
41+
if err := setup(mgr); err != nil {
42+
return err
43+
}
5044
}
5145
return nil
5246
}
@@ -191,6 +185,15 @@ func setupGatewayProxyIndexer(mgr ctrl.Manager) error {
191185
return nil
192186
}
193187

188+
func setupGatewaySecretIndex(mgr ctrl.Manager) error {
189+
return mgr.GetFieldIndexer().IndexField(
190+
context.Background(),
191+
&gatewayv1.Gateway{},
192+
SecretIndexRef,
193+
GatewaySecretIndexFunc,
194+
)
195+
}
196+
194197
func GatewayProxySecretIndexFunc(rawObj client.Object) []string {
195198
gatewayProxy := rawObj.(*v1alpha1.GatewayProxy)
196199
secretKeys := make([]string, 0)
@@ -310,6 +313,32 @@ func IngressSecretIndexFunc(rawObj client.Object) []string {
310313
return secrets
311314
}
312315

316+
func GatewaySecretIndexFunc(rawObj client.Object) (keys []string) {
317+
gateway := rawObj.(*gatewayv1.Gateway)
318+
var m = make(map[string]struct{})
319+
for _, listener := range gateway.Spec.Listeners {
320+
if listener.TLS == nil || len(listener.TLS.CertificateRefs) == 0 {
321+
continue
322+
}
323+
for _, ref := range listener.TLS.CertificateRefs {
324+
if ref.Kind == nil || *ref.Kind != "Secret" {
325+
continue
326+
}
327+
namespace := gateway.GetNamespace()
328+
if ref.Namespace != nil {
329+
namespace = string(*ref.Namespace)
330+
}
331+
key := GenIndexKey(namespace, string(ref.Name))
332+
if _, ok := m[key]; !ok {
333+
m[key] = struct{}{}
334+
keys = append(keys, key)
335+
}
336+
}
337+
}
338+
log.Printf("GatewaySecretIndexFunc keys: %v", keys)
339+
return keys
340+
}
341+
313342
func GenIndexKeyWithGK(group, kind, namespace, name string) string {
314343
gvk := schema.GroupKind{
315344
Group: group,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package controller
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/go-logr/logr"
8+
corev1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/client-go/tools/cache"
11+
ctrl "sigs.k8s.io/controller-runtime"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
13+
"sigs.k8s.io/controller-runtime/pkg/controller"
14+
"sigs.k8s.io/controller-runtime/pkg/event"
15+
"sigs.k8s.io/controller-runtime/pkg/manager"
16+
"sigs.k8s.io/controller-runtime/pkg/predicate"
17+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
18+
19+
"github.com/api7/api7-ingress-controller/internal/controller/indexer"
20+
)
21+
22+
type SecretReconciler struct {
23+
client.Client
24+
25+
Log logr.Logger
26+
Indexer cache.Indexer
27+
28+
GenericEvent chan event.GenericEvent
29+
}
30+
31+
func (r *SecretReconciler) SetupWithManager(mgr manager.Manager) error {
32+
r.GenericEvent = GatewaySecretChan
33+
return ctrl.NewControllerManagedBy(mgr).
34+
Named("CoreV1Secret").
35+
WithOptions(controller.Options{
36+
CacheSyncTimeout: time.Second,
37+
LogConstructor: func(_ *reconcile.Request) logr.Logger {
38+
return r.Log
39+
},
40+
}).
41+
For(&corev1.Secret{}). //builder.WithPredicates(r.predicateFuncs()),
42+
43+
Complete(r)
44+
}
45+
46+
func (r *SecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
47+
r.GenericEvent <- event.GenericEvent{
48+
Object: &corev1.Secret{
49+
TypeMeta: metav1.TypeMeta{},
50+
ObjectMeta: metav1.ObjectMeta{
51+
Namespace: req.Namespace,
52+
Name: req.Name,
53+
},
54+
},
55+
}
56+
return ctrl.Result{}, nil
57+
}
58+
59+
func (r *SecretReconciler) predicateFuncs() predicate.Funcs {
60+
predicateFuncs := predicate.NewPredicateFuncs(func(object client.Object) bool {
61+
if _, ok := object.(*corev1.Secret); !ok {
62+
return false
63+
}
64+
key := indexer.GenIndexKey(object.GetNamespace(), object.GetName())
65+
refs, err := r.Indexer.ByIndex("referent", key)
66+
if err != nil {
67+
r.Log.Error(err, "failed to check whether secret referred", "namespace", object.GetNamespace(), "name", object.GetName())
68+
return false
69+
}
70+
return len(refs) > 0
71+
})
72+
predicateFuncs.DeleteFunc = func(_ event.DeleteEvent) bool {
73+
return true
74+
}
75+
return predicateFuncs
76+
}

internal/controller/utils.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package controller
33
import (
44
"context"
55
"fmt"
6+
"path"
7+
"reflect"
68
"strings"
79

8-
"github.com/api7/gopkg/pkg/log"
910
"github.com/samber/lo"
1011
"go.uber.org/zap"
1112
corev1 "k8s.io/api/core/v1"
@@ -14,9 +15,12 @@ import (
1415
"k8s.io/apimachinery/pkg/labels"
1516
"k8s.io/apimachinery/pkg/types"
1617
"sigs.k8s.io/controller-runtime/pkg/client"
18+
"sigs.k8s.io/controller-runtime/pkg/event"
1719
"sigs.k8s.io/controller-runtime/pkg/reconcile"
1820
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
1921

22+
"github.com/api7/gopkg/pkg/log"
23+
2024
"github.com/api7/api7-ingress-controller/api/v1alpha1"
2125
"github.com/api7/api7-ingress-controller/internal/controller/config"
2226
"github.com/api7/api7-ingress-controller/internal/provider"
@@ -31,6 +35,10 @@ const (
3135
KindGatewayProxy = "GatewayProxy"
3236
)
3337

38+
var (
39+
GatewaySecretChan = make(chan event.GenericEvent, 100)
40+
)
41+
3442
const defaultIngressClassAnnotation = "ingressclass.kubernetes.io/is-default-class"
3543

3644
// IsDefaultIngressClass returns whether an IngressClass is the default IngressClass.
@@ -839,3 +847,14 @@ func ProcessGatewayProxy(r client.Client, tctx *provider.TranslateContext, gatew
839847

840848
return nil
841849
}
850+
851+
// FullTypeName returns the fully qualified name of the type of the given value.
852+
func FullTypeName(a any) string {
853+
typeOf := reflect.TypeOf(a)
854+
pkgPath := typeOf.PkgPath()
855+
name := typeOf.String()
856+
if typeOf.Kind() == reflect.Ptr {
857+
pkgPath = typeOf.Elem().PkgPath()
858+
}
859+
return path.Join(path.Dir(pkgPath), name)
860+
}

internal/manager/controllers.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,9 @@ func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Pro
8686
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("IngressClass"),
8787
Provider: pro,
8888
},
89+
&controller.SecretReconciler{
90+
Client: mgr.GetClient(),
91+
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("Secret"),
92+
},
8993
}, nil
9094
}

0 commit comments

Comments
 (0)