mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
7.0 KiB
218 lines
7.0 KiB
// Copyright (c) HashiCorp, Inc. |
|
// SPDX-License-Identifier: BUSL-1.1 |
|
|
|
package dependency |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
|
|
"github.com/hashicorp/consul/internal/controller" |
|
"github.com/hashicorp/consul/internal/resource" |
|
"github.com/hashicorp/consul/proto-public/pbresource" |
|
) |
|
|
|
// CacheIDModifier is used to alter the Resource ID of the various Cache* |
|
// controller.DependencyMappers prior to making the request to the cache. This is most |
|
// useful to replace the type of resource queried (such as when resource types |
|
// are name aligned) or to modify the tenancy in some way. |
|
type CacheIDModifier func( |
|
ctx context.Context, |
|
rt controller.Runtime, |
|
id *pbresource.ID, |
|
) (*pbresource.ID, error) |
|
|
|
// CacheGetMapper is used to map an event for a watched resource by performing a Get of |
|
// a single cached resource of any type. |
|
func CacheGetMapper(indexedType *pbresource.Type, indexName string, mods ...CacheIDModifier) controller.DependencyMapper { |
|
return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { |
|
id, err := applyCacheIDMods(ctx, rt, res.GetId(), mods...) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if rt.Logger.IsTrace() { |
|
rt.Logger.Trace("mapping dependencies from cache", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
) |
|
} |
|
mapped, err := rt.Cache.Get(indexedType, indexName, id) |
|
if err != nil { |
|
rt.Logger.Error("failed to map dependencies from the cache", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
"error", err, |
|
) |
|
return nil, fmt.Errorf("failed to list from cache index %q on type %q: %w", indexName, resource.ToGVK(indexedType), err) |
|
} |
|
|
|
results := []controller.Request{ |
|
{ID: mapped.GetId()}, |
|
} |
|
if rt.Logger.IsTrace() { |
|
rt.Logger.Trace("mapped dependencies", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
"dependency", mapped.GetId(), |
|
) |
|
} |
|
|
|
return results, nil |
|
} |
|
} |
|
|
|
// CacheListMapper is used to map the incoming resource to a set of requests for all |
|
// the cached resources returned by the caches List operation. |
|
func CacheListMapper(indexedType *pbresource.Type, indexName string, mods ...CacheIDModifier) controller.DependencyMapper { |
|
return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { |
|
id, err := applyCacheIDMods(ctx, rt, res.GetId(), mods...) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if rt.Logger.IsTrace() { |
|
rt.Logger.Trace("mapping dependencies from cache", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
) |
|
} |
|
iter, err := rt.Cache.ListIterator(indexedType, indexName, id) |
|
if err != nil { |
|
rt.Logger.Error("failed to map dependencies from the cache", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
"error", err, |
|
) |
|
return nil, fmt.Errorf("failed to list from cache index %q on type %q: %w", indexName, resource.ToGVK(indexedType), err) |
|
} |
|
|
|
var results []controller.Request |
|
for res := iter.Next(); res != nil; res = iter.Next() { |
|
results = append(results, controller.Request{ID: res.GetId()}) |
|
} |
|
if rt.Logger.IsTrace() { |
|
rt.Logger.Trace("mapped dependencies", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
"dependencies", results, |
|
) |
|
} |
|
|
|
return results, nil |
|
} |
|
} |
|
|
|
// CacheParentsMapper is used to map the incoming resource to a set of requests for all |
|
// the cached resources returned by the caches Parents operation. |
|
func CacheParentsMapper(indexedType *pbresource.Type, indexName string, mods ...CacheIDModifier) controller.DependencyMapper { |
|
return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { |
|
id, err := applyCacheIDMods(ctx, rt, res.GetId(), mods...) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if rt.Logger.IsTrace() { |
|
rt.Logger.Trace("mapping dependencies from cache", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
) |
|
} |
|
iter, err := rt.Cache.ParentsIterator(indexedType, indexName, id) |
|
if err != nil { |
|
rt.Logger.Error("failed to map dependencies from the cache", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
"error", err, |
|
) |
|
return nil, fmt.Errorf("failed to list from cache index %q on type %q: %w", indexName, resource.ToGVK(indexedType), err) |
|
} |
|
|
|
var results []controller.Request |
|
for res := iter.Next(); res != nil; res = iter.Next() { |
|
results = append(results, controller.Request{ID: res.GetId()}) |
|
} |
|
if rt.Logger.IsTrace() { |
|
rt.Logger.Trace("mapped dependencies", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
"dependencies", results, |
|
) |
|
} |
|
|
|
return results, nil |
|
} |
|
} |
|
|
|
// CacheListTransform operates like CacheListMapper. The only difference is that the cache results |
|
// are left as the whole cached resource instead of condensing down to just their IDs. |
|
func CacheListTransform(indexedType *pbresource.Type, indexName string, mods ...CacheIDModifier) DependencyTransform { |
|
return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]*pbresource.Resource, error) { |
|
id, err := applyCacheIDMods(ctx, rt, res.GetId(), mods...) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if rt.Logger.IsTrace() { |
|
rt.Logger.Trace("transforming dependencies from cache", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
) |
|
} |
|
results, err := rt.Cache.List(indexedType, indexName, id) |
|
if err != nil { |
|
rt.Logger.Error("failed to transform dependencies from the cache", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
"error", err, |
|
) |
|
return nil, fmt.Errorf( |
|
"failed to list from cache index %q on type %q: %w", |
|
indexName, |
|
resource.ToGVK(indexedType), |
|
err, |
|
) |
|
} |
|
|
|
if rt.Logger.IsTrace() { |
|
rt.Logger.Trace("transformed dependencies", |
|
"type", resource.ToGVK(indexedType), |
|
"index", indexName, |
|
"resource", resource.IDToString(res.GetId()), |
|
"dependencies", results, |
|
) |
|
} |
|
|
|
return results, nil |
|
} |
|
} |
|
|
|
// ReplaceCacheIDType will generate a CacheIDModifier that replaces the original ID's |
|
// type with the desired type specified. |
|
func ReplaceCacheIDType(desiredType *pbresource.Type) CacheIDModifier { |
|
return func(_ context.Context, _ controller.Runtime, id *pbresource.ID) (*pbresource.ID, error) { |
|
return resource.ReplaceType(desiredType, id), nil |
|
} |
|
} |
|
|
|
func applyCacheIDMods(ctx context.Context, rt controller.Runtime, id *pbresource.ID, mods ...CacheIDModifier) (*pbresource.ID, error) { |
|
var err error |
|
for _, mod := range mods { |
|
id, err = mod(ctx, rt, id) |
|
if err != nil { |
|
return nil, err |
|
} |
|
} |
|
return id, err |
|
}
|
|
|