Skip to content

Commit 88e216a

Browse files
util: cache the controller publish secret
This patch introduces a caching mechanism based on the shared informers for ControllerPublish secrets. Signed-off-by: Niraj Yadav <niryadav@redhat.com>
1 parent 002d02b commit 88e216a

File tree

2 files changed

+90
-1
lines changed

2 files changed

+90
-1
lines changed

internal/controller/controller.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package controller
1818
import (
1919
"fmt"
2020

21+
"github.com/ceph/ceph-csi/internal/util/k8s"
2122
"github.com/ceph/ceph-csi/internal/util/log"
2223

2324
replicationv1alpha1 "github.com/csi-addons/kubernetes-csi-addons/api/replication.storage/v1alpha1"
@@ -111,5 +112,11 @@ func Start(config Config) error {
111112
log.ErrorLogMsg("failed to start manager %s", err)
112113
}
113114

115+
stopCh := make(chan struct{})
116+
defer close(stopCh)
117+
if err := k8s.StartSecretWatcher(stopCh); err != nil {
118+
log.ErrorLogMsg("failed to start secret watcher: %v", err)
119+
}
120+
114121
return err
115122
}

internal/util/k8s/secrets.go

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,90 @@ package k8s
1818

1919
import (
2020
"context"
21+
"crypto/sha1"
22+
"encoding/hex"
2123
"fmt"
24+
"sync"
25+
"time"
2226

27+
corev1 "k8s.io/api/core/v1"
2328
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/client-go/informers"
30+
"k8s.io/client-go/tools/cache"
2431
)
2532

33+
type cachedSecret struct {
34+
data map[string]string
35+
}
36+
37+
var (
38+
secretCache = make(map[string]cachedSecret)
39+
cacheLock sync.RWMutex
40+
)
41+
42+
// cacheKey is a helper to create sha1 hash based on a secret's name
43+
// and namespace. The hash is then used as an index for secretCache
44+
func cacheKey(ns, name string) string {
45+
h := sha1.New()
46+
h.Write([]byte(ns + "/" + name))
47+
48+
return hex.EncodeToString(h.Sum(nil))
49+
}
50+
51+
// StartSecretWatcher handles events on Secrets informer and updates
52+
// or invalidates the secretCache accordingly.
53+
// It should preferably be started early in the bootstraping process.
54+
func StartSecretWatcher(stopCh chan struct{}) error {
55+
client, err := NewK8sClient()
56+
if err != nil {
57+
return fmt.Errorf("failed to connect to Kubernetes: %w", err)
58+
}
59+
60+
factory := informers.NewSharedInformerFactoryWithOptions(
61+
client,
62+
time.Minute*10, // FIXME: We could optionally restrict this to specific NS.
63+
)
64+
65+
informer := factory.Core().V1().Secrets().Informer()
66+
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
67+
UpdateFunc: func(oldObj, newObj any) {
68+
newSecret := newObj.(*corev1.Secret)
69+
key := cacheKey(newSecret.Namespace, newSecret.Name)
70+
71+
cacheLock.Lock()
72+
delete(secretCache, key)
73+
cacheLock.Unlock()
74+
},
75+
DeleteFunc: func(obj any) {
76+
secret := obj.(*corev1.Secret)
77+
key := cacheKey(secret.Namespace, secret.Name)
78+
79+
cacheLock.Lock()
80+
delete(secretCache, key)
81+
cacheLock.Unlock()
82+
},
83+
})
84+
85+
go informer.Run(stopCh)
86+
return nil
87+
}
88+
2689
// GetSecret retrieves a Kubernetes Secret by its name and namespace.
2790
// It returns a map of key-value pairs contained in the Secret's data.
2891
// If the Secret does not exist or cannot be accessed, it returns an error.
2992
func GetSecret(secretName, secretNamespace string) (map[string]string, error) {
93+
key := cacheKey(secretNamespace, secretName)
94+
95+
// Return from cache if present
96+
cacheLock.RLock()
97+
if entry, found := secretCache[key]; found {
98+
cacheLock.RUnlock()
99+
100+
return entry.data, nil
101+
}
102+
cacheLock.RUnlock()
103+
104+
// The entry was not found in cache, fetch it from the API server
30105
client, err := NewK8sClient()
31106
if err != nil {
32107
return nil, fmt.Errorf("failed to connect to Kubernetes: %w", err)
@@ -37,10 +112,17 @@ func GetSecret(secretName, secretNamespace string) (map[string]string, error) {
37112
return nil, fmt.Errorf("failed to get secret %q in namespace %q information: %w", secretName, secretNamespace, err)
38113
}
39114

40-
secretData := map[string]string{}
115+
secretData := make(map[string]string, len(secret.Data))
41116
for key, value := range secret.Data {
42117
secretData[key] = string(value)
43118
}
44119

120+
// Cache the fetched secret
121+
cacheLock.Lock()
122+
secretCache[key] = cachedSecret{
123+
data: secretData,
124+
}
125+
cacheLock.Unlock()
126+
45127
return secretData, nil
46128
}

0 commit comments

Comments
 (0)