1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-22 23:09:41 +02:00

fix(token-cache-manager): refactor to avoid data races EE-4438 (#8094)

This commit is contained in:
andres-portainer 2022-11-22 18:31:14 -03:00 committed by GitHub
parent dd01165224
commit c28be7aced
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 159 additions and 72 deletions

View file

@ -1,84 +1,78 @@
package kubernetes
import (
"strconv"
"sync"
cmap "github.com/orcaman/concurrent-map"
portainer "github.com/portainer/portainer/api"
)
type (
// TokenCacheManager represents a service used to manage multiple tokenCache objects.
TokenCacheManager struct {
tokenCaches cmap.ConcurrentMap
}
// TokenCacheManager represents a service used to manage multiple tokenCache objects.
type TokenCacheManager struct {
tokenCaches map[portainer.EndpointID]*tokenCache
mu sync.Mutex
}
tokenCache struct {
userTokenCache cmap.ConcurrentMap
mutex sync.Mutex
}
)
type tokenCache struct {
userTokenCache map[portainer.UserID]string
mu sync.Mutex
}
// NewTokenCacheManager returns a pointer to a new instance of TokenCacheManager
func NewTokenCacheManager() *TokenCacheManager {
return &TokenCacheManager{
tokenCaches: cmap.New(),
tokenCaches: make(map[portainer.EndpointID]*tokenCache),
}
}
// CreateTokenCache will create a new tokenCache object, associate it to the manager map of caches
// and return a pointer to that tokenCache instance.
func (manager *TokenCacheManager) CreateTokenCache(endpointID int) *tokenCache {
tokenCache := newTokenCache()
key := strconv.Itoa(endpointID)
manager.tokenCaches.Set(key, tokenCache)
return tokenCache
}
// GetOrCreateTokenCache will get the tokenCache from the manager map of caches if it exists,
// otherwise it will create a new tokenCache object, associate it to the manager map of caches
// and return a pointer to that tokenCache instance.
func (manager *TokenCacheManager) GetOrCreateTokenCache(endpointID int) *tokenCache {
key := strconv.Itoa(endpointID)
if epCache, ok := manager.tokenCaches.Get(key); ok {
return epCache.(*tokenCache)
func (manager *TokenCacheManager) GetOrCreateTokenCache(endpointID portainer.EndpointID) *tokenCache {
manager.mu.Lock()
defer manager.mu.Unlock()
if tc, ok := manager.tokenCaches[endpointID]; ok {
return tc
}
return manager.CreateTokenCache(endpointID)
tc := &tokenCache{
userTokenCache: make(map[portainer.UserID]string),
}
manager.tokenCaches[endpointID] = tc
return tc
}
// RemoveUserFromCache will ensure that the specific userID is removed from all registered caches.
func (manager *TokenCacheManager) RemoveUserFromCache(userID int) {
for cache := range manager.tokenCaches.IterBuffered() {
cache.Val.(*tokenCache).removeToken(userID)
func (manager *TokenCacheManager) RemoveUserFromCache(userID portainer.UserID) {
manager.mu.Lock()
for _, tc := range manager.tokenCaches {
tc.removeToken(userID)
}
manager.mu.Unlock()
}
func newTokenCache() *tokenCache {
return &tokenCache{
userTokenCache: cmap.New(),
mutex: sync.Mutex{},
}
}
func (cache *tokenCache) getOrAddToken(userID portainer.UserID, tokenGetFunc func() (string, error)) (string, error) {
cache.mu.Lock()
defer cache.mu.Unlock()
func (cache *tokenCache) getToken(userID int) (string, bool) {
key := strconv.Itoa(userID)
token, ok := cache.userTokenCache.Get(key)
if ok {
return token.(string), true
if tok, ok := cache.userTokenCache[userID]; ok {
return tok, nil
}
return "", false
tok, err := tokenGetFunc()
if err != nil {
return "", err
}
cache.userTokenCache[userID] = tok
return tok, nil
}
func (cache *tokenCache) addToken(userID int, token string) {
key := strconv.Itoa(userID)
cache.userTokenCache.Set(key, token)
}
func (cache *tokenCache) removeToken(userID int) {
key := strconv.Itoa(userID)
cache.userTokenCache.Remove(key)
func (cache *tokenCache) removeToken(userID portainer.UserID) {
cache.mu.Lock()
delete(cache.userTokenCache, userID)
cache.mu.Unlock()
}