* Add generic read/update/delete commands This adds three generic commands that use the resource prefix to determine the subcommand to run and pass through args over to that subcommand. Since this occurs very early on and to avoid loading essentially every internal resource package, I moved subtype definitions to globals. This replaces #3967 Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com> * De-duplicate Subtype code (#4031) Co-authored-by: Timothy Messier <tim.messier@gmail.com> --------- Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com> Co-authored-by: Timothy Messier <tim.messier@gmail.com>
119 lines
3.3 KiB
Go
119 lines
3.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package targets
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/boundary/globals"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
|
"github.com/hashicorp/boundary/internal/session"
|
|
"github.com/hashicorp/boundary/internal/target"
|
|
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// Attributes represent the subtype specific request/response attributes.
|
|
type Attributes interface {
|
|
proto.Message
|
|
|
|
// Options create target.Options to be used to create a target.Target.
|
|
Options() []target.Option
|
|
|
|
// Vet validates the Attributes and returns a map of fields to error messages
|
|
// if any fields are invalid.
|
|
Vet() map[string]string
|
|
|
|
// VetForUpdate validates the Attributes for an updated resource with the
|
|
// provided mask paths and returns a map of fields to error messages if any
|
|
// fields are invalid.
|
|
VetForUpdate([]string) map[string]string
|
|
}
|
|
|
|
type attributeFunc func(any) Attributes
|
|
|
|
type setAttributeFunc func(target.Target, *pb.Target) error
|
|
|
|
// validateSessionStateFunc validates a session's state for the specific target
|
|
// type.
|
|
type validateSessionStateFunc func(context.Context, *session.Session) error
|
|
|
|
type registryEntry struct {
|
|
maskManager handlers.MaskManager
|
|
attrFunc attributeFunc
|
|
setAttrFunc setAttributeFunc
|
|
validateSessionStateFunc validateSessionStateFunc
|
|
}
|
|
|
|
type registry struct {
|
|
*sync.Map
|
|
}
|
|
|
|
func (r registry) get(s globals.Subtype) (*registryEntry, error) {
|
|
v, ok := r.Load(s)
|
|
if !ok {
|
|
return nil, fmt.Errorf("subtype %q not registered", s)
|
|
}
|
|
|
|
re, ok := v.(*registryEntry)
|
|
if !ok {
|
|
return nil, fmt.Errorf("malformed registry subtypye %q registered as incorrect type %T", s, v)
|
|
}
|
|
return re, nil
|
|
}
|
|
|
|
func (r registry) maskManager(s globals.Subtype) (handlers.MaskManager, error) {
|
|
re, err := r.get(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return re.maskManager, nil
|
|
}
|
|
|
|
// newAttribute creates an Attribute for the given subtype. It delegates the
|
|
// allocation of the Attribute to the registered attrFunc for the given
|
|
// subtype. An error is returned if the provided subtype is not registered
|
|
func (r registry) newAttribute(s globals.Subtype, m any) (Attributes, error) {
|
|
re, err := r.get(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
attr := re.attrFunc(m)
|
|
|
|
return attr, nil
|
|
}
|
|
|
|
// setAttributes is used to set the Attrs field on a pb.Target. It delegates the
|
|
// setting of the specific attribute type to the registered setAttributeFunc for
|
|
// the given subtype. An error is returned if the provided subtype is not
|
|
// registered.
|
|
func (r registry) setAttributes(s globals.Subtype, in target.Target, out *pb.Target) error {
|
|
re, err := r.get(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return re.setAttrFunc(in, out)
|
|
}
|
|
|
|
var subtypeRegistry = registry{
|
|
Map: new(sync.Map),
|
|
}
|
|
|
|
// Register registers a subtype for used by the service handler.
|
|
func Register(s globals.Subtype, maskManager handlers.MaskManager, af attributeFunc, sf setAttributeFunc, vsf validateSessionStateFunc) {
|
|
if _, existed := subtypeRegistry.LoadOrStore(s, ®istryEntry{
|
|
maskManager: maskManager,
|
|
attrFunc: af,
|
|
setAttrFunc: sf,
|
|
validateSessionStateFunc: vsf,
|
|
}); existed {
|
|
panic(fmt.Sprintf("subtype %s already registered", s))
|
|
}
|
|
}
|