diff --git a/lib/cache/cache.go b/lib/cache/cache.go
index ddfce8bc224a4..37520f6488b1b 100644
--- a/lib/cache/cache.go
+++ b/lib/cache/cache.go
@@ -525,8 +525,11 @@ type Cache struct {
// cancel triggers exit context closure
cancel context.CancelFunc
- // collections is a registry of resource collections
- collections *cacheCollections
+ // legacyCacheCollections is a registry of resource legacyCollections
+ legacyCacheCollections *legacyCollections
+
+ // collections is a registry of resource collections.
+ collections *collections
// confirmedKinds is a map of kinds confirmed by the server to be included in the current generation
// by resource Kind/SubKind
@@ -613,18 +616,9 @@ func (c *Cache) setReadStatus(ok bool, confirmedKinds map[resourceKind]types.Wat
c.confirmedKinds = confirmedKinds
}
-// readCollectionCache acquires the cache read lock and uses getReader() to select the appropriate target for read
-// operations on resources of the specified collection. The returned guard *must* be released to prevent deadlocks.
-func readCollectionCache[R any](cache *Cache, collection collectionReader[R]) (rg readGuard[R], err error) {
- if collection == nil {
- return rg, trace.BadParameter("cannot read from an uninitialized cache collection")
- }
- return readCache(cache, collection.watchKind(), collection.getReader)
-}
-
// readListResourcesCache acquires the cache read lock and uses getReader() to select the appropriate target
// for listing resources of the specified resourceType. The returned guard *must* be released to prevent deadlocks.
-func readListResourcesCache(cache *Cache, resourceType string) (readGuard[resourceGetter], error) {
+func readListResourcesCache(cache *Cache, resourceType string) (legacyReadGuard[resourceGetter], error) {
getResourceReader := func(cacheOK bool) resourceGetter {
if cacheOK {
return cache.presenceCache
@@ -632,20 +626,53 @@ func readListResourcesCache(cache *Cache, resourceType string) (readGuard[resour
return cache.Config.Presence
}
- return readCache(cache, types.WatchKind{Kind: resourceType}, getResourceReader)
+ return legacyReadCache(cache, types.WatchKind{Kind: resourceType}, getResourceReader)
+}
+
+// readLegacyCollectionCache acquires the cache read lock and uses getReader() to select the appropriate target for read
+// operations on resources of the specified collection. The returned guard *must* be released to prevent deadlocks.
+func readLegacyCollectionCache[R any](cache *Cache, collection collectionReader[R]) (legacyReadGuard[R], error) {
+ if collection == nil {
+ return legacyReadGuard[R]{}, trace.BadParameter("cannot read from an uninitialized cache collection")
+ }
+ return legacyReadCache(cache, collection.watchKind(), collection.getReader)
}
-// readCache acquires the cache read lock and uses getReader() to select the appropriate target for read operations
+// acquireReadGuard provides a readGuard that may be used to determine how
+// a cache read should operate. The returned guard *must* be released to prevent deadlocks.
+func acquireReadGuard[T any, I comparable](cache *Cache, c *collection[T, I]) (readGuard[T, I], error) {
+ if cache.closed.Load() {
+ return readGuard[T, I]{}, trace.Errorf("cache is closed")
+ }
+ cache.rw.RLock()
+
+ if cache.ok {
+ if _, kindOK := cache.confirmedKinds[resourceKind{kind: c.watch.Kind, subkind: c.watch.SubKind}]; kindOK {
+ return readGuard[T, I]{
+ cacheRead: true,
+ release: cache.rw.RUnlock,
+ store: c.store,
+ }, nil
+ }
+ }
+
+ cache.rw.RUnlock()
+ return readGuard[T, I]{
+ cacheRead: false,
+ }, nil
+}
+
+// legacyReadCache acquires the cache read lock and uses getReader() to select the appropriate target for read operations
// on resources of the specified kind. The returned guard *must* be released to prevent deadlocks.
-func readCache[R any](cache *Cache, kind types.WatchKind, getReader func(cacheOK bool) R) (readGuard[R], error) {
+func legacyReadCache[R any](cache *Cache, kind types.WatchKind, getReader func(cacheOK bool) R) (legacyReadGuard[R], error) {
if cache.closed.Load() {
- return readGuard[R]{}, trace.Errorf("cache is closed")
+ return legacyReadGuard[R]{}, trace.Errorf("cache is closed")
}
cache.rw.RLock()
if cache.ok {
if _, kindOK := cache.confirmedKinds[resourceKind{kind: kind.Kind, subkind: kind.SubKind}]; kindOK {
- return readGuard[R]{
+ return legacyReadGuard[R]{
reader: getReader(true),
release: cache.rw.RUnlock,
}, nil
@@ -653,34 +680,60 @@ func readCache[R any](cache *Cache, kind types.WatchKind, getReader func(cacheOK
}
cache.rw.RUnlock()
- return readGuard[R]{
+ return legacyReadGuard[R]{
reader: getReader(false),
release: nil,
}, nil
}
-// readGuard holds a reference to a read-only "backend" R. If the referenced backed is the cache, then readGuard
+// legacyReadGuard holds a reference to a read-only "backend" R. If the referenced backed is the cache, then legacyReadGuard
// also holds the release function for the read lock, and ensures that it is not double-called.
-type readGuard[R any] struct {
- reader R
- release func()
- released bool
+type legacyReadGuard[R any] struct {
+ reader R
+ once sync.Once
+ release func()
}
-// Release releases the read lock if it is held. This method
-// can be called multiple times, but is not thread-safe.
-func (r *readGuard[R]) Release() {
- if r.release != nil && !r.released {
+// Release releases the read lock if it is held. This method
+// can be called multiple times.
+func (r *legacyReadGuard[R]) Release() {
+ r.once.Do(func() {
+ if r.release == nil {
+ return
+ }
+
r.release()
- r.released = true
- }
+ })
}
// IsCacheRead checks if this readGuard holds a cache reference.
-func (r *readGuard[R]) IsCacheRead() bool {
+func (r *legacyReadGuard[R]) IsCacheRead() bool {
return r.release != nil
}
+type readGuard[T any, I comparable] struct {
+ cacheRead bool
+ store *store[T, I]
+ once sync.Once
+ release func()
+}
+
+func (r *readGuard[T, I]) ReadCache() bool {
+ return r.cacheRead
+}
+
+// Release releases the read lock if it is held. This method
+// can be called multiple times.
+func (r *readGuard[T, I]) Release() {
+ r.once.Do(func() {
+ if r.release == nil {
+ return
+ }
+
+ r.release()
+ })
+}
+
// Config defines cache configuration parameters
type Config struct {
// target is an identifying string that allows errors to
@@ -1143,7 +1196,15 @@ func New(config Config) (*Cache, error) {
teleport.ComponentKey: config.Component,
}),
}
- collections, err := setupCollections(cs, config.Watches)
+
+ legacyCollections, err := setupLegacyCollections(cs, config.Watches)
+ if err != nil {
+ cs.Close()
+ return nil, trace.Wrap(err)
+ }
+ cs.legacyCacheCollections = legacyCollections
+
+ collections, err := setupCollections(config, legacyCollections.byKind)
if err != nil {
cs.Close()
return nil, trace.Wrap(err)
@@ -1376,7 +1437,7 @@ func (c *Cache) notify(ctx context.Context, event Event) {
// potentially lagging behind the state of the database.
func (c *Cache) fetchAndWatch(ctx context.Context, retry retryutils.Retry, timer *time.Timer) error {
cacheLastReset.WithLabelValues(c.target).SetToCurrentTime()
- requestKinds := c.watchKinds()
+ requestKinds := c.Config.Watches
watcher, err := c.Events.NewWatcher(c.ctx, types.Watch{
Name: c.Component,
Kinds: requestKinds,
@@ -1678,14 +1739,6 @@ func (c *Cache) performRelativeNodeExpiry(ctx context.Context) error {
return nil
}
-func (c *Cache) watchKinds() []types.WatchKind {
- out := make([]types.WatchKind, 0, len(c.collections.byKind))
- for _, collection := range c.collections.byKind {
- out = append(out, collection.watchKind())
- }
- return out
-}
-
// isClosing checks if the cache has begun closing.
func (c *Cache) isClosing() bool {
if c.closed.Load() {
@@ -1764,9 +1817,9 @@ func (c *Cache) fetch(ctx context.Context, confirmedKinds map[resourceKind]types
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(fetchLimit(c.target))
- applyfns := make([]applyFn, len(c.collections.byKind))
+ applyfns := make([]applyFn, len(c.legacyCacheCollections.byKind)+len(c.collections.byKind))
i := 0
- for kind, collection := range c.collections.byKind {
+ for kind, collection := range c.legacyCacheCollections.byKind {
kind, collection := kind, collection
ii := i
i++
@@ -1792,6 +1845,31 @@ func (c *Cache) fetch(ctx context.Context, confirmedKinds map[resourceKind]types
})
}
+ for kind, handler := range c.collections.byKind {
+ ii := i
+ i++
+
+ g.Go(func() (err error) {
+ ctx, span := c.Tracer.Start(
+ ctx,
+ fmt.Sprintf("cache/fetch/%s", kind.String()),
+ oteltrace.WithAttributes(
+ attribute.String("target", c.target),
+ ),
+ )
+ defer func() { apitracing.EndSpan(span, err) }()
+
+ _, cacheOK := confirmedKinds[resourceKind{kind: kind.kind, subkind: kind.subkind}]
+ applyfn, err := handler.fetch(ctx, cacheOK)
+ if err != nil {
+ return trace.Wrap(err, "failed to fetch resource: %q", kind)
+ }
+
+ applyfns[ii] = tracedApplyFn(fetchSpan, c.Tracer, kind, applyfn)
+ return nil
+ })
+ }
+
if err := g.Wait(); err != nil {
return nil, trace.Wrap(err)
}
@@ -1811,13 +1889,31 @@ func (c *Cache) fetch(ctx context.Context, confirmedKinds map[resourceKind]types
// the event will be emitted via the fanout.
func (c *Cache) processEvent(ctx context.Context, event types.Event) error {
resourceKind := resourceKindFromResource(event.Resource)
- collection, ok := c.collections.byKind[resourceKind]
- if !ok {
- c.Logger.Warnf("Skipping unsupported event %v/%v", event.Resource.GetKind(), event.Resource.GetSubKind())
- return nil
- }
- if err := collection.processEvent(ctx, event); err != nil {
- return trace.Wrap(err)
+
+ legacyCollection, legacyFound := c.legacyCacheCollections.byKind[resourceKind]
+ handler, handlerFound := c.collections.byKind[resourceKind]
+
+ switch {
+ case legacyFound:
+ if err := legacyCollection.processEvent(ctx, event); err != nil {
+ return trace.Wrap(err)
+ }
+ case handlerFound:
+ switch event.Type {
+ case types.OpDelete:
+ if err := handler.onDelete(event.Resource); err != nil {
+ if !trace.IsNotFound(err) {
+ c.Logger.Warnf("Failed to delete resource: %v", err)
+ return trace.Wrap(err)
+ }
+ }
+ case types.OpPut:
+ if err := handler.onPut(event.Resource); err != nil {
+ return trace.Wrap(err)
+ }
+ default:
+ c.Logger.Warnf("Skipping unsupported event type %T", event.Type)
+ }
}
c.eventsFanout.Emit(event)
@@ -1840,7 +1936,7 @@ func (c *Cache) GetCertAuthority(ctx context.Context, id types.CertAuthID, loadS
ctx, span := c.Tracer.Start(ctx, "cache/GetCertAuthority")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.certAuthorities)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.certAuthorities)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -1880,7 +1976,7 @@ func (c *Cache) GetCertAuthorities(ctx context.Context, caType types.CertAuthTyp
ctx, span := c.Tracer.Start(ctx, "cache/GetCertAuthorities")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.certAuthorities)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.certAuthorities)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -1907,7 +2003,7 @@ func (c *Cache) GetStaticTokens() (types.StaticTokens, error) {
_, span := c.Tracer.Start(context.TODO(), "cache/GetStaticTokens")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.staticTokens)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.staticTokens)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -1920,7 +2016,7 @@ func (c *Cache) GetTokens(ctx context.Context) ([]types.ProvisionToken, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetTokens")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.tokens)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.tokens)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -1933,7 +2029,7 @@ func (c *Cache) GetToken(ctx context.Context, name string) (types.ProvisionToken
ctx, span := c.Tracer.Start(ctx, "cache/GetToken")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.tokens)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.tokens)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -1961,7 +2057,7 @@ func (c *Cache) GetClusterAuditConfig(ctx context.Context) (types.ClusterAuditCo
ctx, span := c.Tracer.Start(ctx, "cache/GetClusterAuditConfig")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.clusterAuditConfigs)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.clusterAuditConfigs)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -1984,7 +2080,7 @@ func (c *Cache) GetClusterNetworkingConfig(ctx context.Context) (types.ClusterNe
ctx, span := c.Tracer.Start(ctx, "cache/GetClusterNetworkingConfig")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.clusterNetworkingConfigs)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.clusterNetworkingConfigs)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2007,7 +2103,7 @@ func (c *Cache) GetClusterName(opts ...services.MarshalOption) (types.ClusterNam
ctx, span := c.Tracer.Start(context.TODO(), "cache/GetClusterName")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.clusterNames)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.clusterNames)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2034,7 +2130,7 @@ func (c *Cache) GetAutoUpdateConfig(ctx context.Context) (*autoupdate.AutoUpdate
ctx, span := c.Tracer.Start(ctx, "cache/GetAutoUpdateConfig")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.autoUpdateConfigs)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.autoUpdateConfigs)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2057,7 +2153,7 @@ func (c *Cache) GetAutoUpdateVersion(ctx context.Context) (*autoupdate.AutoUpdat
ctx, span := c.Tracer.Start(ctx, "cache/GetAutoUpdateVersion")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.autoUpdateVersions)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.autoUpdateVersions)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2080,7 +2176,7 @@ func (c *Cache) GetAutoUpdateAgentRollout(ctx context.Context) (*autoupdate.Auto
ctx, span := c.Tracer.Start(ctx, "cache/GetAutoUpdateAgentRollout")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.autoUpdateAgentRollouts)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.autoUpdateAgentRollouts)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2102,7 +2198,7 @@ func (c *Cache) GetUIConfig(ctx context.Context) (types.UIConfig, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetUIConfig")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.uiConfigs)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.uiConfigs)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2117,7 +2213,7 @@ func (c *Cache) GetInstaller(ctx context.Context, name string) (types.Installer,
ctx, span := c.Tracer.Start(ctx, "cache/GetInstaller")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.installers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.installers)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2132,7 +2228,7 @@ func (c *Cache) GetInstallers(ctx context.Context) ([]types.Installer, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetInstallers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.installers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.installers)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2147,7 +2243,7 @@ func (c *Cache) GetRoles(ctx context.Context) ([]types.Role, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetRoles")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.roles)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.roles)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2160,7 +2256,7 @@ func (c *Cache) ListRoles(ctx context.Context, req *proto.ListRolesRequest) (*pr
ctx, span := c.Tracer.Start(ctx, "cache/ListRoles")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.roles)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.roles)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2173,7 +2269,7 @@ func (c *Cache) GetRole(ctx context.Context, name string) (types.Role, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetRole")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.roles)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.roles)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2196,7 +2292,7 @@ func (c *Cache) GetNamespace(name string) (*types.Namespace, error) {
_, span := c.Tracer.Start(context.TODO(), "cache/GetNamespace")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.namespaces)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.namespaces)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2209,7 +2305,7 @@ func (c *Cache) GetNamespaces() ([]types.Namespace, error) {
_, span := c.Tracer.Start(context.TODO(), "cache/GetNamespaces")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.namespaces)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.namespaces)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2222,7 +2318,7 @@ func (c *Cache) GetNode(ctx context.Context, namespace, name string) (types.Serv
ctx, span := c.Tracer.Start(ctx, "cache/GetNode")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.nodes)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.nodes)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2239,7 +2335,7 @@ func (c *Cache) GetNodes(ctx context.Context, namespace string) ([]types.Server,
ctx, span := c.Tracer.Start(ctx, "cache/GetNodes")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.nodes)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.nodes)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2278,7 +2374,7 @@ func (c *Cache) GetAuthServers() ([]types.Server, error) {
_, span := c.Tracer.Start(context.TODO(), "cache/GetAuthServers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.authServers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.authServers)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2291,7 +2387,7 @@ func (c *Cache) GetProxies() ([]types.Server, error) {
_, span := c.Tracer.Start(context.TODO(), "cache/GetProxies")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.proxies)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.proxies)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2308,7 +2404,7 @@ func (c *Cache) GetRemoteClusters(ctx context.Context) ([]types.RemoteCluster, e
ctx, span := c.Tracer.Start(ctx, "cache/GetRemoteClusters")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.remoteClusters)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.remoteClusters)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2336,7 +2432,7 @@ func (c *Cache) GetRemoteCluster(ctx context.Context, clusterName string) (types
ctx, span := c.Tracer.Start(ctx, "cache/GetRemoteCluster")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.remoteClusters)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.remoteClusters)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2370,7 +2466,7 @@ func (c *Cache) ListRemoteClusters(ctx context.Context, pageSize int, nextToken
_, span := c.Tracer.Start(ctx, "cache/ListRemoteClusters")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.remoteClusters)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.remoteClusters)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -2387,7 +2483,7 @@ func (c *Cache) GetUser(ctx context.Context, name string, withSecrets bool) (typ
if withSecrets { // cache never tracks user secrets
return c.Config.Users.GetUser(ctx, name, withSecrets)
}
- rg, err := readCollectionCache(c, c.collections.users)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.users)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2414,7 +2510,7 @@ func (c *Cache) GetUsers(ctx context.Context, withSecrets bool) ([]types.User, e
if withSecrets { // cache never tracks user secrets
return c.Users.GetUsers(ctx, withSecrets)
}
- rg, err := readCollectionCache(c, c.collections.users)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.users)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2431,7 +2527,7 @@ func (c *Cache) ListUsers(ctx context.Context, req *userspb.ListUsersRequest) (*
rsp, err := c.Users.ListUsers(ctx, req)
return rsp, trace.Wrap(err)
}
- rg, err := readCollectionCache(c, c.collections.users)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.users)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2445,7 +2541,7 @@ func (c *Cache) GetTunnelConnections(clusterName string, opts ...services.Marsha
_, span := c.Tracer.Start(context.TODO(), "cache/GetTunnelConnections")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.tunnelConnections)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.tunnelConnections)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2458,7 +2554,7 @@ func (c *Cache) GetAllTunnelConnections(opts ...services.MarshalOption) (conns [
_, span := c.Tracer.Start(context.TODO(), "cache/GetAllTunnelConnections")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.tunnelConnections)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.tunnelConnections)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2471,7 +2567,7 @@ func (c *Cache) GetKubernetesServers(ctx context.Context) ([]types.KubeServer, e
ctx, span := c.Tracer.Start(ctx, "cache/GetKubernetesServers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.kubeServers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.kubeServers)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2486,7 +2582,7 @@ func (c *Cache) ListKubernetesWaitingContainers(ctx context.Context, pageSize in
ctx, span := c.Tracer.Start(ctx, "cache/ListKubernetesWaitingContainers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.kubeWaitingContainers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.kubeWaitingContainers)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -2501,7 +2597,7 @@ func (c *Cache) GetKubernetesWaitingContainer(ctx context.Context, req *kubewait
ctx, span := c.Tracer.Start(ctx, "cache/GetKubernetesWaitingContainer")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.kubeWaitingContainers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.kubeWaitingContainers)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2514,7 +2610,7 @@ func (c *Cache) ListStaticHostUsers(ctx context.Context, pageSize int, pageToken
ctx, span := c.Tracer.Start(ctx, "cache/ListStaticHostUsers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.staticHostUsers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.staticHostUsers)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -2527,7 +2623,7 @@ func (c *Cache) GetStaticHostUser(ctx context.Context, name string) (*userprovis
ctx, span := c.Tracer.Start(ctx, "cache/GetStaticHostUser")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.staticHostUsers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.staticHostUsers)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2540,7 +2636,7 @@ func (c *Cache) GetApplicationServers(ctx context.Context, namespace string) ([]
ctx, span := c.Tracer.Start(ctx, "cache/GetApplicationServers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.appServers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.appServers)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2553,7 +2649,7 @@ func (c *Cache) GetKubernetesClusters(ctx context.Context) ([]types.KubeCluster,
ctx, span := c.Tracer.Start(ctx, "cache/GetKubernetesClusters")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.kubeClusters)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.kubeClusters)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2566,7 +2662,7 @@ func (c *Cache) GetKubernetesCluster(ctx context.Context, name string) (types.Ku
ctx, span := c.Tracer.Start(ctx, "cache/GetKubernetesCluster")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.kubeClusters)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.kubeClusters)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2579,7 +2675,7 @@ func (c *Cache) GetApps(ctx context.Context) ([]types.Application, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetApps")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.apps)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.apps)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2592,7 +2688,7 @@ func (c *Cache) GetApp(ctx context.Context, name string) (types.Application, err
ctx, span := c.Tracer.Start(ctx, "cache/GetApp")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.apps)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.apps)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2605,7 +2701,7 @@ func (c *Cache) GetAppSession(ctx context.Context, req types.GetAppSessionReques
ctx, span := c.Tracer.Start(ctx, "cache/GetAppSession")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.appSessions)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.appSessions)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2630,7 +2726,7 @@ func (c *Cache) ListAppSessions(ctx context.Context, pageSize int, pageToken, us
ctx, span := c.Tracer.Start(ctx, "cache/ListAppSessions")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.appSessions)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.appSessions)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -2643,7 +2739,7 @@ func (c *Cache) GetSnowflakeSession(ctx context.Context, req types.GetSnowflakeS
ctx, span := c.Tracer.Start(ctx, "cache/GetSnowflakeSession")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.snowflakeSessions)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.snowflakeSessions)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2669,7 +2765,7 @@ func (c *Cache) GetSAMLIdPSession(ctx context.Context, req types.GetSAMLIdPSessi
ctx, span := c.Tracer.Start(ctx, "cache/GetSAMLIdPSession")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.samlIdPSessions)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.samlIdPSessions)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2695,7 +2791,7 @@ func (c *Cache) GetDatabaseServers(ctx context.Context, namespace string, opts .
ctx, span := c.Tracer.Start(ctx, "cache/GetDatabaseServers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.databaseServers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.databaseServers)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2708,7 +2804,7 @@ func (c *Cache) GetDatabases(ctx context.Context) ([]types.Database, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetDatabases")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.databases)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.databases)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2720,7 +2816,7 @@ func (c *Cache) GetDatabaseObject(ctx context.Context, name string) (*dbobjectv1
ctx, span := c.Tracer.Start(ctx, "cache/GetDatabaseObject")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.databaseObjects)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.databaseObjects)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2732,7 +2828,7 @@ func (c *Cache) ListDatabaseObjects(ctx context.Context, size int, pageToken str
ctx, span := c.Tracer.Start(ctx, "cache/ListWindowsDesktopServices")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.databaseObjects)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.databaseObjects)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -2745,7 +2841,7 @@ func (c *Cache) GetDatabase(ctx context.Context, name string) (types.Database, e
ctx, span := c.Tracer.Start(ctx, "cache/GetDatabase")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.databases)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.databases)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2758,7 +2854,7 @@ func (c *Cache) GetWebSession(ctx context.Context, req types.GetWebSessionReques
ctx, span := c.Tracer.Start(ctx, "cache/GetWebSession")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.webSessions)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.webSessions)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2784,7 +2880,7 @@ func (c *Cache) GetWebToken(ctx context.Context, req types.GetWebTokenRequest) (
ctx, span := c.Tracer.Start(ctx, "cache/GetWebToken")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.webTokens)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.webTokens)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2797,7 +2893,7 @@ func (c *Cache) GetAuthPreference(ctx context.Context) (types.AuthPreference, er
ctx, span := c.Tracer.Start(ctx, "cache/GetAuthPreference")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.authPreferences)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.authPreferences)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2810,7 +2906,7 @@ func (c *Cache) GetSessionRecordingConfig(ctx context.Context) (types.SessionRec
ctx, span := c.Tracer.Start(ctx, "cache/GetSessionRecordingConfig")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.sessionRecordingConfigs)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.sessionRecordingConfigs)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2823,7 +2919,7 @@ func (c *Cache) GetNetworkRestrictions(ctx context.Context) (types.NetworkRestri
ctx, span := c.Tracer.Start(ctx, "cache/GetNetworkRestrictions")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.networkRestrictions)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.networkRestrictions)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2837,7 +2933,7 @@ func (c *Cache) GetLock(ctx context.Context, name string) (types.Lock, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetLock")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.locks)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.locks)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2862,7 +2958,7 @@ func (c *Cache) GetLocks(ctx context.Context, inForceOnly bool, targets ...types
ctx, span := c.Tracer.Start(ctx, "cache/GetLocks")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.locks)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.locks)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2875,7 +2971,7 @@ func (c *Cache) GetWindowsDesktopServices(ctx context.Context) ([]types.WindowsD
ctx, span := c.Tracer.Start(ctx, "cache/GetWindowsDesktopServices")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.windowsDesktopServices)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.windowsDesktopServices)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2888,7 +2984,7 @@ func (c *Cache) GetWindowsDesktopService(ctx context.Context, name string) (type
ctx, span := c.Tracer.Start(ctx, "cache/GetWindowsDesktopService")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.windowsDesktopServices)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.windowsDesktopServices)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2901,7 +2997,7 @@ func (c *Cache) GetWindowsDesktops(ctx context.Context, filter types.WindowsDesk
ctx, span := c.Tracer.Start(ctx, "cache/GetWindowsDesktops")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.windowsDesktops)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.windowsDesktops)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2914,7 +3010,7 @@ func (c *Cache) ListWindowsDesktops(ctx context.Context, req types.ListWindowsDe
ctx, span := c.Tracer.Start(ctx, "cache/ListWindowsDesktops")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.windowsDesktops)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.windowsDesktops)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2927,7 +3023,7 @@ func (c *Cache) ListWindowsDesktopServices(ctx context.Context, req types.ListWi
ctx, span := c.Tracer.Start(ctx, "cache/ListWindowsDesktopServices")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.windowsDesktopServices)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.windowsDesktopServices)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2940,7 +3036,7 @@ func (c *Cache) GetDynamicWindowsDesktop(ctx context.Context, name string) (type
ctx, span := c.Tracer.Start(ctx, "cache/GetDynamicWindowsDesktop")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.dynamicWindowsDesktops)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.dynamicWindowsDesktops)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2953,7 +3049,7 @@ func (c *Cache) ListDynamicWindowsDesktops(ctx context.Context, pageSize int, ne
ctx, span := c.Tracer.Start(ctx, "cache/ListDynamicWindowsDesktops")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.dynamicWindowsDesktops)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.dynamicWindowsDesktops)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -2966,7 +3062,7 @@ func (c *Cache) ListSAMLIdPServiceProviders(ctx context.Context, pageSize int, n
ctx, span := c.Tracer.Start(ctx, "cache/ListSAMLIdPServiceProviders")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.samlIdPServiceProviders)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.samlIdPServiceProviders)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -2979,7 +3075,7 @@ func (c *Cache) GetSAMLIdPServiceProvider(ctx context.Context, name string) (typ
ctx, span := c.Tracer.Start(ctx, "cache/GetSAMLIdPServiceProvider")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.samlIdPServiceProviders)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.samlIdPServiceProviders)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -2992,7 +3088,7 @@ func (c *Cache) ListUserGroups(ctx context.Context, pageSize int, nextKey string
ctx, span := c.Tracer.Start(ctx, "cache/ListUserGroups")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.userGroups)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.userGroups)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3005,7 +3101,7 @@ func (c *Cache) GetUserGroup(ctx context.Context, name string) (types.UserGroup,
ctx, span := c.Tracer.Start(ctx, "cache/GetUserGroup")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.userGroups)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.userGroups)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3018,7 +3114,7 @@ func (c *Cache) ListOktaImportRules(ctx context.Context, pageSize int, nextKey s
ctx, span := c.Tracer.Start(ctx, "cache/ListOktaImportRules")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.oktaImportRules)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.oktaImportRules)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3031,7 +3127,7 @@ func (c *Cache) GetOktaImportRule(ctx context.Context, name string) (types.OktaI
ctx, span := c.Tracer.Start(ctx, "cache/GetOktaImportRule")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.oktaImportRules)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.oktaImportRules)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3044,7 +3140,7 @@ func (c *Cache) ListOktaAssignments(ctx context.Context, pageSize int, nextKey s
ctx, span := c.Tracer.Start(ctx, "cache/ListOktaAssignments")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.oktaAssignments)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.oktaAssignments)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3057,7 +3153,7 @@ func (c *Cache) GetOktaAssignment(ctx context.Context, name string) (types.OktaA
ctx, span := c.Tracer.Start(ctx, "cache/GetOktaAssignment")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.oktaAssignments)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.oktaAssignments)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3070,7 +3166,7 @@ func (c *Cache) ListIntegrations(ctx context.Context, pageSize int, nextKey stri
ctx, span := c.Tracer.Start(ctx, "cache/ListIntegrations")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.integrations)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.integrations)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3083,7 +3179,7 @@ func (c *Cache) GetIntegration(ctx context.Context, name string) (types.Integrat
ctx, span := c.Tracer.Start(ctx, "cache/GetIntegration")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.integrations)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.integrations)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3096,7 +3192,7 @@ func (c *Cache) ListUserTasks(ctx context.Context, pageSize int64, nextKey strin
ctx, span := c.Tracer.Start(ctx, "cache/ListUserTasks")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.userTasks)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.userTasks)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3109,7 +3205,7 @@ func (c *Cache) GetUserTask(ctx context.Context, name string) (*usertasksv1.User
ctx, span := c.Tracer.Start(ctx, "cache/GetUserTask")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.userTasks)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.userTasks)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3122,7 +3218,7 @@ func (c *Cache) ListDiscoveryConfigs(ctx context.Context, pageSize int, nextKey
ctx, span := c.Tracer.Start(ctx, "cache/ListDiscoveryConfigs")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.discoveryConfigs)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.discoveryConfigs)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3135,7 +3231,7 @@ func (c *Cache) GetDiscoveryConfig(ctx context.Context, name string) (*discovery
ctx, span := c.Tracer.Start(ctx, "cache/GetDiscoveryConfig")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.discoveryConfigs)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.discoveryConfigs)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3148,7 +3244,7 @@ func (c *Cache) ListCrownJewels(ctx context.Context, pageSize int64, nextKey str
ctx, span := c.Tracer.Start(ctx, "cache/ListCrownJewels")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.crownJewels)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.crownJewels)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3161,7 +3257,7 @@ func (c *Cache) GetCrownJewel(ctx context.Context, name string) (*crownjewelv1.C
ctx, span := c.Tracer.Start(ctx, "cache/GetCrownJewel")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.crownJewels)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.crownJewels)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3174,7 +3270,7 @@ func (c *Cache) GetSecurityAuditQuery(ctx context.Context, name string) (*secrep
ctx, span := c.Tracer.Start(ctx, "cache/GetSecurityAuditQuery")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.auditQueries)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.auditQueries)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3187,7 +3283,7 @@ func (c *Cache) GetSecurityAuditQueries(ctx context.Context) ([]*secreports.Audi
ctx, span := c.Tracer.Start(ctx, "cache/GetSecurityAuditQueries")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.auditQueries)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.auditQueries)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3200,7 +3296,7 @@ func (c *Cache) ListSecurityAuditQueries(ctx context.Context, pageSize int, next
ctx, span := c.Tracer.Start(ctx, "cache/ListSecurityAuditQueries")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.auditQueries)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.auditQueries)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3213,7 +3309,7 @@ func (c *Cache) GetSecurityReport(ctx context.Context, name string) (*secreports
ctx, span := c.Tracer.Start(ctx, "cache/GetSecurityReport")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.secReports)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.secReports)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3226,7 +3322,7 @@ func (c *Cache) GetSecurityReports(ctx context.Context) ([]*secreports.Report, e
ctx, span := c.Tracer.Start(ctx, "cache/GetSecurityReports")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.secReports)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.secReports)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3239,7 +3335,7 @@ func (c *Cache) ListSecurityReports(ctx context.Context, pageSize int, nextKey s
ctx, span := c.Tracer.Start(ctx, "cache/ListSecurityReports")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.secReports)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.secReports)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3252,7 +3348,7 @@ func (c *Cache) GetSecurityReportState(ctx context.Context, name string) (*secre
ctx, span := c.Tracer.Start(ctx, "cache/GetSecurityReportState")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.secReportsStates)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.secReportsStates)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3265,7 +3361,7 @@ func (c *Cache) GetSecurityReportsStates(ctx context.Context) ([]*secreports.Rep
ctx, span := c.Tracer.Start(ctx, "cache/GetSecurityReportsStates")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.secReportsStates)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.secReportsStates)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3278,7 +3374,7 @@ func (c *Cache) ListSecurityReportsStates(ctx context.Context, pageSize int, nex
ctx, span := c.Tracer.Start(ctx, "cache/ListSecurityReportsStates")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.secReportsStates)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.secReportsStates)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3291,7 +3387,7 @@ func (c *Cache) GetUserLoginStates(ctx context.Context) ([]*userloginstate.UserL
ctx, span := c.Tracer.Start(ctx, "cache/GetUserLoginStates")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.userLoginStates)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.userLoginStates)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3304,7 +3400,7 @@ func (c *Cache) GetUserLoginState(ctx context.Context, name string) (*userlogins
ctx, span := c.Tracer.Start(ctx, "cache/GetUserLoginState")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.userLoginStates)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.userLoginStates)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3328,7 +3424,7 @@ func (c *Cache) GetAccessLists(ctx context.Context) ([]*accesslist.AccessList, e
ctx, span := c.Tracer.Start(ctx, "cache/GetAccessLists")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessLists)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessLists)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3341,7 +3437,7 @@ func (c *Cache) ListAccessLists(ctx context.Context, pageSize int, nextToken str
ctx, span := c.Tracer.Start(ctx, "cache/ListAccessLists")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessLists)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessLists)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3354,7 +3450,7 @@ func (c *Cache) GetAccessList(ctx context.Context, name string) (*accesslist.Acc
ctx, span := c.Tracer.Start(ctx, "cache/GetAccessList")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessLists)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessLists)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3377,7 +3473,7 @@ func (c *Cache) CountAccessListMembers(ctx context.Context, accessListName strin
ctx, span := c.Tracer.Start(ctx, "cache/CountAccessListMembers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessListMembers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessListMembers)
if err != nil {
return 0, 0, trace.Wrap(err)
}
@@ -3393,7 +3489,7 @@ func (c *Cache) ListAccessListMembers(ctx context.Context, accessListName string
ctx, span := c.Tracer.Start(ctx, "cache/ListAccessListMembers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessListMembers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessListMembers)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3406,7 +3502,7 @@ func (c *Cache) ListAllAccessListMembers(ctx context.Context, pageSize int, page
ctx, span := c.Tracer.Start(ctx, "cache/ListAllAccessListMembers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessListMembers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessListMembers)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3422,7 +3518,7 @@ func (c *Cache) GetAccessListMember(ctx context.Context, accessList string, memb
ctx, span := c.Tracer.Start(ctx, "cache/GetAccessListMember")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessListMembers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessListMembers)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3435,7 +3531,7 @@ func (c *Cache) ListAccessListReviews(ctx context.Context, accessList string, pa
ctx, span := c.Tracer.Start(ctx, "cache/ListAccessListReviews")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessListReviews)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessListReviews)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3448,7 +3544,7 @@ func (c *Cache) ListUserNotifications(ctx context.Context, pageSize int, startKe
ctx, span := c.Tracer.Start(ctx, "cache/ListUserNotifications")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.userNotifications)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.userNotifications)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3464,7 +3560,7 @@ func (c *Cache) ListGlobalNotifications(ctx context.Context, pageSize int, start
ctx, span := c.Tracer.Start(ctx, "cache/ListGlobalNotifications")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.globalNotifications)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.globalNotifications)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3478,7 +3574,7 @@ func (c *Cache) ListAccessMonitoringRules(ctx context.Context, pageSize int, nex
ctx, span := c.Tracer.Start(ctx, "cache/ListAccessMonitoringRules")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessMonitoringRules)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessMonitoringRules)
if err != nil {
return nil, "", trace.Wrap(err)
@@ -3493,7 +3589,7 @@ func (c *Cache) ListAccessMonitoringRulesWithFilter(ctx context.Context, pageSiz
ctx, span := c.Tracer.Start(ctx, "cache/ListAccessMonitoringRules")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessMonitoringRules)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessMonitoringRules)
if err != nil {
return nil, "", trace.Wrap(err)
@@ -3508,7 +3604,7 @@ func (c *Cache) GetAccessMonitoringRule(ctx context.Context, name string) (*acce
ctx, span := c.Tracer.Start(ctx, "cache/GetAccessMonitoringRule")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessMonitoringRules)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessMonitoringRules)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3572,7 +3668,7 @@ func (c *Cache) GetAccessGraphSettings(ctx context.Context) (*clusterconfigpb.Ac
ctx, span := c.Tracer.Start(ctx, "cache/GetAccessGraphSettings")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.accessGraphSettings)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.accessGraphSettings)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3596,7 +3692,7 @@ func (c *Cache) GetProvisioningState(ctx context.Context, downstream services.Do
ctx, span := c.Tracer.Start(ctx, "cache/GetProvisioningState")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.provisioningStates)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.provisioningStates)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3609,7 +3705,7 @@ func (c *Cache) GetAccountAssignment(ctx context.Context, id services.IdentityCe
ctx, span := c.Tracer.Start(ctx, "cache/GetAccountAssignment")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.identityCenterAccountAssignments)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.identityCenterAccountAssignments)
if err != nil {
return services.IdentityCenterAccountAssignment{}, trace.Wrap(err)
}
@@ -3623,7 +3719,7 @@ func (c *Cache) ListAccountAssignments(ctx context.Context, pageSize int, pageTo
ctx, span := c.Tracer.Start(ctx, "cache/ListAccountAssignments")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.identityCenterAccountAssignments)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.identityCenterAccountAssignments)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3636,7 +3732,7 @@ func (c *Cache) GetIdentityCenterAccount(ctx context.Context, name services.Iden
ctx, span := c.Tracer.Start(ctx, "cache/GetIdentityCenterAccount")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.identityCenterAccounts)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.identityCenterAccounts)
if err != nil {
return services.IdentityCenterAccount{}, trace.Wrap(err)
}
@@ -3649,7 +3745,7 @@ func (c *Cache) ListIdentityCenterAccounts(ctx context.Context, pageSize int, to
ctx, span := c.Tracer.Start(ctx, "cache/ListIdentityCenterAccounts")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.identityCenterAccounts)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.identityCenterAccounts)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3662,7 +3758,7 @@ func (c *Cache) GetPrincipalAssignment(ctx context.Context, id services.Principa
ctx, span := c.Tracer.Start(ctx, "cache/GetPrincipalAssignment")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.identityCenterPrincipalAssignments)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.identityCenterPrincipalAssignments)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -3675,7 +3771,7 @@ func (c *Cache) ListPrincipalAssignments(ctx context.Context, pageSize int, req
ctx, span := c.Tracer.Start(ctx, "cache/ListPrincipalAssignments")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.identityCenterPrincipalAssignments)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.identityCenterPrincipalAssignments)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -3688,7 +3784,7 @@ func (c *Cache) ListProvisioningStatesForAllDownstreams(ctx context.Context, pag
ctx, span := c.Tracer.Start(ctx, "cache/ListPrincipalAssignments")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.provisioningStates)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.provisioningStates)
if err != nil {
return nil, "", trace.Wrap(err)
}
diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go
index 2990af386f059..f27b0a3bb9aed 100644
--- a/lib/cache/cache_test.go
+++ b/lib/cache/cache_test.go
@@ -2990,11 +2990,11 @@ func testResources[T types.Resource](t *testing.T, p *testPack, funcs testFuncs[
require.Empty(t, cmp.Diff([]T{r}, out, cmpOpts...))
// Wait until the information has been replicated to the cache.
- require.Eventually(t, func() bool {
+ require.EventuallyWithT(t, func(t *assert.CollectT) {
// Make sure the cache has a single resource in it.
out, err = funcs.cacheList(ctx)
assert.NoError(t, err)
- return len(cmp.Diff([]T{r}, out, cmpOpts...)) == 0
+ assert.Empty(t, cmp.Diff([]T{r}, out, cmpOpts...))
}, time.Second*2, time.Millisecond*250)
// cacheGet is optional as not every resource implements it
@@ -3020,11 +3020,11 @@ func testResources[T types.Resource](t *testing.T, p *testPack, funcs testFuncs[
require.Empty(t, cmp.Diff([]T{r}, out, cmpOpts...))
// Check that information has been replicated to the cache.
- require.Eventually(t, func() bool {
+ require.EventuallyWithT(t, func(t *assert.CollectT) {
// Make sure the cache has a single resource in it.
out, err = funcs.cacheList(ctx)
assert.NoError(t, err)
- return len(cmp.Diff([]T{r}, out, cmpOpts...)) == 0
+ assert.Empty(t, cmp.Diff([]T{r}, out, cmpOpts...))
}, time.Second*2, time.Millisecond*250)
// Remove all service providers from the backend.
@@ -3060,20 +3060,20 @@ func testResources153[T types.Resource153](t *testing.T, p *testPack, funcs test
}
assertCacheContents := func(expected []T) {
- require.EventuallyWithT(t, func(collect *assert.CollectT) {
+ require.EventuallyWithT(t, func(t *assert.CollectT) {
out, err := funcs.cacheList(ctx)
- assert.NoError(collect, err)
+ assert.NoError(t, err)
// If the cache is expected to be empty, then test explicitly for
// *that* rather than do an equality test. An equality test here
// would be overly-pedantic about a service returning `nil` rather
// than an empty slice.
if len(expected) == 0 {
- assert.Empty(collect, out)
+ assert.Empty(t, out)
return
}
- assert.Empty(collect, cmp.Diff(expected, out, cmpOpts...))
+ assert.Empty(t, cmp.Diff(expected, out, cmpOpts...))
}, 2*time.Second, 10*time.Millisecond)
}
diff --git a/lib/cache/collection.go b/lib/cache/collection.go
new file mode 100644
index 0000000000000..e1d7c8c670db6
--- /dev/null
+++ b/lib/cache/collection.go
@@ -0,0 +1,165 @@
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package cache
+
+import (
+ "context"
+ "reflect"
+
+ "github.com/gravitational/trace"
+
+ "github.com/gravitational/teleport/api/types"
+)
+
+// collection is responsible for managing a cached resource.
+type collection[T any, I comparable] struct {
+ // fetcher is called by fetch to retrieve and seed the
+ // store with all known resources from upstream.
+ fetcher func(ctx context.Context, loadSecrets bool) ([]T, error)
+ // store persists all resources in memory.
+ store *store[T, I]
+ // watch contains the kind of resource being monitored.
+ watch types.WatchKind
+ // headerTransform is used when handling delete events in [onDelete]. Since
+ // [types.OpDelete] events only contain information about the resource key,
+ // most event handlers only emit a [types.ResourceHeader] which has enough
+ // information to identify a resource. Some resources do emit a half
+ // populated [T], or have enough information from the key to emit a full [T].
+ //
+ // If this optional transformation is supplied it will be called when
+ // processing delete events before attempting to delete the resource
+ // from the store.
+ headerTransform func(hdr *types.ResourceHeader) T
+ // filter is an optional function used to prevent some resources
+ // from being persisted in the store.
+ filter func(T) bool
+ // singleton indicates if the resource should only ever have a single item.
+ // TODO(tross|fspmarshall|espadolini) investigate if special singleton
+ // behavior can be removed.
+ singleton bool
+}
+
+func (c collection[_, _]) watchKind() types.WatchKind {
+ return c.watch
+}
+
+// onDelete attempts to remove the provided resource from the store.
+// An error is returned if the resource is of an unexpected type, or
+// the resource is a [types.ResourceHeader] and no headerTransform was
+// specified.
+//
+// This is a no-op if the configured filter does not return true.
+func (c *collection[T, _]) onDelete(r types.Resource) error {
+ switch t := r.(type) {
+ case interface{ UnwrapT() T }:
+ tt := t.UnwrapT()
+ if c.filter != nil && !c.filter(tt) {
+ return nil
+ }
+
+ return trace.Wrap(c.store.delete(tt))
+ case *types.ResourceHeader:
+ if c.headerTransform == nil {
+ return trace.BadParameter("unable to convert types.ResourceHeader to %v (no transform specified, this is a bug)", reflect.TypeFor[T]())
+ }
+
+ tt := c.headerTransform(t)
+ if c.filter != nil && !c.filter(tt) {
+ return nil
+ }
+
+ return trace.Wrap(c.store.delete(tt))
+ case T:
+ if c.filter != nil && !c.filter(t) {
+ return nil
+ }
+
+ return trace.Wrap(c.store.delete(t))
+ default:
+ return trace.BadParameter("unexpected type %T (expected %v)", r, reflect.TypeFor[T]())
+ }
+}
+
+// onUpdate attempts to place the resource into the local store.
+// An error is returned if the resource is of an unexpected type
+//
+// This is a no-op if the configured filter does not return true.
+func (c *collection[T, _]) onPut(r types.Resource) error {
+ switch t := r.(type) {
+ case interface{ UnwrapT() T }:
+ tt := t.UnwrapT()
+ if c.filter != nil && !c.filter(tt) {
+ return nil
+ }
+
+ c.store.put(tt)
+ return nil
+ case T:
+ if c.filter != nil && !c.filter(t) {
+ return nil
+ }
+
+ c.store.put(t)
+ return nil
+ default:
+ return trace.BadParameter("unexpected type %T (expected %v)", r, reflect.TypeFor[T]())
+ }
+}
+
+// fetch populates the store with items received by the configured fetcher.
+func (c collection[T, _]) fetch(ctx context.Context, cacheOK bool) (apply func(context.Context) error, err error) {
+ // Singleton objects will only get deleted or updated, not both
+ // TODO(tross|fspmarshall|espadolini) investigate if special singleton
+ // behavior can be removed.
+ deleteSingleton := false
+
+ var resources []T
+ if cacheOK {
+ resources, err = c.fetcher(ctx, c.watch.LoadSecrets)
+ if err != nil {
+ if !trace.IsNotFound(err) {
+ return nil, trace.Wrap(err)
+ }
+ deleteSingleton = true
+ }
+ }
+
+ return func(ctx context.Context) error {
+ // Always perform the delete if this is not a singleton, otherwise
+ // only perform the delete if the singleton wasn't found
+ // or the resource kind isn't cached in the current generation.
+ if !c.singleton || deleteSingleton || !cacheOK {
+ if err := c.store.clear(); err != nil {
+ if !trace.IsNotFound(err) {
+ return trace.Wrap(err)
+ }
+ }
+ }
+ // If this is a singleton and we performed a deletion, return here
+ // because we only want to update or delete a singleton, not both.
+ // Also don't continue if the resource kind isn't cached in the current generation.
+ if c.singleton && deleteSingleton || !cacheOK {
+ return nil
+ }
+ for _, resource := range resources {
+ if err := c.store.put(resource); err != nil {
+ return trace.Wrap(err)
+ }
+ }
+ return nil
+ }, nil
+}
diff --git a/lib/cache/collections.go b/lib/cache/collections.go
index 69ca476134ad4..830ed639a6a3f 100644
--- a/lib/cache/collections.go
+++ b/lib/cache/collections.go
@@ -1,3576 +1,89 @@
-/*
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
-//nolint:unused // Because the executors generate a large amount of false positives.
package cache
import (
"context"
- "fmt"
"github.com/gravitational/trace"
- "github.com/gravitational/teleport/api/client"
- "github.com/gravitational/teleport/api/client/proto"
- apidefaults "github.com/gravitational/teleport/api/defaults"
- accessmonitoringrulesv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1"
- "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1"
- clusterconfigpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1"
- crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1"
- dbobjectv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1"
- identitycenterv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/identitycenter/v1"
- kubewaitingcontainerpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/kubewaitingcontainer/v1"
- machineidv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1"
- notificationsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1"
- provisioningv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/provisioning/v1"
- userprovisioningpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/userprovisioning/v2"
- userspb "github.com/gravitational/teleport/api/gen/proto/go/teleport/users/v1"
- usertasksv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/usertasks/v1"
- workloadidentityv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1"
"github.com/gravitational/teleport/api/types"
- "github.com/gravitational/teleport/api/types/accesslist"
- "github.com/gravitational/teleport/api/types/discoveryconfig"
- "github.com/gravitational/teleport/api/types/secreports"
- "github.com/gravitational/teleport/api/types/userloginstate"
- "github.com/gravitational/teleport/lib/defaults"
- "github.com/gravitational/teleport/lib/services"
)
-// collection is responsible for managing collection
-// of resources updates
-type collection interface {
+// collectionHandler is used by the [Cache] to seed the initial
+// data and process events for a particular resource.
+type collectionHandler interface {
// fetch fetches resources and returns a function which will apply said resources to the cache.
// fetch *must* not mutate cache state outside of the apply function.
// The provided cacheOK flag indicates whether this collection will be included in the cache generation that is
// being prepared. If cacheOK is false, fetch shouldn't fetch any resources, but the apply function that it
// returns must still delete resources from the backend.
fetch(ctx context.Context, cacheOK bool) (apply func(ctx context.Context) error, err error)
- // processEvent processes event
- processEvent(ctx context.Context, e types.Event) error
+ // onDelete will delete a single target resource from the cache. For
+ // singletons, this is usually an alias to clear.
+ onDelete(t types.Resource) error
+ // onPut will update a single target resource from the cache
+ onPut(t types.Resource) error
// watchKind returns a watch
// required for this collection
watchKind() types.WatchKind
}
-// executor[T, R] is a specific way to run the collector operations that we need
-// for the genericCollector for a generic resource type T and its reader type R.
-type executor[T any, R any] interface {
- // getAll returns all of the target resources from the auth server.
- // For singleton objects, this should be a size-1 slice.
- getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]T, error)
-
- // upsert will create or update a target resource in the cache.
- upsert(ctx context.Context, cache *Cache, value T) error
-
- // deleteAll will delete all target resources of the type in the cache.
- deleteAll(ctx context.Context, cache *Cache) error
-
- // delete will delete a single target resource from the cache. For
- // singletons, this is usually an alias to deleteAll.
- delete(ctx context.Context, cache *Cache, resource types.Resource) error
-
- // isSingleton will return true if the target resource is a singleton.
- isSingleton() bool
-
- // getReader returns the appropriate reader type R based on the health status of the cache.
- // Reader type R provides getter methods related to the collection, e.g. GetNodes(), GetRoles().
- // Note that cacheOK set to true means that cache is overall healthy and the collection was confirmed as supported.
- getReader(c *Cache, cacheOK bool) R
-}
-
-// noReader is returned by getReader for resources which aren't directly used by the cache, and therefore have no associated reader.
-type noReader struct{}
-
-type crownjewelsGetter interface {
- ListCrownJewels(ctx context.Context, pageSize int64, nextToken string) ([]*crownjewelv1.CrownJewel, string, error)
- GetCrownJewel(ctx context.Context, name string) (*crownjewelv1.CrownJewel, error)
-}
-
-type userTasksGetter interface {
- ListUserTasks(ctx context.Context, pageSize int64, nextToken string, filters *usertasksv1.ListUserTasksFilters) ([]*usertasksv1.UserTask, string, error)
- GetUserTask(ctx context.Context, name string) (*usertasksv1.UserTask, error)
-}
-
-// cacheCollections is a registry of resource collections used by Cache.
-type cacheCollections struct {
- // byKind is a map of registered collections by resource Kind/SubKind
- byKind map[resourceKind]collection
-
- auditQueries collectionReader[services.SecurityAuditQueryGetter]
- secReports collectionReader[services.SecurityReportGetter]
- secReportsStates collectionReader[services.SecurityReportStateGetter]
- accessLists collectionReader[accessListsGetter]
- accessListMembers collectionReader[accessListMembersGetter]
- accessListReviews collectionReader[accessListReviewsGetter]
- apps collectionReader[services.AppGetter]
- nodes collectionReader[nodeGetter]
- tunnelConnections collectionReader[tunnelConnectionGetter]
- appSessions collectionReader[appSessionGetter]
- appServers collectionReader[appServerGetter]
- authPreferences collectionReader[authPreferenceGetter]
- authServers collectionReader[authServerGetter]
- certAuthorities collectionReader[services.AuthorityGetter]
- clusterAuditConfigs collectionReader[clusterAuditConfigGetter]
- clusterNames collectionReader[clusterNameGetter]
- clusterNetworkingConfigs collectionReader[clusterNetworkingConfigGetter]
- databases collectionReader[services.DatabaseGetter]
- databaseObjects collectionReader[services.DatabaseObjectsGetter]
- databaseServers collectionReader[databaseServerGetter]
- discoveryConfigs collectionReader[services.DiscoveryConfigsGetter]
- installers collectionReader[installerGetter]
- integrations collectionReader[services.IntegrationsGetter]
- userTasks collectionReader[userTasksGetter]
- crownJewels collectionReader[crownjewelsGetter]
- kubeClusters collectionReader[kubernetesClusterGetter]
- kubeWaitingContainers collectionReader[kubernetesWaitingContainerGetter]
- staticHostUsers collectionReader[staticHostUserGetter]
- kubeServers collectionReader[kubeServerGetter]
- locks collectionReader[services.LockGetter]
- namespaces collectionReader[namespaceGetter]
- networkRestrictions collectionReader[networkRestrictionGetter]
- oktaAssignments collectionReader[oktaAssignmentGetter]
- oktaImportRules collectionReader[oktaImportRuleGetter]
- proxies collectionReader[services.ProxyGetter]
- remoteClusters collectionReader[remoteClusterGetter]
- reverseTunnels collectionReader[reverseTunnelGetter]
- roles collectionReader[roleGetter]
- samlIdPServiceProviders collectionReader[samlIdPServiceProviderGetter]
- samlIdPSessions collectionReader[samlIdPSessionGetter]
- sessionRecordingConfigs collectionReader[sessionRecordingConfigGetter]
- snowflakeSessions collectionReader[snowflakeSessionGetter]
- staticTokens collectionReader[staticTokensGetter]
- tokens collectionReader[tokenGetter]
- uiConfigs collectionReader[uiConfigGetter]
- users collectionReader[userGetter]
- userGroups collectionReader[userGroupGetter]
- userLoginStates collectionReader[services.UserLoginStatesGetter]
- webSessions collectionReader[webSessionGetter]
- webTokens collectionReader[webTokenGetter]
- windowsDesktops collectionReader[windowsDesktopsGetter]
- dynamicWindowsDesktops collectionReader[dynamicWindowsDesktopsGetter]
- windowsDesktopServices collectionReader[windowsDesktopServiceGetter]
- userNotifications collectionReader[notificationGetter]
- accessGraphSettings collectionReader[accessGraphSettingsGetter]
- globalNotifications collectionReader[notificationGetter]
- accessMonitoringRules collectionReader[accessMonitoringRuleGetter]
- spiffeFederations collectionReader[SPIFFEFederationReader]
- autoUpdateConfigs collectionReader[autoUpdateConfigGetter]
- autoUpdateVersions collectionReader[autoUpdateVersionGetter]
- autoUpdateAgentRollouts collectionReader[autoUpdateAgentRolloutGetter]
- provisioningStates collectionReader[provisioningStateGetter]
- identityCenterAccounts collectionReader[identityCenterAccountGetter]
- identityCenterPrincipalAssignments collectionReader[identityCenterPrincipalAssignmentGetter]
- identityCenterAccountAssignments collectionReader[identityCenterAccountAssignmentGetter]
- workloadIdentity collectionReader[WorkloadIdentityReader]
- pluginStaticCredentials collectionReader[pluginStaticCredentialsGetter]
- gitServers collectionReader[services.GitServerGetter]
-}
-
-// setupCollections returns a registry of collections.
-func setupCollections(c *Cache, watches []types.WatchKind) (*cacheCollections, error) {
- collections := &cacheCollections{
- byKind: make(map[resourceKind]collection, len(watches)),
- }
- for _, watch := range watches {
- resourceKind := resourceKindFromWatchKind(watch)
- switch watch.Kind {
- case types.KindCertAuthority:
- if c.Trust == nil {
- return nil, trace.BadParameter("missing parameter Trust")
- }
- var filter types.CertAuthorityFilter
- filter.FromMap(watch.Filter)
-
- collections.certAuthorities = &genericCollection[types.CertAuthority, services.AuthorityGetter, certAuthorityExecutor]{
- cache: c,
- exec: certAuthorityExecutor{filter: filter},
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.certAuthorities
- case types.KindStaticTokens:
- if c.ClusterConfig == nil {
- return nil, trace.BadParameter("missing parameter ClusterConfig")
- }
- collections.staticTokens = &genericCollection[types.StaticTokens, staticTokensGetter, staticTokensExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.staticTokens
- case types.KindToken:
- if c.Provisioner == nil {
- return nil, trace.BadParameter("missing parameter Provisioner")
- }
- collections.tokens = &genericCollection[types.ProvisionToken, tokenGetter, provisionTokenExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.tokens
- case types.KindClusterName:
- if c.ClusterConfig == nil {
- return nil, trace.BadParameter("missing parameter ClusterConfig")
- }
- collections.clusterNames = &genericCollection[types.ClusterName, clusterNameGetter, clusterNameExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.clusterNames
- case types.KindClusterAuditConfig:
- if c.ClusterConfig == nil {
- return nil, trace.BadParameter("missing parameter ClusterConfig")
- }
- collections.clusterAuditConfigs = &genericCollection[types.ClusterAuditConfig, clusterAuditConfigGetter, clusterAuditConfigExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.clusterAuditConfigs
- case types.KindClusterNetworkingConfig:
- if c.ClusterConfig == nil {
- return nil, trace.BadParameter("missing parameter ClusterConfig")
- }
- collections.clusterNetworkingConfigs = &genericCollection[types.ClusterNetworkingConfig, clusterNetworkingConfigGetter, clusterNetworkingConfigExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.clusterNetworkingConfigs
- case types.KindClusterAuthPreference:
- if c.ClusterConfig == nil {
- return nil, trace.BadParameter("missing parameter ClusterConfig")
- }
- collections.authPreferences = &genericCollection[types.AuthPreference, authPreferenceGetter, authPreferenceExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.authPreferences
- case types.KindSessionRecordingConfig:
- if c.ClusterConfig == nil {
- return nil, trace.BadParameter("missing parameter ClusterConfig")
- }
- collections.sessionRecordingConfigs = &genericCollection[types.SessionRecordingConfig, sessionRecordingConfigGetter, sessionRecordingConfigExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.sessionRecordingConfigs
- case types.KindInstaller:
- if c.ClusterConfig == nil {
- return nil, trace.BadParameter("missing parameter ClusterConfig")
- }
- collections.installers = &genericCollection[types.Installer, installerGetter, installerConfigExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.installers
- case types.KindUIConfig:
- if c.ClusterConfig == nil {
- return nil, trace.BadParameter("missing parameter ClusterConfig")
- }
- collections.uiConfigs = &genericCollection[types.UIConfig, uiConfigGetter, uiConfigExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.uiConfigs
- case types.KindUser:
- if c.Users == nil {
- return nil, trace.BadParameter("missing parameter Users")
- }
- collections.users = &genericCollection[types.User, userGetter, userExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.users
- case types.KindRole:
- if c.Access == nil {
- return nil, trace.BadParameter("missing parameter Access")
- }
- collections.roles = &genericCollection[types.Role, roleGetter, roleExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.roles
- case types.KindNamespace:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.namespaces = &genericCollection[*types.Namespace, namespaceGetter, namespaceExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.namespaces
- case types.KindNode:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.nodes = &genericCollection[types.Server, nodeGetter, nodeExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.nodes
- case types.KindProxy:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.proxies = &genericCollection[types.Server, services.ProxyGetter, proxyExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.proxies
- case types.KindAuthServer:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.authServers = &genericCollection[types.Server, authServerGetter, authServerExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.authServers
- case types.KindReverseTunnel:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.reverseTunnels = &genericCollection[types.ReverseTunnel, reverseTunnelGetter, reverseTunnelExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.reverseTunnels
- case types.KindTunnelConnection:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.tunnelConnections = &genericCollection[types.TunnelConnection, tunnelConnectionGetter, tunnelConnectionExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.tunnelConnections
- case types.KindRemoteCluster:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.remoteClusters = &genericCollection[types.RemoteCluster, remoteClusterGetter, remoteClusterExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.remoteClusters
- case types.KindAccessRequest:
- if c.DynamicAccess == nil {
- return nil, trace.BadParameter("missing parameter DynamicAccess")
- }
- collections.byKind[resourceKind] = &genericCollection[types.AccessRequest, noReader, accessRequestExecutor]{cache: c, watch: watch}
- case types.KindAppServer:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.appServers = &genericCollection[types.AppServer, appServerGetter, appServerExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.appServers
- case types.KindWebSession:
- switch watch.SubKind {
- case types.KindAppSession:
- if c.AppSession == nil {
- return nil, trace.BadParameter("missing parameter AppSession")
- }
- collections.appSessions = &genericCollection[types.WebSession, appSessionGetter, appSessionExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.appSessions
- case types.KindSnowflakeSession:
- if c.SnowflakeSession == nil {
- return nil, trace.BadParameter("missing parameter SnowflakeSession")
- }
- collections.snowflakeSessions = &genericCollection[types.WebSession, snowflakeSessionGetter, snowflakeSessionExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.snowflakeSessions
- case types.KindSAMLIdPSession:
- if c.SAMLIdPSession == nil {
- return nil, trace.BadParameter("missing parameter SAMLIdPSession")
- }
- collections.samlIdPSessions = &genericCollection[types.WebSession, samlIdPSessionGetter, samlIdPSessionExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.samlIdPSessions
- case types.KindWebSession:
- if c.WebSession == nil {
- return nil, trace.BadParameter("missing parameter WebSession")
- }
- collections.webSessions = &genericCollection[types.WebSession, webSessionGetter, webSessionExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.webSessions
- }
- case types.KindWebToken:
- if c.WebToken == nil {
- return nil, trace.BadParameter("missing parameter WebToken")
- }
- collections.webTokens = &genericCollection[types.WebToken, webTokenGetter, webTokenExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.webTokens
- case types.KindKubeServer:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.kubeServers = &genericCollection[types.KubeServer, kubeServerGetter, kubeServerExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.kubeServers
- case types.KindDatabaseServer:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.databaseServers = &genericCollection[types.DatabaseServer, databaseServerGetter, databaseServerExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.databaseServers
- case types.KindDatabaseService:
- if c.DatabaseServices == nil {
- return nil, trace.BadParameter("missing parameter DatabaseServices")
- }
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.byKind[resourceKind] = &genericCollection[types.DatabaseService, noReader, databaseServiceExecutor]{cache: c, watch: watch}
- case types.KindApp:
- if c.Apps == nil {
- return nil, trace.BadParameter("missing parameter Apps")
- }
- collections.apps = &genericCollection[types.Application, services.AppGetter, appExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.apps
- case types.KindDatabase:
- if c.Databases == nil {
- return nil, trace.BadParameter("missing parameter Databases")
- }
- collections.databases = &genericCollection[types.Database, services.DatabaseGetter, databaseExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.databases
- case types.KindDatabaseObject:
- if c.DatabaseObjects == nil {
- return nil, trace.BadParameter("missing parameter DatabaseObject")
- }
- collections.databaseObjects = &genericCollection[*dbobjectv1.DatabaseObject, services.DatabaseObjectsGetter, databaseObjectExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.databaseObjects
- case types.KindKubernetesCluster:
- if c.Kubernetes == nil {
- return nil, trace.BadParameter("missing parameter Kubernetes")
- }
- collections.kubeClusters = &genericCollection[types.KubeCluster, kubernetesClusterGetter, kubeClusterExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.kubeClusters
- case types.KindCrownJewel:
- if c.CrownJewels == nil {
- return nil, trace.BadParameter("missing parameter crownjewels")
- }
- collections.crownJewels = &genericCollection[*crownjewelv1.CrownJewel, crownjewelsGetter, crownJewelsExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.crownJewels
- case types.KindNetworkRestrictions:
- if c.Restrictions == nil {
- return nil, trace.BadParameter("missing parameter Restrictions")
- }
- collections.networkRestrictions = &genericCollection[types.NetworkRestrictions, networkRestrictionGetter, networkRestrictionsExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.networkRestrictions
- case types.KindLock:
- if c.Access == nil {
- return nil, trace.BadParameter("missing parameter Access")
- }
- collections.locks = &genericCollection[types.Lock, services.LockGetter, lockExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.locks
- case types.KindWindowsDesktopService:
- if c.Presence == nil {
- return nil, trace.BadParameter("missing parameter Presence")
- }
- collections.windowsDesktopServices = &genericCollection[types.WindowsDesktopService, windowsDesktopServiceGetter, windowsDesktopServicesExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.windowsDesktopServices
- case types.KindWindowsDesktop:
- if c.WindowsDesktops == nil {
- return nil, trace.BadParameter("missing parameter WindowsDesktops")
- }
- collections.windowsDesktops = &genericCollection[types.WindowsDesktop, windowsDesktopsGetter, windowsDesktopsExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.windowsDesktops
- case types.KindDynamicWindowsDesktop:
- if c.WindowsDesktops == nil {
- return nil, trace.BadParameter("missing parameter DynamicWindowsDesktops")
- }
- collections.dynamicWindowsDesktops = &genericCollection[types.DynamicWindowsDesktop, dynamicWindowsDesktopsGetter, dynamicWindowsDesktopsExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.dynamicWindowsDesktops
- case types.KindSAMLIdPServiceProvider:
- if c.SAMLIdPServiceProviders == nil {
- return nil, trace.BadParameter("missing parameter SAMLIdPServiceProviders")
- }
- collections.samlIdPServiceProviders = &genericCollection[types.SAMLIdPServiceProvider, samlIdPServiceProviderGetter, samlIdPServiceProvidersExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.samlIdPServiceProviders
- case types.KindUserGroup:
- if c.UserGroups == nil {
- return nil, trace.BadParameter("missing parameter UserGroups")
- }
- collections.userGroups = &genericCollection[types.UserGroup, userGroupGetter, userGroupsExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.userGroups
- case types.KindOktaImportRule:
- if c.Okta == nil {
- return nil, trace.BadParameter("missing parameter Okta")
- }
- collections.oktaImportRules = &genericCollection[types.OktaImportRule, oktaImportRuleGetter, oktaImportRulesExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.oktaImportRules
- case types.KindOktaAssignment:
- if c.Okta == nil {
- return nil, trace.BadParameter("missing parameter Okta")
- }
- collections.oktaAssignments = &genericCollection[types.OktaAssignment, oktaAssignmentGetter, oktaAssignmentsExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.oktaAssignments
- case types.KindIntegration:
- if c.Integrations == nil {
- return nil, trace.BadParameter("missing parameter Integrations")
- }
- collections.integrations = &genericCollection[types.Integration, services.IntegrationsGetter, integrationsExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.integrations
- case types.KindUserTask:
- if c.UserTasks == nil {
- return nil, trace.BadParameter("missing parameter user tasks")
- }
- collections.userTasks = &genericCollection[*usertasksv1.UserTask, userTasksGetter, userTasksExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.userTasks
- case types.KindDiscoveryConfig:
- if c.DiscoveryConfigs == nil {
- return nil, trace.BadParameter("missing parameter DiscoveryConfigs")
- }
- collections.discoveryConfigs = &genericCollection[*discoveryconfig.DiscoveryConfig, services.DiscoveryConfigsGetter, discoveryConfigExecutor]{cache: c, watch: watch}
- collections.byKind[resourceKind] = collections.discoveryConfigs
- case types.KindHeadlessAuthentication:
- // For headless authentications, we need only process events. We don't need to keep the cache up to date.
- collections.byKind[resourceKind] = &genericCollection[*types.HeadlessAuthentication, noReader, noopExecutor]{cache: c, watch: watch}
- case types.KindAuditQuery:
- if c.SecReports == nil {
- return nil, trace.BadParameter("missing parameter SecReports")
- }
- collections.auditQueries = &genericCollection[*secreports.AuditQuery, services.SecurityAuditQueryGetter, auditQueryExecutor]{cache: c, watch: watch}
- collections.byKind[resourceKind] = collections.auditQueries
- case types.KindSecurityReport:
- if c.SecReports == nil {
- return nil, trace.BadParameter("missing parameter KindSecurityReport")
- }
- collections.secReports = &genericCollection[*secreports.Report, services.SecurityReportGetter, secReportExecutor]{cache: c, watch: watch}
- collections.byKind[resourceKind] = collections.secReports
- case types.KindSecurityReportState:
- if c.SecReports == nil {
- return nil, trace.BadParameter("missing parameter KindSecurityReport")
- }
- collections.secReportsStates = &genericCollection[*secreports.ReportState, services.SecurityReportStateGetter, secReportStateExecutor]{cache: c, watch: watch}
- collections.byKind[resourceKind] = collections.secReportsStates
- case types.KindUserLoginState:
- if c.UserLoginStates == nil {
- return nil, trace.BadParameter("missing parameter UserLoginStates")
- }
- collections.userLoginStates = &genericCollection[*userloginstate.UserLoginState, services.UserLoginStatesGetter, userLoginStateExecutor]{cache: c, watch: watch}
- collections.byKind[resourceKind] = collections.userLoginStates
- case types.KindAccessList:
- if c.AccessLists == nil {
- return nil, trace.BadParameter("missing parameter AccessLists")
- }
- collections.accessLists = &genericCollection[*accesslist.AccessList, accessListsGetter, accessListExecutor]{cache: c, watch: watch}
- collections.byKind[resourceKind] = collections.accessLists
- case types.KindAccessListMember:
- if c.AccessLists == nil {
- return nil, trace.BadParameter("missing parameter AccessLists")
- }
- collections.accessListMembers = &genericCollection[*accesslist.AccessListMember, accessListMembersGetter, accessListMemberExecutor]{cache: c, watch: watch}
- collections.byKind[resourceKind] = collections.accessListMembers
- case types.KindAccessListReview:
- if c.AccessLists == nil {
- return nil, trace.BadParameter("missing parameter AccessLists")
- }
- collections.accessListReviews = &genericCollection[*accesslist.Review, accessListReviewsGetter, accessListReviewExecutor]{cache: c, watch: watch}
- collections.byKind[resourceKind] = collections.accessListReviews
- case types.KindKubeWaitingContainer:
- if c.KubeWaitingContainers == nil {
- return nil, trace.BadParameter("missing parameter KubeWaitingContainers")
- }
- collections.kubeWaitingContainers = &genericCollection[*kubewaitingcontainerpb.KubernetesWaitingContainer, kubernetesWaitingContainerGetter, kubeWaitingContainerExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.kubeWaitingContainers
- case types.KindStaticHostUser:
- if c.StaticHostUsers == nil {
- return nil, trace.BadParameter("missing parameter StaticHostUsers")
- }
- collections.staticHostUsers = &genericCollection[*userprovisioningpb.StaticHostUser, staticHostUserGetter, staticHostUserExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.staticHostUsers
- case types.KindNotification:
- if c.Notifications == nil {
- return nil, trace.BadParameter("missing parameter Notifications")
- }
- collections.userNotifications = &genericCollection[*notificationsv1.Notification, notificationGetter, userNotificationExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.userNotifications
- case types.KindGlobalNotification:
- if c.Notifications == nil {
- return nil, trace.BadParameter("missing parameter Notifications")
- }
- collections.globalNotifications = &genericCollection[*notificationsv1.GlobalNotification, notificationGetter, globalNotificationExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.globalNotifications
- case types.KindAccessMonitoringRule:
- if c.AccessMonitoringRules == nil {
- return nil, trace.BadParameter("missing parameter AccessMonitoringRule")
- }
- collections.accessMonitoringRules = &genericCollection[*accessmonitoringrulesv1.AccessMonitoringRule, accessMonitoringRuleGetter, accessMonitoringRulesExecutor]{cache: c, watch: watch}
- collections.byKind[resourceKind] = collections.accessMonitoringRules
- case types.KindAccessGraphSettings:
- if c.ClusterConfig == nil {
- return nil, trace.BadParameter("missing parameter ClusterConfig")
- }
- collections.accessGraphSettings = &genericCollection[*clusterconfigpb.AccessGraphSettings, accessGraphSettingsGetter, accessGraphSettingsExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.accessGraphSettings
- case types.KindSPIFFEFederation:
- if c.Config.SPIFFEFederations == nil {
- return nil, trace.BadParameter("missing parameter SPIFFEFederations")
- }
- collections.spiffeFederations = &genericCollection[*machineidv1.SPIFFEFederation, SPIFFEFederationReader, spiffeFederationExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.spiffeFederations
- case types.KindWorkloadIdentity:
- if c.Config.WorkloadIdentity == nil {
- return nil, trace.BadParameter("missing parameter WorkloadIdentity")
- }
- collections.workloadIdentity = &genericCollection[*workloadidentityv1pb.WorkloadIdentity, WorkloadIdentityReader, workloadIdentityExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.workloadIdentity
- case types.KindAutoUpdateConfig:
- if c.AutoUpdateService == nil {
- return nil, trace.BadParameter("missing parameter AutoUpdateService")
- }
- collections.autoUpdateConfigs = &genericCollection[*autoupdate.AutoUpdateConfig, autoUpdateConfigGetter, autoUpdateConfigExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.autoUpdateConfigs
- case types.KindAutoUpdateVersion:
- if c.AutoUpdateService == nil {
- return nil, trace.BadParameter("missing parameter AutoUpdateService")
- }
- collections.autoUpdateVersions = &genericCollection[*autoupdate.AutoUpdateVersion, autoUpdateVersionGetter, autoUpdateVersionExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.autoUpdateVersions
- case types.KindAutoUpdateAgentRollout:
- if c.AutoUpdateService == nil {
- return nil, trace.BadParameter("missing parameter AutoUpdateService")
- }
- collections.autoUpdateAgentRollouts = &genericCollection[*autoupdate.AutoUpdateAgentRollout, autoUpdateAgentRolloutGetter, autoUpdateAgentRolloutExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.autoUpdateAgentRollouts
-
- case types.KindProvisioningPrincipalState:
- if c.ProvisioningStates == nil {
- return nil, trace.BadParameter("missing parameter KindProvisioningState")
- }
- collections.provisioningStates = &genericCollection[*provisioningv1.PrincipalState, provisioningStateGetter, provisioningStateExecutor]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.provisioningStates
-
- case types.KindIdentityCenterAccount:
- if c.IdentityCenter == nil {
- return nil, trace.BadParameter("missing upstream IdentityCenter collection")
- }
- collections.identityCenterAccounts = &genericCollection[
- services.IdentityCenterAccount,
- identityCenterAccountGetter,
- identityCenterAccountExecutor,
- ]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.identityCenterAccounts
-
- case types.KindIdentityCenterPrincipalAssignment:
- if c.IdentityCenter == nil {
- return nil, trace.BadParameter("missing parameter IdentityCenter")
- }
- collections.identityCenterPrincipalAssignments = &genericCollection[
- *identitycenterv1.PrincipalAssignment,
- identityCenterPrincipalAssignmentGetter,
- identityCenterPrincipalAssignmentExecutor,
- ]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.identityCenterPrincipalAssignments
-
- case types.KindIdentityCenterAccountAssignment:
- if c.IdentityCenter == nil {
- return nil, trace.BadParameter("missing parameter IdentityCenter")
- }
- collections.identityCenterAccountAssignments = &genericCollection[
- services.IdentityCenterAccountAssignment,
- identityCenterAccountAssignmentGetter,
- identityCenterAccountAssignmentExecutor,
- ]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.identityCenterAccountAssignments
-
- case types.KindPluginStaticCredentials:
- if c.PluginStaticCredentials == nil {
- return nil, trace.BadParameter("missing parameter PluginStaticCredentials")
- }
- collections.pluginStaticCredentials = &genericCollection[
- types.PluginStaticCredentials,
- pluginStaticCredentialsGetter,
- pluginStaticCredentialsExecutor,
- ]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.pluginStaticCredentials
-
- case types.KindGitServer:
- if c.GitServers == nil {
- return nil, trace.BadParameter("missing parameter GitServers")
- }
- collections.gitServers = &genericCollection[
- types.Server,
- services.GitServerGetter,
- gitServerExecutor,
- ]{
- cache: c,
- watch: watch,
- }
- collections.byKind[resourceKind] = collections.gitServers
- default:
- return nil, trace.BadParameter("resource %q is not supported", watch.Kind)
- }
- }
- return collections, nil
-}
-
-func resourceKindFromWatchKind(wk types.WatchKind) resourceKind {
- switch wk.Kind {
- case types.KindWebSession:
- // Web sessions use subkind to differentiate between
- // the types of sessions
- return resourceKind{
- kind: wk.Kind,
- subkind: wk.SubKind,
- }
- }
- return resourceKind{
- kind: wk.Kind,
- }
-}
-
-func resourceKindFromResource(res types.Resource) resourceKind {
- switch res.GetKind() {
- case types.KindWebSession:
- // Web sessions use subkind to differentiate between
- // the types of sessions
- return resourceKind{
- kind: res.GetKind(),
- subkind: res.GetSubKind(),
- }
- }
- return resourceKind{
- kind: res.GetKind(),
- }
-}
-
-type resourceKind struct {
- kind string
- subkind string
-}
-
-func (r resourceKind) String() string {
- if r.subkind == "" {
- return r.kind
- }
- return fmt.Sprintf("%s/%s", r.kind, r.subkind)
-}
-
-type accessRequestExecutor struct{}
-
-func (accessRequestExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.AccessRequest, error) {
- return cache.DynamicAccess.GetAccessRequests(ctx, types.AccessRequestFilter{})
-}
-
-func (accessRequestExecutor) upsert(ctx context.Context, cache *Cache, resource types.AccessRequest) error {
- return cache.dynamicAccessCache.UpsertAccessRequest(ctx, resource)
-}
-
-func (accessRequestExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.dynamicAccessCache.DeleteAllAccessRequests(ctx)
-}
-
-func (accessRequestExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.dynamicAccessCache.DeleteAccessRequest(ctx, resource.GetName())
-}
-
-func (accessRequestExecutor) isSingleton() bool { return false }
-
-func (accessRequestExecutor) getReader(_ *Cache, _ bool) noReader {
- return noReader{}
-}
-
-var _ executor[types.AccessRequest, noReader] = accessRequestExecutor{}
-
-type tunnelConnectionExecutor struct{}
-
-func (tunnelConnectionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.TunnelConnection, error) {
- return cache.Trust.GetAllTunnelConnections()
-}
-
-func (tunnelConnectionExecutor) upsert(ctx context.Context, cache *Cache, resource types.TunnelConnection) error {
- return cache.trustCache.UpsertTunnelConnection(resource)
-}
-
-func (tunnelConnectionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.trustCache.DeleteAllTunnelConnections()
-}
-
-func (tunnelConnectionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.trustCache.DeleteTunnelConnection(resource.GetSubKind(), resource.GetName())
-}
-
-func (tunnelConnectionExecutor) isSingleton() bool { return false }
-
-func (tunnelConnectionExecutor) getReader(cache *Cache, cacheOK bool) tunnelConnectionGetter {
- if cacheOK {
- return cache.trustCache
- }
- return cache.Config.Trust
-}
-
-type tunnelConnectionGetter interface {
- GetAllTunnelConnections(opts ...services.MarshalOption) (conns []types.TunnelConnection, err error)
- GetTunnelConnections(clusterName string, opts ...services.MarshalOption) ([]types.TunnelConnection, error)
-}
-
-var _ executor[types.TunnelConnection, tunnelConnectionGetter] = tunnelConnectionExecutor{}
-
-type remoteClusterExecutor struct{}
-
-func (remoteClusterExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.RemoteCluster, error) {
- return cache.Trust.GetRemoteClusters(ctx)
-}
-
-func (remoteClusterExecutor) upsert(ctx context.Context, cache *Cache, resource types.RemoteCluster) error {
- err := cache.trustCache.DeleteRemoteCluster(ctx, resource.GetName())
- if err != nil {
- if !trace.IsNotFound(err) {
- cache.Logger.WithError(err).Warnf("Failed to delete remote cluster %v.", resource.GetName())
- return trace.Wrap(err)
- }
- }
- _, err = cache.trustCache.CreateRemoteCluster(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (remoteClusterExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.trustCache.DeleteAllRemoteClusters(ctx)
-}
-
-func (remoteClusterExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.trustCache.DeleteRemoteCluster(ctx, resource.GetName())
-}
-
-func (remoteClusterExecutor) isSingleton() bool { return false }
-
-func (remoteClusterExecutor) getReader(cache *Cache, cacheOK bool) remoteClusterGetter {
- if cacheOK {
- return cache.trustCache
- }
- return cache.Config.Trust
-}
-
-type remoteClusterGetter interface {
- GetRemoteClusters(ctx context.Context) ([]types.RemoteCluster, error)
- GetRemoteCluster(ctx context.Context, clusterName string) (types.RemoteCluster, error)
- ListRemoteClusters(ctx context.Context, pageSize int, pageToken string) ([]types.RemoteCluster, string, error)
-}
-
-var _ executor[types.RemoteCluster, remoteClusterGetter] = remoteClusterExecutor{}
-
-type proxyExecutor struct{}
-
-func (proxyExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Server, error) {
- return cache.Presence.GetProxies()
-}
-
-func (proxyExecutor) upsert(ctx context.Context, cache *Cache, resource types.Server) error {
- return cache.presenceCache.UpsertProxy(ctx, resource)
-}
-
-func (proxyExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.presenceCache.DeleteAllProxies()
-}
-
-func (proxyExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.presenceCache.DeleteProxy(ctx, resource.GetName())
-}
-
-func (proxyExecutor) isSingleton() bool { return false }
-
-func (proxyExecutor) getReader(cache *Cache, cacheOK bool) services.ProxyGetter {
- if cacheOK {
- return cache.presenceCache
- }
- return cache.Config.Presence
-}
-
-var _ executor[types.Server, services.ProxyGetter] = proxyExecutor{}
-
-type authServerExecutor struct{}
-
-func (authServerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Server, error) {
- return cache.Presence.GetAuthServers()
-}
-
-func (authServerExecutor) upsert(ctx context.Context, cache *Cache, resource types.Server) error {
- return cache.presenceCache.UpsertAuthServer(ctx, resource)
-}
-
-func (authServerExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.presenceCache.DeleteAllAuthServers()
-}
-
-func (authServerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.presenceCache.DeleteAuthServer(resource.GetName())
-}
-
-func (authServerExecutor) isSingleton() bool { return false }
-
-func (authServerExecutor) getReader(cache *Cache, cacheOK bool) authServerGetter {
- if cacheOK {
- return cache.presenceCache
- }
- return cache.Config.Presence
-}
-
-type authServerGetter interface {
- GetAuthServers() ([]types.Server, error)
-}
-
-var _ executor[types.Server, authServerGetter] = authServerExecutor{}
-
-type nodeExecutor struct{}
-
-func (nodeExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Server, error) {
- return cache.Presence.GetNodes(ctx, apidefaults.Namespace)
-}
-
-func (nodeExecutor) upsert(ctx context.Context, cache *Cache, resource types.Server) error {
- _, err := cache.presenceCache.UpsertNode(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (nodeExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.presenceCache.DeleteAllNodes(ctx, apidefaults.Namespace)
-}
-
-func (nodeExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.presenceCache.DeleteNode(ctx, resource.GetMetadata().Namespace, resource.GetName())
-}
-
-func (nodeExecutor) isSingleton() bool { return false }
-
-func (nodeExecutor) getReader(cache *Cache, cacheOK bool) nodeGetter {
- if cacheOK {
- return cache.presenceCache
- }
- return cache.Config.Presence
-}
-
-type nodeGetter interface {
- GetNodes(ctx context.Context, namespace string) ([]types.Server, error)
- GetNode(ctx context.Context, namespace, name string) (types.Server, error)
-}
-
-var _ executor[types.Server, nodeGetter] = nodeExecutor{}
-
-type namespaceExecutor struct{}
-
-func (namespaceExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*types.Namespace, error) {
- namespaces, err := cache.Presence.GetNamespaces()
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- derefNamespaces := make([]*types.Namespace, len(namespaces))
- for i := range namespaces {
- ns := namespaces[i]
- derefNamespaces[i] = &ns
- }
- return derefNamespaces, nil
-}
-
-func (namespaceExecutor) upsert(ctx context.Context, cache *Cache, resource *types.Namespace) error {
- return cache.presenceCache.UpsertNamespace(*resource)
-}
-
-func (namespaceExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.presenceCache.DeleteAllNamespaces()
-}
-
-func (namespaceExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.presenceCache.DeleteNamespace(resource.GetName())
-}
-
-func (namespaceExecutor) isSingleton() bool { return false }
-
-func (namespaceExecutor) getReader(cache *Cache, cacheOK bool) namespaceGetter {
- if cacheOK {
- return cache.presenceCache
- }
- return cache.Config.Presence
-}
-
-type namespaceGetter interface {
- GetNamespaces() ([]types.Namespace, error)
- GetNamespace(name string) (*types.Namespace, error)
-}
-
-var _ executor[*types.Namespace, namespaceGetter] = namespaceExecutor{}
-
-type certAuthorityExecutor struct {
- // extracted from watch.Filter, to avoid rebuilding on every event
- filter types.CertAuthorityFilter
-}
-
-// delete implements executor[types.CertAuthority]
-func (certAuthorityExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- err := cache.trustCache.DeleteCertAuthority(ctx, types.CertAuthID{
- Type: types.CertAuthType(resource.GetSubKind()),
- DomainName: resource.GetName(),
- })
- return trace.Wrap(err)
-}
-
-// deleteAll implements executor[types.CertAuthority]
-func (certAuthorityExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- for _, caType := range types.CertAuthTypes {
- if err := cache.trustCache.DeleteAllCertAuthorities(caType); err != nil {
- return trace.Wrap(err)
- }
- }
- return nil
-}
-
-// getAll implements executor[types.CertAuthority]
-func (e certAuthorityExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.CertAuthority, error) {
- var authorities []types.CertAuthority
- for _, caType := range types.CertAuthTypes {
- cas, err := cache.Trust.GetCertAuthorities(ctx, caType, loadSecrets)
- // if caType was added in this major version we might get a BadParameter
- // error if we're connecting to an older upstream that doesn't know about it
- if err != nil {
- if !types.IsUnsupportedAuthorityErr(err) || !caType.NewlyAdded() {
- return nil, trace.Wrap(err)
- }
- continue
- }
-
- // this can be removed once we get the ability to fetch CAs with a filter,
- // but it should be harmless, and it could be kept as additional safety
- if !e.filter.IsEmpty() {
- filtered := cas[:0]
- for _, ca := range cas {
- if e.filter.Match(ca) {
- filtered = append(filtered, ca)
- }
- }
- cas = filtered
- }
-
- authorities = append(authorities, cas...)
- }
-
- return authorities, nil
-}
-
-// upsert implements executor[types.CertAuthority]
-func (e certAuthorityExecutor) upsert(ctx context.Context, cache *Cache, value types.CertAuthority) error {
- if !e.filter.Match(value) {
- return nil
- }
-
- return cache.trustCache.UpsertCertAuthority(ctx, value)
-}
-
-func (certAuthorityExecutor) isSingleton() bool { return false }
-
-func (certAuthorityExecutor) getReader(cache *Cache, cacheOK bool) services.AuthorityGetter {
- if cacheOK {
- return cache.trustCache
- }
- return cache.Config.Trust
-}
-
-var _ executor[types.CertAuthority, services.AuthorityGetter] = certAuthorityExecutor{}
-
-type staticTokensExecutor struct{}
-
-func (staticTokensExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.StaticTokens, error) {
- token, err := cache.ClusterConfig.GetStaticTokens()
- if err != nil {
- return nil, trace.Wrap(err)
- }
- return []types.StaticTokens{token}, nil
-}
-
-func (staticTokensExecutor) upsert(ctx context.Context, cache *Cache, resource types.StaticTokens) error {
- return cache.clusterConfigCache.SetStaticTokens(resource)
-}
-
-func (staticTokensExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.clusterConfigCache.DeleteStaticTokens()
-}
-
-func (staticTokensExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.clusterConfigCache.DeleteStaticTokens()
-}
-
-func (staticTokensExecutor) isSingleton() bool { return true }
-
-func (staticTokensExecutor) getReader(cache *Cache, cacheOK bool) staticTokensGetter {
- if cacheOK {
- return cache.clusterConfigCache
- }
- return cache.Config.ClusterConfig
-}
-
-type staticTokensGetter interface {
- GetStaticTokens() (types.StaticTokens, error)
-}
-
-var _ executor[types.StaticTokens, staticTokensGetter] = staticTokensExecutor{}
-
-type provisionTokenExecutor struct{}
-
-func (provisionTokenExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.ProvisionToken, error) {
- return cache.Provisioner.GetTokens(ctx)
-}
-
-func (provisionTokenExecutor) upsert(ctx context.Context, cache *Cache, resource types.ProvisionToken) error {
- return cache.provisionerCache.UpsertToken(ctx, resource)
-}
-
-func (provisionTokenExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.provisionerCache.DeleteAllTokens()
-}
-
-func (provisionTokenExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.provisionerCache.DeleteToken(ctx, resource.GetName())
-}
-
-func (provisionTokenExecutor) isSingleton() bool { return false }
-
-func (provisionTokenExecutor) getReader(cache *Cache, cacheOK bool) tokenGetter {
- if cacheOK {
- return cache.provisionerCache
- }
- return cache.Config.Provisioner
-}
-
-type tokenGetter interface {
- GetTokens(ctx context.Context) ([]types.ProvisionToken, error)
- GetToken(ctx context.Context, token string) (types.ProvisionToken, error)
-}
-
-var _ executor[types.ProvisionToken, tokenGetter] = provisionTokenExecutor{}
-
-type clusterNameExecutor struct{}
-
-func (clusterNameExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.ClusterName, error) {
- name, err := cache.ClusterConfig.GetClusterName()
- return []types.ClusterName{name}, trace.Wrap(err)
-}
-
-func (clusterNameExecutor) upsert(ctx context.Context, cache *Cache, resource types.ClusterName) error {
- return cache.clusterConfigCache.UpsertClusterName(resource)
-}
-
-func (clusterNameExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.clusterConfigCache.DeleteClusterName()
-}
-
-func (clusterNameExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.clusterConfigCache.DeleteClusterName()
-}
-
-func (clusterNameExecutor) isSingleton() bool { return true }
-
-func (clusterNameExecutor) getReader(cache *Cache, cacheOK bool) clusterNameGetter {
- if cacheOK {
- return cache.clusterConfigCache
- }
- return cache.Config.ClusterConfig
-}
-
-type clusterNameGetter interface {
- GetClusterName(opts ...services.MarshalOption) (types.ClusterName, error)
-}
-
-var _ executor[types.ClusterName, clusterNameGetter] = clusterNameExecutor{}
-
-type autoUpdateConfigExecutor struct{}
-
-func (autoUpdateConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*autoupdate.AutoUpdateConfig, error) {
- config, err := cache.AutoUpdateService.GetAutoUpdateConfig(ctx)
- return []*autoupdate.AutoUpdateConfig{config}, trace.Wrap(err)
-}
-
-func (autoUpdateConfigExecutor) upsert(ctx context.Context, cache *Cache, resource *autoupdate.AutoUpdateConfig) error {
- _, err := cache.autoUpdateCache.UpsertAutoUpdateConfig(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (autoUpdateConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.autoUpdateCache.DeleteAutoUpdateConfig(ctx)
-}
-
-func (autoUpdateConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.autoUpdateCache.DeleteAutoUpdateConfig(ctx)
-}
-
-func (autoUpdateConfigExecutor) isSingleton() bool { return true }
-
-func (autoUpdateConfigExecutor) getReader(cache *Cache, cacheOK bool) autoUpdateConfigGetter {
- if cacheOK {
- return cache.autoUpdateCache
- }
- return cache.Config.AutoUpdateService
-}
-
-type autoUpdateConfigGetter interface {
- GetAutoUpdateConfig(ctx context.Context) (*autoupdate.AutoUpdateConfig, error)
-}
-
-var _ executor[*autoupdate.AutoUpdateConfig, autoUpdateConfigGetter] = autoUpdateConfigExecutor{}
-
-type autoUpdateVersionExecutor struct{}
-
-func (autoUpdateVersionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*autoupdate.AutoUpdateVersion, error) {
- version, err := cache.AutoUpdateService.GetAutoUpdateVersion(ctx)
- return []*autoupdate.AutoUpdateVersion{version}, trace.Wrap(err)
-}
-
-func (autoUpdateVersionExecutor) upsert(ctx context.Context, cache *Cache, resource *autoupdate.AutoUpdateVersion) error {
- _, err := cache.autoUpdateCache.UpsertAutoUpdateVersion(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (autoUpdateVersionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.autoUpdateCache.DeleteAutoUpdateVersion(ctx)
-}
-
-func (autoUpdateVersionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.autoUpdateCache.DeleteAutoUpdateVersion(ctx)
-}
-
-func (autoUpdateVersionExecutor) isSingleton() bool { return true }
-
-func (autoUpdateVersionExecutor) getReader(cache *Cache, cacheOK bool) autoUpdateVersionGetter {
- if cacheOK {
- return cache.autoUpdateCache
- }
- return cache.Config.AutoUpdateService
-}
-
-type autoUpdateVersionGetter interface {
- GetAutoUpdateVersion(ctx context.Context) (*autoupdate.AutoUpdateVersion, error)
-}
-
-var _ executor[*autoupdate.AutoUpdateVersion, autoUpdateVersionGetter] = autoUpdateVersionExecutor{}
-
-type autoUpdateAgentRolloutExecutor struct{}
-
-func (autoUpdateAgentRolloutExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*autoupdate.AutoUpdateAgentRollout, error) {
- plan, err := cache.AutoUpdateService.GetAutoUpdateAgentRollout(ctx)
- return []*autoupdate.AutoUpdateAgentRollout{plan}, trace.Wrap(err)
-}
-
-func (autoUpdateAgentRolloutExecutor) upsert(ctx context.Context, cache *Cache, resource *autoupdate.AutoUpdateAgentRollout) error {
- _, err := cache.autoUpdateCache.UpsertAutoUpdateAgentRollout(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (autoUpdateAgentRolloutExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.autoUpdateCache.DeleteAutoUpdateAgentRollout(ctx)
-}
-
-func (autoUpdateAgentRolloutExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.autoUpdateCache.DeleteAutoUpdateAgentRollout(ctx)
-}
-
-func (autoUpdateAgentRolloutExecutor) isSingleton() bool { return true }
-
-func (autoUpdateAgentRolloutExecutor) getReader(cache *Cache, cacheOK bool) autoUpdateAgentRolloutGetter {
- if cacheOK {
- return cache.autoUpdateCache
- }
- return cache.Config.AutoUpdateService
-}
-
-type autoUpdateAgentRolloutGetter interface {
- GetAutoUpdateAgentRollout(ctx context.Context) (*autoupdate.AutoUpdateAgentRollout, error)
-}
-
-var _ executor[*autoupdate.AutoUpdateAgentRollout, autoUpdateAgentRolloutGetter] = autoUpdateAgentRolloutExecutor{}
-
-type userExecutor struct{}
-
-func (userExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.User, error) {
- return cache.Users.GetUsers(ctx, loadSecrets)
-}
-
-func (userExecutor) upsert(ctx context.Context, cache *Cache, resource types.User) error {
- _, err := cache.usersCache.UpsertUser(ctx, resource)
- return err
-}
-
-func (userExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.usersCache.DeleteAllUsers(ctx)
-}
-
-func (userExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.usersCache.DeleteUser(ctx, resource.GetName())
-}
-
-func (userExecutor) isSingleton() bool { return false }
-
-func (userExecutor) getReader(cache *Cache, cacheOK bool) userGetter {
- if cacheOK {
- return cache.usersCache
- }
- return cache.Config.Users
-}
-
-type userGetter interface {
- GetUser(ctx context.Context, user string, withSecrets bool) (types.User, error)
- GetUsers(ctx context.Context, withSecrets bool) ([]types.User, error)
- ListUsers(ctx context.Context, req *userspb.ListUsersRequest) (*userspb.ListUsersResponse, error)
-}
-
-var _ executor[types.User, userGetter] = userExecutor{}
-
-type roleExecutor struct{}
-
-func (roleExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Role, error) {
- return cache.Access.GetRoles(ctx)
-}
-
-func (roleExecutor) upsert(ctx context.Context, cache *Cache, resource types.Role) error {
- _, err := cache.accessCache.UpsertRole(ctx, resource)
- return err
-}
-
-func (roleExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.accessCache.DeleteAllRoles(ctx)
-}
-
-func (roleExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.accessCache.DeleteRole(ctx, resource.GetName())
-}
-
-func (roleExecutor) isSingleton() bool { return false }
-
-func (roleExecutor) getReader(cache *Cache, cacheOK bool) roleGetter {
- if cacheOK {
- return cache.accessCache
- }
- return cache.Config.Access
-}
-
-type roleGetter interface {
- GetRoles(ctx context.Context) ([]types.Role, error)
- GetRole(ctx context.Context, name string) (types.Role, error)
- ListRoles(ctx context.Context, req *proto.ListRolesRequest) (*proto.ListRolesResponse, error)
-}
-
-var _ executor[types.Role, roleGetter] = roleExecutor{}
-
-type databaseServerExecutor struct{}
-
-func (databaseServerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.DatabaseServer, error) {
- return cache.Presence.GetDatabaseServers(ctx, apidefaults.Namespace)
-}
-
-func (databaseServerExecutor) upsert(ctx context.Context, cache *Cache, resource types.DatabaseServer) error {
- _, err := cache.presenceCache.UpsertDatabaseServer(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (databaseServerExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.presenceCache.DeleteAllDatabaseServers(ctx, apidefaults.Namespace)
-}
-
-func (databaseServerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.presenceCache.DeleteDatabaseServer(ctx,
- resource.GetMetadata().Namespace,
- resource.GetMetadata().Description, // Cache passes host ID via description field.
- resource.GetName())
-}
-
-func (databaseServerExecutor) isSingleton() bool { return false }
-
-func (databaseServerExecutor) getReader(cache *Cache, cacheOK bool) databaseServerGetter {
- if cacheOK {
- return cache.presenceCache
- }
- return cache.Config.Presence
-}
-
-type databaseServerGetter interface {
- GetDatabaseServers(context.Context, string, ...services.MarshalOption) ([]types.DatabaseServer, error)
-}
-
-var _ executor[types.DatabaseServer, databaseServerGetter] = databaseServerExecutor{}
-
-type databaseServiceExecutor struct{}
-
-func (databaseServiceExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.DatabaseService, error) {
- resources, err := client.GetResourcesWithFilters(ctx, cache.Presence, proto.ListResourcesRequest{ResourceType: types.KindDatabaseService})
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- dbsvcs := make([]types.DatabaseService, len(resources))
- for i, resource := range resources {
- dbsvc, ok := resource.(types.DatabaseService)
- if !ok {
- return nil, trace.BadParameter("unexpected resource %T", resource)
- }
- dbsvcs[i] = dbsvc
- }
-
- return dbsvcs, nil
-}
-
-func (databaseServiceExecutor) upsert(ctx context.Context, cache *Cache, resource types.DatabaseService) error {
- _, err := cache.databaseServicesCache.UpsertDatabaseService(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (databaseServiceExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.databaseServicesCache.DeleteAllDatabaseServices(ctx)
-}
-
-func (databaseServiceExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.databaseServicesCache.DeleteDatabaseService(ctx, resource.GetName())
-}
-
-func (databaseServiceExecutor) isSingleton() bool { return false }
-
-func (databaseServiceExecutor) getReader(_ *Cache, _ bool) noReader {
- return noReader{}
-}
-
-var _ executor[types.DatabaseService, noReader] = databaseServiceExecutor{}
-
-type databaseExecutor struct{}
-
-func (databaseExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Database, error) {
- return cache.Databases.GetDatabases(ctx)
-}
-
-func (databaseExecutor) upsert(ctx context.Context, cache *Cache, resource types.Database) error {
- if err := cache.databasesCache.CreateDatabase(ctx, resource); err != nil {
- if !trace.IsAlreadyExists(err) {
- return trace.Wrap(err)
- }
- return trace.Wrap(cache.databasesCache.UpdateDatabase(ctx, resource))
- }
-
- return nil
-}
-
-func (databaseExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.databasesCache.DeleteAllDatabases(ctx)
-}
-
-func (databaseExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.databasesCache.DeleteDatabase(ctx, resource.GetName())
-}
-
-func (databaseExecutor) isSingleton() bool { return false }
-
-func (databaseExecutor) getReader(cache *Cache, cacheOK bool) services.DatabaseGetter {
- if cacheOK {
- return cache.databasesCache
- }
- return cache.Config.Databases
-}
-
-var _ executor[types.Database, services.DatabaseGetter] = databaseExecutor{}
-
-type databaseObjectExecutor struct{}
-
-func (databaseObjectExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*dbobjectv1.DatabaseObject, error) {
- var out []*dbobjectv1.DatabaseObject
- var nextToken string
- for {
- var page []*dbobjectv1.DatabaseObject
- var err error
-
- page, nextToken, err = cache.DatabaseObjects.ListDatabaseObjects(ctx, 0, nextToken)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- out = append(out, page...)
- if nextToken == "" {
- break
- }
- }
- return out, nil
-}
-
-func (databaseObjectExecutor) upsert(ctx context.Context, cache *Cache, resource *dbobjectv1.DatabaseObject) error {
- _, err := cache.databaseObjectsCache.UpsertDatabaseObject(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (databaseObjectExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return trace.Wrap(cache.databaseObjectsCache.DeleteAllDatabaseObjects(ctx))
-}
-
-func (databaseObjectExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return trace.Wrap(cache.databaseObjectsCache.DeleteDatabaseObject(ctx, resource.GetName()))
-}
-
-func (databaseObjectExecutor) isSingleton() bool { return false }
-
-func (databaseObjectExecutor) getReader(cache *Cache, cacheOK bool) services.DatabaseObjectsGetter {
- if cacheOK {
- return cache.databaseObjectsCache
- }
- return cache.Config.DatabaseObjects
-}
-
-var _ executor[*dbobjectv1.DatabaseObject, services.DatabaseObjectsGetter] = databaseObjectExecutor{}
-
-type appExecutor struct{}
-
-func (appExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Application, error) {
- return cache.Apps.GetApps(ctx)
-}
-
-func (appExecutor) upsert(ctx context.Context, cache *Cache, resource types.Application) error {
- if err := cache.appsCache.CreateApp(ctx, resource); err != nil {
- if !trace.IsAlreadyExists(err) {
- return trace.Wrap(err)
- }
- return trace.Wrap(cache.appsCache.UpdateApp(ctx, resource))
- }
-
- return nil
-}
-
-func (appExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.appsCache.DeleteAllApps(ctx)
-}
-
-func (appExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.appsCache.DeleteApp(ctx, resource.GetName())
-}
-
-func (appExecutor) getReader(cache *Cache, cacheOK bool) services.AppGetter {
- if cacheOK {
- return cache.appsCache
- }
- return cache.Apps
-}
-
-func (appExecutor) isSingleton() bool { return false }
-
-var _ executor[types.Application, services.AppGetter] = appExecutor{}
-
-type appServerExecutor struct{}
-
-func (appServerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.AppServer, error) {
- return cache.Presence.GetApplicationServers(ctx, apidefaults.Namespace)
-}
-
-func (appServerExecutor) upsert(ctx context.Context, cache *Cache, resource types.AppServer) error {
- _, err := cache.presenceCache.UpsertApplicationServer(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (appServerExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.presenceCache.DeleteAllApplicationServers(ctx, apidefaults.Namespace)
-}
-
-func (appServerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.presenceCache.DeleteApplicationServer(ctx,
- resource.GetMetadata().Namespace,
- resource.GetMetadata().Description, // Cache passes host ID via description field.
- resource.GetName())
-}
-
-func (appServerExecutor) isSingleton() bool { return false }
-
-func (appServerExecutor) getReader(cache *Cache, cacheOK bool) appServerGetter {
- if cacheOK {
- return cache.presenceCache
- }
- return cache.Config.Presence
-}
-
-type appServerGetter interface {
- GetApplicationServers(context.Context, string) ([]types.AppServer, error)
-}
-
-var _ executor[types.AppServer, appServerGetter] = appServerExecutor{}
-
-type appSessionExecutor struct{}
-
-func (appSessionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WebSession, error) {
- var (
- startKey string
- sessions []types.WebSession
- )
- for {
- webSessions, nextKey, err := cache.AppSession.ListAppSessions(ctx, 0, startKey, "")
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- if !loadSecrets {
- for i := 0; i < len(webSessions); i++ {
- webSessions[i] = webSessions[i].WithoutSecrets()
- }
- }
-
- sessions = append(sessions, webSessions...)
-
- if nextKey == "" {
- break
- }
- startKey = nextKey
- }
- return sessions, nil
-}
-
-func (appSessionExecutor) upsert(ctx context.Context, cache *Cache, resource types.WebSession) error {
- return cache.appSessionCache.UpsertAppSession(ctx, resource)
-}
-
-func (appSessionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.appSessionCache.DeleteAllAppSessions(ctx)
-}
-
-func (appSessionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.appSessionCache.DeleteAppSession(ctx, types.DeleteAppSessionRequest{
- SessionID: resource.GetName(),
- })
-}
-
-func (appSessionExecutor) isSingleton() bool { return false }
-
-func (appSessionExecutor) getReader(cache *Cache, cacheOK bool) appSessionGetter {
- if cacheOK {
- return cache.appSessionCache
- }
- return cache.Config.AppSession
-}
-
-type appSessionGetter interface {
- GetAppSession(ctx context.Context, req types.GetAppSessionRequest) (types.WebSession, error)
- ListAppSessions(ctx context.Context, pageSize int, pageToken, user string) ([]types.WebSession, string, error)
-}
-
-var _ executor[types.WebSession, appSessionGetter] = appSessionExecutor{}
-
-type snowflakeSessionExecutor struct{}
-
-func (snowflakeSessionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WebSession, error) {
- webSessions, err := cache.SnowflakeSession.GetSnowflakeSessions(ctx)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- if !loadSecrets {
- for i := 0; i < len(webSessions); i++ {
- webSessions[i] = webSessions[i].WithoutSecrets()
- }
- }
-
- return webSessions, nil
-}
-
-func (snowflakeSessionExecutor) upsert(ctx context.Context, cache *Cache, resource types.WebSession) error {
- return cache.snowflakeSessionCache.UpsertSnowflakeSession(ctx, resource)
-}
-
-func (snowflakeSessionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.snowflakeSessionCache.DeleteAllSnowflakeSessions(ctx)
-}
-
-func (snowflakeSessionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.snowflakeSessionCache.DeleteSnowflakeSession(ctx, types.DeleteSnowflakeSessionRequest{
- SessionID: resource.GetName(),
- })
-}
-
-func (snowflakeSessionExecutor) isSingleton() bool { return false }
-
-func (snowflakeSessionExecutor) getReader(cache *Cache, cacheOK bool) snowflakeSessionGetter {
- if cacheOK {
- return cache.snowflakeSessionCache
- }
- return cache.Config.SnowflakeSession
-}
-
-type snowflakeSessionGetter interface {
- GetSnowflakeSession(context.Context, types.GetSnowflakeSessionRequest) (types.WebSession, error)
-}
-
-var _ executor[types.WebSession, snowflakeSessionGetter] = snowflakeSessionExecutor{}
-
-//nolint:revive // Because we want this to be IdP.
-type samlIdPSessionExecutor struct{}
-
-func (samlIdPSessionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WebSession, error) {
- var (
- startKey string
- sessions []types.WebSession
- )
- for {
- webSessions, nextKey, err := cache.SAMLIdPSession.ListSAMLIdPSessions(ctx, 0, startKey, "")
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- if !loadSecrets {
- for i := 0; i < len(webSessions); i++ {
- webSessions[i] = webSessions[i].WithoutSecrets()
- }
- }
-
- sessions = append(sessions, webSessions...)
-
- if nextKey == "" {
- break
- }
- startKey = nextKey
- }
- return sessions, nil
-}
-
-func (samlIdPSessionExecutor) upsert(ctx context.Context, cache *Cache, resource types.WebSession) error {
- return cache.samlIdPSessionCache.UpsertSAMLIdPSession(ctx, resource)
-}
-
-func (samlIdPSessionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.samlIdPSessionCache.DeleteAllSAMLIdPSessions(ctx)
-}
-
-func (samlIdPSessionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.samlIdPSessionCache.DeleteSAMLIdPSession(ctx, types.DeleteSAMLIdPSessionRequest{
- SessionID: resource.GetName(),
- })
-}
-
-func (samlIdPSessionExecutor) isSingleton() bool { return false }
-
-func (samlIdPSessionExecutor) getReader(cache *Cache, cacheOK bool) samlIdPSessionGetter {
- if cacheOK {
- return cache.samlIdPSessionCache
- }
- return cache.Config.SAMLIdPSession
-}
-
-type samlIdPSessionGetter interface {
- GetSAMLIdPSession(context.Context, types.GetSAMLIdPSessionRequest) (types.WebSession, error)
-}
-
-var _ executor[types.WebSession, samlIdPSessionGetter] = samlIdPSessionExecutor{}
-
-type webSessionExecutor struct{}
-
-func (webSessionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WebSession, error) {
- webSessions, err := cache.WebSession.List(ctx)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- if !loadSecrets {
- for i := 0; i < len(webSessions); i++ {
- webSessions[i] = webSessions[i].WithoutSecrets()
- }
- }
-
- return webSessions, nil
-}
-
-func (webSessionExecutor) upsert(ctx context.Context, cache *Cache, resource types.WebSession) error {
- return cache.webSessionCache.Upsert(ctx, resource)
-}
-
-func (webSessionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.webSessionCache.DeleteAll(ctx)
-}
-
-func (webSessionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.webSessionCache.Delete(ctx, types.DeleteWebSessionRequest{
- SessionID: resource.GetName(),
- })
-}
-
-func (webSessionExecutor) isSingleton() bool { return false }
-
-func (webSessionExecutor) getReader(cache *Cache, cacheOK bool) webSessionGetter {
- if cacheOK {
- return cache.webSessionCache
- }
- return cache.Config.WebSession
-}
-
-type webSessionGetter interface {
- Get(ctx context.Context, req types.GetWebSessionRequest) (types.WebSession, error)
-}
-
-var _ executor[types.WebSession, webSessionGetter] = webSessionExecutor{}
-
-type webTokenExecutor struct{}
-
-func (webTokenExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WebToken, error) {
- return cache.WebToken.List(ctx)
-}
-
-func (webTokenExecutor) upsert(ctx context.Context, cache *Cache, resource types.WebToken) error {
- return cache.webTokenCache.Upsert(ctx, resource)
-}
-
-func (webTokenExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.webTokenCache.DeleteAll(ctx)
-}
-
-func (webTokenExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.webTokenCache.Delete(ctx, types.DeleteWebTokenRequest{
- Token: resource.GetName(),
- })
-}
-
-func (webTokenExecutor) isSingleton() bool { return false }
-
-func (webTokenExecutor) getReader(cache *Cache, cacheOK bool) webTokenGetter {
- if cacheOK {
- return cache.webTokenCache
- }
- return cache.Config.WebToken
-}
-
-type webTokenGetter interface {
- Get(ctx context.Context, req types.GetWebTokenRequest) (types.WebToken, error)
-}
-
-var _ executor[types.WebToken, webTokenGetter] = webTokenExecutor{}
-
-type kubeServerExecutor struct{}
-
-func (kubeServerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.KubeServer, error) {
- return cache.Presence.GetKubernetesServers(ctx)
-}
-
-func (kubeServerExecutor) upsert(ctx context.Context, cache *Cache, resource types.KubeServer) error {
- _, err := cache.presenceCache.UpsertKubernetesServer(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (kubeServerExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.presenceCache.DeleteAllKubernetesServers(ctx)
-}
-
-func (kubeServerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.presenceCache.DeleteKubernetesServer(
- ctx,
- resource.GetMetadata().Description, // Cache passes host ID via description field.
- resource.GetName(),
- )
-}
-
-func (kubeServerExecutor) isSingleton() bool { return false }
-
-func (kubeServerExecutor) getReader(cache *Cache, cacheOK bool) kubeServerGetter {
- if cacheOK {
- return cache.presenceCache
- }
- return cache.Config.Presence
-}
-
-type kubeServerGetter interface {
- GetKubernetesServers(context.Context) ([]types.KubeServer, error)
-}
-
-var _ executor[types.KubeServer, kubeServerGetter] = kubeServerExecutor{}
-
-type authPreferenceExecutor struct{}
-
-func (authPreferenceExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.AuthPreference, error) {
- authPref, err := cache.ClusterConfig.GetAuthPreference(ctx)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- return []types.AuthPreference{authPref}, nil
-}
-
-func (authPreferenceExecutor) upsert(ctx context.Context, cache *Cache, resource types.AuthPreference) error {
- _, err := cache.clusterConfigCache.UpsertAuthPreference(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (authPreferenceExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.clusterConfigCache.DeleteAuthPreference(ctx)
-}
-
-func (authPreferenceExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.clusterConfigCache.DeleteAuthPreference(ctx)
-}
-
-func (authPreferenceExecutor) isSingleton() bool { return true }
-
-func (authPreferenceExecutor) getReader(cache *Cache, cacheOK bool) authPreferenceGetter {
- if cacheOK {
- return cache.clusterConfigCache
- }
- return cache.Config.ClusterConfig
-}
-
-type authPreferenceGetter interface {
- GetAuthPreference(ctx context.Context) (types.AuthPreference, error)
-}
-
-var _ executor[types.AuthPreference, authPreferenceGetter] = authPreferenceExecutor{}
-
-type clusterAuditConfigExecutor struct{}
-
-func (clusterAuditConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.ClusterAuditConfig, error) {
- auditConfig, err := cache.ClusterConfig.GetClusterAuditConfig(ctx)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- return []types.ClusterAuditConfig{auditConfig}, nil
-}
-
-func (clusterAuditConfigExecutor) upsert(ctx context.Context, cache *Cache, resource types.ClusterAuditConfig) error {
- return cache.clusterConfigCache.SetClusterAuditConfig(ctx, resource)
-}
-
-func (clusterAuditConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.clusterConfigCache.DeleteClusterAuditConfig(ctx)
-}
-
-func (clusterAuditConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.clusterConfigCache.DeleteClusterAuditConfig(ctx)
-}
-
-func (clusterAuditConfigExecutor) isSingleton() bool { return true }
-
-func (clusterAuditConfigExecutor) getReader(cache *Cache, cacheOK bool) clusterAuditConfigGetter {
- if cacheOK {
- return cache.clusterConfigCache
- }
- return cache.Config.ClusterConfig
-}
-
-type clusterAuditConfigGetter interface {
- GetClusterAuditConfig(context.Context) (types.ClusterAuditConfig, error)
-}
-
-var _ executor[types.ClusterAuditConfig, clusterAuditConfigGetter] = clusterAuditConfigExecutor{}
-
-type clusterNetworkingConfigExecutor struct{}
-
-func (clusterNetworkingConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.ClusterNetworkingConfig, error) {
- networkingConfig, err := cache.ClusterConfig.GetClusterNetworkingConfig(ctx)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- return []types.ClusterNetworkingConfig{networkingConfig}, nil
-}
-
-func (clusterNetworkingConfigExecutor) upsert(ctx context.Context, cache *Cache, resource types.ClusterNetworkingConfig) error {
- _, err := cache.clusterConfigCache.UpsertClusterNetworkingConfig(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (clusterNetworkingConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.clusterConfigCache.DeleteClusterNetworkingConfig(ctx)
-}
-
-func (clusterNetworkingConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.clusterConfigCache.DeleteClusterNetworkingConfig(ctx)
-}
-
-func (clusterNetworkingConfigExecutor) isSingleton() bool { return true }
-
-func (clusterNetworkingConfigExecutor) getReader(cache *Cache, cacheOK bool) clusterNetworkingConfigGetter {
- if cacheOK {
- return cache.clusterConfigCache
- }
- return cache.Config.ClusterConfig
-}
-
-type clusterNetworkingConfigGetter interface {
- GetClusterNetworkingConfig(context.Context) (types.ClusterNetworkingConfig, error)
-}
-
-var _ executor[types.ClusterNetworkingConfig, clusterNetworkingConfigGetter] = clusterNetworkingConfigExecutor{}
-
-type uiConfigExecutor struct{}
-
-func (uiConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.UIConfig, error) {
- uiConfig, err := cache.ClusterConfig.GetUIConfig(ctx)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- return []types.UIConfig{uiConfig}, nil
-}
-
-func (uiConfigExecutor) upsert(ctx context.Context, cache *Cache, resource types.UIConfig) error {
- return cache.clusterConfigCache.SetUIConfig(ctx, resource)
-}
-
-func (uiConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.clusterConfigCache.DeleteUIConfig(ctx)
-}
-
-func (uiConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.clusterConfigCache.DeleteUIConfig(ctx)
-}
-
-func (uiConfigExecutor) isSingleton() bool { return true }
-
-func (uiConfigExecutor) getReader(cache *Cache, cacheOK bool) uiConfigGetter {
- if cacheOK {
- return cache.clusterConfigCache
- }
- return cache.Config.ClusterConfig
-}
-
-type uiConfigGetter interface {
- GetUIConfig(context.Context) (types.UIConfig, error)
-}
-
-var _ executor[types.UIConfig, uiConfigGetter] = uiConfigExecutor{}
-
-type sessionRecordingConfigExecutor struct{}
-
-func (sessionRecordingConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.SessionRecordingConfig, error) {
- sessionRecordingConfig, err := cache.ClusterConfig.GetSessionRecordingConfig(ctx)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- return []types.SessionRecordingConfig{sessionRecordingConfig}, nil
-}
-
-func (sessionRecordingConfigExecutor) upsert(ctx context.Context, cache *Cache, resource types.SessionRecordingConfig) error {
- _, err := cache.clusterConfigCache.UpsertSessionRecordingConfig(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (sessionRecordingConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.clusterConfigCache.DeleteSessionRecordingConfig(ctx)
-}
-
-func (sessionRecordingConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.clusterConfigCache.DeleteSessionRecordingConfig(ctx)
-}
-
-func (sessionRecordingConfigExecutor) isSingleton() bool { return true }
-
-func (sessionRecordingConfigExecutor) getReader(cache *Cache, cacheOK bool) sessionRecordingConfigGetter {
- if cacheOK {
- return cache.clusterConfigCache
- }
- return cache.Config.ClusterConfig
-}
-
-type sessionRecordingConfigGetter interface {
- GetSessionRecordingConfig(ctx context.Context) (types.SessionRecordingConfig, error)
-}
-
-var _ executor[types.SessionRecordingConfig, sessionRecordingConfigGetter] = sessionRecordingConfigExecutor{}
-
-type installerConfigExecutor struct{}
-
-func (installerConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Installer, error) {
- return cache.ClusterConfig.GetInstallers(ctx)
-}
-
-func (installerConfigExecutor) upsert(ctx context.Context, cache *Cache, resource types.Installer) error {
- return cache.clusterConfigCache.SetInstaller(ctx, resource)
-}
-
-func (installerConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.clusterConfigCache.DeleteAllInstallers(ctx)
-}
-
-func (installerConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.clusterConfigCache.DeleteInstaller(ctx, resource.GetName())
-}
-
-func (installerConfigExecutor) isSingleton() bool { return false }
-
-func (installerConfigExecutor) getReader(cache *Cache, cacheOK bool) installerGetter {
- if cacheOK {
- return cache.clusterConfigCache
- }
- return cache.Config.ClusterConfig
-}
-
-type installerGetter interface {
- GetInstallers(context.Context) ([]types.Installer, error)
- GetInstaller(ctx context.Context, name string) (types.Installer, error)
-}
-
-var _ executor[types.Installer, installerGetter] = installerConfigExecutor{}
-
-type networkRestrictionsExecutor struct{}
-
-func (networkRestrictionsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.NetworkRestrictions, error) {
- restrictions, err := cache.Restrictions.GetNetworkRestrictions(ctx)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- return []types.NetworkRestrictions{restrictions}, nil
-}
-
-func (networkRestrictionsExecutor) upsert(ctx context.Context, cache *Cache, resource types.NetworkRestrictions) error {
- return cache.restrictionsCache.SetNetworkRestrictions(ctx, resource)
-}
-
-func (networkRestrictionsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.restrictionsCache.DeleteNetworkRestrictions(ctx)
-}
-
-func (networkRestrictionsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.restrictionsCache.DeleteNetworkRestrictions(ctx)
-}
-
-func (networkRestrictionsExecutor) isSingleton() bool { return true }
-
-func (networkRestrictionsExecutor) getReader(cache *Cache, cacheOK bool) networkRestrictionGetter {
- if cacheOK {
- return cache.restrictionsCache
- }
- return cache.Config.Restrictions
-}
-
-type networkRestrictionGetter interface {
- GetNetworkRestrictions(context.Context) (types.NetworkRestrictions, error)
-}
-
-var _ executor[types.NetworkRestrictions, networkRestrictionGetter] = networkRestrictionsExecutor{}
-
-type lockExecutor struct{}
-
-func (lockExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Lock, error) {
- return cache.Access.GetLocks(ctx, false)
-}
-
-func (lockExecutor) upsert(ctx context.Context, cache *Cache, resource types.Lock) error {
- return cache.accessCache.UpsertLock(ctx, resource)
-}
-
-func (lockExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.accessCache.DeleteAllLocks(ctx)
-}
-
-func (lockExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.accessCache.DeleteLock(ctx, resource.GetName())
-}
-
-func (lockExecutor) isSingleton() bool { return false }
-
-func (lockExecutor) getReader(cache *Cache, cacheOK bool) services.LockGetter {
- if cacheOK {
- return cache.accessCache
- }
- return cache.Config.Access
-}
-
-var _ executor[types.Lock, services.LockGetter] = lockExecutor{}
-
-type windowsDesktopServicesExecutor struct{}
-
-func (windowsDesktopServicesExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WindowsDesktopService, error) {
- return cache.Presence.GetWindowsDesktopServices(ctx)
-}
-
-func (windowsDesktopServicesExecutor) upsert(ctx context.Context, cache *Cache, resource types.WindowsDesktopService) error {
- _, err := cache.presenceCache.UpsertWindowsDesktopService(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (windowsDesktopServicesExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.presenceCache.DeleteAllWindowsDesktopServices(ctx)
-}
-
-func (windowsDesktopServicesExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.presenceCache.DeleteWindowsDesktopService(ctx, resource.GetName())
-}
-
-func (windowsDesktopServicesExecutor) isSingleton() bool { return false }
-
-func (windowsDesktopServicesExecutor) getReader(cache *Cache, cacheOK bool) windowsDesktopServiceGetter {
- if cacheOK {
- return windowsDesktopServiceAggregate{
- Presence: cache.presenceCache,
- WindowsDesktops: cache.windowsDesktopsCache,
- }
- }
- return windowsDesktopServiceAggregate{
- Presence: cache.Config.Presence,
- WindowsDesktops: cache.Config.WindowsDesktops,
- }
-}
-
-type windowsDesktopServiceAggregate struct {
- services.Presence
- services.WindowsDesktops
-}
-
-type windowsDesktopServiceGetter interface {
- GetWindowsDesktopServices(ctx context.Context) ([]types.WindowsDesktopService, error)
- GetWindowsDesktopService(ctx context.Context, name string) (types.WindowsDesktopService, error)
- ListWindowsDesktopServices(ctx context.Context, req types.ListWindowsDesktopServicesRequest) (*types.ListWindowsDesktopServicesResponse, error)
-}
-
-var _ executor[types.WindowsDesktopService, windowsDesktopServiceGetter] = windowsDesktopServicesExecutor{}
-
-type windowsDesktopsExecutor struct{}
-
-func (windowsDesktopsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WindowsDesktop, error) {
- return cache.WindowsDesktops.GetWindowsDesktops(ctx, types.WindowsDesktopFilter{})
-}
-
-func (windowsDesktopsExecutor) upsert(ctx context.Context, cache *Cache, resource types.WindowsDesktop) error {
- return cache.windowsDesktopsCache.UpsertWindowsDesktop(ctx, resource)
-}
-
-func (windowsDesktopsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.windowsDesktopsCache.DeleteAllWindowsDesktops(ctx)
-}
-
-func (windowsDesktopsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.windowsDesktopsCache.DeleteWindowsDesktop(ctx,
- resource.GetMetadata().Description, // Cache passes host ID via description field.
- resource.GetName(),
- )
-}
-
-func (windowsDesktopsExecutor) isSingleton() bool { return false }
-
-func (windowsDesktopsExecutor) getReader(cache *Cache, cacheOK bool) windowsDesktopsGetter {
- if cacheOK {
- return cache.windowsDesktopsCache
- }
- return cache.Config.WindowsDesktops
-}
-
-type windowsDesktopsGetter interface {
- GetWindowsDesktops(context.Context, types.WindowsDesktopFilter) ([]types.WindowsDesktop, error)
- ListWindowsDesktops(ctx context.Context, req types.ListWindowsDesktopsRequest) (*types.ListWindowsDesktopsResponse, error)
-}
-
-var _ executor[types.WindowsDesktop, windowsDesktopsGetter] = windowsDesktopsExecutor{}
-
-type dynamicWindowsDesktopsExecutor struct{}
-
-func (dynamicWindowsDesktopsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.DynamicWindowsDesktop, error) {
- var desktops []types.DynamicWindowsDesktop
- next := ""
- for {
- d, token, err := cache.Config.DynamicWindowsDesktops.ListDynamicWindowsDesktops(ctx, defaults.MaxIterationLimit, next)
- if err != nil {
- return nil, err
- }
- desktops = append(desktops, d...)
- if token == "" {
- break
- }
- next = token
- }
- return desktops, nil
-}
-
-func (dynamicWindowsDesktopsExecutor) upsert(ctx context.Context, cache *Cache, resource types.DynamicWindowsDesktop) error {
- _, err := cache.dynamicWindowsDesktopsCache.UpsertDynamicWindowsDesktop(ctx, resource)
- return err
-}
-
-func (dynamicWindowsDesktopsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.dynamicWindowsDesktopsCache.DeleteAllDynamicWindowsDesktops(ctx)
-}
-
-func (dynamicWindowsDesktopsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.dynamicWindowsDesktopsCache.DeleteDynamicWindowsDesktop(ctx, resource.GetName())
-}
-
-func (dynamicWindowsDesktopsExecutor) isSingleton() bool { return false }
-
-func (dynamicWindowsDesktopsExecutor) getReader(cache *Cache, cacheOK bool) dynamicWindowsDesktopsGetter {
- if cacheOK {
- return cache.dynamicWindowsDesktopsCache
- }
- return cache.Config.DynamicWindowsDesktops
-}
-
-type dynamicWindowsDesktopsGetter interface {
- GetDynamicWindowsDesktop(ctx context.Context, name string) (types.DynamicWindowsDesktop, error)
- ListDynamicWindowsDesktops(ctx context.Context, pageSize int, nextPage string) ([]types.DynamicWindowsDesktop, string, error)
-}
-
-var _ executor[types.DynamicWindowsDesktop, dynamicWindowsDesktopsGetter] = dynamicWindowsDesktopsExecutor{}
-
-type kubeClusterExecutor struct{}
-
-func (kubeClusterExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.KubeCluster, error) {
- return cache.Kubernetes.GetKubernetesClusters(ctx)
-}
-
-func (kubeClusterExecutor) upsert(ctx context.Context, cache *Cache, resource types.KubeCluster) error {
- if err := cache.kubernetesCache.CreateKubernetesCluster(ctx, resource); err != nil {
- if !trace.IsAlreadyExists(err) {
- return trace.Wrap(err)
- }
- return trace.Wrap(cache.kubernetesCache.UpdateKubernetesCluster(ctx, resource))
- }
-
- return nil
-}
-
-func (kubeClusterExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.kubernetesCache.DeleteAllKubernetesClusters(ctx)
-}
-
-func (kubeClusterExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.kubernetesCache.DeleteKubernetesCluster(ctx, resource.GetName())
-}
-
-func (kubeClusterExecutor) isSingleton() bool { return false }
-
-func (kubeClusterExecutor) getReader(cache *Cache, cacheOK bool) kubernetesClusterGetter {
- if cacheOK {
- return cache.kubernetesCache
- }
- return cache.Config.Kubernetes
-}
-
-type kubernetesClusterGetter interface {
- GetKubernetesClusters(ctx context.Context) ([]types.KubeCluster, error)
- GetKubernetesCluster(ctx context.Context, name string) (types.KubeCluster, error)
-}
-
-type kubeWaitingContainerExecutor struct{}
-
-func (kubeWaitingContainerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*kubewaitingcontainerpb.KubernetesWaitingContainer, error) {
- var (
- startKey string
- allConts []*kubewaitingcontainerpb.KubernetesWaitingContainer
- )
- for {
- conts, nextKey, err := cache.KubeWaitingContainers.ListKubernetesWaitingContainers(ctx, 0, startKey)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- allConts = append(allConts, conts...)
-
- if nextKey == "" {
- break
- }
- startKey = nextKey
- }
- return allConts, nil
-}
-
-func (kubeWaitingContainerExecutor) upsert(ctx context.Context, cache *Cache, resource *kubewaitingcontainerpb.KubernetesWaitingContainer) error {
- _, err := cache.kubeWaitingContsCache.UpsertKubernetesWaitingContainer(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (kubeWaitingContainerExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return trace.Wrap(cache.kubeWaitingContsCache.DeleteAllKubernetesWaitingContainers(ctx))
-}
-
-func (kubeWaitingContainerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- switch r := resource.(type) {
- case types.Resource153Unwrapper:
- switch wc := r.Unwrap().(type) {
- case *kubewaitingcontainerpb.KubernetesWaitingContainer:
- err := cache.kubeWaitingContsCache.DeleteKubernetesWaitingContainer(ctx, &kubewaitingcontainerpb.DeleteKubernetesWaitingContainerRequest{
- Username: wc.Spec.Username,
- Cluster: wc.Spec.Cluster,
- Namespace: wc.Spec.Namespace,
- PodName: wc.Spec.PodName,
- ContainerName: wc.Spec.ContainerName,
- })
- return trace.Wrap(err)
- }
- }
-
- return trace.BadParameter("unknown KubeWaitingContainer type, expected *kubewaitingcontainerpb.KubernetesWaitingContainer, got %T", resource)
-}
-
-func (kubeWaitingContainerExecutor) isSingleton() bool { return false }
-
-func (kubeWaitingContainerExecutor) getReader(cache *Cache, cacheOK bool) kubernetesWaitingContainerGetter {
- if cacheOK {
- return cache.kubeWaitingContsCache
- }
- return cache.Config.KubeWaitingContainers
-}
-
-type kubernetesWaitingContainerGetter interface {
- ListKubernetesWaitingContainers(ctx context.Context, pageSize int, pageToken string) ([]*kubewaitingcontainerpb.KubernetesWaitingContainer, string, error)
- GetKubernetesWaitingContainer(ctx context.Context, req *kubewaitingcontainerpb.GetKubernetesWaitingContainerRequest) (*kubewaitingcontainerpb.KubernetesWaitingContainer, error)
-}
-
-var _ executor[*kubewaitingcontainerpb.KubernetesWaitingContainer, kubernetesWaitingContainerGetter] = kubeWaitingContainerExecutor{}
-
-type staticHostUserExecutor struct{}
-
-func (staticHostUserExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*userprovisioningpb.StaticHostUser, error) {
- var (
- startKey string
- allUsers []*userprovisioningpb.StaticHostUser
- )
- for {
- users, nextKey, err := cache.StaticHostUsers.ListStaticHostUsers(ctx, 0, startKey)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- allUsers = append(allUsers, users...)
-
- if nextKey == "" {
- break
- }
- startKey = nextKey
- }
- return allUsers, nil
-}
-
-func (staticHostUserExecutor) upsert(ctx context.Context, cache *Cache, resource *userprovisioningpb.StaticHostUser) error {
- _, err := cache.staticHostUsersCache.UpsertStaticHostUser(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (staticHostUserExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return trace.Wrap(cache.staticHostUsersCache.DeleteAllStaticHostUsers(ctx))
-}
-
-func (staticHostUserExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return trace.Wrap(cache.staticHostUsersCache.DeleteStaticHostUser(ctx, resource.GetName()))
-}
-
-func (staticHostUserExecutor) isSingleton() bool { return false }
-
-func (staticHostUserExecutor) getReader(cache *Cache, cacheOK bool) staticHostUserGetter {
- if cacheOK {
- return cache.staticHostUsersCache
- }
- return cache.Config.StaticHostUsers
-}
-
-type staticHostUserGetter interface {
- ListStaticHostUsers(ctx context.Context, pageSize int, pageToken string) ([]*userprovisioningpb.StaticHostUser, string, error)
- GetStaticHostUser(ctx context.Context, name string) (*userprovisioningpb.StaticHostUser, error)
-}
-
-type crownJewelsExecutor struct{}
-
-func (crownJewelsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*crownjewelv1.CrownJewel, error) {
- var resources []*crownjewelv1.CrownJewel
- var nextToken string
- for {
- var page []*crownjewelv1.CrownJewel
- var err error
- page, nextToken, err = cache.CrownJewels.ListCrownJewels(ctx, 0 /* page size */, nextToken)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- resources = append(resources, page...)
-
- if nextToken == "" {
- break
- }
- }
- return resources, nil
-}
-
-func (crownJewelsExecutor) upsert(ctx context.Context, cache *Cache, resource *crownjewelv1.CrownJewel) error {
- _, err := cache.crownJewelsCache.UpsertCrownJewel(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (crownJewelsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.crownJewelsCache.DeleteAllCrownJewels(ctx)
-}
-
-func (crownJewelsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.crownJewelsCache.DeleteCrownJewel(ctx, resource.GetName())
-}
-
-func (crownJewelsExecutor) isSingleton() bool { return false }
-
-func (crownJewelsExecutor) getReader(cache *Cache, cacheOK bool) crownjewelsGetter {
- if cacheOK {
- return cache.crownJewelsCache
- }
- return cache.Config.CrownJewels
-}
-
-var _ executor[*crownjewelv1.CrownJewel, crownjewelsGetter] = crownJewelsExecutor{}
-
-type userTasksExecutor struct{}
-
-func (userTasksExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*usertasksv1.UserTask, error) {
- var resources []*usertasksv1.UserTask
- var nextToken string
- for {
- var page []*usertasksv1.UserTask
- var err error
- page, nextToken, err = cache.UserTasks.ListUserTasks(ctx, 0 /* page size */, nextToken, &usertasksv1.ListUserTasksFilters{})
- if err != nil {
- return nil, trace.Wrap(err)
- }
- resources = append(resources, page...)
-
- if nextToken == "" {
- break
- }
- }
- return resources, nil
-}
-
-func (userTasksExecutor) upsert(ctx context.Context, cache *Cache, resource *usertasksv1.UserTask) error {
- _, err := cache.userTasksCache.UpsertUserTask(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (userTasksExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.userTasksCache.DeleteAllUserTasks(ctx)
-}
-
-func (userTasksExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.userTasksCache.DeleteUserTask(ctx, resource.GetName())
-}
-
-func (userTasksExecutor) isSingleton() bool { return false }
-
-func (userTasksExecutor) getReader(cache *Cache, cacheOK bool) userTasksGetter {
- if cacheOK {
- return cache.userTasksCache
- }
- return cache.Config.UserTasks
-}
-
-var _ executor[*usertasksv1.UserTask, userTasksGetter] = userTasksExecutor{}
-
-//nolint:revive // Because we want this to be IdP.
-type samlIdPServiceProvidersExecutor struct{}
-
-func (samlIdPServiceProvidersExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.SAMLIdPServiceProvider, error) {
- var (
- startKey string
- sps []types.SAMLIdPServiceProvider
- )
- for {
- var samlProviders []types.SAMLIdPServiceProvider
- var err error
- samlProviders, startKey, err = cache.SAMLIdPServiceProviders.ListSAMLIdPServiceProviders(ctx, 0, startKey)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- sps = append(sps, samlProviders...)
-
- if startKey == "" {
- break
- }
- }
-
- return sps, nil
-}
-
-func (samlIdPServiceProvidersExecutor) upsert(ctx context.Context, cache *Cache, resource types.SAMLIdPServiceProvider) error {
- err := cache.samlIdPServiceProvidersCache.CreateSAMLIdPServiceProvider(ctx, resource)
- if trace.IsAlreadyExists(err) {
- err = cache.samlIdPServiceProvidersCache.UpdateSAMLIdPServiceProvider(ctx, resource)
- }
- return trace.Wrap(err)
-}
-
-func (samlIdPServiceProvidersExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.samlIdPServiceProvidersCache.DeleteAllSAMLIdPServiceProviders(ctx)
-}
-
-func (samlIdPServiceProvidersExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.samlIdPServiceProvidersCache.DeleteSAMLIdPServiceProvider(ctx, resource.GetName())
-}
-
-func (samlIdPServiceProvidersExecutor) isSingleton() bool { return false }
-
-func (samlIdPServiceProvidersExecutor) getReader(cache *Cache, cacheOK bool) samlIdPServiceProviderGetter {
- if cacheOK {
- return cache.samlIdPServiceProvidersCache
- }
- return cache.Config.SAMLIdPServiceProviders
-}
-
-type samlIdPServiceProviderGetter interface {
- ListSAMLIdPServiceProviders(context.Context, int, string) ([]types.SAMLIdPServiceProvider, string, error)
- GetSAMLIdPServiceProvider(ctx context.Context, name string) (types.SAMLIdPServiceProvider, error)
-}
-
-var _ executor[types.SAMLIdPServiceProvider, samlIdPServiceProviderGetter] = samlIdPServiceProvidersExecutor{}
-
-type userGroupsExecutor struct{}
-
-func (userGroupsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.UserGroup, error) {
- var (
- startKey string
- resources []types.UserGroup
- )
- for {
- var userGroups []types.UserGroup
- var err error
- userGroups, startKey, err = cache.UserGroups.ListUserGroups(ctx, 0, startKey)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- resources = append(resources, userGroups...)
-
- if startKey == "" {
- break
- }
- }
-
- return resources, nil
-}
-
-func (userGroupsExecutor) upsert(ctx context.Context, cache *Cache, resource types.UserGroup) error {
- err := cache.userGroupsCache.CreateUserGroup(ctx, resource)
- if trace.IsAlreadyExists(err) {
- err = cache.userGroupsCache.UpdateUserGroup(ctx, resource)
- }
- return trace.Wrap(err)
-}
-
-func (userGroupsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.userGroupsCache.DeleteAllUserGroups(ctx)
-}
-
-func (userGroupsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.userGroupsCache.DeleteUserGroup(ctx, resource.GetName())
-}
-
-func (userGroupsExecutor) isSingleton() bool { return false }
-
-func (userGroupsExecutor) getReader(cache *Cache, cacheOK bool) userGroupGetter {
- if cacheOK {
- return cache.userGroupsCache
- }
- return cache.Config.UserGroups
-}
-
-type userGroupGetter interface {
- GetUserGroup(ctx context.Context, name string) (types.UserGroup, error)
- ListUserGroups(context.Context, int, string) ([]types.UserGroup, string, error)
-}
-
-var _ executor[types.UserGroup, userGroupGetter] = userGroupsExecutor{}
-
-type oktaImportRulesExecutor struct{}
-
-func (oktaImportRulesExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.OktaImportRule, error) {
- var (
- startKey string
- resources []types.OktaImportRule
- )
- for {
- var importRules []types.OktaImportRule
- var err error
- importRules, startKey, err = cache.Okta.ListOktaImportRules(ctx, 0, startKey)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- resources = append(resources, importRules...)
-
- if startKey == "" {
- break
- }
- }
-
- return resources, nil
-}
-
-func (oktaImportRulesExecutor) upsert(ctx context.Context, cache *Cache, resource types.OktaImportRule) error {
- _, err := cache.oktaCache.CreateOktaImportRule(ctx, resource)
- if trace.IsAlreadyExists(err) {
- _, err = cache.oktaCache.UpdateOktaImportRule(ctx, resource)
- }
- return trace.Wrap(err)
-}
-
-func (oktaImportRulesExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.oktaCache.DeleteAllOktaImportRules(ctx)
-}
-
-func (oktaImportRulesExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.oktaCache.DeleteOktaImportRule(ctx, resource.GetName())
-}
-
-func (oktaImportRulesExecutor) isSingleton() bool { return false }
-
-func (oktaImportRulesExecutor) getReader(cache *Cache, cacheOK bool) oktaImportRuleGetter {
- if cacheOK {
- return cache.oktaCache
- }
- return cache.Config.Okta
-}
-
-type oktaImportRuleGetter interface {
- ListOktaImportRules(context.Context, int, string) ([]types.OktaImportRule, string, error)
- GetOktaImportRule(ctx context.Context, name string) (types.OktaImportRule, error)
-}
-
-var _ executor[types.OktaImportRule, oktaImportRuleGetter] = oktaImportRulesExecutor{}
-
-type oktaAssignmentsExecutor struct{}
-
-func (oktaAssignmentsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.OktaAssignment, error) {
- var (
- startKey string
- resources []types.OktaAssignment
- )
- for {
- var assignments []types.OktaAssignment
- var err error
- assignments, startKey, err = cache.Okta.ListOktaAssignments(ctx, 0, startKey)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- resources = append(resources, assignments...)
-
- if startKey == "" {
- break
- }
- }
-
- return resources, nil
-}
-
-func (oktaAssignmentsExecutor) upsert(ctx context.Context, cache *Cache, resource types.OktaAssignment) error {
- _, err := cache.oktaCache.CreateOktaAssignment(ctx, resource)
- if trace.IsAlreadyExists(err) {
- _, err = cache.oktaCache.UpdateOktaAssignment(ctx, resource)
- }
- return trace.Wrap(err)
-}
-
-func (oktaAssignmentsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.oktaCache.DeleteAllOktaAssignments(ctx)
-}
-
-func (oktaAssignmentsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.oktaCache.DeleteOktaAssignment(ctx, resource.GetName())
-}
-
-func (oktaAssignmentsExecutor) isSingleton() bool { return false }
-
-func (oktaAssignmentsExecutor) getReader(cache *Cache, cacheOK bool) oktaAssignmentGetter {
- if cacheOK {
- return cache.oktaCache
- }
- return cache.Config.Okta
-}
-
-type oktaAssignmentGetter interface {
- GetOktaAssignment(ctx context.Context, name string) (types.OktaAssignment, error)
- ListOktaAssignments(context.Context, int, string) ([]types.OktaAssignment, string, error)
-}
-
-var _ executor[types.OktaAssignment, oktaAssignmentGetter] = oktaAssignmentsExecutor{}
-
-// collectionReader extends the collection interface, adding routing capabilities.
-type collectionReader[R any] interface {
- collection
-
- // getReader returns the appropriate reader type T based on the health status of the cache.
- // Reader type R provides getter methods related to the collection, e.g. GetNodes(), GetRoles().
- // Note that cacheOK set to true means that cache is overall healthy and the collection was confirmed as supported.
- getReader(cacheOK bool) R
-}
-
-type resourceGetter interface {
- ListResources(ctx context.Context, req proto.ListResourcesRequest) (*types.ListResourcesResponse, error)
-}
-
-type integrationsExecutor struct{}
-
-func (integrationsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Integration, error) {
- var (
- startKey string
- resources []types.Integration
- )
- for {
- var igs []types.Integration
- var err error
- igs, startKey, err = cache.Integrations.ListIntegrations(ctx, 0, startKey)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- resources = append(resources, igs...)
-
- if startKey == "" {
- break
- }
- }
-
- return resources, nil
-}
-
-func (integrationsExecutor) upsert(ctx context.Context, cache *Cache, resource types.Integration) error {
- _, err := cache.integrationsCache.CreateIntegration(ctx, resource)
- if trace.IsAlreadyExists(err) {
- _, err = cache.integrationsCache.UpdateIntegration(ctx, resource)
- }
- return trace.Wrap(err)
-}
-
-func (integrationsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.integrationsCache.DeleteAllIntegrations(ctx)
-}
-
-func (integrationsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.integrationsCache.DeleteIntegration(ctx, resource.GetName())
-}
-
-func (integrationsExecutor) isSingleton() bool { return false }
-
-func (integrationsExecutor) getReader(cache *Cache, cacheOK bool) services.IntegrationsGetter {
- if cacheOK {
- return cache.integrationsCache
- }
- return cache.Config.Integrations
+// collections is the group of resource [collection]s
+// that the [Cache] supports.
+type collections struct {
+ byKind map[resourceKind]collectionHandler
}
-var _ executor[types.Integration, services.IntegrationsGetter] = integrationsExecutor{}
-
-type discoveryConfigExecutor struct{}
-
-func (discoveryConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*discoveryconfig.DiscoveryConfig, error) {
- var discoveryConfigs []*discoveryconfig.DiscoveryConfig
- var nextToken string
- for {
- var page []*discoveryconfig.DiscoveryConfig
- var err error
-
- page, nextToken, err = cache.DiscoveryConfigs.ListDiscoveryConfigs(ctx, 0 /* default page size */, nextToken)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- discoveryConfigs = append(discoveryConfigs, page...)
-
- if nextToken == "" {
- break
- }
+// isKnownUncollectedKind is true if a resource kind is not stored in
+// the cache itself but it's only configured in the cache so that the
+// resources events can be processed by downstream watchers.
+func isKnownUncollectedKind(kind string) bool {
+ switch kind {
+ case types.KindAccessRequest, types.KindHeadlessAuthentication:
+ return true
+ default:
+ return false
}
- return discoveryConfigs, nil
}
-func (discoveryConfigExecutor) upsert(ctx context.Context, cache *Cache, resource *discoveryconfig.DiscoveryConfig) error {
- _, err := cache.discoveryConfigsCache.UpsertDiscoveryConfig(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (discoveryConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.discoveryConfigsCache.DeleteAllDiscoveryConfigs(ctx)
-}
-
-func (discoveryConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.discoveryConfigsCache.DeleteDiscoveryConfig(ctx, resource.GetName())
-}
-
-func (discoveryConfigExecutor) isSingleton() bool { return false }
-
-func (discoveryConfigExecutor) getReader(cache *Cache, cacheOK bool) services.DiscoveryConfigsGetter {
- if cacheOK {
- return cache.discoveryConfigsCache
+// setupCollections ensures that the appropriate [collection] is
+// initialized for all provided [types.WatcKind]s. An error is
+// returned if a [types.WatchKind] has no associated [collection].
+func setupCollections(c Config, legacyCollections map[resourceKind]legacyCollection) (*collections, error) {
+ out := &collections{
+ byKind: make(map[resourceKind]collectionHandler, 1),
}
- return cache.Config.DiscoveryConfigs
-}
-var _ executor[*discoveryconfig.DiscoveryConfig, services.DiscoveryConfigsGetter] = discoveryConfigExecutor{}
-
-type auditQueryExecutor struct{}
-
-func (auditQueryExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*secreports.AuditQuery, error) {
- var out []*secreports.AuditQuery
- var nextToken string
- for {
- var page []*secreports.AuditQuery
- var err error
-
- page, nextToken, err = cache.secReportsCache.ListSecurityAuditQueries(ctx, 0 /* default page size */, nextToken)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- out = append(out, page...)
- if nextToken == "" {
- break
+ for _, watch := range c.Watches {
+ if isKnownUncollectedKind(watch.Kind) {
+ continue
}
- }
- return out, nil
-}
-
-func (auditQueryExecutor) upsert(ctx context.Context, cache *Cache, resource *secreports.AuditQuery) error {
- err := cache.secReportsCache.UpsertSecurityAuditQuery(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (auditQueryExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return trace.Wrap(cache.secReportsCache.DeleteAllSecurityReports(ctx))
-}
-
-func (auditQueryExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return trace.Wrap(cache.secReportsCache.DeleteSecurityAuditQuery(ctx, resource.GetName()))
-}
-
-func (auditQueryExecutor) isSingleton() bool { return false }
-
-func (auditQueryExecutor) getReader(cache *Cache, cacheOK bool) services.SecurityAuditQueryGetter {
- if cacheOK {
- return cache.secReportsCache
- }
- return cache.Config.SecReports
-}
-
-var _ executor[*secreports.AuditQuery, services.SecurityAuditQueryGetter] = auditQueryExecutor{}
-
-type secReportExecutor struct{}
-func (secReportExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*secreports.Report, error) {
- var out []*secreports.Report
- var nextToken string
- for {
- var page []*secreports.Report
- var err error
-
- page, nextToken, err = cache.secReportsCache.ListSecurityReports(ctx, 0 /* default page size */, nextToken)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- out = append(out, page...)
- if nextToken == "" {
- break
+ resourceKind := resourceKindFromWatchKind(watch)
+ switch watch.Kind {
+ default:
+ _, legacyOk := legacyCollections[resourceKind]
+ if _, ok := out.byKind[resourceKind]; !ok && !legacyOk {
+ return nil, trace.BadParameter("resource %q is not supported", watch.Kind)
+ }
}
- }
- return out, nil
-}
-
-func (secReportExecutor) upsert(ctx context.Context, cache *Cache, resource *secreports.Report) error {
- err := cache.secReportsCache.UpsertSecurityReport(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (secReportExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return trace.Wrap(cache.secReportsCache.DeleteAllSecurityReports(ctx))
-}
-
-func (secReportExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return trace.Wrap(cache.secReportsCache.DeleteSecurityReport(ctx, resource.GetName()))
-}
-
-func (secReportExecutor) isSingleton() bool { return false }
-func (secReportExecutor) getReader(cache *Cache, cacheOK bool) services.SecurityReportGetter {
- if cacheOK {
- return cache.secReportsCache
}
- return cache.Config.SecReports
-}
-
-var _ executor[*secreports.Report, services.SecurityReportGetter] = secReportExecutor{}
-
-type secReportStateExecutor struct{}
-
-func (secReportStateExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*secreports.ReportState, error) {
- var out []*secreports.ReportState
- var nextToken string
- for {
- var page []*secreports.ReportState
- var err error
- page, nextToken, err = cache.secReportsCache.ListSecurityReportsStates(ctx, 0 /* default page size */, nextToken)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- out = append(out, page...)
- if nextToken == "" {
- break
- }
- }
return out, nil
}
-
-func (secReportStateExecutor) upsert(ctx context.Context, cache *Cache, resource *secreports.ReportState) error {
- err := cache.secReportsCache.UpsertSecurityReportsState(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (secReportStateExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return trace.Wrap(cache.secReportsCache.DeleteAllSecurityReportsStates(ctx))
-}
-
-func (secReportStateExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return trace.Wrap(cache.secReportsCache.DeleteSecurityReportsState(ctx, resource.GetName()))
-}
-
-func (secReportStateExecutor) isSingleton() bool { return false }
-
-func (secReportStateExecutor) getReader(cache *Cache, cacheOK bool) services.SecurityReportStateGetter {
- if cacheOK {
- return cache.secReportsCache
- }
- return cache.Config.SecReports
-}
-
-var _ executor[*secreports.ReportState, services.SecurityReportStateGetter] = secReportStateExecutor{}
-
-// noopExecutor can be used when a resource's events do not need to processed by
-// the cache itself, only passed on to other watchers.
-type noopExecutor struct{}
-
-func (noopExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*types.HeadlessAuthentication, error) {
- return nil, nil
-}
-
-func (noopExecutor) upsert(ctx context.Context, cache *Cache, resource *types.HeadlessAuthentication) error {
- return nil
-}
-
-func (noopExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return nil
-}
-
-func (noopExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return nil
-}
-
-func (noopExecutor) isSingleton() bool { return false }
-
-func (noopExecutor) getReader(_ *Cache, _ bool) noReader {
- return noReader{}
-}
-
-var _ executor[*types.HeadlessAuthentication, noReader] = noopExecutor{}
-
-type userLoginStateExecutor struct{}
-
-func (userLoginStateExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*userloginstate.UserLoginState, error) {
- resources, err := cache.UserLoginStates.GetUserLoginStates(ctx)
- return resources, trace.Wrap(err)
-}
-
-func (userLoginStateExecutor) upsert(ctx context.Context, cache *Cache, resource *userloginstate.UserLoginState) error {
- _, err := cache.userLoginStateCache.UpsertUserLoginState(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (userLoginStateExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.userLoginStateCache.DeleteAllUserLoginStates(ctx)
-}
-
-func (userLoginStateExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.userLoginStateCache.DeleteUserLoginState(ctx, resource.GetName())
-}
-
-func (userLoginStateExecutor) isSingleton() bool { return false }
-
-func (userLoginStateExecutor) getReader(cache *Cache, cacheOK bool) services.UserLoginStatesGetter {
- if cacheOK {
- return cache.userLoginStateCache
- }
- return cache.Config.UserLoginStates
-}
-
-var _ executor[*userloginstate.UserLoginState, services.UserLoginStatesGetter] = userLoginStateExecutor{}
-
-type accessListExecutor struct{}
-
-func (accessListExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*accesslist.AccessList, error) {
- var resources []*accesslist.AccessList
- var nextToken string
- for {
- var page []*accesslist.AccessList
- var err error
- page, nextToken, err = cache.AccessLists.ListAccessLists(ctx, 0 /* page size */, nextToken)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- resources = append(resources, page...)
-
- if nextToken == "" {
- break
- }
- }
- return resources, nil
-}
-
-func (accessListExecutor) upsert(ctx context.Context, cache *Cache, resource *accesslist.AccessList) error {
- _, err := cache.accessListCache.UnconditionalUpsertAccessList(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (accessListExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.accessListCache.DeleteAllAccessLists(ctx)
-}
-
-func (accessListExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.accessListCache.UnconditionalDeleteAccessList(ctx, resource.GetName())
-}
-
-func (accessListExecutor) isSingleton() bool { return false }
-
-func (accessListExecutor) getReader(cache *Cache, cacheOK bool) accessListsGetter {
- if cacheOK {
- return cache.accessListCache
- }
- return cache.Config.AccessLists
-}
-
-type accessListsGetter interface {
- GetAccessLists(ctx context.Context) ([]*accesslist.AccessList, error)
- ListAccessLists(ctx context.Context, pageSize int, nextToken string) ([]*accesslist.AccessList, string, error)
- GetAccessList(ctx context.Context, name string) (*accesslist.AccessList, error)
-}
-
-type accessListMemberExecutor struct{}
-
-func (accessListMemberExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*accesslist.AccessListMember, error) {
- var resources []*accesslist.AccessListMember
- var nextToken string
- for {
- var page []*accesslist.AccessListMember
- var err error
- page, nextToken, err = cache.AccessLists.ListAllAccessListMembers(ctx, 0 /* page size */, nextToken)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- resources = append(resources, page...)
-
- if nextToken == "" {
- break
- }
- }
- return resources, nil
-}
-
-func (accessListMemberExecutor) upsert(ctx context.Context, cache *Cache, resource *accesslist.AccessListMember) error {
- _, err := cache.accessListCache.UnconditionalUpsertAccessListMember(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (accessListMemberExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.accessListCache.DeleteAllAccessListMembers(ctx)
-}
-
-func (accessListMemberExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.accessListCache.UnconditionalDeleteAccessListMember(ctx,
- resource.GetMetadata().Description, // Cache passes access ID via description field.
- resource.GetName())
-}
-
-func (accessListMemberExecutor) isSingleton() bool { return false }
-
-func (accessListMemberExecutor) getReader(cache *Cache, cacheOK bool) accessListMembersGetter {
- if cacheOK {
- return cache.accessListCache
- }
- return cache.Config.AccessLists
-}
-
-type accessListMembersGetter interface {
- CountAccessListMembers(ctx context.Context, accessListName string) (uint32, uint32, error)
- ListAccessListMembers(ctx context.Context, accessListName string, pageSize int, nextToken string) ([]*accesslist.AccessListMember, string, error)
- GetAccessListMember(ctx context.Context, accessList string, memberName string) (*accesslist.AccessListMember, error)
- ListAllAccessListMembers(ctx context.Context, pageSize int, pageToken string) ([]*accesslist.AccessListMember, string, error)
-}
-
-type accessListReviewExecutor struct{}
-
-func (accessListReviewExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*accesslist.Review, error) {
- var resources []*accesslist.Review
- var nextToken string
- for {
- var page []*accesslist.Review
- var err error
- page, nextToken, err = cache.AccessLists.ListAllAccessListReviews(ctx, 0 /* page size */, nextToken)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- resources = append(resources, page...)
-
- if nextToken == "" {
- break
- }
- }
- return resources, nil
-}
-
-func (accessListReviewExecutor) upsert(ctx context.Context, cache *Cache, resource *accesslist.Review) error {
- if _, _, err := cache.accessListCache.CreateAccessListReview(ctx, resource); err != nil {
- if !trace.IsAlreadyExists(err) {
- return trace.Wrap(err)
- }
-
- if err := cache.accessListCache.DeleteAccessListReview(ctx, resource.Spec.AccessList, resource.GetName()); err != nil {
- return trace.Wrap(err)
- }
-
- if _, _, err := cache.accessListCache.CreateAccessListReview(ctx, resource); err != nil {
- return trace.Wrap(err)
- }
- }
- return nil
-}
-
-func (accessListReviewExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.accessListCache.DeleteAllAccessListReviews(ctx)
-}
-
-func (accessListReviewExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.accessListCache.DeleteAccessListReview(ctx,
- resource.GetMetadata().Description, // Cache passes access ID via description field.
- resource.GetName())
-}
-
-func (accessListReviewExecutor) isSingleton() bool { return false }
-
-func (accessListReviewExecutor) getReader(cache *Cache, cacheOK bool) accessListReviewsGetter {
- if cacheOK {
- return cache.accessListCache
- }
- return cache.Config.AccessLists
-}
-
-type accessListReviewsGetter interface {
- ListAccessListReviews(ctx context.Context, accessList string, pageSize int, pageToken string) (reviews []*accesslist.Review, nextToken string, err error)
-}
-
-type notificationGetter interface {
- ListUserNotifications(ctx context.Context, pageSize int, startKey string) ([]*notificationsv1.Notification, string, error)
- ListGlobalNotifications(ctx context.Context, pageSize int, startKey string) ([]*notificationsv1.GlobalNotification, string, error)
-}
-
-type userNotificationExecutor struct{}
-
-func (userNotificationExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*notificationsv1.Notification, error) {
- var notifications []*notificationsv1.Notification
- var startKey string
- for {
- notifs, nextKey, err := cache.notificationsCache.ListUserNotifications(ctx, 0, startKey)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- notifications = append(notifications, notifs...)
-
- if nextKey == "" {
- break
- }
- startKey = nextKey
- }
-
- return notifications, nil
-}
-
-func (userNotificationExecutor) upsert(ctx context.Context, cache *Cache, notification *notificationsv1.Notification) error {
- _, err := cache.notificationsCache.UpsertUserNotification(ctx, notification)
- if err != nil {
- return trace.Wrap(err)
- }
-
- return nil
-}
-
-func (userNotificationExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.notificationsCache.DeleteAllUserNotifications(ctx)
-}
-
-func (userNotificationExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- r, ok := resource.(types.Resource153Unwrapper)
- if !ok {
- return trace.BadParameter("unknown resource type, expected types.Resource153Unwrapper, got %T", resource)
- }
-
- notification, ok := r.Unwrap().(*notificationsv1.Notification)
- if !ok {
- return trace.BadParameter("unknown Notification type, expected *notificationsv1.Notification, got %T", resource)
- }
-
- username := notification.GetSpec().GetUsername()
- notificationId := notification.GetMetadata().GetName()
-
- err := cache.notificationsCache.DeleteUserNotification(ctx, username, notificationId)
- return trace.Wrap(err)
-}
-
-func (userNotificationExecutor) isSingleton() bool { return false }
-
-func (userNotificationExecutor) getReader(cache *Cache, cacheOK bool) notificationGetter {
- if cacheOK {
- return cache.notificationsCache
- }
- return cache.Config.Notifications
-}
-
-var _ executor[*notificationsv1.Notification, notificationGetter] = userNotificationExecutor{}
-
-type globalNotificationExecutor struct{}
-
-func (globalNotificationExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*notificationsv1.GlobalNotification, error) {
- var notifications []*notificationsv1.GlobalNotification
- var startKey string
- for {
- notifs, nextKey, err := cache.notificationsCache.ListGlobalNotifications(ctx, 0, startKey)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- notifications = append(notifications, notifs...)
-
- if nextKey == "" {
- break
- }
- startKey = nextKey
- }
-
- return notifications, nil
-}
-
-func (globalNotificationExecutor) upsert(ctx context.Context, cache *Cache, notification *notificationsv1.GlobalNotification) error {
- if _, err := cache.notificationsCache.UpsertGlobalNotification(ctx, notification); err != nil {
- return trace.Wrap(err)
- }
-
- return nil
-}
-
-func (globalNotificationExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.notificationsCache.DeleteAllGlobalNotifications(ctx)
-}
-
-func (globalNotificationExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
-
- r, ok := resource.(types.Resource153Unwrapper)
- if !ok {
- return trace.BadParameter("unknown resource type, expected types.Resource153Unwrapper, got %T", resource)
- }
-
- globalNotification, ok := r.Unwrap().(*notificationsv1.GlobalNotification)
- if !ok {
- return trace.BadParameter("unknown Notification type, expected *notificationsv1.GlobalNotification, got %T", resource)
- }
-
- notificationId := globalNotification.GetMetadata().GetName()
-
- err := cache.notificationsCache.DeleteGlobalNotification(ctx, notificationId)
- return trace.Wrap(err)
-}
-
-func (globalNotificationExecutor) isSingleton() bool { return false }
-
-func (globalNotificationExecutor) getReader(cache *Cache, cacheOK bool) notificationGetter {
- if cacheOK {
- return cache.notificationsCache
- }
- return cache.Config.Notifications
-}
-
-var _ executor[*notificationsv1.GlobalNotification, notificationGetter] = globalNotificationExecutor{}
-
-type accessMonitoringRulesExecutor struct{}
-
-func (accessMonitoringRulesExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*accessmonitoringrulesv1.AccessMonitoringRule, error) {
- var resources []*accessmonitoringrulesv1.AccessMonitoringRule
- var nextToken string
- for {
- var page []*accessmonitoringrulesv1.AccessMonitoringRule
- var err error
- page, nextToken, err = cache.AccessMonitoringRules.ListAccessMonitoringRules(ctx, 0 /* page size */, nextToken)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- resources = append(resources, page...)
-
- if nextToken == "" {
- break
- }
- }
- return resources, nil
-}
-
-func (accessMonitoringRulesExecutor) upsert(ctx context.Context, cache *Cache, resource *accessmonitoringrulesv1.AccessMonitoringRule) error {
- _, err := cache.accessMontoringRuleCache.UpsertAccessMonitoringRule(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (accessMonitoringRulesExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return cache.accessMontoringRuleCache.DeleteAllAccessMonitoringRules(ctx)
-}
-
-func (accessMonitoringRulesExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return cache.accessMontoringRuleCache.DeleteAccessMonitoringRule(ctx, resource.GetName())
-}
-
-func (accessMonitoringRulesExecutor) isSingleton() bool { return false }
-
-func (accessMonitoringRulesExecutor) getReader(cache *Cache, cacheOK bool) accessMonitoringRuleGetter {
- if cacheOK {
- return cache.accessMontoringRuleCache
- }
- return cache.Config.AccessMonitoringRules
-}
-
-type accessMonitoringRuleGetter interface {
- GetAccessMonitoringRule(ctx context.Context, name string) (*accessmonitoringrulesv1.AccessMonitoringRule, error)
- ListAccessMonitoringRules(ctx context.Context, limit int, startKey string) ([]*accessmonitoringrulesv1.AccessMonitoringRule, string, error)
- ListAccessMonitoringRulesWithFilter(ctx context.Context, pageSize int, nextToken string, subjects []string, notificationName string) ([]*accessmonitoringrulesv1.AccessMonitoringRule, string, error)
-}
-
-type accessGraphSettingsExecutor struct{}
-
-func (accessGraphSettingsExecutor) getAll(ctx context.Context, cache *Cache, _ bool) ([]*clusterconfigpb.AccessGraphSettings, error) {
- set, err := cache.ClusterConfig.GetAccessGraphSettings(ctx)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- return []*clusterconfigpb.AccessGraphSettings{set}, nil
-}
-
-func (accessGraphSettingsExecutor) upsert(ctx context.Context, cache *Cache, resource *clusterconfigpb.AccessGraphSettings) error {
- _, err := cache.clusterConfigCache.UpsertAccessGraphSettings(ctx, resource)
- return trace.Wrap(err)
-}
-
-func (accessGraphSettingsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
- return trace.Wrap(cache.clusterConfigCache.DeleteAccessGraphSettings(ctx))
-}
-
-func (accessGraphSettingsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
- return trace.Wrap(cache.clusterConfigCache.DeleteAccessGraphSettings(ctx))
-}
-
-func (accessGraphSettingsExecutor) isSingleton() bool { return false }
-
-func (accessGraphSettingsExecutor) getReader(cache *Cache, cacheOK bool) accessGraphSettingsGetter {
- if cacheOK {
- return cache.clusterConfigCache
- }
- return cache.Config.ClusterConfig
-}
-
-type accessGraphSettingsGetter interface {
- GetAccessGraphSettings(context.Context) (*clusterconfigpb.AccessGraphSettings, error)
-}
-
-var _ executor[*clusterconfigpb.AccessGraphSettings, accessGraphSettingsGetter] = accessGraphSettingsExecutor{}
diff --git a/lib/cache/genericcollection.go b/lib/cache/generic_legacy_collection.go
similarity index 97%
rename from lib/cache/genericcollection.go
rename to lib/cache/generic_legacy_collection.go
index ac0260da844d9..031bf6506e741 100644
--- a/lib/cache/genericcollection.go
+++ b/lib/cache/generic_legacy_collection.go
@@ -117,7 +117,7 @@ func (g *genericCollection[T, R, _]) watchKind() types.WatchKind {
return g.watch
}
-var _ collection = (*genericCollection[types.Resource, any, executor[types.Resource, any]])(nil)
+var _ legacyCollection = (*genericCollection[types.Resource, any, executor[types.Resource, any]])(nil)
// genericCollection obtains the reader object from the executor based on the provided health status of the cache.
// Note that cacheOK set to true means that cache is overall healthy and the collection was confirmed as supported.
diff --git a/lib/cache/generic_operations.go b/lib/cache/generic_operations.go
new file mode 100644
index 0000000000000..d514dbdf9f929
--- /dev/null
+++ b/lib/cache/generic_operations.go
@@ -0,0 +1,125 @@
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package cache
+
+import (
+ "context"
+
+ "github.com/gravitational/trace"
+
+ "github.com/gravitational/teleport/api/defaults"
+)
+
+// genericGetter is a helper to retrieve a single item from a cache collection.
+type genericGetter[T any, I comparable] struct {
+ // cache to perform the primary read from.
+ cache *Cache
+ // collection that contains the item.
+ collection *collection[T, I]
+ // index of the collection to read with.
+ index I
+ // upstreamGet is used to retrieve the item if the
+ // cache is not healthy.
+ upstreamGet func(context.Context, string) (T, error)
+}
+
+// get retrieves a single item by an identifier from
+// a cache collection. If the cache is not healthy, then the item is retrieved
+// from the upstream backend. The item returned is cloned and ownership
+// is retained by the caller.
+func (g genericGetter[T, I]) get(ctx context.Context, identifier string) (T, error) {
+ var t T
+ rg, err := acquireReadGuard(g.cache, g.collection)
+ if err != nil {
+ return t, trace.Wrap(err)
+ }
+ defer rg.Release()
+
+ if !rg.ReadCache() {
+ out, err := g.upstreamGet(ctx, identifier)
+ return out, trace.Wrap(err)
+ }
+
+ out, err := rg.store.get(g.index, identifier)
+ if err != nil {
+ return t, trace.Wrap(err)
+ }
+
+ return g.collection.store.clone(out), nil
+}
+
+// genericLister is a helper to retrieve a page of items from a cache collection.
+type genericLister[T any, I comparable] struct {
+ // cache to perform the primary read from.
+ cache *Cache
+ // collection that contains the item.
+ collection *collection[T, I]
+ // index of the collection to read with.
+ index I
+ // defaultPageSize optionally defines a page size to use if
+ // one is not specified by the caller. If not set then
+ // [defaults.DefaultChunkSize] is used.
+ defaultPageSize int
+ // upstreamList is used to retrieve the items if the
+ // cache is not healthy.
+ upstreamList func(context.Context, int, string) ([]T, string, error)
+ // nextToken is used to derive the next token returned from
+ // the item at which the next page should start from.
+ nextToken func(T) string
+ // filter is an optional function used to exclude items from
+ // cache reads.
+ filter func(T) bool
+}
+
+// list retrieves a page of items from the configured cache collection.
+// If the cache is not healthy, then the items are retrieved from the upstream backend.
+// The items returned are cloned and ownership is retained by the caller.
+func (l genericLister[T, I]) list(ctx context.Context, pageSize int, startToken string) ([]T, string, error) {
+ rg, err := acquireReadGuard(l.cache, l.collection)
+ if err != nil {
+ return nil, "", trace.Wrap(err)
+ }
+ defer rg.Release()
+
+ if !rg.ReadCache() {
+ out, next, err := l.upstreamList(ctx, pageSize, startToken)
+ return out, next, trace.Wrap(err)
+ }
+
+ defaultPageSize := defaults.DefaultChunkSize
+ if l.defaultPageSize > 0 {
+ defaultPageSize = l.defaultPageSize
+ }
+
+ if pageSize <= 0 {
+ pageSize = defaultPageSize
+ }
+
+ var out []T
+ for sf := range rg.store.resources(l.index, startToken, "") {
+ if len(out) == pageSize {
+ return out, l.nextToken(sf), nil
+ }
+
+ if l.filter != nil && !l.filter(sf) {
+ continue
+ }
+ out = append(out, l.collection.store.clone(sf))
+ }
+
+ return out, "", nil
+}
diff --git a/lib/cache/generic_operations_test.go b/lib/cache/generic_operations_test.go
new file mode 100644
index 0000000000000..5fe08828efc67
--- /dev/null
+++ b/lib/cache/generic_operations_test.go
@@ -0,0 +1,147 @@
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package cache
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/gravitational/teleport/api/types"
+)
+
+func TestGetter(t *testing.T) {
+ t.Parallel()
+ p := newTestPack(t, ForAuth)
+ t.Cleanup(p.Close)
+
+ store := newStore(
+ func(role types.Role) types.Role {
+ return role
+ },
+ map[string]func(types.Role) string{
+ "default": types.Role.GetName,
+ })
+
+ require.NoError(t, store.put(&types.RoleV6{Metadata: types.Metadata{Name: "a"}}))
+
+ var upstreamRead bool
+ g := genericGetter[types.Role, string]{
+ cache: p.cache,
+ index: "default",
+ upstreamGet: func(ctx context.Context, s string) (types.Role, error) {
+ upstreamRead = true
+ return &types.RoleV6{Metadata: types.Metadata{Name: "upstream-" + s}}, nil
+ },
+ collection: &collection[types.Role, string]{
+ store: store,
+ fetcher: func(ctx context.Context, loadSecrets bool) ([]types.Role, error) {
+ return []types.Role{
+ &types.RoleV6{Metadata: types.Metadata{Name: "a"}},
+ &types.RoleV6{Metadata: types.Metadata{Name: "b"}},
+ &types.RoleV6{Metadata: types.Metadata{Name: "c"}},
+ }, nil
+ },
+ headerTransform: func(hdr *types.ResourceHeader) types.Role {
+ return &types.RoleV6{
+ Kind: hdr.Kind,
+ Version: hdr.Version,
+ Metadata: types.Metadata{
+ Name: hdr.Metadata.Name,
+ },
+ }
+ },
+ watch: types.WatchKind{Kind: types.KindRole},
+ },
+ }
+
+ out, err := g.get(context.Background(), "a")
+ require.NoError(t, err)
+ assert.Equal(t, "a", out.GetName())
+ assert.False(t, upstreamRead)
+
+ p.cache.ok = false
+
+ out, err = g.get(context.Background(), "a")
+ require.NoError(t, err)
+ assert.Equal(t, "upstream-a", out.GetName())
+ assert.True(t, upstreamRead)
+}
+
+func TestLister(t *testing.T) {
+ t.Parallel()
+ p := newTestPack(t, ForAuth)
+ t.Cleanup(p.Close)
+
+ store := newStore(
+ func(role types.Role) types.Role {
+ return role
+ },
+ map[string]func(types.Role) string{
+ "default": types.Role.GetName,
+ })
+
+ require.NoError(t, store.put(&types.RoleV6{Metadata: types.Metadata{Name: "a"}}))
+
+ var upstreamRead bool
+ g := genericLister[types.Role, string]{
+ cache: p.cache,
+ index: "default",
+ upstreamList: func(ctx context.Context, limit int, start string) ([]types.Role, string, error) {
+ upstreamRead = true
+ return []types.Role{&types.RoleV6{Metadata: types.Metadata{Name: "upstream-role"}}}, "", nil
+ },
+ collection: &collection[types.Role, string]{
+ store: store,
+ fetcher: func(ctx context.Context, loadSecrets bool) ([]types.Role, error) {
+ return []types.Role{
+ &types.RoleV6{Metadata: types.Metadata{Name: "a"}},
+ &types.RoleV6{Metadata: types.Metadata{Name: "b"}},
+ &types.RoleV6{Metadata: types.Metadata{Name: "c"}},
+ }, nil
+ },
+ headerTransform: func(hdr *types.ResourceHeader) types.Role {
+ return &types.RoleV6{
+ Kind: hdr.Kind,
+ Version: hdr.Version,
+ Metadata: types.Metadata{
+ Name: hdr.Metadata.Name,
+ },
+ }
+ },
+ watch: types.WatchKind{Kind: types.KindRole},
+ },
+ }
+
+ out, next, err := g.list(context.Background(), 10, "")
+ require.NoError(t, err)
+ require.Len(t, out, 1)
+ assert.Equal(t, "a", out[0].GetName())
+ assert.Empty(t, next)
+ assert.False(t, upstreamRead)
+
+ p.cache.ok = false
+
+ out, next, err = g.list(context.Background(), 10, "")
+ require.NoError(t, err)
+ require.Len(t, out, 1)
+ assert.Equal(t, "upstream-role", out[0].GetName())
+ assert.Empty(t, next)
+ assert.True(t, upstreamRead)
+}
diff --git a/lib/cache/git_server.go b/lib/cache/git_server.go
index b585b0b169817..75192da298334 100644
--- a/lib/cache/git_server.go
+++ b/lib/cache/git_server.go
@@ -42,7 +42,7 @@ func (c *Cache) GetGitServer(ctx context.Context, name string) (types.Server, er
ctx, span := c.Tracer.Start(ctx, "cache/GetGitServer")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.gitServers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.gitServers)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -54,7 +54,7 @@ func (c *Cache) ListGitServers(ctx context.Context, pageSize int, pageToken stri
ctx, span := c.Tracer.Start(ctx, "cache/ListGitServers")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.gitServers)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.gitServers)
if err != nil {
return nil, "", trace.Wrap(err)
}
diff --git a/lib/cache/legacy_collections.go b/lib/cache/legacy_collections.go
new file mode 100644
index 0000000000000..2749347778d0c
--- /dev/null
+++ b/lib/cache/legacy_collections.go
@@ -0,0 +1,3512 @@
+/*
+ * Teleport
+ * Copyright (C) 2023 Gravitational, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+//nolint:unused // Because the executors generate a large amount of false positives.
+package cache
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/gravitational/trace"
+
+ "github.com/gravitational/teleport/api/client"
+ "github.com/gravitational/teleport/api/client/proto"
+ apidefaults "github.com/gravitational/teleport/api/defaults"
+ accessmonitoringrulesv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1"
+ "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1"
+ clusterconfigpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1"
+ crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1"
+ dbobjectv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1"
+ identitycenterv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/identitycenter/v1"
+ kubewaitingcontainerpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/kubewaitingcontainer/v1"
+ machineidv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1"
+ notificationsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1"
+ provisioningv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/provisioning/v1"
+ userprovisioningpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/userprovisioning/v2"
+ userspb "github.com/gravitational/teleport/api/gen/proto/go/teleport/users/v1"
+ usertasksv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/usertasks/v1"
+ workloadidentityv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1"
+ "github.com/gravitational/teleport/api/types"
+ "github.com/gravitational/teleport/api/types/accesslist"
+ "github.com/gravitational/teleport/api/types/discoveryconfig"
+ "github.com/gravitational/teleport/api/types/secreports"
+ "github.com/gravitational/teleport/api/types/userloginstate"
+ "github.com/gravitational/teleport/lib/defaults"
+ "github.com/gravitational/teleport/lib/services"
+)
+
+// legacyCollection is responsible for managing collection
+// of resources updates
+type legacyCollection interface {
+ // fetch fetches resources and returns a function which will apply said resources to the cache.
+ // fetch *must* not mutate cache state outside of the apply function.
+ // The provided cacheOK flag indicates whether this collection will be included in the cache generation that is
+ // being prepared. If cacheOK is false, fetch shouldn't fetch any resources, but the apply function that it
+ // returns must still delete resources from the backend.
+ fetch(ctx context.Context, cacheOK bool) (apply func(ctx context.Context) error, err error)
+ // processEvent processes event
+ processEvent(ctx context.Context, e types.Event) error
+ // watchKind returns a watch
+ // required for this collection
+ watchKind() types.WatchKind
+}
+
+// executor[T, R] is a specific way to run the collector operations that we need
+// for the genericCollector for a generic resource type T and its reader type R.
+type executor[T any, R any] interface {
+ // getAll returns all of the target resources from the auth server.
+ // For singleton objects, this should be a size-1 slice.
+ getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]T, error)
+
+ // upsert will create or update a target resource in the cache.
+ upsert(ctx context.Context, cache *Cache, value T) error
+
+ // deleteAll will delete all target resources of the type in the cache.
+ deleteAll(ctx context.Context, cache *Cache) error
+
+ // delete will delete a single target resource from the cache. For
+ // singletons, this is usually an alias to deleteAll.
+ delete(ctx context.Context, cache *Cache, resource types.Resource) error
+
+ // isSingleton will return true if the target resource is a singleton.
+ isSingleton() bool
+
+ // getReader returns the appropriate reader type R based on the health status of the cache.
+ // Reader type R provides getter methods related to the collection, e.g. GetNodes(), GetRoles().
+ // Note that cacheOK set to true means that cache is overall healthy and the collection was confirmed as supported.
+ getReader(c *Cache, cacheOK bool) R
+}
+
+// noReader is returned by getReader for resources which aren't directly used by the cache, and therefore have no associated reader.
+type noReader struct{}
+
+type crownjewelsGetter interface {
+ ListCrownJewels(ctx context.Context, pageSize int64, nextToken string) ([]*crownjewelv1.CrownJewel, string, error)
+ GetCrownJewel(ctx context.Context, name string) (*crownjewelv1.CrownJewel, error)
+}
+
+type userTasksGetter interface {
+ ListUserTasks(ctx context.Context, pageSize int64, nextToken string, filters *usertasksv1.ListUserTasksFilters) ([]*usertasksv1.UserTask, string, error)
+ GetUserTask(ctx context.Context, name string) (*usertasksv1.UserTask, error)
+}
+
+// legacyCollections is a registry of resource collections used by Cache.
+type legacyCollections struct {
+ // byKind is a map of registered collections by resource Kind/SubKind
+ byKind map[resourceKind]legacyCollection
+
+ auditQueries collectionReader[services.SecurityAuditQueryGetter]
+ secReports collectionReader[services.SecurityReportGetter]
+ secReportsStates collectionReader[services.SecurityReportStateGetter]
+ accessLists collectionReader[accessListsGetter]
+ accessListMembers collectionReader[accessListMembersGetter]
+ accessListReviews collectionReader[accessListReviewsGetter]
+ apps collectionReader[services.AppGetter]
+ nodes collectionReader[nodeGetter]
+ tunnelConnections collectionReader[tunnelConnectionGetter]
+ appSessions collectionReader[appSessionGetter]
+ appServers collectionReader[appServerGetter]
+ authPreferences collectionReader[authPreferenceGetter]
+ authServers collectionReader[authServerGetter]
+ certAuthorities collectionReader[services.AuthorityGetter]
+ clusterAuditConfigs collectionReader[clusterAuditConfigGetter]
+ clusterNames collectionReader[clusterNameGetter]
+ clusterNetworkingConfigs collectionReader[clusterNetworkingConfigGetter]
+ databases collectionReader[services.DatabaseGetter]
+ databaseObjects collectionReader[services.DatabaseObjectsGetter]
+ databaseServers collectionReader[databaseServerGetter]
+ discoveryConfigs collectionReader[services.DiscoveryConfigsGetter]
+ installers collectionReader[installerGetter]
+ integrations collectionReader[services.IntegrationsGetter]
+ userTasks collectionReader[userTasksGetter]
+ crownJewels collectionReader[crownjewelsGetter]
+ kubeClusters collectionReader[kubernetesClusterGetter]
+ kubeWaitingContainers collectionReader[kubernetesWaitingContainerGetter]
+ staticHostUsers collectionReader[staticHostUserGetter]
+ kubeServers collectionReader[kubeServerGetter]
+ locks collectionReader[services.LockGetter]
+ namespaces collectionReader[namespaceGetter]
+ networkRestrictions collectionReader[networkRestrictionGetter]
+ oktaAssignments collectionReader[oktaAssignmentGetter]
+ oktaImportRules collectionReader[oktaImportRuleGetter]
+ proxies collectionReader[services.ProxyGetter]
+ remoteClusters collectionReader[remoteClusterGetter]
+ reverseTunnels collectionReader[reverseTunnelGetter]
+ roles collectionReader[roleGetter]
+ samlIdPServiceProviders collectionReader[samlIdPServiceProviderGetter]
+ samlIdPSessions collectionReader[samlIdPSessionGetter]
+ sessionRecordingConfigs collectionReader[sessionRecordingConfigGetter]
+ snowflakeSessions collectionReader[snowflakeSessionGetter]
+ staticTokens collectionReader[staticTokensGetter]
+ tokens collectionReader[tokenGetter]
+ uiConfigs collectionReader[uiConfigGetter]
+ users collectionReader[userGetter]
+ userGroups collectionReader[userGroupGetter]
+ userLoginStates collectionReader[services.UserLoginStatesGetter]
+ webSessions collectionReader[webSessionGetter]
+ webTokens collectionReader[webTokenGetter]
+ windowsDesktops collectionReader[windowsDesktopsGetter]
+ dynamicWindowsDesktops collectionReader[dynamicWindowsDesktopsGetter]
+ windowsDesktopServices collectionReader[windowsDesktopServiceGetter]
+ userNotifications collectionReader[notificationGetter]
+ accessGraphSettings collectionReader[accessGraphSettingsGetter]
+ globalNotifications collectionReader[notificationGetter]
+ accessMonitoringRules collectionReader[accessMonitoringRuleGetter]
+ spiffeFederations collectionReader[SPIFFEFederationReader]
+ autoUpdateConfigs collectionReader[autoUpdateConfigGetter]
+ autoUpdateVersions collectionReader[autoUpdateVersionGetter]
+ autoUpdateAgentRollouts collectionReader[autoUpdateAgentRolloutGetter]
+ provisioningStates collectionReader[provisioningStateGetter]
+ identityCenterAccounts collectionReader[identityCenterAccountGetter]
+ identityCenterPrincipalAssignments collectionReader[identityCenterPrincipalAssignmentGetter]
+ identityCenterAccountAssignments collectionReader[identityCenterAccountAssignmentGetter]
+ workloadIdentity collectionReader[WorkloadIdentityReader]
+ pluginStaticCredentials collectionReader[pluginStaticCredentialsGetter]
+ gitServers collectionReader[services.GitServerGetter]
+}
+
+// setupLegacyCollections returns a registry of legacyCollections.
+func setupLegacyCollections(c *Cache, watches []types.WatchKind) (*legacyCollections, error) {
+ collections := &legacyCollections{
+ byKind: make(map[resourceKind]legacyCollection, len(watches)),
+ }
+ for _, watch := range watches {
+ resourceKind := resourceKindFromWatchKind(watch)
+ switch watch.Kind {
+ case types.KindCertAuthority:
+ if c.Trust == nil {
+ return nil, trace.BadParameter("missing parameter Trust")
+ }
+ var filter types.CertAuthorityFilter
+ filter.FromMap(watch.Filter)
+
+ collections.certAuthorities = &genericCollection[types.CertAuthority, services.AuthorityGetter, certAuthorityExecutor]{
+ cache: c,
+ exec: certAuthorityExecutor{filter: filter},
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.certAuthorities
+ case types.KindStaticTokens:
+ if c.ClusterConfig == nil {
+ return nil, trace.BadParameter("missing parameter ClusterConfig")
+ }
+ collections.staticTokens = &genericCollection[types.StaticTokens, staticTokensGetter, staticTokensExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.staticTokens
+ case types.KindToken:
+ if c.Provisioner == nil {
+ return nil, trace.BadParameter("missing parameter Provisioner")
+ }
+ collections.tokens = &genericCollection[types.ProvisionToken, tokenGetter, provisionTokenExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.tokens
+ case types.KindClusterName:
+ if c.ClusterConfig == nil {
+ return nil, trace.BadParameter("missing parameter ClusterConfig")
+ }
+ collections.clusterNames = &genericCollection[types.ClusterName, clusterNameGetter, clusterNameExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.clusterNames
+ case types.KindClusterAuditConfig:
+ if c.ClusterConfig == nil {
+ return nil, trace.BadParameter("missing parameter ClusterConfig")
+ }
+ collections.clusterAuditConfigs = &genericCollection[types.ClusterAuditConfig, clusterAuditConfigGetter, clusterAuditConfigExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.clusterAuditConfigs
+ case types.KindClusterNetworkingConfig:
+ if c.ClusterConfig == nil {
+ return nil, trace.BadParameter("missing parameter ClusterConfig")
+ }
+ collections.clusterNetworkingConfigs = &genericCollection[types.ClusterNetworkingConfig, clusterNetworkingConfigGetter, clusterNetworkingConfigExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.clusterNetworkingConfigs
+ case types.KindClusterAuthPreference:
+ if c.ClusterConfig == nil {
+ return nil, trace.BadParameter("missing parameter ClusterConfig")
+ }
+ collections.authPreferences = &genericCollection[types.AuthPreference, authPreferenceGetter, authPreferenceExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.authPreferences
+ case types.KindSessionRecordingConfig:
+ if c.ClusterConfig == nil {
+ return nil, trace.BadParameter("missing parameter ClusterConfig")
+ }
+ collections.sessionRecordingConfigs = &genericCollection[types.SessionRecordingConfig, sessionRecordingConfigGetter, sessionRecordingConfigExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.sessionRecordingConfigs
+ case types.KindInstaller:
+ if c.ClusterConfig == nil {
+ return nil, trace.BadParameter("missing parameter ClusterConfig")
+ }
+ collections.installers = &genericCollection[types.Installer, installerGetter, installerConfigExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.installers
+ case types.KindUIConfig:
+ if c.ClusterConfig == nil {
+ return nil, trace.BadParameter("missing parameter ClusterConfig")
+ }
+ collections.uiConfigs = &genericCollection[types.UIConfig, uiConfigGetter, uiConfigExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.uiConfigs
+ case types.KindUser:
+ if c.Users == nil {
+ return nil, trace.BadParameter("missing parameter Users")
+ }
+ collections.users = &genericCollection[types.User, userGetter, userExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.users
+ case types.KindRole:
+ if c.Access == nil {
+ return nil, trace.BadParameter("missing parameter Access")
+ }
+ collections.roles = &genericCollection[types.Role, roleGetter, roleExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.roles
+ case types.KindNamespace:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.namespaces = &genericCollection[*types.Namespace, namespaceGetter, namespaceExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.namespaces
+ case types.KindNode:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.nodes = &genericCollection[types.Server, nodeGetter, nodeExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.nodes
+ case types.KindProxy:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.proxies = &genericCollection[types.Server, services.ProxyGetter, proxyExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.proxies
+ case types.KindAuthServer:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.authServers = &genericCollection[types.Server, authServerGetter, authServerExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.authServers
+ case types.KindReverseTunnel:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.reverseTunnels = &genericCollection[types.ReverseTunnel, reverseTunnelGetter, reverseTunnelExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.reverseTunnels
+ case types.KindTunnelConnection:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.tunnelConnections = &genericCollection[types.TunnelConnection, tunnelConnectionGetter, tunnelConnectionExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.tunnelConnections
+ case types.KindRemoteCluster:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.remoteClusters = &genericCollection[types.RemoteCluster, remoteClusterGetter, remoteClusterExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.remoteClusters
+ case types.KindAppServer:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.appServers = &genericCollection[types.AppServer, appServerGetter, appServerExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.appServers
+ case types.KindWebSession:
+ switch watch.SubKind {
+ case types.KindAppSession:
+ if c.AppSession == nil {
+ return nil, trace.BadParameter("missing parameter AppSession")
+ }
+ collections.appSessions = &genericCollection[types.WebSession, appSessionGetter, appSessionExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.appSessions
+ case types.KindSnowflakeSession:
+ if c.SnowflakeSession == nil {
+ return nil, trace.BadParameter("missing parameter SnowflakeSession")
+ }
+ collections.snowflakeSessions = &genericCollection[types.WebSession, snowflakeSessionGetter, snowflakeSessionExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.snowflakeSessions
+ case types.KindSAMLIdPSession:
+ if c.SAMLIdPSession == nil {
+ return nil, trace.BadParameter("missing parameter SAMLIdPSession")
+ }
+ collections.samlIdPSessions = &genericCollection[types.WebSession, samlIdPSessionGetter, samlIdPSessionExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.samlIdPSessions
+ case types.KindWebSession:
+ if c.WebSession == nil {
+ return nil, trace.BadParameter("missing parameter WebSession")
+ }
+ collections.webSessions = &genericCollection[types.WebSession, webSessionGetter, webSessionExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.webSessions
+ }
+ case types.KindWebToken:
+ if c.WebToken == nil {
+ return nil, trace.BadParameter("missing parameter WebToken")
+ }
+ collections.webTokens = &genericCollection[types.WebToken, webTokenGetter, webTokenExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.webTokens
+ case types.KindKubeServer:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.kubeServers = &genericCollection[types.KubeServer, kubeServerGetter, kubeServerExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.kubeServers
+ case types.KindDatabaseServer:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.databaseServers = &genericCollection[types.DatabaseServer, databaseServerGetter, databaseServerExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.databaseServers
+ case types.KindDatabaseService:
+ if c.DatabaseServices == nil {
+ return nil, trace.BadParameter("missing parameter DatabaseServices")
+ }
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.byKind[resourceKind] = &genericCollection[types.DatabaseService, noReader, databaseServiceExecutor]{cache: c, watch: watch}
+ case types.KindApp:
+ if c.Apps == nil {
+ return nil, trace.BadParameter("missing parameter Apps")
+ }
+ collections.apps = &genericCollection[types.Application, services.AppGetter, appExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.apps
+ case types.KindDatabase:
+ if c.Databases == nil {
+ return nil, trace.BadParameter("missing parameter Databases")
+ }
+ collections.databases = &genericCollection[types.Database, services.DatabaseGetter, databaseExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.databases
+ case types.KindDatabaseObject:
+ if c.DatabaseObjects == nil {
+ return nil, trace.BadParameter("missing parameter DatabaseObject")
+ }
+ collections.databaseObjects = &genericCollection[*dbobjectv1.DatabaseObject, services.DatabaseObjectsGetter, databaseObjectExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.databaseObjects
+ case types.KindKubernetesCluster:
+ if c.Kubernetes == nil {
+ return nil, trace.BadParameter("missing parameter Kubernetes")
+ }
+ collections.kubeClusters = &genericCollection[types.KubeCluster, kubernetesClusterGetter, kubeClusterExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.kubeClusters
+ case types.KindCrownJewel:
+ if c.CrownJewels == nil {
+ return nil, trace.BadParameter("missing parameter crownjewels")
+ }
+ collections.crownJewels = &genericCollection[*crownjewelv1.CrownJewel, crownjewelsGetter, crownJewelsExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.crownJewels
+ case types.KindNetworkRestrictions:
+ if c.Restrictions == nil {
+ return nil, trace.BadParameter("missing parameter Restrictions")
+ }
+ collections.networkRestrictions = &genericCollection[types.NetworkRestrictions, networkRestrictionGetter, networkRestrictionsExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.networkRestrictions
+ case types.KindLock:
+ if c.Access == nil {
+ return nil, trace.BadParameter("missing parameter Access")
+ }
+ collections.locks = &genericCollection[types.Lock, services.LockGetter, lockExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.locks
+ case types.KindWindowsDesktopService:
+ if c.Presence == nil {
+ return nil, trace.BadParameter("missing parameter Presence")
+ }
+ collections.windowsDesktopServices = &genericCollection[types.WindowsDesktopService, windowsDesktopServiceGetter, windowsDesktopServicesExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.windowsDesktopServices
+ case types.KindWindowsDesktop:
+ if c.WindowsDesktops == nil {
+ return nil, trace.BadParameter("missing parameter WindowsDesktops")
+ }
+ collections.windowsDesktops = &genericCollection[types.WindowsDesktop, windowsDesktopsGetter, windowsDesktopsExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.windowsDesktops
+ case types.KindDynamicWindowsDesktop:
+ if c.WindowsDesktops == nil {
+ return nil, trace.BadParameter("missing parameter DynamicWindowsDesktops")
+ }
+ collections.dynamicWindowsDesktops = &genericCollection[types.DynamicWindowsDesktop, dynamicWindowsDesktopsGetter, dynamicWindowsDesktopsExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.dynamicWindowsDesktops
+ case types.KindSAMLIdPServiceProvider:
+ if c.SAMLIdPServiceProviders == nil {
+ return nil, trace.BadParameter("missing parameter SAMLIdPServiceProviders")
+ }
+ collections.samlIdPServiceProviders = &genericCollection[types.SAMLIdPServiceProvider, samlIdPServiceProviderGetter, samlIdPServiceProvidersExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.samlIdPServiceProviders
+ case types.KindUserGroup:
+ if c.UserGroups == nil {
+ return nil, trace.BadParameter("missing parameter UserGroups")
+ }
+ collections.userGroups = &genericCollection[types.UserGroup, userGroupGetter, userGroupsExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.userGroups
+ case types.KindOktaImportRule:
+ if c.Okta == nil {
+ return nil, trace.BadParameter("missing parameter Okta")
+ }
+ collections.oktaImportRules = &genericCollection[types.OktaImportRule, oktaImportRuleGetter, oktaImportRulesExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.oktaImportRules
+ case types.KindOktaAssignment:
+ if c.Okta == nil {
+ return nil, trace.BadParameter("missing parameter Okta")
+ }
+ collections.oktaAssignments = &genericCollection[types.OktaAssignment, oktaAssignmentGetter, oktaAssignmentsExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.oktaAssignments
+ case types.KindIntegration:
+ if c.Integrations == nil {
+ return nil, trace.BadParameter("missing parameter Integrations")
+ }
+ collections.integrations = &genericCollection[types.Integration, services.IntegrationsGetter, integrationsExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.integrations
+ case types.KindUserTask:
+ if c.UserTasks == nil {
+ return nil, trace.BadParameter("missing parameter user tasks")
+ }
+ collections.userTasks = &genericCollection[*usertasksv1.UserTask, userTasksGetter, userTasksExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.userTasks
+ case types.KindDiscoveryConfig:
+ if c.DiscoveryConfigs == nil {
+ return nil, trace.BadParameter("missing parameter DiscoveryConfigs")
+ }
+ collections.discoveryConfigs = &genericCollection[*discoveryconfig.DiscoveryConfig, services.DiscoveryConfigsGetter, discoveryConfigExecutor]{cache: c, watch: watch}
+ collections.byKind[resourceKind] = collections.discoveryConfigs
+ case types.KindAuditQuery:
+ if c.SecReports == nil {
+ return nil, trace.BadParameter("missing parameter SecReports")
+ }
+ collections.auditQueries = &genericCollection[*secreports.AuditQuery, services.SecurityAuditQueryGetter, auditQueryExecutor]{cache: c, watch: watch}
+ collections.byKind[resourceKind] = collections.auditQueries
+ case types.KindSecurityReport:
+ if c.SecReports == nil {
+ return nil, trace.BadParameter("missing parameter KindSecurityReport")
+ }
+ collections.secReports = &genericCollection[*secreports.Report, services.SecurityReportGetter, secReportExecutor]{cache: c, watch: watch}
+ collections.byKind[resourceKind] = collections.secReports
+ case types.KindSecurityReportState:
+ if c.SecReports == nil {
+ return nil, trace.BadParameter("missing parameter KindSecurityReport")
+ }
+ collections.secReportsStates = &genericCollection[*secreports.ReportState, services.SecurityReportStateGetter, secReportStateExecutor]{cache: c, watch: watch}
+ collections.byKind[resourceKind] = collections.secReportsStates
+ case types.KindUserLoginState:
+ if c.UserLoginStates == nil {
+ return nil, trace.BadParameter("missing parameter UserLoginStates")
+ }
+ collections.userLoginStates = &genericCollection[*userloginstate.UserLoginState, services.UserLoginStatesGetter, userLoginStateExecutor]{cache: c, watch: watch}
+ collections.byKind[resourceKind] = collections.userLoginStates
+ case types.KindAccessList:
+ if c.AccessLists == nil {
+ return nil, trace.BadParameter("missing parameter AccessLists")
+ }
+ collections.accessLists = &genericCollection[*accesslist.AccessList, accessListsGetter, accessListExecutor]{cache: c, watch: watch}
+ collections.byKind[resourceKind] = collections.accessLists
+ case types.KindAccessListMember:
+ if c.AccessLists == nil {
+ return nil, trace.BadParameter("missing parameter AccessLists")
+ }
+ collections.accessListMembers = &genericCollection[*accesslist.AccessListMember, accessListMembersGetter, accessListMemberExecutor]{cache: c, watch: watch}
+ collections.byKind[resourceKind] = collections.accessListMembers
+ case types.KindAccessListReview:
+ if c.AccessLists == nil {
+ return nil, trace.BadParameter("missing parameter AccessLists")
+ }
+ collections.accessListReviews = &genericCollection[*accesslist.Review, accessListReviewsGetter, accessListReviewExecutor]{cache: c, watch: watch}
+ collections.byKind[resourceKind] = collections.accessListReviews
+ case types.KindKubeWaitingContainer:
+ if c.KubeWaitingContainers == nil {
+ return nil, trace.BadParameter("missing parameter KubeWaitingContainers")
+ }
+ collections.kubeWaitingContainers = &genericCollection[*kubewaitingcontainerpb.KubernetesWaitingContainer, kubernetesWaitingContainerGetter, kubeWaitingContainerExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.kubeWaitingContainers
+ case types.KindStaticHostUser:
+ if c.StaticHostUsers == nil {
+ return nil, trace.BadParameter("missing parameter StaticHostUsers")
+ }
+ collections.staticHostUsers = &genericCollection[*userprovisioningpb.StaticHostUser, staticHostUserGetter, staticHostUserExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.staticHostUsers
+ case types.KindNotification:
+ if c.Notifications == nil {
+ return nil, trace.BadParameter("missing parameter Notifications")
+ }
+ collections.userNotifications = &genericCollection[*notificationsv1.Notification, notificationGetter, userNotificationExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.userNotifications
+ case types.KindGlobalNotification:
+ if c.Notifications == nil {
+ return nil, trace.BadParameter("missing parameter Notifications")
+ }
+ collections.globalNotifications = &genericCollection[*notificationsv1.GlobalNotification, notificationGetter, globalNotificationExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.globalNotifications
+ case types.KindAccessMonitoringRule:
+ if c.AccessMonitoringRules == nil {
+ return nil, trace.BadParameter("missing parameter AccessMonitoringRule")
+ }
+ collections.accessMonitoringRules = &genericCollection[*accessmonitoringrulesv1.AccessMonitoringRule, accessMonitoringRuleGetter, accessMonitoringRulesExecutor]{cache: c, watch: watch}
+ collections.byKind[resourceKind] = collections.accessMonitoringRules
+ case types.KindAccessGraphSettings:
+ if c.ClusterConfig == nil {
+ return nil, trace.BadParameter("missing parameter ClusterConfig")
+ }
+ collections.accessGraphSettings = &genericCollection[*clusterconfigpb.AccessGraphSettings, accessGraphSettingsGetter, accessGraphSettingsExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.accessGraphSettings
+ case types.KindSPIFFEFederation:
+ if c.Config.SPIFFEFederations == nil {
+ return nil, trace.BadParameter("missing parameter SPIFFEFederations")
+ }
+ collections.spiffeFederations = &genericCollection[*machineidv1.SPIFFEFederation, SPIFFEFederationReader, spiffeFederationExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.spiffeFederations
+ case types.KindWorkloadIdentity:
+ if c.Config.WorkloadIdentity == nil {
+ return nil, trace.BadParameter("missing parameter WorkloadIdentity")
+ }
+ collections.workloadIdentity = &genericCollection[*workloadidentityv1pb.WorkloadIdentity, WorkloadIdentityReader, workloadIdentityExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.workloadIdentity
+ case types.KindAutoUpdateConfig:
+ if c.AutoUpdateService == nil {
+ return nil, trace.BadParameter("missing parameter AutoUpdateService")
+ }
+ collections.autoUpdateConfigs = &genericCollection[*autoupdate.AutoUpdateConfig, autoUpdateConfigGetter, autoUpdateConfigExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.autoUpdateConfigs
+ case types.KindAutoUpdateVersion:
+ if c.AutoUpdateService == nil {
+ return nil, trace.BadParameter("missing parameter AutoUpdateService")
+ }
+ collections.autoUpdateVersions = &genericCollection[*autoupdate.AutoUpdateVersion, autoUpdateVersionGetter, autoUpdateVersionExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.autoUpdateVersions
+ case types.KindAutoUpdateAgentRollout:
+ if c.AutoUpdateService == nil {
+ return nil, trace.BadParameter("missing parameter AutoUpdateService")
+ }
+ collections.autoUpdateAgentRollouts = &genericCollection[*autoupdate.AutoUpdateAgentRollout, autoUpdateAgentRolloutGetter, autoUpdateAgentRolloutExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.autoUpdateAgentRollouts
+
+ case types.KindProvisioningPrincipalState:
+ if c.ProvisioningStates == nil {
+ return nil, trace.BadParameter("missing parameter KindProvisioningState")
+ }
+ collections.provisioningStates = &genericCollection[*provisioningv1.PrincipalState, provisioningStateGetter, provisioningStateExecutor]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.provisioningStates
+
+ case types.KindIdentityCenterAccount:
+ if c.IdentityCenter == nil {
+ return nil, trace.BadParameter("missing upstream IdentityCenter collection")
+ }
+ collections.identityCenterAccounts = &genericCollection[
+ services.IdentityCenterAccount,
+ identityCenterAccountGetter,
+ identityCenterAccountExecutor,
+ ]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.identityCenterAccounts
+
+ case types.KindIdentityCenterPrincipalAssignment:
+ if c.IdentityCenter == nil {
+ return nil, trace.BadParameter("missing parameter IdentityCenter")
+ }
+ collections.identityCenterPrincipalAssignments = &genericCollection[
+ *identitycenterv1.PrincipalAssignment,
+ identityCenterPrincipalAssignmentGetter,
+ identityCenterPrincipalAssignmentExecutor,
+ ]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.identityCenterPrincipalAssignments
+
+ case types.KindIdentityCenterAccountAssignment:
+ if c.IdentityCenter == nil {
+ return nil, trace.BadParameter("missing parameter IdentityCenter")
+ }
+ collections.identityCenterAccountAssignments = &genericCollection[
+ services.IdentityCenterAccountAssignment,
+ identityCenterAccountAssignmentGetter,
+ identityCenterAccountAssignmentExecutor,
+ ]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.identityCenterAccountAssignments
+
+ case types.KindPluginStaticCredentials:
+ if c.PluginStaticCredentials == nil {
+ return nil, trace.BadParameter("missing parameter PluginStaticCredentials")
+ }
+ collections.pluginStaticCredentials = &genericCollection[
+ types.PluginStaticCredentials,
+ pluginStaticCredentialsGetter,
+ pluginStaticCredentialsExecutor,
+ ]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.pluginStaticCredentials
+
+ case types.KindGitServer:
+ if c.GitServers == nil {
+ return nil, trace.BadParameter("missing parameter GitServers")
+ }
+ collections.gitServers = &genericCollection[
+ types.Server,
+ services.GitServerGetter,
+ gitServerExecutor,
+ ]{
+ cache: c,
+ watch: watch,
+ }
+ collections.byKind[resourceKind] = collections.gitServers
+ }
+ }
+ return collections, nil
+}
+
+func resourceKindFromWatchKind(wk types.WatchKind) resourceKind {
+ switch wk.Kind {
+ case types.KindWebSession:
+ // Web sessions use subkind to differentiate between
+ // the types of sessions
+ return resourceKind{
+ kind: wk.Kind,
+ subkind: wk.SubKind,
+ }
+ }
+ return resourceKind{
+ kind: wk.Kind,
+ }
+}
+
+func resourceKindFromResource(res types.Resource) resourceKind {
+ switch res.GetKind() {
+ case types.KindWebSession:
+ // Web sessions use subkind to differentiate between
+ // the types of sessions
+ return resourceKind{
+ kind: res.GetKind(),
+ subkind: res.GetSubKind(),
+ }
+ }
+ return resourceKind{
+ kind: res.GetKind(),
+ }
+}
+
+type resourceKind struct {
+ kind string
+ subkind string
+}
+
+func (r resourceKind) String() string {
+ if r.subkind == "" {
+ return r.kind
+ }
+ return fmt.Sprintf("%s/%s", r.kind, r.subkind)
+}
+
+type tunnelConnectionExecutor struct{}
+
+func (tunnelConnectionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.TunnelConnection, error) {
+ return cache.Trust.GetAllTunnelConnections()
+}
+
+func (tunnelConnectionExecutor) upsert(ctx context.Context, cache *Cache, resource types.TunnelConnection) error {
+ return cache.trustCache.UpsertTunnelConnection(resource)
+}
+
+func (tunnelConnectionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.trustCache.DeleteAllTunnelConnections()
+}
+
+func (tunnelConnectionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.trustCache.DeleteTunnelConnection(resource.GetSubKind(), resource.GetName())
+}
+
+func (tunnelConnectionExecutor) isSingleton() bool { return false }
+
+func (tunnelConnectionExecutor) getReader(cache *Cache, cacheOK bool) tunnelConnectionGetter {
+ if cacheOK {
+ return cache.trustCache
+ }
+ return cache.Config.Trust
+}
+
+type tunnelConnectionGetter interface {
+ GetAllTunnelConnections(opts ...services.MarshalOption) (conns []types.TunnelConnection, err error)
+ GetTunnelConnections(clusterName string, opts ...services.MarshalOption) ([]types.TunnelConnection, error)
+}
+
+var _ executor[types.TunnelConnection, tunnelConnectionGetter] = tunnelConnectionExecutor{}
+
+type remoteClusterExecutor struct{}
+
+func (remoteClusterExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.RemoteCluster, error) {
+ return cache.Trust.GetRemoteClusters(ctx)
+}
+
+func (remoteClusterExecutor) upsert(ctx context.Context, cache *Cache, resource types.RemoteCluster) error {
+ err := cache.trustCache.DeleteRemoteCluster(ctx, resource.GetName())
+ if err != nil {
+ if !trace.IsNotFound(err) {
+ cache.Logger.WithError(err).Warnf("Failed to delete remote cluster %v.", resource.GetName())
+ return trace.Wrap(err)
+ }
+ }
+ _, err = cache.trustCache.CreateRemoteCluster(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (remoteClusterExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.trustCache.DeleteAllRemoteClusters(ctx)
+}
+
+func (remoteClusterExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.trustCache.DeleteRemoteCluster(ctx, resource.GetName())
+}
+
+func (remoteClusterExecutor) isSingleton() bool { return false }
+
+func (remoteClusterExecutor) getReader(cache *Cache, cacheOK bool) remoteClusterGetter {
+ if cacheOK {
+ return cache.trustCache
+ }
+ return cache.Config.Trust
+}
+
+type remoteClusterGetter interface {
+ GetRemoteClusters(ctx context.Context) ([]types.RemoteCluster, error)
+ GetRemoteCluster(ctx context.Context, clusterName string) (types.RemoteCluster, error)
+ ListRemoteClusters(ctx context.Context, pageSize int, pageToken string) ([]types.RemoteCluster, string, error)
+}
+
+var _ executor[types.RemoteCluster, remoteClusterGetter] = remoteClusterExecutor{}
+
+type proxyExecutor struct{}
+
+func (proxyExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Server, error) {
+ return cache.Presence.GetProxies()
+}
+
+func (proxyExecutor) upsert(ctx context.Context, cache *Cache, resource types.Server) error {
+ return cache.presenceCache.UpsertProxy(ctx, resource)
+}
+
+func (proxyExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.presenceCache.DeleteAllProxies()
+}
+
+func (proxyExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.presenceCache.DeleteProxy(ctx, resource.GetName())
+}
+
+func (proxyExecutor) isSingleton() bool { return false }
+
+func (proxyExecutor) getReader(cache *Cache, cacheOK bool) services.ProxyGetter {
+ if cacheOK {
+ return cache.presenceCache
+ }
+ return cache.Config.Presence
+}
+
+var _ executor[types.Server, services.ProxyGetter] = proxyExecutor{}
+
+type authServerExecutor struct{}
+
+func (authServerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Server, error) {
+ return cache.Presence.GetAuthServers()
+}
+
+func (authServerExecutor) upsert(ctx context.Context, cache *Cache, resource types.Server) error {
+ return cache.presenceCache.UpsertAuthServer(ctx, resource)
+}
+
+func (authServerExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.presenceCache.DeleteAllAuthServers()
+}
+
+func (authServerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.presenceCache.DeleteAuthServer(resource.GetName())
+}
+
+func (authServerExecutor) isSingleton() bool { return false }
+
+func (authServerExecutor) getReader(cache *Cache, cacheOK bool) authServerGetter {
+ if cacheOK {
+ return cache.presenceCache
+ }
+ return cache.Config.Presence
+}
+
+type authServerGetter interface {
+ GetAuthServers() ([]types.Server, error)
+}
+
+var _ executor[types.Server, authServerGetter] = authServerExecutor{}
+
+type nodeExecutor struct{}
+
+func (nodeExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Server, error) {
+ return cache.Presence.GetNodes(ctx, apidefaults.Namespace)
+}
+
+func (nodeExecutor) upsert(ctx context.Context, cache *Cache, resource types.Server) error {
+ _, err := cache.presenceCache.UpsertNode(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (nodeExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.presenceCache.DeleteAllNodes(ctx, apidefaults.Namespace)
+}
+
+func (nodeExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.presenceCache.DeleteNode(ctx, resource.GetMetadata().Namespace, resource.GetName())
+}
+
+func (nodeExecutor) isSingleton() bool { return false }
+
+func (nodeExecutor) getReader(cache *Cache, cacheOK bool) nodeGetter {
+ if cacheOK {
+ return cache.presenceCache
+ }
+ return cache.Config.Presence
+}
+
+type nodeGetter interface {
+ GetNodes(ctx context.Context, namespace string) ([]types.Server, error)
+ GetNode(ctx context.Context, namespace, name string) (types.Server, error)
+}
+
+var _ executor[types.Server, nodeGetter] = nodeExecutor{}
+
+type namespaceExecutor struct{}
+
+func (namespaceExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*types.Namespace, error) {
+ namespaces, err := cache.Presence.GetNamespaces()
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ derefNamespaces := make([]*types.Namespace, len(namespaces))
+ for i := range namespaces {
+ ns := namespaces[i]
+ derefNamespaces[i] = &ns
+ }
+ return derefNamespaces, nil
+}
+
+func (namespaceExecutor) upsert(ctx context.Context, cache *Cache, resource *types.Namespace) error {
+ return cache.presenceCache.UpsertNamespace(*resource)
+}
+
+func (namespaceExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.presenceCache.DeleteAllNamespaces()
+}
+
+func (namespaceExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.presenceCache.DeleteNamespace(resource.GetName())
+}
+
+func (namespaceExecutor) isSingleton() bool { return false }
+
+func (namespaceExecutor) getReader(cache *Cache, cacheOK bool) namespaceGetter {
+ if cacheOK {
+ return cache.presenceCache
+ }
+ return cache.Config.Presence
+}
+
+type namespaceGetter interface {
+ GetNamespaces() ([]types.Namespace, error)
+ GetNamespace(name string) (*types.Namespace, error)
+}
+
+var _ executor[*types.Namespace, namespaceGetter] = namespaceExecutor{}
+
+type certAuthorityExecutor struct {
+ // extracted from watch.Filter, to avoid rebuilding on every event
+ filter types.CertAuthorityFilter
+}
+
+// delete implements executor[types.CertAuthority]
+func (certAuthorityExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ err := cache.trustCache.DeleteCertAuthority(ctx, types.CertAuthID{
+ Type: types.CertAuthType(resource.GetSubKind()),
+ DomainName: resource.GetName(),
+ })
+ return trace.Wrap(err)
+}
+
+// deleteAll implements executor[types.CertAuthority]
+func (certAuthorityExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ for _, caType := range types.CertAuthTypes {
+ if err := cache.trustCache.DeleteAllCertAuthorities(caType); err != nil {
+ return trace.Wrap(err)
+ }
+ }
+ return nil
+}
+
+// getAll implements executor[types.CertAuthority]
+func (e certAuthorityExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.CertAuthority, error) {
+ var authorities []types.CertAuthority
+ for _, caType := range types.CertAuthTypes {
+ cas, err := cache.Trust.GetCertAuthorities(ctx, caType, loadSecrets)
+ // if caType was added in this major version we might get a BadParameter
+ // error if we're connecting to an older upstream that doesn't know about it
+ if err != nil {
+ if !types.IsUnsupportedAuthorityErr(err) || !caType.NewlyAdded() {
+ return nil, trace.Wrap(err)
+ }
+ continue
+ }
+
+ // this can be removed once we get the ability to fetch CAs with a filter,
+ // but it should be harmless, and it could be kept as additional safety
+ if !e.filter.IsEmpty() {
+ filtered := cas[:0]
+ for _, ca := range cas {
+ if e.filter.Match(ca) {
+ filtered = append(filtered, ca)
+ }
+ }
+ cas = filtered
+ }
+
+ authorities = append(authorities, cas...)
+ }
+
+ return authorities, nil
+}
+
+// upsert implements executor[types.CertAuthority]
+func (e certAuthorityExecutor) upsert(ctx context.Context, cache *Cache, value types.CertAuthority) error {
+ if !e.filter.Match(value) {
+ return nil
+ }
+
+ return cache.trustCache.UpsertCertAuthority(ctx, value)
+}
+
+func (certAuthorityExecutor) isSingleton() bool { return false }
+
+func (certAuthorityExecutor) getReader(cache *Cache, cacheOK bool) services.AuthorityGetter {
+ if cacheOK {
+ return cache.trustCache
+ }
+ return cache.Config.Trust
+}
+
+var _ executor[types.CertAuthority, services.AuthorityGetter] = certAuthorityExecutor{}
+
+type staticTokensExecutor struct{}
+
+func (staticTokensExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.StaticTokens, error) {
+ token, err := cache.ClusterConfig.GetStaticTokens()
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return []types.StaticTokens{token}, nil
+}
+
+func (staticTokensExecutor) upsert(ctx context.Context, cache *Cache, resource types.StaticTokens) error {
+ return cache.clusterConfigCache.SetStaticTokens(resource)
+}
+
+func (staticTokensExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.clusterConfigCache.DeleteStaticTokens()
+}
+
+func (staticTokensExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.clusterConfigCache.DeleteStaticTokens()
+}
+
+func (staticTokensExecutor) isSingleton() bool { return true }
+
+func (staticTokensExecutor) getReader(cache *Cache, cacheOK bool) staticTokensGetter {
+ if cacheOK {
+ return cache.clusterConfigCache
+ }
+ return cache.Config.ClusterConfig
+}
+
+type staticTokensGetter interface {
+ GetStaticTokens() (types.StaticTokens, error)
+}
+
+var _ executor[types.StaticTokens, staticTokensGetter] = staticTokensExecutor{}
+
+type provisionTokenExecutor struct{}
+
+func (provisionTokenExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.ProvisionToken, error) {
+ return cache.Provisioner.GetTokens(ctx)
+}
+
+func (provisionTokenExecutor) upsert(ctx context.Context, cache *Cache, resource types.ProvisionToken) error {
+ return cache.provisionerCache.UpsertToken(ctx, resource)
+}
+
+func (provisionTokenExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.provisionerCache.DeleteAllTokens()
+}
+
+func (provisionTokenExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.provisionerCache.DeleteToken(ctx, resource.GetName())
+}
+
+func (provisionTokenExecutor) isSingleton() bool { return false }
+
+func (provisionTokenExecutor) getReader(cache *Cache, cacheOK bool) tokenGetter {
+ if cacheOK {
+ return cache.provisionerCache
+ }
+ return cache.Config.Provisioner
+}
+
+type tokenGetter interface {
+ GetTokens(ctx context.Context) ([]types.ProvisionToken, error)
+ GetToken(ctx context.Context, token string) (types.ProvisionToken, error)
+}
+
+var _ executor[types.ProvisionToken, tokenGetter] = provisionTokenExecutor{}
+
+type clusterNameExecutor struct{}
+
+func (clusterNameExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.ClusterName, error) {
+ name, err := cache.ClusterConfig.GetClusterName()
+ return []types.ClusterName{name}, trace.Wrap(err)
+}
+
+func (clusterNameExecutor) upsert(ctx context.Context, cache *Cache, resource types.ClusterName) error {
+ return cache.clusterConfigCache.UpsertClusterName(resource)
+}
+
+func (clusterNameExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.clusterConfigCache.DeleteClusterName()
+}
+
+func (clusterNameExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.clusterConfigCache.DeleteClusterName()
+}
+
+func (clusterNameExecutor) isSingleton() bool { return true }
+
+func (clusterNameExecutor) getReader(cache *Cache, cacheOK bool) clusterNameGetter {
+ if cacheOK {
+ return cache.clusterConfigCache
+ }
+ return cache.Config.ClusterConfig
+}
+
+type clusterNameGetter interface {
+ GetClusterName(opts ...services.MarshalOption) (types.ClusterName, error)
+}
+
+var _ executor[types.ClusterName, clusterNameGetter] = clusterNameExecutor{}
+
+type autoUpdateConfigExecutor struct{}
+
+func (autoUpdateConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*autoupdate.AutoUpdateConfig, error) {
+ config, err := cache.AutoUpdateService.GetAutoUpdateConfig(ctx)
+ return []*autoupdate.AutoUpdateConfig{config}, trace.Wrap(err)
+}
+
+func (autoUpdateConfigExecutor) upsert(ctx context.Context, cache *Cache, resource *autoupdate.AutoUpdateConfig) error {
+ _, err := cache.autoUpdateCache.UpsertAutoUpdateConfig(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (autoUpdateConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.autoUpdateCache.DeleteAutoUpdateConfig(ctx)
+}
+
+func (autoUpdateConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.autoUpdateCache.DeleteAutoUpdateConfig(ctx)
+}
+
+func (autoUpdateConfigExecutor) isSingleton() bool { return true }
+
+func (autoUpdateConfigExecutor) getReader(cache *Cache, cacheOK bool) autoUpdateConfigGetter {
+ if cacheOK {
+ return cache.autoUpdateCache
+ }
+ return cache.Config.AutoUpdateService
+}
+
+type autoUpdateConfigGetter interface {
+ GetAutoUpdateConfig(ctx context.Context) (*autoupdate.AutoUpdateConfig, error)
+}
+
+var _ executor[*autoupdate.AutoUpdateConfig, autoUpdateConfigGetter] = autoUpdateConfigExecutor{}
+
+type autoUpdateVersionExecutor struct{}
+
+func (autoUpdateVersionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*autoupdate.AutoUpdateVersion, error) {
+ version, err := cache.AutoUpdateService.GetAutoUpdateVersion(ctx)
+ return []*autoupdate.AutoUpdateVersion{version}, trace.Wrap(err)
+}
+
+func (autoUpdateVersionExecutor) upsert(ctx context.Context, cache *Cache, resource *autoupdate.AutoUpdateVersion) error {
+ _, err := cache.autoUpdateCache.UpsertAutoUpdateVersion(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (autoUpdateVersionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.autoUpdateCache.DeleteAutoUpdateVersion(ctx)
+}
+
+func (autoUpdateVersionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.autoUpdateCache.DeleteAutoUpdateVersion(ctx)
+}
+
+func (autoUpdateVersionExecutor) isSingleton() bool { return true }
+
+func (autoUpdateVersionExecutor) getReader(cache *Cache, cacheOK bool) autoUpdateVersionGetter {
+ if cacheOK {
+ return cache.autoUpdateCache
+ }
+ return cache.Config.AutoUpdateService
+}
+
+type autoUpdateVersionGetter interface {
+ GetAutoUpdateVersion(ctx context.Context) (*autoupdate.AutoUpdateVersion, error)
+}
+
+var _ executor[*autoupdate.AutoUpdateVersion, autoUpdateVersionGetter] = autoUpdateVersionExecutor{}
+
+type autoUpdateAgentRolloutExecutor struct{}
+
+func (autoUpdateAgentRolloutExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*autoupdate.AutoUpdateAgentRollout, error) {
+ plan, err := cache.AutoUpdateService.GetAutoUpdateAgentRollout(ctx)
+ return []*autoupdate.AutoUpdateAgentRollout{plan}, trace.Wrap(err)
+}
+
+func (autoUpdateAgentRolloutExecutor) upsert(ctx context.Context, cache *Cache, resource *autoupdate.AutoUpdateAgentRollout) error {
+ _, err := cache.autoUpdateCache.UpsertAutoUpdateAgentRollout(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (autoUpdateAgentRolloutExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.autoUpdateCache.DeleteAutoUpdateAgentRollout(ctx)
+}
+
+func (autoUpdateAgentRolloutExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.autoUpdateCache.DeleteAutoUpdateAgentRollout(ctx)
+}
+
+func (autoUpdateAgentRolloutExecutor) isSingleton() bool { return true }
+
+func (autoUpdateAgentRolloutExecutor) getReader(cache *Cache, cacheOK bool) autoUpdateAgentRolloutGetter {
+ if cacheOK {
+ return cache.autoUpdateCache
+ }
+ return cache.Config.AutoUpdateService
+}
+
+type autoUpdateAgentRolloutGetter interface {
+ GetAutoUpdateAgentRollout(ctx context.Context) (*autoupdate.AutoUpdateAgentRollout, error)
+}
+
+var _ executor[*autoupdate.AutoUpdateAgentRollout, autoUpdateAgentRolloutGetter] = autoUpdateAgentRolloutExecutor{}
+
+type userExecutor struct{}
+
+func (userExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.User, error) {
+ return cache.Users.GetUsers(ctx, loadSecrets)
+}
+
+func (userExecutor) upsert(ctx context.Context, cache *Cache, resource types.User) error {
+ _, err := cache.usersCache.UpsertUser(ctx, resource)
+ return err
+}
+
+func (userExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.usersCache.DeleteAllUsers(ctx)
+}
+
+func (userExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.usersCache.DeleteUser(ctx, resource.GetName())
+}
+
+func (userExecutor) isSingleton() bool { return false }
+
+func (userExecutor) getReader(cache *Cache, cacheOK bool) userGetter {
+ if cacheOK {
+ return cache.usersCache
+ }
+ return cache.Config.Users
+}
+
+type userGetter interface {
+ GetUser(ctx context.Context, user string, withSecrets bool) (types.User, error)
+ GetUsers(ctx context.Context, withSecrets bool) ([]types.User, error)
+ ListUsers(ctx context.Context, req *userspb.ListUsersRequest) (*userspb.ListUsersResponse, error)
+}
+
+var _ executor[types.User, userGetter] = userExecutor{}
+
+type roleExecutor struct{}
+
+func (roleExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Role, error) {
+ return cache.Access.GetRoles(ctx)
+}
+
+func (roleExecutor) upsert(ctx context.Context, cache *Cache, resource types.Role) error {
+ _, err := cache.accessCache.UpsertRole(ctx, resource)
+ return err
+}
+
+func (roleExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.accessCache.DeleteAllRoles(ctx)
+}
+
+func (roleExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.accessCache.DeleteRole(ctx, resource.GetName())
+}
+
+func (roleExecutor) isSingleton() bool { return false }
+
+func (roleExecutor) getReader(cache *Cache, cacheOK bool) roleGetter {
+ if cacheOK {
+ return cache.accessCache
+ }
+ return cache.Config.Access
+}
+
+type roleGetter interface {
+ GetRoles(ctx context.Context) ([]types.Role, error)
+ GetRole(ctx context.Context, name string) (types.Role, error)
+ ListRoles(ctx context.Context, req *proto.ListRolesRequest) (*proto.ListRolesResponse, error)
+}
+
+var _ executor[types.Role, roleGetter] = roleExecutor{}
+
+type databaseServerExecutor struct{}
+
+func (databaseServerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.DatabaseServer, error) {
+ return cache.Presence.GetDatabaseServers(ctx, apidefaults.Namespace)
+}
+
+func (databaseServerExecutor) upsert(ctx context.Context, cache *Cache, resource types.DatabaseServer) error {
+ _, err := cache.presenceCache.UpsertDatabaseServer(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (databaseServerExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.presenceCache.DeleteAllDatabaseServers(ctx, apidefaults.Namespace)
+}
+
+func (databaseServerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.presenceCache.DeleteDatabaseServer(ctx,
+ resource.GetMetadata().Namespace,
+ resource.GetMetadata().Description, // Cache passes host ID via description field.
+ resource.GetName())
+}
+
+func (databaseServerExecutor) isSingleton() bool { return false }
+
+func (databaseServerExecutor) getReader(cache *Cache, cacheOK bool) databaseServerGetter {
+ if cacheOK {
+ return cache.presenceCache
+ }
+ return cache.Config.Presence
+}
+
+type databaseServerGetter interface {
+ GetDatabaseServers(context.Context, string, ...services.MarshalOption) ([]types.DatabaseServer, error)
+}
+
+var _ executor[types.DatabaseServer, databaseServerGetter] = databaseServerExecutor{}
+
+type databaseServiceExecutor struct{}
+
+func (databaseServiceExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.DatabaseService, error) {
+ resources, err := client.GetResourcesWithFilters(ctx, cache.Presence, proto.ListResourcesRequest{ResourceType: types.KindDatabaseService})
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ dbsvcs := make([]types.DatabaseService, len(resources))
+ for i, resource := range resources {
+ dbsvc, ok := resource.(types.DatabaseService)
+ if !ok {
+ return nil, trace.BadParameter("unexpected resource %T", resource)
+ }
+ dbsvcs[i] = dbsvc
+ }
+
+ return dbsvcs, nil
+}
+
+func (databaseServiceExecutor) upsert(ctx context.Context, cache *Cache, resource types.DatabaseService) error {
+ _, err := cache.databaseServicesCache.UpsertDatabaseService(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (databaseServiceExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.databaseServicesCache.DeleteAllDatabaseServices(ctx)
+}
+
+func (databaseServiceExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.databaseServicesCache.DeleteDatabaseService(ctx, resource.GetName())
+}
+
+func (databaseServiceExecutor) isSingleton() bool { return false }
+
+func (databaseServiceExecutor) getReader(_ *Cache, _ bool) noReader {
+ return noReader{}
+}
+
+var _ executor[types.DatabaseService, noReader] = databaseServiceExecutor{}
+
+type databaseExecutor struct{}
+
+func (databaseExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Database, error) {
+ return cache.Databases.GetDatabases(ctx)
+}
+
+func (databaseExecutor) upsert(ctx context.Context, cache *Cache, resource types.Database) error {
+ if err := cache.databasesCache.CreateDatabase(ctx, resource); err != nil {
+ if !trace.IsAlreadyExists(err) {
+ return trace.Wrap(err)
+ }
+ return trace.Wrap(cache.databasesCache.UpdateDatabase(ctx, resource))
+ }
+
+ return nil
+}
+
+func (databaseExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.databasesCache.DeleteAllDatabases(ctx)
+}
+
+func (databaseExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.databasesCache.DeleteDatabase(ctx, resource.GetName())
+}
+
+func (databaseExecutor) isSingleton() bool { return false }
+
+func (databaseExecutor) getReader(cache *Cache, cacheOK bool) services.DatabaseGetter {
+ if cacheOK {
+ return cache.databasesCache
+ }
+ return cache.Config.Databases
+}
+
+var _ executor[types.Database, services.DatabaseGetter] = databaseExecutor{}
+
+type databaseObjectExecutor struct{}
+
+func (databaseObjectExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*dbobjectv1.DatabaseObject, error) {
+ var out []*dbobjectv1.DatabaseObject
+ var nextToken string
+ for {
+ var page []*dbobjectv1.DatabaseObject
+ var err error
+
+ page, nextToken, err = cache.DatabaseObjects.ListDatabaseObjects(ctx, 0, nextToken)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ out = append(out, page...)
+ if nextToken == "" {
+ break
+ }
+ }
+ return out, nil
+}
+
+func (databaseObjectExecutor) upsert(ctx context.Context, cache *Cache, resource *dbobjectv1.DatabaseObject) error {
+ _, err := cache.databaseObjectsCache.UpsertDatabaseObject(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (databaseObjectExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return trace.Wrap(cache.databaseObjectsCache.DeleteAllDatabaseObjects(ctx))
+}
+
+func (databaseObjectExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return trace.Wrap(cache.databaseObjectsCache.DeleteDatabaseObject(ctx, resource.GetName()))
+}
+
+func (databaseObjectExecutor) isSingleton() bool { return false }
+
+func (databaseObjectExecutor) getReader(cache *Cache, cacheOK bool) services.DatabaseObjectsGetter {
+ if cacheOK {
+ return cache.databaseObjectsCache
+ }
+ return cache.Config.DatabaseObjects
+}
+
+var _ executor[*dbobjectv1.DatabaseObject, services.DatabaseObjectsGetter] = databaseObjectExecutor{}
+
+type appExecutor struct{}
+
+func (appExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Application, error) {
+ return cache.Apps.GetApps(ctx)
+}
+
+func (appExecutor) upsert(ctx context.Context, cache *Cache, resource types.Application) error {
+ if err := cache.appsCache.CreateApp(ctx, resource); err != nil {
+ if !trace.IsAlreadyExists(err) {
+ return trace.Wrap(err)
+ }
+ return trace.Wrap(cache.appsCache.UpdateApp(ctx, resource))
+ }
+
+ return nil
+}
+
+func (appExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.appsCache.DeleteAllApps(ctx)
+}
+
+func (appExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.appsCache.DeleteApp(ctx, resource.GetName())
+}
+
+func (appExecutor) getReader(cache *Cache, cacheOK bool) services.AppGetter {
+ if cacheOK {
+ return cache.appsCache
+ }
+ return cache.Apps
+}
+
+func (appExecutor) isSingleton() bool { return false }
+
+var _ executor[types.Application, services.AppGetter] = appExecutor{}
+
+type appServerExecutor struct{}
+
+func (appServerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.AppServer, error) {
+ return cache.Presence.GetApplicationServers(ctx, apidefaults.Namespace)
+}
+
+func (appServerExecutor) upsert(ctx context.Context, cache *Cache, resource types.AppServer) error {
+ _, err := cache.presenceCache.UpsertApplicationServer(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (appServerExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.presenceCache.DeleteAllApplicationServers(ctx, apidefaults.Namespace)
+}
+
+func (appServerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.presenceCache.DeleteApplicationServer(ctx,
+ resource.GetMetadata().Namespace,
+ resource.GetMetadata().Description, // Cache passes host ID via description field.
+ resource.GetName())
+}
+
+func (appServerExecutor) isSingleton() bool { return false }
+
+func (appServerExecutor) getReader(cache *Cache, cacheOK bool) appServerGetter {
+ if cacheOK {
+ return cache.presenceCache
+ }
+ return cache.Config.Presence
+}
+
+type appServerGetter interface {
+ GetApplicationServers(context.Context, string) ([]types.AppServer, error)
+}
+
+var _ executor[types.AppServer, appServerGetter] = appServerExecutor{}
+
+type appSessionExecutor struct{}
+
+func (appSessionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WebSession, error) {
+ var (
+ startKey string
+ sessions []types.WebSession
+ )
+ for {
+ webSessions, nextKey, err := cache.AppSession.ListAppSessions(ctx, 0, startKey, "")
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if !loadSecrets {
+ for i := 0; i < len(webSessions); i++ {
+ webSessions[i] = webSessions[i].WithoutSecrets()
+ }
+ }
+
+ sessions = append(sessions, webSessions...)
+
+ if nextKey == "" {
+ break
+ }
+ startKey = nextKey
+ }
+ return sessions, nil
+}
+
+func (appSessionExecutor) upsert(ctx context.Context, cache *Cache, resource types.WebSession) error {
+ return cache.appSessionCache.UpsertAppSession(ctx, resource)
+}
+
+func (appSessionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.appSessionCache.DeleteAllAppSessions(ctx)
+}
+
+func (appSessionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.appSessionCache.DeleteAppSession(ctx, types.DeleteAppSessionRequest{
+ SessionID: resource.GetName(),
+ })
+}
+
+func (appSessionExecutor) isSingleton() bool { return false }
+
+func (appSessionExecutor) getReader(cache *Cache, cacheOK bool) appSessionGetter {
+ if cacheOK {
+ return cache.appSessionCache
+ }
+ return cache.Config.AppSession
+}
+
+type appSessionGetter interface {
+ GetAppSession(ctx context.Context, req types.GetAppSessionRequest) (types.WebSession, error)
+ ListAppSessions(ctx context.Context, pageSize int, pageToken, user string) ([]types.WebSession, string, error)
+}
+
+var _ executor[types.WebSession, appSessionGetter] = appSessionExecutor{}
+
+type snowflakeSessionExecutor struct{}
+
+func (snowflakeSessionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WebSession, error) {
+ webSessions, err := cache.SnowflakeSession.GetSnowflakeSessions(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if !loadSecrets {
+ for i := 0; i < len(webSessions); i++ {
+ webSessions[i] = webSessions[i].WithoutSecrets()
+ }
+ }
+
+ return webSessions, nil
+}
+
+func (snowflakeSessionExecutor) upsert(ctx context.Context, cache *Cache, resource types.WebSession) error {
+ return cache.snowflakeSessionCache.UpsertSnowflakeSession(ctx, resource)
+}
+
+func (snowflakeSessionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.snowflakeSessionCache.DeleteAllSnowflakeSessions(ctx)
+}
+
+func (snowflakeSessionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.snowflakeSessionCache.DeleteSnowflakeSession(ctx, types.DeleteSnowflakeSessionRequest{
+ SessionID: resource.GetName(),
+ })
+}
+
+func (snowflakeSessionExecutor) isSingleton() bool { return false }
+
+func (snowflakeSessionExecutor) getReader(cache *Cache, cacheOK bool) snowflakeSessionGetter {
+ if cacheOK {
+ return cache.snowflakeSessionCache
+ }
+ return cache.Config.SnowflakeSession
+}
+
+type snowflakeSessionGetter interface {
+ GetSnowflakeSession(context.Context, types.GetSnowflakeSessionRequest) (types.WebSession, error)
+}
+
+var _ executor[types.WebSession, snowflakeSessionGetter] = snowflakeSessionExecutor{}
+
+//nolint:revive // Because we want this to be IdP.
+type samlIdPSessionExecutor struct{}
+
+func (samlIdPSessionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WebSession, error) {
+ var (
+ startKey string
+ sessions []types.WebSession
+ )
+ for {
+ webSessions, nextKey, err := cache.SAMLIdPSession.ListSAMLIdPSessions(ctx, 0, startKey, "")
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if !loadSecrets {
+ for i := 0; i < len(webSessions); i++ {
+ webSessions[i] = webSessions[i].WithoutSecrets()
+ }
+ }
+
+ sessions = append(sessions, webSessions...)
+
+ if nextKey == "" {
+ break
+ }
+ startKey = nextKey
+ }
+ return sessions, nil
+}
+
+func (samlIdPSessionExecutor) upsert(ctx context.Context, cache *Cache, resource types.WebSession) error {
+ return cache.samlIdPSessionCache.UpsertSAMLIdPSession(ctx, resource)
+}
+
+func (samlIdPSessionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.samlIdPSessionCache.DeleteAllSAMLIdPSessions(ctx)
+}
+
+func (samlIdPSessionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.samlIdPSessionCache.DeleteSAMLIdPSession(ctx, types.DeleteSAMLIdPSessionRequest{
+ SessionID: resource.GetName(),
+ })
+}
+
+func (samlIdPSessionExecutor) isSingleton() bool { return false }
+
+func (samlIdPSessionExecutor) getReader(cache *Cache, cacheOK bool) samlIdPSessionGetter {
+ if cacheOK {
+ return cache.samlIdPSessionCache
+ }
+ return cache.Config.SAMLIdPSession
+}
+
+type samlIdPSessionGetter interface {
+ GetSAMLIdPSession(context.Context, types.GetSAMLIdPSessionRequest) (types.WebSession, error)
+}
+
+var _ executor[types.WebSession, samlIdPSessionGetter] = samlIdPSessionExecutor{}
+
+type webSessionExecutor struct{}
+
+func (webSessionExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WebSession, error) {
+ webSessions, err := cache.WebSession.List(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if !loadSecrets {
+ for i := 0; i < len(webSessions); i++ {
+ webSessions[i] = webSessions[i].WithoutSecrets()
+ }
+ }
+
+ return webSessions, nil
+}
+
+func (webSessionExecutor) upsert(ctx context.Context, cache *Cache, resource types.WebSession) error {
+ return cache.webSessionCache.Upsert(ctx, resource)
+}
+
+func (webSessionExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.webSessionCache.DeleteAll(ctx)
+}
+
+func (webSessionExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.webSessionCache.Delete(ctx, types.DeleteWebSessionRequest{
+ SessionID: resource.GetName(),
+ })
+}
+
+func (webSessionExecutor) isSingleton() bool { return false }
+
+func (webSessionExecutor) getReader(cache *Cache, cacheOK bool) webSessionGetter {
+ if cacheOK {
+ return cache.webSessionCache
+ }
+ return cache.Config.WebSession
+}
+
+type webSessionGetter interface {
+ Get(ctx context.Context, req types.GetWebSessionRequest) (types.WebSession, error)
+}
+
+var _ executor[types.WebSession, webSessionGetter] = webSessionExecutor{}
+
+type webTokenExecutor struct{}
+
+func (webTokenExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WebToken, error) {
+ return cache.WebToken.List(ctx)
+}
+
+func (webTokenExecutor) upsert(ctx context.Context, cache *Cache, resource types.WebToken) error {
+ return cache.webTokenCache.Upsert(ctx, resource)
+}
+
+func (webTokenExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.webTokenCache.DeleteAll(ctx)
+}
+
+func (webTokenExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.webTokenCache.Delete(ctx, types.DeleteWebTokenRequest{
+ Token: resource.GetName(),
+ })
+}
+
+func (webTokenExecutor) isSingleton() bool { return false }
+
+func (webTokenExecutor) getReader(cache *Cache, cacheOK bool) webTokenGetter {
+ if cacheOK {
+ return cache.webTokenCache
+ }
+ return cache.Config.WebToken
+}
+
+type webTokenGetter interface {
+ Get(ctx context.Context, req types.GetWebTokenRequest) (types.WebToken, error)
+}
+
+var _ executor[types.WebToken, webTokenGetter] = webTokenExecutor{}
+
+type kubeServerExecutor struct{}
+
+func (kubeServerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.KubeServer, error) {
+ return cache.Presence.GetKubernetesServers(ctx)
+}
+
+func (kubeServerExecutor) upsert(ctx context.Context, cache *Cache, resource types.KubeServer) error {
+ _, err := cache.presenceCache.UpsertKubernetesServer(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (kubeServerExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.presenceCache.DeleteAllKubernetesServers(ctx)
+}
+
+func (kubeServerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.presenceCache.DeleteKubernetesServer(
+ ctx,
+ resource.GetMetadata().Description, // Cache passes host ID via description field.
+ resource.GetName(),
+ )
+}
+
+func (kubeServerExecutor) isSingleton() bool { return false }
+
+func (kubeServerExecutor) getReader(cache *Cache, cacheOK bool) kubeServerGetter {
+ if cacheOK {
+ return cache.presenceCache
+ }
+ return cache.Config.Presence
+}
+
+type kubeServerGetter interface {
+ GetKubernetesServers(context.Context) ([]types.KubeServer, error)
+}
+
+var _ executor[types.KubeServer, kubeServerGetter] = kubeServerExecutor{}
+
+type authPreferenceExecutor struct{}
+
+func (authPreferenceExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.AuthPreference, error) {
+ authPref, err := cache.ClusterConfig.GetAuthPreference(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return []types.AuthPreference{authPref}, nil
+}
+
+func (authPreferenceExecutor) upsert(ctx context.Context, cache *Cache, resource types.AuthPreference) error {
+ _, err := cache.clusterConfigCache.UpsertAuthPreference(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (authPreferenceExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.clusterConfigCache.DeleteAuthPreference(ctx)
+}
+
+func (authPreferenceExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.clusterConfigCache.DeleteAuthPreference(ctx)
+}
+
+func (authPreferenceExecutor) isSingleton() bool { return true }
+
+func (authPreferenceExecutor) getReader(cache *Cache, cacheOK bool) authPreferenceGetter {
+ if cacheOK {
+ return cache.clusterConfigCache
+ }
+ return cache.Config.ClusterConfig
+}
+
+type authPreferenceGetter interface {
+ GetAuthPreference(ctx context.Context) (types.AuthPreference, error)
+}
+
+var _ executor[types.AuthPreference, authPreferenceGetter] = authPreferenceExecutor{}
+
+type clusterAuditConfigExecutor struct{}
+
+func (clusterAuditConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.ClusterAuditConfig, error) {
+ auditConfig, err := cache.ClusterConfig.GetClusterAuditConfig(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return []types.ClusterAuditConfig{auditConfig}, nil
+}
+
+func (clusterAuditConfigExecutor) upsert(ctx context.Context, cache *Cache, resource types.ClusterAuditConfig) error {
+ return cache.clusterConfigCache.SetClusterAuditConfig(ctx, resource)
+}
+
+func (clusterAuditConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.clusterConfigCache.DeleteClusterAuditConfig(ctx)
+}
+
+func (clusterAuditConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.clusterConfigCache.DeleteClusterAuditConfig(ctx)
+}
+
+func (clusterAuditConfigExecutor) isSingleton() bool { return true }
+
+func (clusterAuditConfigExecutor) getReader(cache *Cache, cacheOK bool) clusterAuditConfigGetter {
+ if cacheOK {
+ return cache.clusterConfigCache
+ }
+ return cache.Config.ClusterConfig
+}
+
+type clusterAuditConfigGetter interface {
+ GetClusterAuditConfig(context.Context) (types.ClusterAuditConfig, error)
+}
+
+var _ executor[types.ClusterAuditConfig, clusterAuditConfigGetter] = clusterAuditConfigExecutor{}
+
+type clusterNetworkingConfigExecutor struct{}
+
+func (clusterNetworkingConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.ClusterNetworkingConfig, error) {
+ networkingConfig, err := cache.ClusterConfig.GetClusterNetworkingConfig(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return []types.ClusterNetworkingConfig{networkingConfig}, nil
+}
+
+func (clusterNetworkingConfigExecutor) upsert(ctx context.Context, cache *Cache, resource types.ClusterNetworkingConfig) error {
+ _, err := cache.clusterConfigCache.UpsertClusterNetworkingConfig(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (clusterNetworkingConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.clusterConfigCache.DeleteClusterNetworkingConfig(ctx)
+}
+
+func (clusterNetworkingConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.clusterConfigCache.DeleteClusterNetworkingConfig(ctx)
+}
+
+func (clusterNetworkingConfigExecutor) isSingleton() bool { return true }
+
+func (clusterNetworkingConfigExecutor) getReader(cache *Cache, cacheOK bool) clusterNetworkingConfigGetter {
+ if cacheOK {
+ return cache.clusterConfigCache
+ }
+ return cache.Config.ClusterConfig
+}
+
+type clusterNetworkingConfigGetter interface {
+ GetClusterNetworkingConfig(context.Context) (types.ClusterNetworkingConfig, error)
+}
+
+var _ executor[types.ClusterNetworkingConfig, clusterNetworkingConfigGetter] = clusterNetworkingConfigExecutor{}
+
+type uiConfigExecutor struct{}
+
+func (uiConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.UIConfig, error) {
+ uiConfig, err := cache.ClusterConfig.GetUIConfig(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return []types.UIConfig{uiConfig}, nil
+}
+
+func (uiConfigExecutor) upsert(ctx context.Context, cache *Cache, resource types.UIConfig) error {
+ return cache.clusterConfigCache.SetUIConfig(ctx, resource)
+}
+
+func (uiConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.clusterConfigCache.DeleteUIConfig(ctx)
+}
+
+func (uiConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.clusterConfigCache.DeleteUIConfig(ctx)
+}
+
+func (uiConfigExecutor) isSingleton() bool { return true }
+
+func (uiConfigExecutor) getReader(cache *Cache, cacheOK bool) uiConfigGetter {
+ if cacheOK {
+ return cache.clusterConfigCache
+ }
+ return cache.Config.ClusterConfig
+}
+
+type uiConfigGetter interface {
+ GetUIConfig(context.Context) (types.UIConfig, error)
+}
+
+var _ executor[types.UIConfig, uiConfigGetter] = uiConfigExecutor{}
+
+type sessionRecordingConfigExecutor struct{}
+
+func (sessionRecordingConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.SessionRecordingConfig, error) {
+ sessionRecordingConfig, err := cache.ClusterConfig.GetSessionRecordingConfig(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return []types.SessionRecordingConfig{sessionRecordingConfig}, nil
+}
+
+func (sessionRecordingConfigExecutor) upsert(ctx context.Context, cache *Cache, resource types.SessionRecordingConfig) error {
+ _, err := cache.clusterConfigCache.UpsertSessionRecordingConfig(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (sessionRecordingConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.clusterConfigCache.DeleteSessionRecordingConfig(ctx)
+}
+
+func (sessionRecordingConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.clusterConfigCache.DeleteSessionRecordingConfig(ctx)
+}
+
+func (sessionRecordingConfigExecutor) isSingleton() bool { return true }
+
+func (sessionRecordingConfigExecutor) getReader(cache *Cache, cacheOK bool) sessionRecordingConfigGetter {
+ if cacheOK {
+ return cache.clusterConfigCache
+ }
+ return cache.Config.ClusterConfig
+}
+
+type sessionRecordingConfigGetter interface {
+ GetSessionRecordingConfig(ctx context.Context) (types.SessionRecordingConfig, error)
+}
+
+var _ executor[types.SessionRecordingConfig, sessionRecordingConfigGetter] = sessionRecordingConfigExecutor{}
+
+type installerConfigExecutor struct{}
+
+func (installerConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Installer, error) {
+ return cache.ClusterConfig.GetInstallers(ctx)
+}
+
+func (installerConfigExecutor) upsert(ctx context.Context, cache *Cache, resource types.Installer) error {
+ return cache.clusterConfigCache.SetInstaller(ctx, resource)
+}
+
+func (installerConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.clusterConfigCache.DeleteAllInstallers(ctx)
+}
+
+func (installerConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.clusterConfigCache.DeleteInstaller(ctx, resource.GetName())
+}
+
+func (installerConfigExecutor) isSingleton() bool { return false }
+
+func (installerConfigExecutor) getReader(cache *Cache, cacheOK bool) installerGetter {
+ if cacheOK {
+ return cache.clusterConfigCache
+ }
+ return cache.Config.ClusterConfig
+}
+
+type installerGetter interface {
+ GetInstallers(context.Context) ([]types.Installer, error)
+ GetInstaller(ctx context.Context, name string) (types.Installer, error)
+}
+
+var _ executor[types.Installer, installerGetter] = installerConfigExecutor{}
+
+type networkRestrictionsExecutor struct{}
+
+func (networkRestrictionsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.NetworkRestrictions, error) {
+ restrictions, err := cache.Restrictions.GetNetworkRestrictions(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return []types.NetworkRestrictions{restrictions}, nil
+}
+
+func (networkRestrictionsExecutor) upsert(ctx context.Context, cache *Cache, resource types.NetworkRestrictions) error {
+ return cache.restrictionsCache.SetNetworkRestrictions(ctx, resource)
+}
+
+func (networkRestrictionsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.restrictionsCache.DeleteNetworkRestrictions(ctx)
+}
+
+func (networkRestrictionsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.restrictionsCache.DeleteNetworkRestrictions(ctx)
+}
+
+func (networkRestrictionsExecutor) isSingleton() bool { return true }
+
+func (networkRestrictionsExecutor) getReader(cache *Cache, cacheOK bool) networkRestrictionGetter {
+ if cacheOK {
+ return cache.restrictionsCache
+ }
+ return cache.Config.Restrictions
+}
+
+type networkRestrictionGetter interface {
+ GetNetworkRestrictions(context.Context) (types.NetworkRestrictions, error)
+}
+
+var _ executor[types.NetworkRestrictions, networkRestrictionGetter] = networkRestrictionsExecutor{}
+
+type lockExecutor struct{}
+
+func (lockExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Lock, error) {
+ return cache.Access.GetLocks(ctx, false)
+}
+
+func (lockExecutor) upsert(ctx context.Context, cache *Cache, resource types.Lock) error {
+ return cache.accessCache.UpsertLock(ctx, resource)
+}
+
+func (lockExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.accessCache.DeleteAllLocks(ctx)
+}
+
+func (lockExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.accessCache.DeleteLock(ctx, resource.GetName())
+}
+
+func (lockExecutor) isSingleton() bool { return false }
+
+func (lockExecutor) getReader(cache *Cache, cacheOK bool) services.LockGetter {
+ if cacheOK {
+ return cache.accessCache
+ }
+ return cache.Config.Access
+}
+
+var _ executor[types.Lock, services.LockGetter] = lockExecutor{}
+
+type windowsDesktopServicesExecutor struct{}
+
+func (windowsDesktopServicesExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WindowsDesktopService, error) {
+ return cache.Presence.GetWindowsDesktopServices(ctx)
+}
+
+func (windowsDesktopServicesExecutor) upsert(ctx context.Context, cache *Cache, resource types.WindowsDesktopService) error {
+ _, err := cache.presenceCache.UpsertWindowsDesktopService(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (windowsDesktopServicesExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.presenceCache.DeleteAllWindowsDesktopServices(ctx)
+}
+
+func (windowsDesktopServicesExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.presenceCache.DeleteWindowsDesktopService(ctx, resource.GetName())
+}
+
+func (windowsDesktopServicesExecutor) isSingleton() bool { return false }
+
+func (windowsDesktopServicesExecutor) getReader(cache *Cache, cacheOK bool) windowsDesktopServiceGetter {
+ if cacheOK {
+ return windowsDesktopServiceAggregate{
+ Presence: cache.presenceCache,
+ WindowsDesktops: cache.windowsDesktopsCache,
+ }
+ }
+ return windowsDesktopServiceAggregate{
+ Presence: cache.Config.Presence,
+ WindowsDesktops: cache.Config.WindowsDesktops,
+ }
+}
+
+type windowsDesktopServiceAggregate struct {
+ services.Presence
+ services.WindowsDesktops
+}
+
+type windowsDesktopServiceGetter interface {
+ GetWindowsDesktopServices(ctx context.Context) ([]types.WindowsDesktopService, error)
+ GetWindowsDesktopService(ctx context.Context, name string) (types.WindowsDesktopService, error)
+ ListWindowsDesktopServices(ctx context.Context, req types.ListWindowsDesktopServicesRequest) (*types.ListWindowsDesktopServicesResponse, error)
+}
+
+var _ executor[types.WindowsDesktopService, windowsDesktopServiceGetter] = windowsDesktopServicesExecutor{}
+
+type windowsDesktopsExecutor struct{}
+
+func (windowsDesktopsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.WindowsDesktop, error) {
+ return cache.WindowsDesktops.GetWindowsDesktops(ctx, types.WindowsDesktopFilter{})
+}
+
+func (windowsDesktopsExecutor) upsert(ctx context.Context, cache *Cache, resource types.WindowsDesktop) error {
+ return cache.windowsDesktopsCache.UpsertWindowsDesktop(ctx, resource)
+}
+
+func (windowsDesktopsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.windowsDesktopsCache.DeleteAllWindowsDesktops(ctx)
+}
+
+func (windowsDesktopsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.windowsDesktopsCache.DeleteWindowsDesktop(ctx,
+ resource.GetMetadata().Description, // Cache passes host ID via description field.
+ resource.GetName(),
+ )
+}
+
+func (windowsDesktopsExecutor) isSingleton() bool { return false }
+
+func (windowsDesktopsExecutor) getReader(cache *Cache, cacheOK bool) windowsDesktopsGetter {
+ if cacheOK {
+ return cache.windowsDesktopsCache
+ }
+ return cache.Config.WindowsDesktops
+}
+
+type windowsDesktopsGetter interface {
+ GetWindowsDesktops(context.Context, types.WindowsDesktopFilter) ([]types.WindowsDesktop, error)
+ ListWindowsDesktops(ctx context.Context, req types.ListWindowsDesktopsRequest) (*types.ListWindowsDesktopsResponse, error)
+}
+
+var _ executor[types.WindowsDesktop, windowsDesktopsGetter] = windowsDesktopsExecutor{}
+
+type dynamicWindowsDesktopsExecutor struct{}
+
+func (dynamicWindowsDesktopsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.DynamicWindowsDesktop, error) {
+ var desktops []types.DynamicWindowsDesktop
+ next := ""
+ for {
+ d, token, err := cache.Config.DynamicWindowsDesktops.ListDynamicWindowsDesktops(ctx, defaults.MaxIterationLimit, next)
+ if err != nil {
+ return nil, err
+ }
+ desktops = append(desktops, d...)
+ if token == "" {
+ break
+ }
+ next = token
+ }
+ return desktops, nil
+}
+
+func (dynamicWindowsDesktopsExecutor) upsert(ctx context.Context, cache *Cache, resource types.DynamicWindowsDesktop) error {
+ _, err := cache.dynamicWindowsDesktopsCache.UpsertDynamicWindowsDesktop(ctx, resource)
+ return err
+}
+
+func (dynamicWindowsDesktopsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.dynamicWindowsDesktopsCache.DeleteAllDynamicWindowsDesktops(ctx)
+}
+
+func (dynamicWindowsDesktopsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.dynamicWindowsDesktopsCache.DeleteDynamicWindowsDesktop(ctx, resource.GetName())
+}
+
+func (dynamicWindowsDesktopsExecutor) isSingleton() bool { return false }
+
+func (dynamicWindowsDesktopsExecutor) getReader(cache *Cache, cacheOK bool) dynamicWindowsDesktopsGetter {
+ if cacheOK {
+ return cache.dynamicWindowsDesktopsCache
+ }
+ return cache.Config.DynamicWindowsDesktops
+}
+
+type dynamicWindowsDesktopsGetter interface {
+ GetDynamicWindowsDesktop(ctx context.Context, name string) (types.DynamicWindowsDesktop, error)
+ ListDynamicWindowsDesktops(ctx context.Context, pageSize int, nextPage string) ([]types.DynamicWindowsDesktop, string, error)
+}
+
+var _ executor[types.DynamicWindowsDesktop, dynamicWindowsDesktopsGetter] = dynamicWindowsDesktopsExecutor{}
+
+type kubeClusterExecutor struct{}
+
+func (kubeClusterExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.KubeCluster, error) {
+ return cache.Kubernetes.GetKubernetesClusters(ctx)
+}
+
+func (kubeClusterExecutor) upsert(ctx context.Context, cache *Cache, resource types.KubeCluster) error {
+ if err := cache.kubernetesCache.CreateKubernetesCluster(ctx, resource); err != nil {
+ if !trace.IsAlreadyExists(err) {
+ return trace.Wrap(err)
+ }
+ return trace.Wrap(cache.kubernetesCache.UpdateKubernetesCluster(ctx, resource))
+ }
+
+ return nil
+}
+
+func (kubeClusterExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.kubernetesCache.DeleteAllKubernetesClusters(ctx)
+}
+
+func (kubeClusterExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.kubernetesCache.DeleteKubernetesCluster(ctx, resource.GetName())
+}
+
+func (kubeClusterExecutor) isSingleton() bool { return false }
+
+func (kubeClusterExecutor) getReader(cache *Cache, cacheOK bool) kubernetesClusterGetter {
+ if cacheOK {
+ return cache.kubernetesCache
+ }
+ return cache.Config.Kubernetes
+}
+
+type kubernetesClusterGetter interface {
+ GetKubernetesClusters(ctx context.Context) ([]types.KubeCluster, error)
+ GetKubernetesCluster(ctx context.Context, name string) (types.KubeCluster, error)
+}
+
+type kubeWaitingContainerExecutor struct{}
+
+func (kubeWaitingContainerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*kubewaitingcontainerpb.KubernetesWaitingContainer, error) {
+ var (
+ startKey string
+ allConts []*kubewaitingcontainerpb.KubernetesWaitingContainer
+ )
+ for {
+ conts, nextKey, err := cache.KubeWaitingContainers.ListKubernetesWaitingContainers(ctx, 0, startKey)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ allConts = append(allConts, conts...)
+
+ if nextKey == "" {
+ break
+ }
+ startKey = nextKey
+ }
+ return allConts, nil
+}
+
+func (kubeWaitingContainerExecutor) upsert(ctx context.Context, cache *Cache, resource *kubewaitingcontainerpb.KubernetesWaitingContainer) error {
+ _, err := cache.kubeWaitingContsCache.UpsertKubernetesWaitingContainer(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (kubeWaitingContainerExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return trace.Wrap(cache.kubeWaitingContsCache.DeleteAllKubernetesWaitingContainers(ctx))
+}
+
+func (kubeWaitingContainerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ switch r := resource.(type) {
+ case types.Resource153Unwrapper:
+ switch wc := r.Unwrap().(type) {
+ case *kubewaitingcontainerpb.KubernetesWaitingContainer:
+ err := cache.kubeWaitingContsCache.DeleteKubernetesWaitingContainer(ctx, &kubewaitingcontainerpb.DeleteKubernetesWaitingContainerRequest{
+ Username: wc.Spec.Username,
+ Cluster: wc.Spec.Cluster,
+ Namespace: wc.Spec.Namespace,
+ PodName: wc.Spec.PodName,
+ ContainerName: wc.Spec.ContainerName,
+ })
+ return trace.Wrap(err)
+ }
+ }
+
+ return trace.BadParameter("unknown KubeWaitingContainer type, expected *kubewaitingcontainerpb.KubernetesWaitingContainer, got %T", resource)
+}
+
+func (kubeWaitingContainerExecutor) isSingleton() bool { return false }
+
+func (kubeWaitingContainerExecutor) getReader(cache *Cache, cacheOK bool) kubernetesWaitingContainerGetter {
+ if cacheOK {
+ return cache.kubeWaitingContsCache
+ }
+ return cache.Config.KubeWaitingContainers
+}
+
+type kubernetesWaitingContainerGetter interface {
+ ListKubernetesWaitingContainers(ctx context.Context, pageSize int, pageToken string) ([]*kubewaitingcontainerpb.KubernetesWaitingContainer, string, error)
+ GetKubernetesWaitingContainer(ctx context.Context, req *kubewaitingcontainerpb.GetKubernetesWaitingContainerRequest) (*kubewaitingcontainerpb.KubernetesWaitingContainer, error)
+}
+
+var _ executor[*kubewaitingcontainerpb.KubernetesWaitingContainer, kubernetesWaitingContainerGetter] = kubeWaitingContainerExecutor{}
+
+type staticHostUserExecutor struct{}
+
+func (staticHostUserExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*userprovisioningpb.StaticHostUser, error) {
+ var (
+ startKey string
+ allUsers []*userprovisioningpb.StaticHostUser
+ )
+ for {
+ users, nextKey, err := cache.StaticHostUsers.ListStaticHostUsers(ctx, 0, startKey)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ allUsers = append(allUsers, users...)
+
+ if nextKey == "" {
+ break
+ }
+ startKey = nextKey
+ }
+ return allUsers, nil
+}
+
+func (staticHostUserExecutor) upsert(ctx context.Context, cache *Cache, resource *userprovisioningpb.StaticHostUser) error {
+ _, err := cache.staticHostUsersCache.UpsertStaticHostUser(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (staticHostUserExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return trace.Wrap(cache.staticHostUsersCache.DeleteAllStaticHostUsers(ctx))
+}
+
+func (staticHostUserExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return trace.Wrap(cache.staticHostUsersCache.DeleteStaticHostUser(ctx, resource.GetName()))
+}
+
+func (staticHostUserExecutor) isSingleton() bool { return false }
+
+func (staticHostUserExecutor) getReader(cache *Cache, cacheOK bool) staticHostUserGetter {
+ if cacheOK {
+ return cache.staticHostUsersCache
+ }
+ return cache.Config.StaticHostUsers
+}
+
+type staticHostUserGetter interface {
+ ListStaticHostUsers(ctx context.Context, pageSize int, pageToken string) ([]*userprovisioningpb.StaticHostUser, string, error)
+ GetStaticHostUser(ctx context.Context, name string) (*userprovisioningpb.StaticHostUser, error)
+}
+
+type crownJewelsExecutor struct{}
+
+func (crownJewelsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*crownjewelv1.CrownJewel, error) {
+ var resources []*crownjewelv1.CrownJewel
+ var nextToken string
+ for {
+ var page []*crownjewelv1.CrownJewel
+ var err error
+ page, nextToken, err = cache.CrownJewels.ListCrownJewels(ctx, 0 /* page size */, nextToken)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ resources = append(resources, page...)
+
+ if nextToken == "" {
+ break
+ }
+ }
+ return resources, nil
+}
+
+func (crownJewelsExecutor) upsert(ctx context.Context, cache *Cache, resource *crownjewelv1.CrownJewel) error {
+ _, err := cache.crownJewelsCache.UpsertCrownJewel(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (crownJewelsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.crownJewelsCache.DeleteAllCrownJewels(ctx)
+}
+
+func (crownJewelsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.crownJewelsCache.DeleteCrownJewel(ctx, resource.GetName())
+}
+
+func (crownJewelsExecutor) isSingleton() bool { return false }
+
+func (crownJewelsExecutor) getReader(cache *Cache, cacheOK bool) crownjewelsGetter {
+ if cacheOK {
+ return cache.crownJewelsCache
+ }
+ return cache.Config.CrownJewels
+}
+
+var _ executor[*crownjewelv1.CrownJewel, crownjewelsGetter] = crownJewelsExecutor{}
+
+type userTasksExecutor struct{}
+
+func (userTasksExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*usertasksv1.UserTask, error) {
+ var resources []*usertasksv1.UserTask
+ var nextToken string
+ for {
+ var page []*usertasksv1.UserTask
+ var err error
+ page, nextToken, err = cache.UserTasks.ListUserTasks(ctx, 0 /* page size */, nextToken, &usertasksv1.ListUserTasksFilters{})
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ resources = append(resources, page...)
+
+ if nextToken == "" {
+ break
+ }
+ }
+ return resources, nil
+}
+
+func (userTasksExecutor) upsert(ctx context.Context, cache *Cache, resource *usertasksv1.UserTask) error {
+ _, err := cache.userTasksCache.UpsertUserTask(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (userTasksExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.userTasksCache.DeleteAllUserTasks(ctx)
+}
+
+func (userTasksExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.userTasksCache.DeleteUserTask(ctx, resource.GetName())
+}
+
+func (userTasksExecutor) isSingleton() bool { return false }
+
+func (userTasksExecutor) getReader(cache *Cache, cacheOK bool) userTasksGetter {
+ if cacheOK {
+ return cache.userTasksCache
+ }
+ return cache.Config.UserTasks
+}
+
+var _ executor[*usertasksv1.UserTask, userTasksGetter] = userTasksExecutor{}
+
+//nolint:revive // Because we want this to be IdP.
+type samlIdPServiceProvidersExecutor struct{}
+
+func (samlIdPServiceProvidersExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.SAMLIdPServiceProvider, error) {
+ var (
+ startKey string
+ sps []types.SAMLIdPServiceProvider
+ )
+ for {
+ var samlProviders []types.SAMLIdPServiceProvider
+ var err error
+ samlProviders, startKey, err = cache.SAMLIdPServiceProviders.ListSAMLIdPServiceProviders(ctx, 0, startKey)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ sps = append(sps, samlProviders...)
+
+ if startKey == "" {
+ break
+ }
+ }
+
+ return sps, nil
+}
+
+func (samlIdPServiceProvidersExecutor) upsert(ctx context.Context, cache *Cache, resource types.SAMLIdPServiceProvider) error {
+ err := cache.samlIdPServiceProvidersCache.CreateSAMLIdPServiceProvider(ctx, resource)
+ if trace.IsAlreadyExists(err) {
+ err = cache.samlIdPServiceProvidersCache.UpdateSAMLIdPServiceProvider(ctx, resource)
+ }
+ return trace.Wrap(err)
+}
+
+func (samlIdPServiceProvidersExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.samlIdPServiceProvidersCache.DeleteAllSAMLIdPServiceProviders(ctx)
+}
+
+func (samlIdPServiceProvidersExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.samlIdPServiceProvidersCache.DeleteSAMLIdPServiceProvider(ctx, resource.GetName())
+}
+
+func (samlIdPServiceProvidersExecutor) isSingleton() bool { return false }
+
+func (samlIdPServiceProvidersExecutor) getReader(cache *Cache, cacheOK bool) samlIdPServiceProviderGetter {
+ if cacheOK {
+ return cache.samlIdPServiceProvidersCache
+ }
+ return cache.Config.SAMLIdPServiceProviders
+}
+
+type samlIdPServiceProviderGetter interface {
+ ListSAMLIdPServiceProviders(context.Context, int, string) ([]types.SAMLIdPServiceProvider, string, error)
+ GetSAMLIdPServiceProvider(ctx context.Context, name string) (types.SAMLIdPServiceProvider, error)
+}
+
+var _ executor[types.SAMLIdPServiceProvider, samlIdPServiceProviderGetter] = samlIdPServiceProvidersExecutor{}
+
+type userGroupsExecutor struct{}
+
+func (userGroupsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.UserGroup, error) {
+ var (
+ startKey string
+ resources []types.UserGroup
+ )
+ for {
+ var userGroups []types.UserGroup
+ var err error
+ userGroups, startKey, err = cache.UserGroups.ListUserGroups(ctx, 0, startKey)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ resources = append(resources, userGroups...)
+
+ if startKey == "" {
+ break
+ }
+ }
+
+ return resources, nil
+}
+
+func (userGroupsExecutor) upsert(ctx context.Context, cache *Cache, resource types.UserGroup) error {
+ err := cache.userGroupsCache.CreateUserGroup(ctx, resource)
+ if trace.IsAlreadyExists(err) {
+ err = cache.userGroupsCache.UpdateUserGroup(ctx, resource)
+ }
+ return trace.Wrap(err)
+}
+
+func (userGroupsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.userGroupsCache.DeleteAllUserGroups(ctx)
+}
+
+func (userGroupsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.userGroupsCache.DeleteUserGroup(ctx, resource.GetName())
+}
+
+func (userGroupsExecutor) isSingleton() bool { return false }
+
+func (userGroupsExecutor) getReader(cache *Cache, cacheOK bool) userGroupGetter {
+ if cacheOK {
+ return cache.userGroupsCache
+ }
+ return cache.Config.UserGroups
+}
+
+type userGroupGetter interface {
+ GetUserGroup(ctx context.Context, name string) (types.UserGroup, error)
+ ListUserGroups(context.Context, int, string) ([]types.UserGroup, string, error)
+}
+
+var _ executor[types.UserGroup, userGroupGetter] = userGroupsExecutor{}
+
+type oktaImportRulesExecutor struct{}
+
+func (oktaImportRulesExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.OktaImportRule, error) {
+ var (
+ startKey string
+ resources []types.OktaImportRule
+ )
+ for {
+ var importRules []types.OktaImportRule
+ var err error
+ importRules, startKey, err = cache.Okta.ListOktaImportRules(ctx, 0, startKey)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ resources = append(resources, importRules...)
+
+ if startKey == "" {
+ break
+ }
+ }
+
+ return resources, nil
+}
+
+func (oktaImportRulesExecutor) upsert(ctx context.Context, cache *Cache, resource types.OktaImportRule) error {
+ _, err := cache.oktaCache.CreateOktaImportRule(ctx, resource)
+ if trace.IsAlreadyExists(err) {
+ _, err = cache.oktaCache.UpdateOktaImportRule(ctx, resource)
+ }
+ return trace.Wrap(err)
+}
+
+func (oktaImportRulesExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.oktaCache.DeleteAllOktaImportRules(ctx)
+}
+
+func (oktaImportRulesExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.oktaCache.DeleteOktaImportRule(ctx, resource.GetName())
+}
+
+func (oktaImportRulesExecutor) isSingleton() bool { return false }
+
+func (oktaImportRulesExecutor) getReader(cache *Cache, cacheOK bool) oktaImportRuleGetter {
+ if cacheOK {
+ return cache.oktaCache
+ }
+ return cache.Config.Okta
+}
+
+type oktaImportRuleGetter interface {
+ ListOktaImportRules(context.Context, int, string) ([]types.OktaImportRule, string, error)
+ GetOktaImportRule(ctx context.Context, name string) (types.OktaImportRule, error)
+}
+
+var _ executor[types.OktaImportRule, oktaImportRuleGetter] = oktaImportRulesExecutor{}
+
+type oktaAssignmentsExecutor struct{}
+
+func (oktaAssignmentsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.OktaAssignment, error) {
+ var (
+ startKey string
+ resources []types.OktaAssignment
+ )
+ for {
+ var assignments []types.OktaAssignment
+ var err error
+ assignments, startKey, err = cache.Okta.ListOktaAssignments(ctx, 0, startKey)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ resources = append(resources, assignments...)
+
+ if startKey == "" {
+ break
+ }
+ }
+
+ return resources, nil
+}
+
+func (oktaAssignmentsExecutor) upsert(ctx context.Context, cache *Cache, resource types.OktaAssignment) error {
+ _, err := cache.oktaCache.CreateOktaAssignment(ctx, resource)
+ if trace.IsAlreadyExists(err) {
+ _, err = cache.oktaCache.UpdateOktaAssignment(ctx, resource)
+ }
+ return trace.Wrap(err)
+}
+
+func (oktaAssignmentsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.oktaCache.DeleteAllOktaAssignments(ctx)
+}
+
+func (oktaAssignmentsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.oktaCache.DeleteOktaAssignment(ctx, resource.GetName())
+}
+
+func (oktaAssignmentsExecutor) isSingleton() bool { return false }
+
+func (oktaAssignmentsExecutor) getReader(cache *Cache, cacheOK bool) oktaAssignmentGetter {
+ if cacheOK {
+ return cache.oktaCache
+ }
+ return cache.Config.Okta
+}
+
+type oktaAssignmentGetter interface {
+ GetOktaAssignment(ctx context.Context, name string) (types.OktaAssignment, error)
+ ListOktaAssignments(context.Context, int, string) ([]types.OktaAssignment, string, error)
+}
+
+var _ executor[types.OktaAssignment, oktaAssignmentGetter] = oktaAssignmentsExecutor{}
+
+// collectionReader extends the collection interface, adding routing capabilities.
+type collectionReader[R any] interface {
+ legacyCollection
+
+ // getReader returns the appropriate reader type T based on the health status of the cache.
+ // Reader type R provides getter methods related to the collection, e.g. GetNodes(), GetRoles().
+ // Note that cacheOK set to true means that cache is overall healthy and the collection was confirmed as supported.
+ getReader(cacheOK bool) R
+}
+
+type resourceGetter interface {
+ ListResources(ctx context.Context, req proto.ListResourcesRequest) (*types.ListResourcesResponse, error)
+}
+
+type integrationsExecutor struct{}
+
+func (integrationsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.Integration, error) {
+ var (
+ startKey string
+ resources []types.Integration
+ )
+ for {
+ var igs []types.Integration
+ var err error
+ igs, startKey, err = cache.Integrations.ListIntegrations(ctx, 0, startKey)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ resources = append(resources, igs...)
+
+ if startKey == "" {
+ break
+ }
+ }
+
+ return resources, nil
+}
+
+func (integrationsExecutor) upsert(ctx context.Context, cache *Cache, resource types.Integration) error {
+ _, err := cache.integrationsCache.CreateIntegration(ctx, resource)
+ if trace.IsAlreadyExists(err) {
+ _, err = cache.integrationsCache.UpdateIntegration(ctx, resource)
+ }
+ return trace.Wrap(err)
+}
+
+func (integrationsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.integrationsCache.DeleteAllIntegrations(ctx)
+}
+
+func (integrationsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.integrationsCache.DeleteIntegration(ctx, resource.GetName())
+}
+
+func (integrationsExecutor) isSingleton() bool { return false }
+
+func (integrationsExecutor) getReader(cache *Cache, cacheOK bool) services.IntegrationsGetter {
+ if cacheOK {
+ return cache.integrationsCache
+ }
+ return cache.Config.Integrations
+}
+
+var _ executor[types.Integration, services.IntegrationsGetter] = integrationsExecutor{}
+
+type discoveryConfigExecutor struct{}
+
+func (discoveryConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*discoveryconfig.DiscoveryConfig, error) {
+ var discoveryConfigs []*discoveryconfig.DiscoveryConfig
+ var nextToken string
+ for {
+ var page []*discoveryconfig.DiscoveryConfig
+ var err error
+
+ page, nextToken, err = cache.DiscoveryConfigs.ListDiscoveryConfigs(ctx, 0 /* default page size */, nextToken)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ discoveryConfigs = append(discoveryConfigs, page...)
+
+ if nextToken == "" {
+ break
+ }
+ }
+ return discoveryConfigs, nil
+}
+
+func (discoveryConfigExecutor) upsert(ctx context.Context, cache *Cache, resource *discoveryconfig.DiscoveryConfig) error {
+ _, err := cache.discoveryConfigsCache.UpsertDiscoveryConfig(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (discoveryConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.discoveryConfigsCache.DeleteAllDiscoveryConfigs(ctx)
+}
+
+func (discoveryConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.discoveryConfigsCache.DeleteDiscoveryConfig(ctx, resource.GetName())
+}
+
+func (discoveryConfigExecutor) isSingleton() bool { return false }
+
+func (discoveryConfigExecutor) getReader(cache *Cache, cacheOK bool) services.DiscoveryConfigsGetter {
+ if cacheOK {
+ return cache.discoveryConfigsCache
+ }
+ return cache.Config.DiscoveryConfigs
+}
+
+var _ executor[*discoveryconfig.DiscoveryConfig, services.DiscoveryConfigsGetter] = discoveryConfigExecutor{}
+
+type auditQueryExecutor struct{}
+
+func (auditQueryExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*secreports.AuditQuery, error) {
+ var out []*secreports.AuditQuery
+ var nextToken string
+ for {
+ var page []*secreports.AuditQuery
+ var err error
+
+ page, nextToken, err = cache.secReportsCache.ListSecurityAuditQueries(ctx, 0 /* default page size */, nextToken)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ out = append(out, page...)
+ if nextToken == "" {
+ break
+ }
+ }
+ return out, nil
+}
+
+func (auditQueryExecutor) upsert(ctx context.Context, cache *Cache, resource *secreports.AuditQuery) error {
+ err := cache.secReportsCache.UpsertSecurityAuditQuery(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (auditQueryExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return trace.Wrap(cache.secReportsCache.DeleteAllSecurityReports(ctx))
+}
+
+func (auditQueryExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return trace.Wrap(cache.secReportsCache.DeleteSecurityAuditQuery(ctx, resource.GetName()))
+}
+
+func (auditQueryExecutor) isSingleton() bool { return false }
+
+func (auditQueryExecutor) getReader(cache *Cache, cacheOK bool) services.SecurityAuditQueryGetter {
+ if cacheOK {
+ return cache.secReportsCache
+ }
+ return cache.Config.SecReports
+}
+
+var _ executor[*secreports.AuditQuery, services.SecurityAuditQueryGetter] = auditQueryExecutor{}
+
+type secReportExecutor struct{}
+
+func (secReportExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*secreports.Report, error) {
+ var out []*secreports.Report
+ var nextToken string
+ for {
+ var page []*secreports.Report
+ var err error
+
+ page, nextToken, err = cache.secReportsCache.ListSecurityReports(ctx, 0 /* default page size */, nextToken)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ out = append(out, page...)
+ if nextToken == "" {
+ break
+ }
+ }
+ return out, nil
+}
+
+func (secReportExecutor) upsert(ctx context.Context, cache *Cache, resource *secreports.Report) error {
+ err := cache.secReportsCache.UpsertSecurityReport(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (secReportExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return trace.Wrap(cache.secReportsCache.DeleteAllSecurityReports(ctx))
+}
+
+func (secReportExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return trace.Wrap(cache.secReportsCache.DeleteSecurityReport(ctx, resource.GetName()))
+}
+
+func (secReportExecutor) isSingleton() bool { return false }
+
+func (secReportExecutor) getReader(cache *Cache, cacheOK bool) services.SecurityReportGetter {
+ if cacheOK {
+ return cache.secReportsCache
+ }
+ return cache.Config.SecReports
+}
+
+var _ executor[*secreports.Report, services.SecurityReportGetter] = secReportExecutor{}
+
+type secReportStateExecutor struct{}
+
+func (secReportStateExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*secreports.ReportState, error) {
+ var out []*secreports.ReportState
+ var nextToken string
+ for {
+ var page []*secreports.ReportState
+ var err error
+
+ page, nextToken, err = cache.secReportsCache.ListSecurityReportsStates(ctx, 0 /* default page size */, nextToken)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ out = append(out, page...)
+ if nextToken == "" {
+ break
+ }
+ }
+ return out, nil
+}
+
+func (secReportStateExecutor) upsert(ctx context.Context, cache *Cache, resource *secreports.ReportState) error {
+ err := cache.secReportsCache.UpsertSecurityReportsState(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (secReportStateExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return trace.Wrap(cache.secReportsCache.DeleteAllSecurityReportsStates(ctx))
+}
+
+func (secReportStateExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return trace.Wrap(cache.secReportsCache.DeleteSecurityReportsState(ctx, resource.GetName()))
+}
+
+func (secReportStateExecutor) isSingleton() bool { return false }
+
+func (secReportStateExecutor) getReader(cache *Cache, cacheOK bool) services.SecurityReportStateGetter {
+ if cacheOK {
+ return cache.secReportsCache
+ }
+ return cache.Config.SecReports
+}
+
+var _ executor[*secreports.ReportState, services.SecurityReportStateGetter] = secReportStateExecutor{}
+
+type userLoginStateExecutor struct{}
+
+func (userLoginStateExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*userloginstate.UserLoginState, error) {
+ resources, err := cache.UserLoginStates.GetUserLoginStates(ctx)
+ return resources, trace.Wrap(err)
+}
+
+func (userLoginStateExecutor) upsert(ctx context.Context, cache *Cache, resource *userloginstate.UserLoginState) error {
+ _, err := cache.userLoginStateCache.UpsertUserLoginState(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (userLoginStateExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.userLoginStateCache.DeleteAllUserLoginStates(ctx)
+}
+
+func (userLoginStateExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.userLoginStateCache.DeleteUserLoginState(ctx, resource.GetName())
+}
+
+func (userLoginStateExecutor) isSingleton() bool { return false }
+
+func (userLoginStateExecutor) getReader(cache *Cache, cacheOK bool) services.UserLoginStatesGetter {
+ if cacheOK {
+ return cache.userLoginStateCache
+ }
+ return cache.Config.UserLoginStates
+}
+
+var _ executor[*userloginstate.UserLoginState, services.UserLoginStatesGetter] = userLoginStateExecutor{}
+
+type accessListExecutor struct{}
+
+func (accessListExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*accesslist.AccessList, error) {
+ var resources []*accesslist.AccessList
+ var nextToken string
+ for {
+ var page []*accesslist.AccessList
+ var err error
+ page, nextToken, err = cache.AccessLists.ListAccessLists(ctx, 0 /* page size */, nextToken)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ resources = append(resources, page...)
+
+ if nextToken == "" {
+ break
+ }
+ }
+ return resources, nil
+}
+
+func (accessListExecutor) upsert(ctx context.Context, cache *Cache, resource *accesslist.AccessList) error {
+ _, err := cache.accessListCache.UnconditionalUpsertAccessList(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (accessListExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.accessListCache.DeleteAllAccessLists(ctx)
+}
+
+func (accessListExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.accessListCache.UnconditionalDeleteAccessList(ctx, resource.GetName())
+}
+
+func (accessListExecutor) isSingleton() bool { return false }
+
+func (accessListExecutor) getReader(cache *Cache, cacheOK bool) accessListsGetter {
+ if cacheOK {
+ return cache.accessListCache
+ }
+ return cache.Config.AccessLists
+}
+
+type accessListsGetter interface {
+ GetAccessLists(ctx context.Context) ([]*accesslist.AccessList, error)
+ ListAccessLists(ctx context.Context, pageSize int, nextToken string) ([]*accesslist.AccessList, string, error)
+ GetAccessList(ctx context.Context, name string) (*accesslist.AccessList, error)
+}
+
+type accessListMemberExecutor struct{}
+
+func (accessListMemberExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*accesslist.AccessListMember, error) {
+ var resources []*accesslist.AccessListMember
+ var nextToken string
+ for {
+ var page []*accesslist.AccessListMember
+ var err error
+ page, nextToken, err = cache.AccessLists.ListAllAccessListMembers(ctx, 0 /* page size */, nextToken)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ resources = append(resources, page...)
+
+ if nextToken == "" {
+ break
+ }
+ }
+ return resources, nil
+}
+
+func (accessListMemberExecutor) upsert(ctx context.Context, cache *Cache, resource *accesslist.AccessListMember) error {
+ _, err := cache.accessListCache.UnconditionalUpsertAccessListMember(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (accessListMemberExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.accessListCache.DeleteAllAccessListMembers(ctx)
+}
+
+func (accessListMemberExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.accessListCache.UnconditionalDeleteAccessListMember(ctx,
+ resource.GetMetadata().Description, // Cache passes access ID via description field.
+ resource.GetName())
+}
+
+func (accessListMemberExecutor) isSingleton() bool { return false }
+
+func (accessListMemberExecutor) getReader(cache *Cache, cacheOK bool) accessListMembersGetter {
+ if cacheOK {
+ return cache.accessListCache
+ }
+ return cache.Config.AccessLists
+}
+
+type accessListMembersGetter interface {
+ CountAccessListMembers(ctx context.Context, accessListName string) (uint32, uint32, error)
+ ListAccessListMembers(ctx context.Context, accessListName string, pageSize int, nextToken string) ([]*accesslist.AccessListMember, string, error)
+ GetAccessListMember(ctx context.Context, accessList string, memberName string) (*accesslist.AccessListMember, error)
+ ListAllAccessListMembers(ctx context.Context, pageSize int, pageToken string) ([]*accesslist.AccessListMember, string, error)
+}
+
+type accessListReviewExecutor struct{}
+
+func (accessListReviewExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*accesslist.Review, error) {
+ var resources []*accesslist.Review
+ var nextToken string
+ for {
+ var page []*accesslist.Review
+ var err error
+ page, nextToken, err = cache.AccessLists.ListAllAccessListReviews(ctx, 0 /* page size */, nextToken)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ resources = append(resources, page...)
+
+ if nextToken == "" {
+ break
+ }
+ }
+ return resources, nil
+}
+
+func (accessListReviewExecutor) upsert(ctx context.Context, cache *Cache, resource *accesslist.Review) error {
+ if _, _, err := cache.accessListCache.CreateAccessListReview(ctx, resource); err != nil {
+ if !trace.IsAlreadyExists(err) {
+ return trace.Wrap(err)
+ }
+
+ if err := cache.accessListCache.DeleteAccessListReview(ctx, resource.Spec.AccessList, resource.GetName()); err != nil {
+ return trace.Wrap(err)
+ }
+
+ if _, _, err := cache.accessListCache.CreateAccessListReview(ctx, resource); err != nil {
+ return trace.Wrap(err)
+ }
+ }
+ return nil
+}
+
+func (accessListReviewExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.accessListCache.DeleteAllAccessListReviews(ctx)
+}
+
+func (accessListReviewExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.accessListCache.DeleteAccessListReview(ctx,
+ resource.GetMetadata().Description, // Cache passes access ID via description field.
+ resource.GetName())
+}
+
+func (accessListReviewExecutor) isSingleton() bool { return false }
+
+func (accessListReviewExecutor) getReader(cache *Cache, cacheOK bool) accessListReviewsGetter {
+ if cacheOK {
+ return cache.accessListCache
+ }
+ return cache.Config.AccessLists
+}
+
+type accessListReviewsGetter interface {
+ ListAccessListReviews(ctx context.Context, accessList string, pageSize int, pageToken string) (reviews []*accesslist.Review, nextToken string, err error)
+}
+
+type notificationGetter interface {
+ ListUserNotifications(ctx context.Context, pageSize int, startKey string) ([]*notificationsv1.Notification, string, error)
+ ListGlobalNotifications(ctx context.Context, pageSize int, startKey string) ([]*notificationsv1.GlobalNotification, string, error)
+}
+
+type userNotificationExecutor struct{}
+
+func (userNotificationExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*notificationsv1.Notification, error) {
+ var notifications []*notificationsv1.Notification
+ var startKey string
+ for {
+ notifs, nextKey, err := cache.notificationsCache.ListUserNotifications(ctx, 0, startKey)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ notifications = append(notifications, notifs...)
+
+ if nextKey == "" {
+ break
+ }
+ startKey = nextKey
+ }
+
+ return notifications, nil
+}
+
+func (userNotificationExecutor) upsert(ctx context.Context, cache *Cache, notification *notificationsv1.Notification) error {
+ _, err := cache.notificationsCache.UpsertUserNotification(ctx, notification)
+ if err != nil {
+ return trace.Wrap(err)
+ }
+
+ return nil
+}
+
+func (userNotificationExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.notificationsCache.DeleteAllUserNotifications(ctx)
+}
+
+func (userNotificationExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ r, ok := resource.(types.Resource153Unwrapper)
+ if !ok {
+ return trace.BadParameter("unknown resource type, expected types.Resource153Unwrapper, got %T", resource)
+ }
+
+ notification, ok := r.Unwrap().(*notificationsv1.Notification)
+ if !ok {
+ return trace.BadParameter("unknown Notification type, expected *notificationsv1.Notification, got %T", resource)
+ }
+
+ username := notification.GetSpec().GetUsername()
+ notificationId := notification.GetMetadata().GetName()
+
+ err := cache.notificationsCache.DeleteUserNotification(ctx, username, notificationId)
+ return trace.Wrap(err)
+}
+
+func (userNotificationExecutor) isSingleton() bool { return false }
+
+func (userNotificationExecutor) getReader(cache *Cache, cacheOK bool) notificationGetter {
+ if cacheOK {
+ return cache.notificationsCache
+ }
+ return cache.Config.Notifications
+}
+
+var _ executor[*notificationsv1.Notification, notificationGetter] = userNotificationExecutor{}
+
+type globalNotificationExecutor struct{}
+
+func (globalNotificationExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*notificationsv1.GlobalNotification, error) {
+ var notifications []*notificationsv1.GlobalNotification
+ var startKey string
+ for {
+ notifs, nextKey, err := cache.notificationsCache.ListGlobalNotifications(ctx, 0, startKey)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ notifications = append(notifications, notifs...)
+
+ if nextKey == "" {
+ break
+ }
+ startKey = nextKey
+ }
+
+ return notifications, nil
+}
+
+func (globalNotificationExecutor) upsert(ctx context.Context, cache *Cache, notification *notificationsv1.GlobalNotification) error {
+ if _, err := cache.notificationsCache.UpsertGlobalNotification(ctx, notification); err != nil {
+ return trace.Wrap(err)
+ }
+
+ return nil
+}
+
+func (globalNotificationExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.notificationsCache.DeleteAllGlobalNotifications(ctx)
+}
+
+func (globalNotificationExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+
+ r, ok := resource.(types.Resource153Unwrapper)
+ if !ok {
+ return trace.BadParameter("unknown resource type, expected types.Resource153Unwrapper, got %T", resource)
+ }
+
+ globalNotification, ok := r.Unwrap().(*notificationsv1.GlobalNotification)
+ if !ok {
+ return trace.BadParameter("unknown Notification type, expected *notificationsv1.GlobalNotification, got %T", resource)
+ }
+
+ notificationId := globalNotification.GetMetadata().GetName()
+
+ err := cache.notificationsCache.DeleteGlobalNotification(ctx, notificationId)
+ return trace.Wrap(err)
+}
+
+func (globalNotificationExecutor) isSingleton() bool { return false }
+
+func (globalNotificationExecutor) getReader(cache *Cache, cacheOK bool) notificationGetter {
+ if cacheOK {
+ return cache.notificationsCache
+ }
+ return cache.Config.Notifications
+}
+
+var _ executor[*notificationsv1.GlobalNotification, notificationGetter] = globalNotificationExecutor{}
+
+type accessMonitoringRulesExecutor struct{}
+
+func (accessMonitoringRulesExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*accessmonitoringrulesv1.AccessMonitoringRule, error) {
+ var resources []*accessmonitoringrulesv1.AccessMonitoringRule
+ var nextToken string
+ for {
+ var page []*accessmonitoringrulesv1.AccessMonitoringRule
+ var err error
+ page, nextToken, err = cache.AccessMonitoringRules.ListAccessMonitoringRules(ctx, 0 /* page size */, nextToken)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ resources = append(resources, page...)
+
+ if nextToken == "" {
+ break
+ }
+ }
+ return resources, nil
+}
+
+func (accessMonitoringRulesExecutor) upsert(ctx context.Context, cache *Cache, resource *accessmonitoringrulesv1.AccessMonitoringRule) error {
+ _, err := cache.accessMontoringRuleCache.UpsertAccessMonitoringRule(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (accessMonitoringRulesExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return cache.accessMontoringRuleCache.DeleteAllAccessMonitoringRules(ctx)
+}
+
+func (accessMonitoringRulesExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return cache.accessMontoringRuleCache.DeleteAccessMonitoringRule(ctx, resource.GetName())
+}
+
+func (accessMonitoringRulesExecutor) isSingleton() bool { return false }
+
+func (accessMonitoringRulesExecutor) getReader(cache *Cache, cacheOK bool) accessMonitoringRuleGetter {
+ if cacheOK {
+ return cache.accessMontoringRuleCache
+ }
+ return cache.Config.AccessMonitoringRules
+}
+
+type accessMonitoringRuleGetter interface {
+ GetAccessMonitoringRule(ctx context.Context, name string) (*accessmonitoringrulesv1.AccessMonitoringRule, error)
+ ListAccessMonitoringRules(ctx context.Context, limit int, startKey string) ([]*accessmonitoringrulesv1.AccessMonitoringRule, string, error)
+ ListAccessMonitoringRulesWithFilter(ctx context.Context, pageSize int, nextToken string, subjects []string, notificationName string) ([]*accessmonitoringrulesv1.AccessMonitoringRule, string, error)
+}
+
+type accessGraphSettingsExecutor struct{}
+
+func (accessGraphSettingsExecutor) getAll(ctx context.Context, cache *Cache, _ bool) ([]*clusterconfigpb.AccessGraphSettings, error) {
+ set, err := cache.ClusterConfig.GetAccessGraphSettings(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ return []*clusterconfigpb.AccessGraphSettings{set}, nil
+}
+
+func (accessGraphSettingsExecutor) upsert(ctx context.Context, cache *Cache, resource *clusterconfigpb.AccessGraphSettings) error {
+ _, err := cache.clusterConfigCache.UpsertAccessGraphSettings(ctx, resource)
+ return trace.Wrap(err)
+}
+
+func (accessGraphSettingsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
+ return trace.Wrap(cache.clusterConfigCache.DeleteAccessGraphSettings(ctx))
+}
+
+func (accessGraphSettingsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
+ return trace.Wrap(cache.clusterConfigCache.DeleteAccessGraphSettings(ctx))
+}
+
+func (accessGraphSettingsExecutor) isSingleton() bool { return false }
+
+func (accessGraphSettingsExecutor) getReader(cache *Cache, cacheOK bool) accessGraphSettingsGetter {
+ if cacheOK {
+ return cache.clusterConfigCache
+ }
+ return cache.Config.ClusterConfig
+}
+
+type accessGraphSettingsGetter interface {
+ GetAccessGraphSettings(context.Context) (*clusterconfigpb.AccessGraphSettings, error)
+}
+
+var _ executor[*clusterconfigpb.AccessGraphSettings, accessGraphSettingsGetter] = accessGraphSettingsExecutor{}
diff --git a/lib/cache/plugin_static_credentials.go b/lib/cache/plugin_static_credentials.go
index a756eb419ce35..a6cb1ee161a81 100644
--- a/lib/cache/plugin_static_credentials.go
+++ b/lib/cache/plugin_static_credentials.go
@@ -28,7 +28,7 @@ func (c *Cache) GetPluginStaticCredentials(ctx context.Context, name string) (ty
ctx, span := c.Tracer.Start(ctx, "cache/GetPluginStaticCredentials")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.pluginStaticCredentials)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.pluginStaticCredentials)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -40,7 +40,7 @@ func (c *Cache) GetPluginStaticCredentialsByLabels(ctx context.Context, labels m
ctx, span := c.Tracer.Start(ctx, "cache/GetPluginStaticCredentialsByLabels")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.pluginStaticCredentials)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.pluginStaticCredentials)
if err != nil {
return nil, trace.Wrap(err)
}
diff --git a/lib/cache/resource_reverse_tunnel.go b/lib/cache/resource_reverse_tunnel.go
index 34e963ca970c0..799750a8467e3 100644
--- a/lib/cache/resource_reverse_tunnel.go
+++ b/lib/cache/resource_reverse_tunnel.go
@@ -31,7 +31,7 @@ func (c *Cache) GetReverseTunnels(ctx context.Context) ([]types.ReverseTunnel, e
ctx, span := c.Tracer.Start(ctx, "cache/GetReverseTunnels")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.reverseTunnels)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.reverseTunnels)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -44,7 +44,7 @@ func (c *Cache) ListReverseTunnels(ctx context.Context, pageSize int, pageToken
ctx, span := c.Tracer.Start(ctx, "cache/ListReverseTunnels")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.reverseTunnels)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.reverseTunnels)
if err != nil {
return nil, "", trace.Wrap(err)
}
diff --git a/lib/cache/resource_spiffe_federation.go b/lib/cache/resource_spiffe_federation.go
index 8e6a1b38adb10..9c47e3943fefb 100644
--- a/lib/cache/resource_spiffe_federation.go
+++ b/lib/cache/resource_spiffe_federation.go
@@ -94,7 +94,7 @@ func (c *Cache) ListSPIFFEFederations(ctx context.Context, pageSize int, nextTok
ctx, span := c.Tracer.Start(ctx, "cache/ListSPIFFEFederations")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.spiffeFederations)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.spiffeFederations)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -108,7 +108,7 @@ func (c *Cache) GetSPIFFEFederation(ctx context.Context, name string) (*machinei
ctx, span := c.Tracer.Start(ctx, "cache/GetSPIFFEFederation")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.spiffeFederations)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.spiffeFederations)
if err != nil {
return nil, trace.Wrap(err)
}
diff --git a/lib/cache/resource_workload_identity.go b/lib/cache/resource_workload_identity.go
index 75efb50fedbd5..fe29e09a30b05 100644
--- a/lib/cache/resource_workload_identity.go
+++ b/lib/cache/resource_workload_identity.go
@@ -95,7 +95,7 @@ func (c *Cache) ListWorkloadIdentities(ctx context.Context, pageSize int, nextTo
ctx, span := c.Tracer.Start(ctx, "cache/ListWorkloadIdentities")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.workloadIdentity)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.workloadIdentity)
if err != nil {
return nil, "", trace.Wrap(err)
}
@@ -109,7 +109,7 @@ func (c *Cache) GetWorkloadIdentity(ctx context.Context, name string) (*workload
ctx, span := c.Tracer.Start(ctx, "cache/GetWorkloadIdentity")
defer span.End()
- rg, err := readCollectionCache(c, c.collections.workloadIdentity)
+ rg, err := readLegacyCollectionCache(c, c.legacyCacheCollections.workloadIdentity)
if err != nil {
return nil, trace.Wrap(err)
}
diff --git a/lib/cache/store.go b/lib/cache/store.go
new file mode 100644
index 0000000000000..a605ab75c21d5
--- /dev/null
+++ b/lib/cache/store.go
@@ -0,0 +1,103 @@
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package cache
+
+import (
+ "iter"
+
+ "github.com/gravitational/trace"
+
+ "github.com/gravitational/teleport/lib/utils/sortcache"
+)
+
+// store persists cached resources directly in memory.
+type store[T any, I comparable] struct {
+ cache *sortcache.SortCache[T, I]
+ clone func(T) T
+ indexes map[I]func(T) string
+}
+
+// newStore creates a store that will index the resource
+// based on the provided indexes.
+func newStore[T any, I comparable](clone func(T) T, indexes map[I]func(T) string) *store[T, I] {
+ return &store[T, I]{
+ clone: clone,
+ indexes: indexes,
+ cache: sortcache.New(sortcache.Config[T, I]{
+ Indexes: indexes,
+ }),
+ }
+}
+
+// clear removes all items from the store.
+func (s *store[T, I]) clear() error {
+ s.cache.Clear()
+ return nil
+}
+
+// put adds a new item, or updates an existing item.
+func (s *store[T, I]) put(t T) error {
+ s.cache.Put(s.clone(t))
+ return nil
+}
+
+// delete removes the provided item if any of the indexes match.
+func (s *store[T, I]) delete(t T) error {
+ for idx, transform := range s.indexes {
+ s.cache.Delete(idx, transform(t))
+ }
+
+ return nil
+}
+
+// len returns the number of values currently stored.
+func (s *store[T, I]) len() int {
+ return s.cache.Len()
+}
+
+// get returns the item matching the provided index and item,
+// or a [trace.NotFoundError] if no match was found.
+//
+// It is the responsibility of the caller to clone the resource
+// before propagating it further.
+func (s *store[T, I]) get(index I, key string) (T, error) {
+ t, ok := s.cache.Get(index, key)
+ if !ok {
+ return t, trace.NotFound("no value for key %q in index %v", key, index)
+ }
+
+ return t, nil
+}
+
+// resources returns an iterator over all items in the provided range
+// for the given index in ascending order.
+//
+// It is the responsibility of the caller to clone the resource
+// before propagating it further.
+func (s *store[T, I]) resources(index I, start, stop string) iter.Seq[T] {
+ return s.cache.Ascend(index, start, stop)
+}
+
+// count returns the number of items that exist in the provided range.
+func (s *store[T, I]) count(index I, start, stop string) int {
+ var n int
+ for range s.cache.Ascend(index, start, stop) {
+ n++
+ }
+
+ return n
+}
diff --git a/lib/cache/store_test.go b/lib/cache/store_test.go
new file mode 100644
index 0000000000000..0a5d956f9d65c
--- /dev/null
+++ b/lib/cache/store_test.go
@@ -0,0 +1,71 @@
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package cache
+
+import (
+ "slices"
+ "strconv"
+ "testing"
+
+ "github.com/gravitational/trace"
+ "github.com/stretchr/testify/require"
+)
+
+func TestResourceStore(t *testing.T) {
+ store := newStore(
+ func(i int) int { return i },
+ map[string]func(i int) string{
+ "numbers": strconv.Itoa,
+ "characters": func(i int) string { return strconv.FormatUint(uint64(i), 16) },
+ })
+
+ for i := 0; i < 100; i++ {
+ require.NoError(t, store.put(i))
+ }
+ require.Equal(t, 100, store.len())
+
+ zero, err := store.get("numbers", "0")
+ require.NoError(t, err)
+ require.Equal(t, 0, zero)
+
+ n, err := store.get("numbers", "1000")
+ require.ErrorIs(t, err, &trace.NotFoundError{Message: `no value for key "1000" in index numbers`})
+ require.Equal(t, 0, n)
+ require.Equal(t, 2, store.count("numbers", "1", "100"))
+
+ v, err := store.get("characters", "1c")
+ require.NoError(t, err)
+ require.Equal(t, 28, v)
+
+ out := slices.Collect(store.resources("numbers", "", ""))
+ require.Len(t, out, 100)
+
+ out = slices.Collect(store.resources("characters", "", ""))
+ require.Len(t, out, 100)
+
+ require.NoError(t, store.delete(0))
+ _, err = store.get("numbers", "0")
+ require.ErrorIs(t, err, &trace.NotFoundError{Message: `no value for key "0" in index numbers`})
+
+ require.NoError(t, store.clear())
+
+ _, err = store.get("numbers", "0")
+ require.ErrorIs(t, err, &trace.NotFoundError{Message: `no value for key "0" in index numbers`})
+
+ require.Zero(t, store.len())
+ require.Zero(t, store.count("numbers", "1", "100"))
+}