Skip to content

Commit 1f1dbc3

Browse files
committed
kubelet: ctb: use generics to handle alpha/beta APIs for CTB projection
1 parent 5b3b68a commit 1f1dbc3

File tree

2 files changed

+397
-117
lines changed

2 files changed

+397
-117
lines changed

pkg/kubelet/clustertrustbundle/clustertrustbundle_manager.go

Lines changed: 128 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,16 @@ import (
2727
"time"
2828

2929
"github.com/go-logr/logr"
30+
certificatesv1alpha1 "k8s.io/api/certificates/v1alpha1"
3031
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
3132
k8serrors "k8s.io/apimachinery/pkg/api/errors"
3233
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34+
"k8s.io/apimachinery/pkg/labels"
35+
"k8s.io/apimachinery/pkg/runtime/schema"
3336
lrucache "k8s.io/apimachinery/pkg/util/cache"
3437
"k8s.io/apimachinery/pkg/util/sets"
3538
"k8s.io/client-go/informers"
36-
certinformersv1beta1 "k8s.io/client-go/informers/certificates/v1beta1"
3739
clientset "k8s.io/client-go/kubernetes"
38-
certlistersv1beta1 "k8s.io/client-go/listers/certificates/v1beta1"
3940
"k8s.io/client-go/tools/cache"
4041
"k8s.io/klog/v2"
4142
)
@@ -44,6 +45,51 @@ const (
4445
maxLabelSelectorLength = 100 * 1024
4546
)
4647

