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.
boundary/internal/target/registry.go

180 lines
5.1 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package target
import (
"context"
"fmt"
"sync"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/types/resource"
)
const Domain = "target"
// NewFunc is a function that creates a Target with the provided project and options.
type NewFunc func(ctx context.Context, projectId string, opt ...Option) (Target, error)
// AllocFunc is a function that creates an in-memory Target.
type AllocFunc func() Target
// VetFunc is a function that checks the given Target to ensure it can
// be used by the Repository.
type VetFunc func(context.Context, Target) error
// VetForUpdateFunc is a function that checks the given Target and field mask
// paths are valid and be used to update a target in the Repository.
type VetForUpdateFunc func(context.Context, Target, []string) error
// VetCredentialSourcesFunc is a function that checks the given CredentialLibraries
// and StaticCredentials to ensure that they are valid for a Target subtype.
type VetCredentialSourcesFunc func(context.Context, []*CredentialLibrary, []*StaticCredential) error
// targetHooks defines the interface containing all the hooks needed for
// managing target suptypes.
type targetHooks interface {
// NewTarget creates a new in memory target.
NewTarget(ctx context.Context, projectId string, opt ...Option) (Target, error)
// AllocTarget will allocate an empty target.
AllocTarget() Target
// Vet validates that the given Target has the proper fields and values
// for creation in the database for this type of target.
Vet(ctx context.Context, t Target) error
// VetForUpdate validates that the given Target has the proper fields
// and values for updating the database for this type of target given the
// field mask paths.
VetForUpdate(ctx context.Context, t Target, paths []string) error
// VetCredentialSources checks that the provided credential libriaries and
// static credentials are properly built for association with a target of this type.
VetCredentialSources(ctx context.Context, cls []*CredentialLibrary, creds []*StaticCredential) error
}
type registryEntry struct {
targetHooks targetHooks
prefix string
}
type registry struct {
m map[globals.Subtype]*registryEntry
sync.RWMutex
}
func (r *registry) set(s globals.Subtype, entry *registryEntry) {
r.Lock()
defer r.Unlock()
_, previouslySet := r.m[s]
if previouslySet {
panic(fmt.Sprintf("target subtype %s already registered", s))
}
globals.RegisterPrefixToResourceInfo(entry.prefix, resource.Target, Domain, s)
r.m[s] = entry
}
func (r *registry) get(s globals.Subtype) (*registryEntry, bool) {
r.RLock()
defer r.RUnlock()
entry, ok := r.m[s]
if ok {
return entry, ok
}
return nil, ok
}
func (r *registry) newFunc(s globals.Subtype) (NewFunc, bool) {
entry, ok := r.get(s)
if !ok {
return nil, ok
}
return entry.targetHooks.NewTarget, ok
}
func (r *registry) allocFunc(s globals.Subtype) (AllocFunc, bool) {
entry, ok := r.get(s)
if !ok {
return nil, ok
}
return entry.targetHooks.AllocTarget, ok
}
func (r *registry) vetFunc(s globals.Subtype) (VetFunc, bool) {
entry, ok := r.get(s)
if !ok {
return nil, ok
}
return entry.targetHooks.Vet, ok
}
func (r *registry) vetForUpdateFunc(s globals.Subtype) (VetForUpdateFunc, bool) {
entry, ok := r.get(s)
if !ok {
return nil, ok
}
return entry.targetHooks.VetForUpdate, ok
}
func (r *registry) vetCredentialSourcesFunc(s globals.Subtype) (VetCredentialSourcesFunc, bool) {
entry, ok := r.get(s)
if !ok {
return nil, ok
}
return entry.targetHooks.VetCredentialSources, ok
}
func (r *registry) idPrefix(s globals.Subtype) (string, bool) {
entry, ok := r.get(s)
if !ok {
return "", ok
}
return entry.prefix, ok
}
var subtypeRegistry = registry{
m: make(map[globals.Subtype]*registryEntry),
}
// SubtypeFromType returns the Subtype from the provided string or if
// no Subtype was registered with that string Unknown is returned.
func SubtypeFromType(t string) globals.Subtype {
return globals.Subtype(t)
}
// SubtypeFromId returns the Subtype from the provided id if the id's prefix
// was registered with a Subtype. Otherwise Unknown is returned.
func SubtypeFromId(id string) globals.Subtype {
return globals.ResourceInfoFromPrefix(id).Subtype
}
// Prefixes returns the list of all known target Prefixes.
func Prefixes() []string {
return globals.PrefixesFromDomain(Domain)
}
// New creates a Target of the given subtype and projectId.
func New(ctx context.Context, subtype globals.Subtype, projectId string, opt ...Option) (Target, error) {
const op = "target.New"
nf, ok := subtypeRegistry.newFunc(subtype)
if !ok {
return nil, errors.New(ctx, errors.InvalidParameter, op, "unsupported subtype")
}
return nf(ctx, projectId, opt...)
}
// Register registers repository hooks and the prefixes for a provided Subtype. Register
// panics if the subtype has already been registered or if any of the
// prefixes are associated with another subtype.
func Register(s globals.Subtype, th targetHooks, prefix string) {
subtypeRegistry.set(s, &registryEntry{
targetHooks: th,
prefix: prefix,
})
}