Skip to content

Commit dafb4bf

Browse files
committed
fix: Clear related caches on clusters update
This fixes an issue where the clustersNamespaces and the userNamespaces caches were not updated when Clusters were deleted. This was most noticeable for clustersNamespaces since the GetAll there would return everything in the cache, dated or not. For the userNamespaces cache this was hidden, since the GetAll in this case would receive an up to date list of the cached clusters and then use that to look up individual keys, returning them in a collated list. This meant it _looked like_ the cache was updated, but it was only showing us what we wanted to see; the cache was still quietly building up. The solution here is to generate a Hash based on an ordered list of the names of current clusters. When updating the clustersNamespaces cache, we compare the saved with the current hash, and clear the clustersNamespaces if they differ. We also clear the userNamespaces at this point; since the userNamespaces cache is dependent on both the clusters and the clustersNamespaces cache, it is simpler to clear everything together. The `userNsList` fun has been pulled apart for testing purposes, but is otherwise the same. The testing that the userNamespaces cache has been cleared was harder, since, as I said above the GetAll does not _really_ get all, but "gets all based on this cluster list". I can expose new methods and change this into a genuine List if that is preferred, but have not done that for now.
1 parent 1698acc commit dafb4bf

File tree

9 files changed

+282
-26
lines changed

9 files changed

+282
-26
lines changed

core/clustersmngr/clustersmngrfakes/fake_clients_factory.go

