Files
capsule/internal/cache/impersonation_clients.go
cc4fb45d70 feat: upstream enterprise preview (#1841)
feat: upstream enterprise preview

---------

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
Co-authored-by: CorentinPtrl <pitrel.corentin@gmail.com>
2026-05-28 00:58:58 +02:00

120 lines
2.5 KiB
Go

// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package cache
import (
"context"
"sync"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/projectcapsule/capsule/pkg/api/meta"
"github.com/projectcapsule/capsule/pkg/users"
)
type Key struct {
Namespace string
Name string
}
type ImpersonationCache struct {
mu sync.RWMutex
clients map[Key]client.Client
}
func NewImpersonationCache() *ImpersonationCache {
return &ImpersonationCache{
clients: make(map[Key]client.Client),
}
}
// Get returns a cached client if present.
func (c *ImpersonationCache) Get(ns, name string) (client.Client, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
cl, ok := c.clients[Key{Namespace: ns, Name: name}]
return cl, ok
}
// Set stores a client explicitly (rarely needed).
func (c *ImpersonationCache) Set(namespace, name string, cl client.Client) {
c.mu.Lock()
defer c.mu.Unlock()
if cl == nil {
return
}
c.clients[Key{Namespace: namespace, Name: name}] = cl
}
// Invalidate removes one entry.
func (c *ImpersonationCache) Invalidate(namespace, name string) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.clients, Key{Namespace: namespace, Name: name})
}
// Clear drops all cached clients.
func (c *ImpersonationCache) Reset() {
c.mu.Lock()
defer c.mu.Unlock()
c.clients = make(map[Key]client.Client)
}
// Stats helps you log cache state.
func (c *ImpersonationCache) Stats() (entries int) {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.clients)
}
// LoadOrCreate returns a cached impersonated client for the given service account,
// creating and caching it if missing.
func (c *ImpersonationCache) LoadOrCreate(
ctx context.Context,
log logr.Logger,
baseREST *rest.Config,
scheme *runtime.Scheme,
sa meta.NamespacedRFC1123ObjectReferenceWithNamespace,
) (client.Client, error) {
key := Key{Namespace: string(sa.Namespace), Name: string(sa.Name)}
// Fast path
if cl, ok := c.Get(key.Namespace, key.Name); ok {
return cl, nil
}
cl, err := users.ImpersonatedKubernetesClientForServiceAccount(
baseREST,
scheme,
sa,
)
if err != nil {
log.Error(err, "failed to create impersonated client", "namespace", key.Namespace, "name", key.Name)
return nil, err
}
// Store (double-check to avoid duplicate creation races)
c.mu.Lock()
defer c.mu.Unlock()
if existing := c.clients[key]; existing != nil {
return existing, nil
}
c.clients[key] = cl
return cl, nil
}