mirror of https://github.com/hashicorp/consul
214 lines
5.9 KiB
Go
214 lines
5.9 KiB
Go
|
// Copyright (c) HashiCorp, Inc.
|
||
|
// SPDX-License-Identifier: BUSL-1.1
|
||
|
|
||
|
package cache
|
||
|
|
||
|
import (
|
||
|
"github.com/hashicorp/consul/internal/controller/cache/index"
|
||
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||
|
)
|
||
|
|
||
|
// Query is the function type to use for named query callbacks
|
||
|
type Query func(c ReadOnlyCache, args ...any) (ResourceIterator, error)
|
||
|
|
||
|
type Cache interface {
|
||
|
// AddType will add a new resource type to the cache. This will
|
||
|
// include configuring an `id` index based on the resources Id
|
||
|
AddType(it *pbresource.Type)
|
||
|
// AddIndex will add a new index for the specified type to the cache.
|
||
|
// If the type isn't yet known to the cache, it will first add it
|
||
|
// including setting up its `id` index.
|
||
|
AddIndex(it *pbresource.Type, index *index.Index) error
|
||
|
// AddQuery will add a new named query to the cache. This query
|
||
|
// can potentially use multiple different cache indexes to come
|
||
|
// up with the final result iterator.
|
||
|
AddQuery(name string, fn Query) error
|
||
|
|
||
|
ReadOnlyCache
|
||
|
WriteCache
|
||
|
}
|
||
|
|
||
|
// ReadOnlyCache is the set of methods on the Resource cache that can be used
|
||
|
// to query the cache.
|
||
|
type ReadOnlyCache interface {
|
||
|
// Get retrieves a single resource from the specified index that matches the provided args.
|
||
|
// If more than one match is found the first is returned.
|
||
|
Get(it *pbresource.Type, indexName string, args ...any) (*pbresource.Resource, error)
|
||
|
|
||
|
// List retrieves all the resources from the specified index matching the provided args.
|
||
|
List(it *pbresource.Type, indexName string, args ...any) ([]*pbresource.Resource, error)
|
||
|
|
||
|
// ListIterator retrieves an iterator over all resources from the specified index matching the provided args.
|
||
|
ListIterator(it *pbresource.Type, indexName string, args ...any) (ResourceIterator, error)
|
||
|
|
||
|
// Parents retrieves all resources whos index value is a parent (or prefix) of the value calculated
|
||
|
// from the provided args.
|
||
|
Parents(it *pbresource.Type, indexName string, args ...any) ([]*pbresource.Resource, error)
|
||
|
|
||
|
// ParentsIterator retrieves an iterator over all resources whos index value is a parent (or prefix)
|
||
|
// of the value calculated from the provided args.
|
||
|
ParentsIterator(it *pbresource.Type, indexName string, args ...any) (ResourceIterator, error)
|
||
|
|
||
|
// Query will execute a named query against the cache and return an interator over its results
|
||
|
Query(name string, args ...any) (ResourceIterator, error)
|
||
|
}
|
||
|
|
||
|
type WriteCache interface {
|
||
|
// Insert will add a single resource into the cache. If it already exists, this will update
|
||
|
// all indexing to the current values.
|
||
|
Insert(r *pbresource.Resource) error
|
||
|
|
||
|
// Delete will remove a single resource from the cache.
|
||
|
Delete(r *pbresource.Resource) error
|
||
|
}
|
||
|
|
||
|
type ResourceIterator interface {
|
||
|
Next() *pbresource.Resource
|
||
|
}
|
||
|
|
||
|
type unversionedType struct {
|
||
|
Group string
|
||
|
Kind string
|
||
|
}
|
||
|
|
||
|
type cache struct {
|
||
|
kinds map[unversionedType]*kindIndices
|
||
|
queries map[string]Query
|
||
|
}
|
||
|
|
||
|
func New() Cache {
|
||
|
return newCache()
|
||
|
}
|
||
|
|
||
|
func newCache() *cache {
|
||
|
return &cache{
|
||
|
kinds: make(map[unversionedType]*kindIndices),
|
||
|
queries: make(map[string]Query),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *cache) ensureTypeCached(it *pbresource.Type) *kindIndices {
|
||
|
ut := unversionedType{Group: it.Group, Kind: it.Kind}
|
||
|
|
||
|
_, ok := c.kinds[ut]
|
||
|
if !ok {
|
||
|
c.kinds[ut] = newKindIndices()
|
||
|
}
|
||
|
|
||
|
return c.kinds[ut]
|
||
|
}
|
||
|
|
||
|
func (c *cache) AddType(it *pbresource.Type) {
|
||
|
c.ensureTypeCached(it)
|
||
|
}
|
||
|
|
||
|
func (c *cache) AddIndex(it *pbresource.Type, index *index.Index) error {
|
||
|
kind := c.ensureTypeCached(it)
|
||
|
err := kind.addIndex(index)
|
||
|
if err != nil {
|
||
|
return CacheTypeError{it: unversionedType{Group: it.Group, Kind: it.Kind}, err: err}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *cache) AddQuery(name string, fn Query) error {
|
||
|
if fn == nil {
|
||
|
return QueryRequired
|
||
|
}
|
||
|
if _, found := c.queries[name]; found {
|
||
|
return DuplicateQueryError{name: name}
|
||
|
}
|
||
|
|
||
|
c.queries[name] = fn
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *cache) Query(name string, args ...any) (ResourceIterator, error) {
|
||
|
fn, found := c.queries[name]
|
||
|
if !found {
|
||
|
return nil, QueryNotFoundError{name: name}
|
||
|
}
|
||
|
|
||
|
return fn(c, args...)
|
||
|
}
|
||
|
|
||
|
func (c *cache) Get(it *pbresource.Type, indexName string, args ...any) (*pbresource.Resource, error) {
|
||
|
indices, err := c.getTypeIndices(it)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return indices.get(indexName, args...)
|
||
|
}
|
||
|
|
||
|
func (c *cache) ListIterator(it *pbresource.Type, indexName string, args ...any) (ResourceIterator, error) {
|
||
|
indices, err := c.getTypeIndices(it)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return indices.listIterator(indexName, args...)
|
||
|
}
|
||
|
|
||
|
func (c *cache) List(it *pbresource.Type, indexName string, args ...any) ([]*pbresource.Resource, error) {
|
||
|
return expandIterator(c.ListIterator(it, indexName, args...))
|
||
|
}
|
||
|
|
||
|
func (c *cache) ParentsIterator(it *pbresource.Type, indexName string, args ...any) (ResourceIterator, error) {
|
||
|
indices, err := c.getTypeIndices(it)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return indices.parentsIterator(indexName, args...)
|
||
|
}
|
||
|
|
||
|
func (c *cache) Parents(it *pbresource.Type, indexName string, args ...any) ([]*pbresource.Resource, error) {
|
||
|
return expandIterator(c.ParentsIterator(it, indexName, args...))
|
||
|
}
|
||
|
|
||
|
func (c *cache) Insert(r *pbresource.Resource) error {
|
||
|
indices, err := c.getTypeIndices(r.Id.Type)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return indices.insert(r)
|
||
|
}
|
||
|
|
||
|
func (c *cache) Delete(r *pbresource.Resource) error {
|
||
|
indices, err := c.getTypeIndices(r.Id.Type)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return indices.delete(r)
|
||
|
}
|
||
|
|
||
|
func (c *cache) getTypeIndices(it *pbresource.Type) (*kindIndices, error) {
|
||
|
if it == nil {
|
||
|
return nil, TypeUnspecifiedError
|
||
|
}
|
||
|
|
||
|
ut := unversionedType{Group: it.Group, Kind: it.Kind}
|
||
|
|
||
|
indices, ok := c.kinds[ut]
|
||
|
if !ok {
|
||
|
return nil, TypeNotIndexedError
|
||
|
}
|
||
|
return indices, nil
|
||
|
}
|
||
|
|
||
|
func expandIterator(iter ResourceIterator, err error) ([]*pbresource.Resource, error) {
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var results []*pbresource.Resource
|
||
|
for res := iter.Next(); res != nil; res = iter.Next() {
|
||
|
results = append(results, res)
|
||
|
}
|
||
|
|
||
|
return results, nil
|
||
|
}
|