Lines changed: 115 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/clustersmngr/factory.go

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@ type ClientsFactory interface {
3333
UpdateClusters(ctx context.Context) error
3434
// UpdateNamespaces updates the namespaces all namespaces for all clusters
3535
UpdateNamespaces(ctx context.Context) error
36+
// UpdateUsers updates the namespaces all namespaces for all clusters
37+
UpdateUsers(ctx context.Context, user *auth.UserPrincipal)
3638
// GetServerClient returns the cluster client with gitops server permissions
3739
GetServerClient(ctx context.Context) (Client, error)
3840
// GetClustersNamespaces returns the namespaces for all clusters
3941
GetClustersNamespaces() map[string][]v1.Namespace
42+
// GetClustersNamespaces returns the namespaces for all clusters
43+
GetUsersNamespaces(user *auth.UserPrincipal) map[string][]v1.Namespace
4044
// Start starts go routines to keep clusters and namespaces lists up to date
4145
Start(ctx context.Context)
4246
}
@@ -48,6 +52,8 @@ type clientsFactory struct {
4852

4953
// list of clusters returned by the clusters fetcher
5054
clusters *Clusters
55+
// string containing ordered list of cluster names, used to refresh dependent caches
56+
clustersHash string
5157
// the lists of all namespaces of each cluster
5258
clustersNamespaces *ClustersNamespaces
5359
// lists of namespaces accessible by the user on every cluster
@@ -124,6 +130,8 @@ func (cf *clientsFactory) UpdateNamespaces(ctx context.Context) error {
124130
return err
125131
}
126132

133+
cf.syncCaches()
134+
127135
wg := sync.WaitGroup{}
128136

129137
for clusterName, c := range clients {
@@ -147,6 +155,20 @@ func (cf *clientsFactory) UpdateNamespaces(ctx context.Context) error {
147155
return nil
148156
}
149157

158+
func (cf *clientsFactory) GetClustersNamespaces() map[string][]v1.Namespace {
159+
return cf.clustersNamespaces.namespaces
160+
}
161+
162+
func (cf *clientsFactory) syncCaches() {
163+
newHash := cf.clusters.Hash(cf.clusters.Get())
164+
165+
if newHash != cf.clustersHash {
166+
cf.clustersNamespaces.Clear()
167+
cf.usersNamespaces.Clear()
168+
cf.clustersHash = newHash
169+
}
170+
}
171+
150172
func (cf *clientsFactory) GetImpersonatedClient(ctx context.Context, user *auth.UserPrincipal) (Client, error) {
151173
pool := NewClustersClientsPool()
152174

@@ -171,26 +193,7 @@ func (cf *clientsFactory) GetServerClient(ctx context.Context) (Client, error) {
171193
return NewClient(pool, cf.clustersNamespaces.namespaces), nil
172194
}
173195

174-
func (cf *clientsFactory) GetClustersNamespaces() map[string][]v1.Namespace {
175-
return cf.clustersNamespaces.namespaces
176-
}
177-
178-
func restConfigFromCluster(cluster Cluster) *rest.Config {
179-
return &rest.Config{
180-
Host: cluster.Server,
181-
BearerToken: cluster.BearerToken,
182-
TLSClientConfig: cluster.TLSConfig,
183-
QPS: ClientQPS,
184-
Burst: ClientBurst,
185-
}
186-
}
187-
188-
func (cf *clientsFactory) userNsList(ctx context.Context, user *auth.UserPrincipal) map[string][]v1.Namespace {
189-
userNamespaces := cf.usersNamespaces.GetAll(user, cf.clusters.Get())
190-
if len(userNamespaces) > 0 {
191-
return userNamespaces
192-
}
193-
196+
func (cf *clientsFactory) UpdateUsers(ctx context.Context, user *auth.UserPrincipal) {
194197
wg := sync.WaitGroup{}
195198

196199
for _, cluster := range cf.clusters.Get() {
@@ -211,10 +214,23 @@ func (cf *clientsFactory) userNsList(ctx context.Context, user *auth.UserPrincip
211214
}
212215

213216
wg.Wait()
217+
}
214218

219+
func (cf *clientsFactory) GetUsersNamespaces(user *auth.UserPrincipal) map[string][]v1.Namespace {
215220
return cf.usersNamespaces.GetAll(user, cf.clusters.Get())
216221
}
217222

223+
func (cf *clientsFactory) userNsList(ctx context.Context, user *auth.UserPrincipal) map[string][]v1.Namespace {
224+
userNamespaces := cf.GetUsersNamespaces(user)
225+
if len(userNamespaces) > 0 {
226+
return userNamespaces
227+
}
228+
229+
cf.UpdateUsers(ctx, user)
230+
231+
return cf.GetUsersNamespaces(user)
232+
}
233+
218234
func impersonatedConfig(cluster Cluster, user *auth.UserPrincipal) *rest.Config {
219235
shallowCopy := *restConfigFromCluster(cluster)
220236

@@ -240,3 +256,13 @@ func clientsForClusters(clusters []Cluster) (map[string]client.Client, error) {
240256

241257
return clients, nil
242258
}
259+
260+
func restConfigFromCluster(cluster Cluster) *rest.Config {
261+
return &rest.Config{
262+
Host: cluster.Server,
263+
BearerToken: cluster.BearerToken,
264+
TLSClientConfig: cluster.TLSConfig,
265+
QPS: ClientQPS,
266+
Burst: ClientBurst,
267+
}
268+
}

core/clustersmngr/factory_caches.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package clustersmngr
22

33
import (
44
"fmt"
5+
"sort"
6+
"strings"
57
"sync"
68

79
"github.com/cheshir/ttlcache"
@@ -28,6 +30,18 @@ func (c *Clusters) Get() []Cluster {
2830
return c.clusters
2931
}
3032

33+
func (c *Clusters) Hash(clusters []Cluster) string {
34+
names := []string{}
35+
36+
for _, cluster := range clusters {
37+
names = append(names, cluster.Name)
38+
}
39+
40+
sort.Strings(names)
41+
42+
return strings.Join(names, "")
43+
}
44+
3145
type ClustersNamespaces struct {
3246
sync.RWMutex
3347
namespaces map[string][]v1.Namespace
@@ -44,6 +58,13 @@ func (cn *ClustersNamespaces) Set(cluster string, namespaces []v1.Namespace) {
4458
cn.namespaces[cluster] = namespaces
4559
}
4660

61+
func (cn *ClustersNamespaces) Clear() {
62+
cn.Lock()
63+
defer cn.Unlock()
64+
65+
cn.namespaces = make(map[string][]v1.Namespace)
66+
}
67+
4768
func (cn *ClustersNamespaces) Get(cluster string) []v1.Namespace {
4869
cn.Lock()
4970
defer cn.Unlock()
@@ -67,6 +88,8 @@ func (un *UsersNamespaces) Set(user *auth.UserPrincipal, cluster string, nsList
6788
un.Cache.Set(un.cacheKey(user, cluster), nsList, userNamespaceTTL)
6889
}
6990

91+
// GetAll will return all namespace mappings based on the list of clusters provided.
92+
// The cache very well may contain more, but this List is targeted.
7093
func (un *UsersNamespaces) GetAll(user *auth.UserPrincipal, clusters []Cluster) map[string][]v1.Namespace {
7194
namespaces := map[string][]v1.Namespace{}
7295

@@ -79,6 +102,10 @@ func (un *UsersNamespaces) GetAll(user *auth.UserPrincipal, clusters []Cluster)
79102
return namespaces
80103
}
81104

105+
func (un *UsersNamespaces) Clear() {
106+
un.Cache.Clear()
107+
}
108+
82109
func (u UsersNamespaces) cacheKey(user *auth.UserPrincipal, cluster string) uint64 {
83110
return ttlcache.StringKey(fmt.Sprintf("%s:%s", user.ID, cluster))
84111
}

core/clustersmngr/factory_caches_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,8 @@ func TestClustersNamespaces(t *testing.T) {
7171
cs.Set(clusterName, []v1.Namespace{ns})
7272

7373
g.Expect(cs.Get(clusterName)).To(Equal([]v1.Namespace{ns}))
74+
75+
cs.Clear()
76+
77+
g.Expect(cs.Get(clusterName)).To(HaveLen(0))
7478
}

0 commit comments

Comments
 (0)