* feat(server): Refactor LookupWorkerByName to be a test method * feat(server): Split worker tags table * refact(worker): Export api and config tags * refact(server): Return worker id from UpsertWorkerStatus * refact(server): Create ListWorkerStorageBucketUpdates domain function * refact(server): Use dedicated HCPb-worker list func * refact(server): Create ConnectionRoute domain function * refact(handlers): Move worker selection logic to internal/server package This commit moves all the logic to select a worker to handle a session to the internal/server package. Most functions were moved to this package with no changes, however, there are a couple of notable exceptions: - Given that we don't use GRPC errors in repos, all errors were adjusted to use the internal/errors package instead. - StorageBucketFilterCredIdFn is now used to prevent the internal/server package from importing internal/storage/plugin, which would cause a dep cycle. Callers of SelectSessionWorkers must implement this func which is responsible for returning a storage bucket's worker filter as well as its associated credential id. - authorizeSessionWithWorkerFilter now receives an internal/server.(*Repository) object when called. This is transparent to SelectSessionWorkers callers as it is handled internally. * feat(server): Add FilteredWithFeatures fn and new server options * refact(server): create new list and lookup worker queries * test(server): Add WorkerList unit tests * feat(server): Implement WorkerList randomness using crypto/rand pkg * chore(db): add index to server_worker table * refact(server): prefix test method with Test * refact(server): rename Downstreamers interface to Graph * chore(server): unexport and rename method * chore: add doc string to WorkerAddress (#5420) --------- Co-authored-by: Irena Rindos <irenarindos@users.noreply.github.com> Co-authored-by: irenarindos <irena.rindos@gmail.com>
224 lines
7.2 KiB
Go
224 lines
7.2 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package server
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/fatih/structs"
|
|
"github.com/hashicorp/boundary/internal/db/timestamp"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
"github.com/hashicorp/boundary/internal/server/store"
|
|
"github.com/hashicorp/boundary/sdk/pbs/plugin"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
)
|
|
|
|
type (
|
|
WorkerType string
|
|
OperationalState string
|
|
LocalStorageState string
|
|
)
|
|
|
|
const (
|
|
UnknownWorkerType WorkerType = "unknown"
|
|
KmsWorkerType WorkerType = "kms"
|
|
PkiWorkerType WorkerType = "pki"
|
|
ActiveOperationalState OperationalState = "active"
|
|
ShutdownOperationalState OperationalState = "shutdown"
|
|
UnknownOperationalState OperationalState = "unknown"
|
|
AvailableLocalStorageState LocalStorageState = "available"
|
|
LowStorageLocalStorageState LocalStorageState = "low storage"
|
|
CriticallyLowStorageLocalStorageState LocalStorageState = "critically low storage"
|
|
OutOfStorageLocalStorageState LocalStorageState = "out of storage"
|
|
NotConfiguredLocalStorageState LocalStorageState = "not configured"
|
|
UnknownLocalStorageState LocalStorageState = "unknown"
|
|
)
|
|
|
|
func (t WorkerType) Valid() bool {
|
|
switch t {
|
|
case KmsWorkerType, PkiWorkerType:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t WorkerType) String() string {
|
|
switch t {
|
|
case KmsWorkerType, PkiWorkerType:
|
|
return string(t)
|
|
}
|
|
return string(UnknownWorkerType)
|
|
}
|
|
|
|
type workerAuthWorkerId struct {
|
|
WorkerId string `mapstructure:"worker_id"`
|
|
}
|
|
|
|
func ValidOperationalState(s string) bool {
|
|
switch s {
|
|
case ActiveOperationalState.String(), ShutdownOperationalState.String():
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t OperationalState) String() string {
|
|
switch t {
|
|
case ActiveOperationalState, ShutdownOperationalState:
|
|
return string(t)
|
|
}
|
|
return string(UnknownOperationalState)
|
|
}
|
|
|
|
func ValidLocalStorageState(s string) bool {
|
|
switch s {
|
|
case AvailableLocalStorageState.String(), LowStorageLocalStorageState.String(),
|
|
CriticallyLowStorageLocalStorageState.String(), OutOfStorageLocalStorageState.String(),
|
|
NotConfiguredLocalStorageState.String(), UnknownLocalStorageState.String():
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t LocalStorageState) String() string {
|
|
switch t {
|
|
case AvailableLocalStorageState, LowStorageLocalStorageState,
|
|
OutOfStorageLocalStorageState, NotConfiguredLocalStorageState,
|
|
CriticallyLowStorageLocalStorageState:
|
|
return string(t)
|
|
}
|
|
return string(UnknownLocalStorageState)
|
|
}
|
|
|
|
// AttachWorkerIdToState accepts a workerId and creates a struct for use with the Nodeenrollment lib
|
|
// This is intended for use in worker authorization; AuthorizeNode in the lib accepts the option WithState
|
|
// so that the workerId is passed through to storage and associated with a WorkerAuth record
|
|
func AttachWorkerIdToState(ctx context.Context, workerId string) (*structpb.Struct, error) {
|
|
const op = "server.(Worker).AttachWorkerIdToState"
|
|
if workerId == "" {
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing workerId")
|
|
}
|
|
|
|
workerMap := &workerAuthWorkerId{WorkerId: workerId}
|
|
s := structs.New(workerMap)
|
|
s.TagName = "mapstructure"
|
|
return structpb.NewStruct(s.Map())
|
|
}
|
|
|
|
// A Worker is a server that provides an address which can be used to proxy
|
|
// session connections. It can be tagged with custom tags and is used when
|
|
// authorizing and establishing a session. It is owned by a scope.
|
|
type Worker struct {
|
|
*store.Worker
|
|
ApiTags Tags `json:"api_tags" gorm:"->"`
|
|
ConfigTags Tags `json:"config_tags" gorm:"->"`
|
|
ActiveConnectionCount uint32 `gorm:"->"`
|
|
|
|
// inputTags is not specified to be api or config tags and is not intended
|
|
// to be read by clients. Since config tags and api tags are applied in
|
|
// mutually exclusive contexts, inputTags is interpreted to be one or the
|
|
// other based on the context in which the worker is passed. As such
|
|
// inputTags should only be read when performing mutations on the database.
|
|
inputTags []*Tag `gorm:"-"`
|
|
|
|
// This is used to pass the token back to the calling function
|
|
ControllerGeneratedActivationToken string `gorm:"-"`
|
|
|
|
// RemoteStorageStates is a map of storage buckets and their storage bucket credential states
|
|
RemoteStorageStates map[string]*plugin.StorageBucketCredentialState `gorm:"-"`
|
|
}
|
|
|
|
// NewWorker returns a new Worker. Valid options are WithName, WithDescription
|
|
// WithAddress, and WithWorkerTags. All other options are ignored. This does
|
|
// not set any of the worker reported values.
|
|
func NewWorker(scopeId string, opt ...Option) *Worker {
|
|
opts := GetOpts(opt...)
|
|
worker := &Worker{
|
|
Worker: &store.Worker{
|
|
ScopeId: scopeId,
|
|
Name: opts.withName,
|
|
Description: opts.withDescription,
|
|
Address: opts.withAddress,
|
|
ReleaseVersion: opts.withReleaseVersion,
|
|
OperationalState: opts.withOperationalState,
|
|
LocalStorageState: opts.withLocalStorageState,
|
|
},
|
|
inputTags: opts.withWorkerTags,
|
|
}
|
|
if opts.withTestUseInputTagsAsApiTags {
|
|
worker.ApiTags = worker.inputTags
|
|
}
|
|
return worker
|
|
}
|
|
|
|
// allocWorker will allocate a Worker
|
|
func allocWorker() Worker {
|
|
return Worker{Worker: &store.Worker{}}
|
|
}
|
|
|
|
func (w *Worker) clone() *Worker {
|
|
if w == nil {
|
|
return nil
|
|
}
|
|
cw := proto.Clone(w.Worker)
|
|
cWorker := &Worker{
|
|
Worker: cw.(*store.Worker),
|
|
}
|
|
if w.ApiTags != nil {
|
|
cWorker.ApiTags = w.ApiTags.clone()
|
|
}
|
|
if w.ConfigTags != nil {
|
|
cWorker.ConfigTags = w.ConfigTags.clone()
|
|
}
|
|
if w.inputTags != nil {
|
|
cWorker.inputTags = make([]*Tag, 0, len(w.inputTags))
|
|
for _, t := range w.inputTags {
|
|
cWorker.inputTags = append(cWorker.inputTags, &Tag{Key: t.Key, Value: t.Value})
|
|
}
|
|
}
|
|
return cWorker
|
|
}
|
|
|
|
// CanonicalTags is the deduplicated set of tags contained on both the resource
|
|
// set over the API as well as the tags reported by the worker itself. This
|
|
// function is guaranteed to return a non-nil map.
|
|
func (w *Worker) CanonicalTags(opt ...Option) map[string][]string {
|
|
return compactTags(&w.ApiTags, &w.ConfigTags)
|
|
}
|
|
|
|
// GetConfigTags returns the tags for this worker which has been set through
|
|
// the worker daemon's configuration file.
|
|
func (w *Worker) GetConfigTags() map[string][]string {
|
|
tags := make(map[string][]string)
|
|
for _, t := range w.ConfigTags {
|
|
tags[t.Key] = append(tags[t.Key], t.Value)
|
|
}
|
|
return tags
|
|
}
|
|
|
|
// GetApiTags returns the api tags which have been set for this worker.
|
|
func (w *Worker) GetApiTags() map[string][]string {
|
|
tags := make(map[string][]string)
|
|
for _, t := range w.ApiTags {
|
|
tags[t.Key] = append(tags[t.Key], t.Value)
|
|
}
|
|
return tags
|
|
}
|
|
|
|
// GetLastStatusTime contains the last time the worker has reported to the
|
|
// controller its connection status. If the worker has never reported to a
|
|
// controller then nil is returned.
|
|
func (w *Worker) GetLastStatusTime() *timestamp.Timestamp {
|
|
if w == nil || w.Worker == nil || w.Worker.GetLastStatusTime().AsTime() == timestamp.NegativeInfinityTS {
|
|
return nil
|
|
}
|
|
return w.Worker.GetLastStatusTime()
|
|
}
|
|
|
|
// TableName overrides the table name used by Worker to `server_worker`
|
|
func (Worker) TableName() string {
|
|
return "server_worker"
|
|
}
|