180 lines
5.1 KiB
Go
180 lines
5.1 KiB
Go
// Copyright IBM Corp. 2020, 2026
|
|
// 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, ®istryEntry{
|
|
targetHooks: th,
|
|
prefix: prefix,
|
|
})
|
|
}
|