48+
// clusterTrustBundle is a type constraint for version-independent ClusterTrustBundle API
49+
type clusterTrustBundle interface {
50+
certificatesv1alpha1.ClusterTrustBundle | certificatesv1beta1.ClusterTrustBundle
51+
}
52+
53+
// clusterTrustBundlesLister is an API-verion independent ClusterTrustBundles lister
54+
type clusterTrustBundlesLister[T clusterTrustBundle] interface {
55+
Get(string) (*T, error)
56+
List(labels.Selector) ([]*T, error)
57+
}
58+
59+
type clusterTrustBundleHandlers[T clusterTrustBundle] interface {
60+
GetName(*T) string
61+
GetSignerName(*T) string
62+
GetTrustBundle(*T) string
63+
}
64+
65+
type alphaClusterTrustBundleHandlers struct{}
66+
67+
type betaClusterTrustBundleHandlers struct{}
68+
69+
func (b *alphaClusterTrustBundleHandlers) GetName(ctb *certificatesv1alpha1.ClusterTrustBundle) string {
70+
return ctb.Name
71+
}
72+
73+
func (b *alphaClusterTrustBundleHandlers) GetSignerName(ctb *certificatesv1alpha1.ClusterTrustBundle) string {
74+
return ctb.Spec.SignerName
75+
}
76+
77+
func (b *alphaClusterTrustBundleHandlers) GetTrustBundle(ctb *certificatesv1alpha1.ClusterTrustBundle) string {
78+
return ctb.Spec.TrustBundle
79+
}
80+
81+
func (b betaClusterTrustBundleHandlers) GetName(ctb *certificatesv1beta1.ClusterTrustBundle) string {
82+
return ctb.Name
83+
}
84+
85+
func (b *betaClusterTrustBundleHandlers) GetSignerName(ctb *certificatesv1beta1.ClusterTrustBundle) string {
86+
return ctb.Spec.SignerName
87+
}
88+
89+
func (b *betaClusterTrustBundleHandlers) GetTrustBundle(ctb *certificatesv1beta1.ClusterTrustBundle) string {
90+
return ctb.Spec.TrustBundle
91+
}
92+
4793
// Manager abstracts over the ability to get trust anchors.
4894
type Manager interface {
4995
GetTrustAnchorsByName(name string, allowMissing bool) ([]byte, error)
@@ -52,23 +98,44 @@ type Manager interface {
5298

5399
// InformerManager is the "real" manager. It uses informers to track
54100
// ClusterTrustBundle objects.
55-
type InformerManager struct {
101+
type InformerManager[T clusterTrustBundle] struct {
56102
ctbInformer cache.SharedIndexInformer
57-
ctbLister certlistersv1beta1.ClusterTrustBundleLister
103+
ctbLister clusterTrustBundlesLister[T]
104+
105+
ctbHandlers clusterTrustBundleHandlers[T]
58106

59107
normalizationCache *lrucache.LRUExpireCache
60108
cacheTTL time.Duration
61109
}
62110

63-
var _ Manager = (*InformerManager)(nil)
111+
var _ Manager = (*InformerManager[certificatesv1beta1.ClusterTrustBundle])(nil)
112+
113+
func NewAlphaInformerManager(
114+
ctx context.Context, informerFactory informers.SharedInformerFactory, cacheSize int, cacheTTL time.Duration,
115+
) (Manager, error) {
116+
bundlesInformer := informerFactory.Certificates().V1alpha1().ClusterTrustBundles()
117+
return newInformerManager(
118+
ctx, &alphaClusterTrustBundleHandlers{}, bundlesInformer.Informer(), bundlesInformer.Lister(), cacheSize, cacheTTL,
119+
)
120+
}
121+
122+
func NewBetaInformerManager(
123+
ctx context.Context, informerFactory informers.SharedInformerFactory, cacheSize int, cacheTTL time.Duration,
124+
) (Manager, error) {
125+
bundlesInformer := informerFactory.Certificates().V1beta1().ClusterTrustBundles()
126+
return newInformerManager(
127+
ctx, &betaClusterTrustBundleHandlers{}, bundlesInformer.Informer(), bundlesInformer.Lister(), cacheSize, cacheTTL,
128+
)
129+
}
64130

65-
// NewInformerManager returns an initialized InformerManager.
66-
func NewInformerManager(ctx context.Context, bundles certinformersv1beta1.ClusterTrustBundleInformer, cacheSize int, cacheTTL time.Duration) (*InformerManager, error) {
131+
// newInformerManager returns an initialized InformerManager.
132+
func newInformerManager[T clusterTrustBundle](ctx context.Context, handlers clusterTrustBundleHandlers[T], informer cache.SharedIndexInformer, lister clusterTrustBundlesLister[T], cacheSize int, cacheTTL time.Duration) (Manager, error) {
67133
// We need to call Informer() before calling start on the shared informer
68134
// factory, or the informer won't be registered to be started.
69-
m := &InformerManager{
70-
ctbInformer: bundles.Informer(),
71-
ctbLister: bundles.Lister(),
135+
m := &InformerManager[T]{
136+
ctbInformer: informer,
137+
ctbLister: lister,
138+
ctbHandlers: handlers,
72139
normalizationCache: lrucache.NewLRUExpireCache(cacheSize),
73140
cacheTTL: cacheTTL,
74141
}
@@ -78,34 +145,34 @@ func NewInformerManager(ctx context.Context, bundles certinformersv1beta1.Cluste
78145
// apply to them.
79146
_, err := m.ctbInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
80147
AddFunc: func(obj any) {
81-
ctb, ok := obj.(*certificatesv1beta1.ClusterTrustBundle)
148+
ctb, ok := obj.(*T)
82149
if !ok {
83150
return
84151
}
85-
logger.Info("Dropping all cache entries for signer", "signerName", ctb.Spec.SignerName)
152+
logger.Info("Dropping all cache entries for signer", "signerName", m.ctbHandlers.GetSignerName(ctb))
86153
m.dropCacheFor(ctb)
87154
},
88155
UpdateFunc: func(old, new any) {
89-
ctb, ok := new.(*certificatesv1beta1.ClusterTrustBundle)
156+
ctb, ok := new.(*T)
90157
if !ok {
91158
return
92159
}
93-
logger.Info("Dropping cache for ClusterTrustBundle", "signerName", ctb.Spec.SignerName)
94-
m.dropCacheFor(new.(*certificatesv1beta1.ClusterTrustBundle))
160+
logger.Info("Dropping cache for ClusterTrustBundle", "signerName", m.ctbHandlers.GetSignerName(ctb))
161+
m.dropCacheFor(new.(*T))
95162
},
96163
DeleteFunc: func(obj any) {
97-
ctb, ok := obj.(*certificatesv1beta1.ClusterTrustBundle)
164+
ctb, ok := obj.(*T)
98165
if !ok {
99166
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
100167
if !ok {
101168
return
102169
}
103-
ctb, ok = tombstone.Obj.(*certificatesv1beta1.ClusterTrustBundle)
170+
ctb, ok = tombstone.Obj.(*T)
104171
if !ok {
105172
return
106173
}
107174
}
108-
logger.Info("Dropping cache for ClusterTrustBundle", "signerName", ctb.Spec.SignerName)
175+
logger.Info("Dropping cache for ClusterTrustBundle", "signerName", m.ctbHandlers.GetSignerName(ctb))
109176
m.dropCacheFor(ctb)
110177
},
111178
})
@@ -116,21 +183,21 @@ func NewInformerManager(ctx context.Context, bundles certinformersv1beta1.Cluste
116183
return m, nil
117184
}
118185

119-
func (m *InformerManager) dropCacheFor(ctb *certificatesv1beta1.ClusterTrustBundle) {
120-
if ctb.Spec.SignerName != "" {
186+
func (m *InformerManager[T]) dropCacheFor(ctb *T) {
187+
if ctbSignerName := m.ctbHandlers.GetSignerName(ctb); ctbSignerName != "" {
121188
m.normalizationCache.RemoveAll(func(key any) bool {
122-
return key.(cacheKeyType).signerName == ctb.Spec.SignerName
189+
return key.(cacheKeyType).signerName == ctbSignerName
123190
})
124191
} else {
125192
m.normalizationCache.RemoveAll(func(key any) bool {
126-
return key.(cacheKeyType).ctbName == ctb.ObjectMeta.Name
193+
return key.(cacheKeyType).ctbName == m.ctbHandlers.GetName(ctb)
127194
})
128195
}
129196
}
130197

131198
// GetTrustAnchorsByName returns normalized and deduplicated trust anchors from
132199
// a single named ClusterTrustBundle.
133-
func (m *InformerManager) GetTrustAnchorsByName(name string, allowMissing bool) ([]byte, error) {
200+
func (m *InformerManager[T]) GetTrustAnchorsByName(name string, allowMissing bool) ([]byte, error) {
134201
if !m.ctbInformer.HasSynced() {
135202
return nil, fmt.Errorf("ClusterTrustBundle informer has not yet synced")
136203
}
@@ -149,7 +216,7 @@ func (m *InformerManager) GetTrustAnchorsByName(name string, allowMissing bool)
149216
return nil, fmt.Errorf("while getting ClusterTrustBundle: %w", err)
150217
}
151218

152-
pemTrustAnchors, err := m.normalizeTrustAnchors([]*certificatesv1beta1.ClusterTrustBundle{ctb})
219+
pemTrustAnchors, err := m.normalizeTrustAnchors([]*T{ctb})
153220
if err != nil {
154221
return nil, fmt.Errorf("while normalizing trust anchors: %w", err)
155222
}
@@ -161,7 +228,7 @@ func (m *InformerManager) GetTrustAnchorsByName(name string, allowMissing bool)
161228

162229
// GetTrustAnchorsBySigner returns normalized and deduplicated trust anchors
163230
// from a set of selected ClusterTrustBundles.
164-
func (m *InformerManager) GetTrustAnchorsBySigner(signerName string, labelSelector *metav1.LabelSelector, allowMissing bool) ([]byte, error) {
231+
func (m *InformerManager[T]) GetTrustAnchorsBySigner(signerName string, labelSelector *metav1.LabelSelector, allowMissing bool) ([]byte, error) {
165232
if !m.ctbInformer.HasSynced() {
166233
return nil, fmt.Errorf("ClusterTrustBundle informer has not yet synced")
167234
}
@@ -188,9 +255,9 @@ func (m *InformerManager) GetTrustAnchorsBySigner(signerName string, labelSelect
188255
return nil, fmt.Errorf("while listing ClusterTrustBundles matching label selector %v: %w", labelSelector, err)
189256
}
190257

191-
ctbList := []*certificatesv1beta1.ClusterTrustBundle{}
258+
ctbList := []*T{}
192259
for _, ctb := range rawCTBList {
193-
if ctb.Spec.SignerName == signerName {
260+
if m.ctbHandlers.GetSignerName(ctb) == signerName {
194261
ctbList = append(ctbList, ctb)
195262
}
196263
}
@@ -212,11 +279,11 @@ func (m *InformerManager) GetTrustAnchorsBySigner(signerName string, labelSelect
212279
return pemTrustAnchors, nil
213280
}
214281

215-
func (m *InformerManager) normalizeTrustAnchors(ctbList []*certificatesv1beta1.ClusterTrustBundle) ([]byte, error) {
282+
func (m *InformerManager[T]) normalizeTrustAnchors(ctbList []*T) ([]byte, error) {
216283
// Deduplicate trust anchors from all ClusterTrustBundles.
217284
trustAnchorSet := sets.Set[string]{}
218285
for _, ctb := range ctbList {
219-
rest := []byte(ctb.Spec.TrustBundle)
286+
rest := []byte(m.ctbHandlers.GetTrustBundle(ctb))
220287
var b *pem.Block
221288
for {
222289
b, rest = pem.Decode(rest)
@@ -309,6 +376,8 @@ func (m *LazyInformerManager) isManagerSet() bool {
309376
return m.manager != nil
310377
}
311378

379+
type managerConstructor func(ctx context.Context, informerFactory informers.SharedInformerFactory, cacheSize int, cacheTTL time.Duration) (Manager, error)
380+
312381
func (m *LazyInformerManager) ensureManagerSet() error {
313382
if m.isManagerSet() {
314383
return nil
@@ -321,24 +390,42 @@ func (m *LazyInformerManager) ensureManagerSet() error {
321390
return nil
322391
}
323392

324-
ctbAPIAvailable, err := clusterTrustBundlesAvailable(m.client)
325-
if err != nil {
326-
return fmt.Errorf("failed to determine which informer manager to choose: %w", err)
393+
managerSchema := map[schema.GroupVersion]managerConstructor{
394+
certificatesv1alpha1.SchemeGroupVersion: NewAlphaInformerManager,
395+
certificatesv1beta1.SchemeGroupVersion: NewBetaInformerManager,
327396
}
328397

329-
if !ctbAPIAvailable {
398+
kubeInformers := informers.NewSharedInformerFactoryWithOptions(m.client, 0)
399+
400+
var clusterTrustBundleManager Manager
401+
var foundGV string
402+
for _, gv := range []schema.GroupVersion{certificatesv1beta1.SchemeGroupVersion, certificatesv1alpha1.SchemeGroupVersion} {
403+
ctbAPIAvailable, err := clusterTrustBundlesAvailable(m.client, gv)
404+
if err != nil {
405+
return fmt.Errorf("failed to determine which informer manager to choose: %w", err)
406+
}
407+
408+
if !ctbAPIAvailable {
409+
continue
410+
}
411+
412+
clusterTrustBundleManager, err = managerSchema[gv](m.contextWithLogger, kubeInformers, m.cacheSize, 5*time.Minute)
413+
if err != nil {
414+
return fmt.Errorf("error starting informer-based ClusterTrustBundle manager: %w", err)
415+
}
416+
foundGV = gv.String()
417+
break
418+
}
419+
420+
if clusterTrustBundleManager == nil {
330421
m.manager = &NoopManager{}
422+
m.logger.Info("No version of the ClusterTrustBundle API was found, the ClusterTrustBundle informer won't be started")
331423
return nil
332424
}
333425

334-
kubeInformers := informers.NewSharedInformerFactoryWithOptions(m.client, 0)
335-
clusterTrustBundleManager, err := NewInformerManager(m.contextWithLogger, kubeInformers.Certificates().V1beta1().ClusterTrustBundles(), m.cacheSize, 5*time.Minute)
336-
if err != nil {
337-
return fmt.Errorf("error starting informer-based ClusterTrustBundle manager: %w", err)
338-
}
339426
m.manager = clusterTrustBundleManager
340427
kubeInformers.Start(m.contextWithLogger.Done())
341-
m.logger.Info("Started ClusterTrustBundle informer")
428+
m.logger.Info("Started ClusterTrustBundle informer", "apiGroup", foundGV)
342429

343430
// a cache fetch will likely follow right after, wait for the freshly started
344431
// informers to sync
@@ -358,8 +445,8 @@ func (m *LazyInformerManager) ensureManagerSet() error {
358445
return nil
359446
}
360447

361-
func clusterTrustBundlesAvailable(client clientset.Interface) (bool, error) {
362-
resList, err := client.Discovery().ServerResourcesForGroupVersion(certificatesv1beta1.SchemeGroupVersion.String())
448+
func clusterTrustBundlesAvailable(client clientset.Interface, gv schema.GroupVersion) (bool, error) {
449+
resList, err := client.Discovery().ServerResourcesForGroupVersion(gv.String())
363450
if k8serrors.IsNotFound(err) {
364451
return false, nil
365452
}

0 commit comments

Comments
 (0)