From f748af7b02e07b085e0374caf6bf3fda41d77fdc Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 27 Apr 2022 16:25:05 -0400 Subject: [PATCH 001/100] Initial BYOW auth PoC (#29) --- go.mod | 13 +- go.sum | 13 +- internal/cmd/base/option.go | 45 ++-- internal/cmd/base/servers.go | 10 +- internal/cmd/commands/dev/dev.go | 153 ++++++++----- .../commands/server/listener_reload_test.go | 5 +- internal/cmd/commands/server/server.go | 32 ++- .../server/worker_shutdown_reload_test.go | 4 + .../server/worker_tags_reload_test.go | 10 +- internal/servers/controller/controller.go | 22 ++ internal/servers/controller/handler.go | 65 ++++++ .../handlers/workers/multihop_service.go | 45 ++++ .../controller/intercepting_listener.go | 63 ++++-- internal/servers/controller/listeners.go | 27 ++- .../servers/controller/worker_tls_config.go | 3 +- .../servers/worker/controller_connection.go | 100 ++++++--- internal/servers/worker/listeners.go | 210 +++++++++++++----- internal/servers/worker/listeners_test.go | 193 +++------------- internal/servers/worker/status.go | 8 + internal/servers/worker/worker.go | 65 ++++-- internal/servers/worker/worker_test.go | 25 ++- 21 files changed, 707 insertions(+), 404 deletions(-) create mode 100644 internal/servers/controller/handlers/workers/multihop_service.go diff --git a/go.mod b/go.mod index b76ebc2ad..90781b4ce 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/hashicorp/go-bexpr v0.1.10 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-hclog v1.1.0 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.4 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.5 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.0 github.com/hashicorp/go-rootcerts v1.0.2 @@ -40,7 +40,7 @@ require ( github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2 github.com/hashicorp/go-secure-stdlib/password v0.1.1 - github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.0 + github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.1 github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 github.com/hashicorp/go-sockaddr v1.0.2 @@ -65,11 +65,13 @@ require ( github.com/pires/go-proxyproto v0.6.1 github.com/pkg/errors v0.9.1 github.com/posener/complete v1.2.3 + github.com/prometheus/client_golang v1.12.1 github.com/ryanuber/go-glob v1.0.0 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.1 github.com/zalando/go-keyring v0.2.1 go.uber.org/atomic v1.9.0 golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/tools v0.1.10 @@ -85,10 +87,7 @@ require ( require github.com/hashicorp/go-dbw v0.0.0-20211215222256-2ff0d37184ff // this is a branch and should be updated before merging -require ( - github.com/prometheus/client_golang v1.12.1 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c -) +require github.com/hashicorp/nodeenrollment v0.0.0-20220427165109-a2f5dad593c7 require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect diff --git a/go.sum b/go.sum index a2b2f40f2..ebc320bf9 100644 --- a/go.sum +++ b/go.sum @@ -367,8 +367,8 @@ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.2 h1:it114Kjpk79JVh6AfxFMP5oi1gb+RPzcSLiG3zF/J0w= github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.2/go.mod h1:b9mq0bG5xJ10ZcTEQJLZ22O9y71hIt2MGTLqROA/SAM= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.4 h1:7BSAm60AqJom/TRb73wmaCzwco9A+Qolr/wCi0PORmY= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.4/go.mod h1:sDQAfwJGv25uGPZA04x87ERglCG6avnRcBT9wYoMII8= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.5 h1:rOFDv+3k05mnW0oaDLffhVUwg03Csn0mvfO98Wdd2bE= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.5/go.mod h1:sDQAfwJGv25uGPZA04x87ERglCG6avnRcBT9wYoMII8= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -400,8 +400,8 @@ github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2 h1:Tz6v3Jb2DRnDCfifRSjYKG github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/password v0.1.1 h1:6JzmBqXprakgFEHwBgdchsjaA9x3GyjdI568bXKxa60= github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= -github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.0 h1:TszfJJ1vJWdvPkPxYivP5TUTkOaXziE1jTKGmfOck94= -github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.0/go.mod h1:Io+g8iYs//ESo5x2Gy1rTTYaIHUyCZD7h16K7DiOUI4= +github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.1 h1:s47ax4xEIStj1YV7TRz6zS8LlNrTU86aEhL8pfSOG4U= +github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.1/go.mod h1:Mh8pP9IH9qnBQSSH3PYtSkGq3+KlIuw5kAS9W8++BLc= github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI= github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= @@ -425,6 +425,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/nodeenrollment v0.0.0-20220427165109-a2f5dad593c7 h1:pOKDBZ6t9IZCL4xXWt0hJw0TM6Mn0BrRk4NAu7sJKS4= +github.com/hashicorp/nodeenrollment v0.0.0-20220427165109-a2f5dad593c7/go.mod h1:yOOykLWw4G7O1Z7+6X3gzXcQCb/5au1Zna4d1nlQLmM= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= @@ -764,8 +766,9 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= diff --git a/internal/cmd/base/option.go b/internal/cmd/base/option.go index d5c4735e0..25a241886 100644 --- a/internal/cmd/base/option.go +++ b/internal/cmd/base/option.go @@ -22,24 +22,25 @@ type Option func(*Options) // Options - how Options are represented. type Options struct { - withNoTokenScope bool - withNoTokenValue bool - withSkipDatabaseDestruction bool - withSkipAuthMethodCreation bool - withSkipOidcAuthMethodCreation bool - withSkipScopesCreation bool - withSkipHostResourcesCreation bool - withSkipTargetCreation bool - withContainerImage string - withDialect string - withDatabaseTemplate string - withEventerConfig *event.EventerConfig - withEventFlags *EventFlags - withEventWrapper wrapping.Wrapper - withAttributeFieldPrefix string - withStatusCode int - withHostPlugin func() (string, plugin.HostPluginServiceClient) - withEventGating bool + withNoTokenScope bool + withNoTokenValue bool + withSkipDatabaseDestruction bool + withSkipAuthMethodCreation bool + withSkipOidcAuthMethodCreation bool + withSkipScopesCreation bool + withSkipHostResourcesCreation bool + withSkipTargetCreation bool + withContainerImage string + withDialect string + withDatabaseTemplate string + withEventerConfig *event.EventerConfig + withEventFlags *EventFlags + withEventWrapper wrapping.Wrapper + withAttributeFieldPrefix string + withStatusCode int + withHostPlugin func() (string, plugin.HostPluginServiceClient) + withEventGating bool + withSkipWorkerAuthKmsInstantiation bool } func getDefaultOptions() Options { @@ -188,3 +189,11 @@ func WithEventGating(with bool) Option { o.withEventGating = with } } + +// WithSkipWorkerAuthKmsInstantiation causes KMS methods used for worker-auth to +// not be processed, useful for dev mode +func WithSkipWorkerAuthKmsInstantiation(with bool) Option { + return func(o *Options) { + o.withSkipWorkerAuthKmsInstantiation = with + } +} diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index 2b0f4c0d6..99c2abf7b 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -517,7 +517,9 @@ func (b *Server) SetupListeners(ui cli.Ui, config *configutil.SharedConfig, allo // SetupKMSes takes in a parsed config, does some minor checking on purposes, // and sends each off to configutil to instantiate a wrapper. -func (b *Server) SetupKMSes(ctx context.Context, ui cli.Ui, config *config.Config) error { +func (b *Server) SetupKMSes(ctx context.Context, ui cli.Ui, config *config.Config, opt ...Option) error { + opts := getOpts(opt...) + sharedConfig := config.SharedConfig var pluginLogger hclog.Logger var err error @@ -527,7 +529,11 @@ func (b *Server) SetupKMSes(ctx context.Context, ui cli.Ui, config *config.Confi switch purpose { case "": return errors.New("KMS block missing 'purpose'") - case globals.KmsPurposeRoot, globals.KmsPurposeWorkerAuth, globals.KmsPurposeConfig: + case globals.KmsPurposeWorkerAuth: + if opts.withSkipWorkerAuthKmsInstantiation { + continue + } + case globals.KmsPurposeRoot, globals.KmsPurposeConfig: case globals.KmsPurposeRecovery: if config.Controller != nil && config.DevRecoveryKey != "" { kms.Config["key"] = config.DevRecoveryKey diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 5861b398a..668ab7b91 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -29,8 +29,10 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/hashicorp/nodeenrollment/nodetypes" "github.com/mitchellh/cli" "github.com/posener/complete" + "go.uber.org/atomic" ) var ( @@ -51,39 +53,40 @@ type Command struct { controller *controller.Controller worker *worker.Worker - flagLogLevel string - flagLogFormat string - flagCombineLogs bool - flagLoginName string - flagPassword string - flagUnprivilegedLoginName string - flagUnprivilegedPassword string - flagIdSuffix string - flagHostAddress string - flagTargetDefaultPort int - flagTargetSessionMaxSeconds int - flagTargetSessionConnectionLimit int - flagControllerAPIListenAddr string - flagControllerClusterListenAddr string - flagControllerPublicClusterAddr string - flagControllerOnly bool - flagWorkerAuthKey string - flagWorkerProxyListenAddr string - flagWorkerPublicAddr string - flagOpsListenAddr string - flagUiPassthroughDir string - flagRecoveryKey string - flagDatabaseUrl string - flagContainerImage string - flagDisableDatabaseDestruction bool - flagEventFormat string - flagAudit string - flagObservations string - flagSysEvents string - flagEveryEventAllowFilters []string - flagEveryEventDenyFilters []string - flagCreateLoopbackHostPlugin bool - flagPluginExecutionDir string + flagLogLevel string + flagLogFormat string + flagCombineLogs bool + flagLoginName string + flagPassword string + flagUnprivilegedLoginName string + flagUnprivilegedPassword string + flagIdSuffix string + flagHostAddress string + flagTargetDefaultPort int + flagTargetSessionMaxSeconds int + flagTargetSessionConnectionLimit int + flagControllerAPIListenAddr string + flagControllerClusterListenAddr string + flagControllerPublicClusterAddr string + flagControllerOnly bool + flagWorkerAuthKey string + flagWorkerProxyListenAddr string + flagWorkerPublicAddr string + flagOpsListenAddr string + flagUiPassthroughDir string + flagRecoveryKey string + flagDatabaseUrl string + flagContainerImage string + flagDisableDatabaseDestruction bool + flagEventFormat string + flagAudit string + flagObservations string + flagSysEvents string + flagEveryEventAllowFilters []string + flagEveryEventDenyFilters []string + flagCreateLoopbackHostPlugin bool + flagPluginExecutionDir string + flagUseEphemeralKmsWorkerAuthMethod bool } func (c *Command) Synopsis() string { @@ -327,6 +330,11 @@ func (c *Command) Flags() *base.FlagSets { EnvVar: "BOUNDARY_DEV_PLUGIN_EXECUTION_DIR", Usage: "Specifies where Boundary should write plugins that it is executing; if not set defaults to system temp directory.", }) + f.BoolVar(&base.BoolVar{ + Name: "use-ephemeral-kms-worker-auth-method", + Target: &c.flagUseEphemeralKmsWorkerAuthMethod, + Usage: "If set, the original \"ephemeral\" method of worker auth will be used to connect the initial dev worker to the controller.", + }) f.BoolVar(&base.BoolVar{ Name: "create-loopback-host-plugin", @@ -540,7 +548,7 @@ func (c *Command) Run(args []string) int { if c.flagRecoveryKey != "" { c.Config.DevRecoveryKey = c.flagRecoveryKey } - if err := c.SetupKMSes(c.Context, c.UI, c.Config); err != nil { + if err := c.SetupKMSes(c.Context, c.UI, c.Config, base.WithSkipWorkerAuthKmsInstantiation(!c.flagUseEphemeralKmsWorkerAuthMethod)); err != nil { c.UI.Error(err.Error()) return base.CommandUserError } @@ -548,14 +556,16 @@ func (c *Command) Run(args []string) int { c.UI.Error("Controller KMS not found after parsing KMS blocks") return base.CommandUserError } - if c.WorkerAuthKms == nil { - c.UI.Error("Worker Auth KMS not found after parsing KMS blocks") - return base.CommandUserError + if c.flagUseEphemeralKmsWorkerAuthMethod { + if c.WorkerAuthKms == nil { + c.UI.Error("Worker Auth KMS not found after parsing KMS blocks") + return base.CommandUserError + } + c.InfoKeys = append(c.InfoKeys, "[Worker-Auth] AEAD Key Bytes") + c.Info["[Worker-Auth] AEAD Key Bytes"] = c.Config.DevWorkerAuthKey } c.InfoKeys = append(c.InfoKeys, "[Controller] AEAD Key Bytes") c.Info["[Controller] AEAD Key Bytes"] = c.Config.DevControllerKey - c.InfoKeys = append(c.InfoKeys, "[Worker-Auth] AEAD Key Bytes") - c.Info["[Worker-Auth] AEAD Key Bytes"] = c.Config.DevWorkerAuthKey c.InfoKeys = append(c.InfoKeys, "[Recovery] AEAD Key Bytes") c.Info["[Recovery] AEAD Key Bytes"] = c.Config.DevRecoveryKey @@ -613,12 +623,6 @@ func (c *Command) Run(args []string) int { } } - c.PrintInfo(c.UI) - if err := c.ReleaseLogGate(); err != nil { - c.UI.Error(fmt.Errorf("Error releasing event gate: %w", err).Error()) - return base.CommandCliError - } - { c.EnabledPlugins = append(c.EnabledPlugins, base.EnabledPluginHostAws, base.EnabledPluginHostAzure) conf := &controller.Config{ @@ -644,6 +648,8 @@ func (c *Command) Run(args []string) int { } } + errorEncountered := atomic.NewBool(false) + if !c.flagControllerOnly { conf := &worker.Config{ RawConfig: c.Config, @@ -669,6 +675,45 @@ func (c *Command) Run(args []string) int { } return base.CommandCliError } + + if !c.flagUseEphemeralKmsWorkerAuthMethod { + if c.worker.NodeeKeyId == "" { + c.UI.Error("No worker key ID found at worker registration time") + return base.CommandCliError + } + c.InfoKeys = append(c.InfoKeys, "worker key identifier") + c.Info["worker key identifier"] = c.worker.NodeeKeyId + go func() { + for { + select { + case <-c.Context.Done(): + return + case <-time.After(time.Second): + waitingNodes, err := c.controller.NodeeFileStorage.List(c.Context, (*nodetypes.NodeInformation)(nil)) + if err != nil { + c.UI.Error(fmt.Errorf("Error listing dev worker for authentication: %w", err).Error()) + errorEncountered.Store(true) + return + } + if len(waitingNodes) == 0 { + continue + } + if err := c.controller.AuthorizeNodeeWorker(c.worker.NodeeKeyId); err != nil { + c.UI.Error(fmt.Errorf("Error authorizing node: %w", err).Error()) + errorEncountered.Store(true) + return + } + return + } + } + }() + } + } + + c.PrintInfo(c.UI) + if err := c.ReleaseLogGate(); err != nil { + c.UI.Error(fmt.Errorf("Error releasing event gate: %w", err).Error()) + return base.CommandCliError } opsServer, err := ops.NewServer(c.Logger, c.controller, c.Listeners...) @@ -683,7 +728,7 @@ func (c *Command) Run(args []string) int { // Wait for shutdown shutdownTriggered := false - for !shutdownTriggered { + for !shutdownTriggered && !errorEncountered.Load() { select { case <-c.ShutdownCh: c.UI.Output("==> Boundary dev environment shutdown triggered, interrupt again to force") @@ -704,6 +749,15 @@ func (c *Command) Run(args []string) int { } }() + shutdownTriggered = true + + case <-c.SigUSR2Ch: + buf := make([]byte, 32*1024*1024) + n := runtime.Stack(buf[:], true) + event.WriteSysEvent(context.TODO(), op, "goroutine trace", "stack", string(buf[:n])) + } + + if shutdownTriggered { if c.Config.Controller != nil { c.opsServer.WaitIfHealthExists(c.Config.Controller.GracefulShutdownWaitDuration, c.UI) } @@ -722,13 +776,6 @@ func (c *Command) Run(args []string) int { if err != nil { c.UI.Error(fmt.Errorf("Failed to shutdown ops listeners: %w", err).Error()) } - - shutdownTriggered = true - - case <-c.SigUSR2Ch: - buf := make([]byte, 32*1024*1024) - n := runtime.Stack(buf[:], true) - event.WriteSysEvent(context.TODO(), op, "goroutine trace", "stack", string(buf[:n])) } } diff --git a/internal/cmd/commands/server/listener_reload_test.go b/internal/cmd/commands/server/listener_reload_test.go index 77398aa0d..4f19e884f 100644 --- a/internal/cmd/commands/server/listener_reload_test.go +++ b/internal/cmd/commands/server/listener_reload_test.go @@ -62,6 +62,7 @@ kms "aead" { listener "tcp" { purpose = "api" + address = "127.0.0.1:9500" tls_cert_file = "%s/bundle.pem" tls_key_file = "%s/bundle.pem" cors_enabled = true @@ -69,10 +70,12 @@ listener "tcp" { } listener "tcp" { + address = "127.0.0.1:9501" purpose = "cluster" } listener "tcp" { + address = "127.0.0.1:9502" purpose = "proxy" } ` @@ -131,7 +134,7 @@ func TestServer_ReloadListener(t *testing.T) { }() testCertificateSerial := func(serial string) { - conn, err := tls.Dial("tcp", "127.0.0.1:9200", &tls.Config{ + conn, err := tls.Dial("tcp", "127.0.0.1:9500", &tls.Config{ RootCAs: certPool, }) require.NoError(err) diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index bd9e3a2e4..eeadaf369 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -188,10 +188,6 @@ func (c *Command) Run(args []string) int { return base.CommandUserError } } - if c.WorkerAuthKms == nil { - c.UI.Error("Worker Auth KMS not found after parsing KMS blocks") - return base.CommandUserError - } if c.Config.DefaultMaxRequestDuration != 0 { globals.DefaultMaxRequestDuration = c.Config.DefaultMaxRequestDuration @@ -450,12 +446,6 @@ func (c *Command) Run(args []string) int { } }() - c.PrintInfo(c.UI) - if err := c.ReleaseLogGate(); err != nil { - c.UI.Error(fmt.Errorf("Error releasing event gate: %w", err).Error()) - return base.CommandCliError - } - if c.Config.Controller != nil { c.EnabledPlugins = append(c.EnabledPlugins, base.EnabledPluginHostAws, base.EnabledPluginHostAzure) if err := c.StartController(c.Context); err != nil { @@ -474,6 +464,28 @@ func (c *Command) Run(args []string) int { } return base.CommandCliError } + if c.WorkerAuthKms == nil { + if c.worker.NodeeKeyId == "" { + retErr := fmt.Errorf("No worker key ID found at worker registration time") + if err := c.worker.Shutdown(); err != nil { + c.UI.Error(retErr.Error()) + retErr = fmt.Errorf("Error shutting down worker: %w", err) + } + c.UI.Error(retErr.Error()) + if err := c.controller.Shutdown(); err != nil { + c.UI.Error(fmt.Errorf("Error with controller shutdown: %w", err).Error()) + } + return base.CommandCliError + } + c.InfoKeys = append(c.InfoKeys, "worker key identifier") + c.Info["worker key identifier"] = c.worker.NodeeKeyId + } + } + + c.PrintInfo(c.UI) + if err := c.ReleaseLogGate(); err != nil { + c.UI.Error(fmt.Errorf("Error releasing event gate: %w", err).Error()) + return base.CommandCliError } opsServer, err := ops.NewServer(c.Logger, c.controller, c.Listeners...) diff --git a/internal/cmd/commands/server/worker_shutdown_reload_test.go b/internal/cmd/commands/server/worker_shutdown_reload_test.go index 9fafaee11..77a82c32e 100644 --- a/internal/cmd/commands/server/worker_shutdown_reload_test.go +++ b/internal/cmd/commands/server/worker_shutdown_reload_test.go @@ -125,6 +125,10 @@ func TestServer_ShutdownWorker(t *testing.T) { }() waitCh(t, workerCmd.startedCh) + // Give the worker time for initial status or we can fail because no worker + // is ready for the connection yet + time.Sleep(5 * time.Second) + // Set up the target ctx := context.Background() client := buildClient(t, shutdownReloadApiAddr) diff --git a/internal/cmd/commands/server/worker_tags_reload_test.go b/internal/cmd/commands/server/worker_tags_reload_test.go index 66da2faed..dc278af51 100644 --- a/internal/cmd/commands/server/worker_tags_reload_test.go +++ b/internal/cmd/commands/server/worker_tags_reload_test.go @@ -60,7 +60,7 @@ kms "aead" { listener "tcp" { purpose = "api" - address = "127.0.0.1:9203" + address = "127.0.0.1:9403" tls_disable = true cors_enabled = true cors_allowed_origins = ["*"] @@ -68,12 +68,12 @@ listener "tcp" { listener "tcp" { purpose = "cluster" - address = "127.0.0.1:9204" + address = "127.0.0.1:9404" } listener "tcp" { purpose = "proxy" - address = "127.0.0.1:9205" + address = "127.0.0.1:9405" } ` @@ -82,7 +82,7 @@ const tag1Config = ` worker { name = "dev-worker" description = "A default worker created in dev mode" - controllers = ["127.0.0.1:9204"] + controllers = ["127.0.0.1:9404"] tags { type = ["dev", "local"] } @@ -93,7 +93,7 @@ const tag2Config = ` worker { name = "dev-worker" description = "A default worker created in dev mode" - controllers = ["127.0.0.1:9204"] + controllers = ["127.0.0.1:9404"] tags { foo = ["bar", "baz"] } diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 175dbb345..aeeb2b60f 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -36,6 +36,9 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/hashicorp/go-secure-stdlib/pluginutil/v2" + "github.com/hashicorp/nodeenrollment/noderegistration" + nodeefile "github.com/hashicorp/nodeenrollment/nodestorage/file" + "github.com/hashicorp/nodeenrollment/nodetypes" ua "go.uber.org/atomic" "google.golang.org/grpc" ) @@ -86,6 +89,9 @@ type Controller struct { // Used to signal the Health Service to start // replying to queries with "503 Service Unavailable". HealthService *health.Service + + // PoC: Testing bits for BYOW + NodeeFileStorage *nodeefile.FileStorage } func New(ctx context.Context, conf *Config) (*Controller, error) { @@ -299,6 +305,7 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { c.ConnectionRepoFn = func() (*session.ConnectionRepository, error) { return session.NewConnectionRepository(ctx, dbase, dbase, c.kms) } + return c, nil } @@ -309,6 +316,17 @@ func (c *Controller) Start() error { return nil } c.baseContext, c.baseCancel = context.WithCancel(context.Background()) + + var err error + c.NodeeFileStorage, err = nodeefile.NewFileStorage(c.baseContext) + if err != nil { + return err + } + _, err = nodetypes.RotateRootCertificates(c.baseContext, c.NodeeFileStorage) + if err != nil { + return err + } + if err := c.registerJobs(); err != nil { return fmt.Errorf("error registering jobs: %w", err) } @@ -391,3 +409,7 @@ func (c *Controller) Shutdown() error { func (c *Controller) WorkerStatusUpdateTimes() *sync.Map { return c.workerStatusUpdateTimes } + +func (c *Controller) AuthorizeNodeeWorker(keyId string) error { + return noderegistration.AuthorizeNode(c.baseContext, c.NodeeFileStorage, keyId) +} diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index 2f13dbac9..4a32ea81d 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "io/ioutil" "net/http" "net/textproto" @@ -42,6 +43,7 @@ import ( "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-secure-stdlib/listenerutil" "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/hashicorp/nodeenrollment/nodetypes" "github.com/mr-tron/base58" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -65,6 +67,7 @@ func (c *Controller) apiHandler(props HandlerProperties) (http.Handler, error) { if err != nil { return nil, err } + mux.Handle("/v1/nodes", handleNodes(c)) mux.Handle("/v1/", grpcGwMux) mux.Handle("/", handleUi(c)) @@ -561,6 +564,68 @@ func wrapHandlerWithCallbackInterceptor(h http.Handler, c *Controller) http.Hand }) } +func handleNodes(c *Controller) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + switch req.Method { + case http.MethodGet: + type vals struct { + WaitingNodes []string `json:"waiting_nodes"` + } + var err error + var currVals vals + currVals.WaitingNodes, err = c.NodeeFileStorage.List(c.baseContext, (*nodetypes.NodeInformation)(nil)) + if err != nil { + _, _ = w.Write([]byte(err.Error())) + w.WriteHeader(500) + return + } + ret, err := json.Marshal(currVals) + if err != nil { + _, _ = w.Write([]byte(err.Error())) + w.WriteHeader(500) + return + } + _, err = w.Write(ret) + if err != nil { + _, _ = w.Write([]byte(err.Error())) + w.WriteHeader(500) + return + } + w.WriteHeader(200) + return + + case http.MethodPost: + body, err := io.ReadAll(req.Body) + req.Body.Close() + if err != nil { + _, _ = w.Write([]byte(err.Error())) + w.WriteHeader(500) + return + } + type val struct { + KeyId string `json:"key_id"` + } + var currVal val + if err := json.Unmarshal(body, &currVal); err != nil { + _, _ = w.Write([]byte(err.Error())) + w.WriteHeader(500) + return + } + if err := c.AuthorizeNodeeWorker(currVal.KeyId); err != nil { + _, _ = w.Write([]byte(err.Error())) + w.WriteHeader(500) + return + } + w.WriteHeader(204) + return + + default: + w.WriteHeader(http.StatusBadRequest) + return + } + }) +} + /* func WrapForwardedForHandler(h http.Handler, authorizedAddrs []*sockaddr.SockAddrMarshaler, rejectNotPresent, rejectNonAuthz bool, hopSkips int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/servers/controller/handlers/workers/multihop_service.go b/internal/servers/controller/handlers/workers/multihop_service.go new file mode 100644 index 000000000..9b3fa4dbf --- /dev/null +++ b/internal/servers/controller/handlers/workers/multihop_service.go @@ -0,0 +1,45 @@ +package workers + +import ( + "context" + "fmt" + + pbs "github.com/hashicorp/nodeenrollment/multihop" + "github.com/hashicorp/nodeenrollment/nodeauth" + "github.com/hashicorp/nodeenrollment/noderegistration" + "github.com/hashicorp/nodeenrollment/nodetypes" +) + +type multihopServiceServer struct { + pbs.UnimplementedMultihopServiceServer + + currentParams nodeauth.CurrentParameterFactory +} + +func NewMultihopServiceServer( + currentParams nodeauth.CurrentParameterFactory, +) *multihopServiceServer { + return &multihopServiceServer{ + currentParams: currentParams, + } +} + +var _ pbs.MultihopServiceServer = (*multihopServiceServer)(nil) + +func (m *multihopServiceServer) FetchNodeCredentials(ctx context.Context, req *nodetypes.FetchNodeCredentialsRequest) (*nodetypes.FetchNodeCredentialsResponse, error) { + const op = "workers.(multihopServiceServer).FetchNodeCredentials" + _, storage, opt, err := m.currentParams() + if err != nil { + return nil, fmt.Errorf("%s: error getting current parameters: %w", op, err) + } + return noderegistration.FetchNodeCredentials(ctx, storage, req, opt...) +} + +func (m *multihopServiceServer) GenerateServerCertificates(ctx context.Context, req *nodetypes.GenerateServerCertificatesRequest) (*nodetypes.GenerateServerCertificatesResponse, error) { + const op = "workers.(multihopServiceServer).GenerateServerCertificates" + _, storage, opt, err := m.currentParams() + if err != nil { + return nil, fmt.Errorf("%s: error getting current parameters: %w", op, err) + } + return noderegistration.GenerateServerCertificates(ctx, storage, req, opt...) +} diff --git a/internal/servers/controller/intercepting_listener.go b/internal/servers/controller/intercepting_listener.go index 7cece060b..0088c8422 100644 --- a/internal/servers/controller/intercepting_listener.go +++ b/internal/servers/controller/intercepting_listener.go @@ -1,12 +1,15 @@ package controller import ( - "context" + "crypto/tls" "errors" "fmt" "net" + "strings" "github.com/hashicorp/boundary/internal/observability/event" + nodee "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/nodeauth" ) // interceptingListener allows us to validate the nonce from a connection before @@ -45,42 +48,54 @@ func newInterceptingListener(c *Controller, baseLn net.Listener) *interceptingLi func (m *interceptingListener) Accept() (net.Conn, error) { const op = "controller.(interceptingListener).Accept" - ctx := context.TODO() + ctx := m.c.baseContext conn, err := m.baseLn.Accept() if err != nil { + // We may already be closed but can't be sure, so just try anyways if conn != nil { if err := conn.Close(); err != nil { - event.WriteError(context.TODO(), op, err, event.WithInfoMsg("error closing worker connection")) + event.WriteError(ctx, op, err, event.WithInfoMsg("error closing worker connection")) } } return nil, err } - nonce := make([]byte, 20) - read, err := conn.Read(nonce) - if err != nil { - if err := conn.Close(); err != nil { - event.WriteError(ctx, op, err, event.WithInfoMsg("error closing worker connection")) + tlsConn := conn.(*tls.Conn) + switch { + case nodeauth.ContainsNodeAuthAlpnProto(tlsConn.ConnectionState().NegotiatedProtocol): + event.WriteSysEvent(ctx, op, "worker successfully authed", "key_id", nodee.KeyIdFromPkix(tlsConn.ConnectionState().PeerCertificates[0].SubjectKeyId)) + return conn, nil + + case strings.HasPrefix(tlsConn.ConnectionState().NegotiatedProtocol, "v1workerauth"): + nonce := make([]byte, 20) + read, err := conn.Read(nonce) + if err != nil { + if err := conn.Close(); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error closing worker connection")) + } + return nil, fmt.Errorf("error reading nonce from connection: %w", err) } - return nil, fmt.Errorf("error reading nonce from connection: %w", err) - } - if read != len(nonce) { - if err := conn.Close(); err != nil { - event.WriteError(ctx, op, err, event.WithInfoMsg("error closing worker connection")) + if read != len(nonce) { + if err := conn.Close(); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error closing worker connection")) + } + return nil, fmt.Errorf("error reading nonce from worker, expected %d bytes, got %d", 20, read) } - return nil, fmt.Errorf("error reading nonce from worker, expected %d bytes, got %d", 20, read) - } - workerInfoRaw, found := m.c.workerAuthCache.Load(string(nonce)) - if !found { - if err := conn.Close(); err != nil { - event.WriteError(ctx, op, err, event.WithInfoMsg("error closing worker connection")) + workerInfoRaw, found := m.c.workerAuthCache.Load(string(nonce)) + if !found { + if err := conn.Close(); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error closing worker connection")) + } + return nil, errors.New("did not find valid nonce for incoming worker") } - return nil, errors.New("did not find valid nonce for incoming worker") + workerInfo := workerInfoRaw.(*workerAuthEntry) + workerInfo.conn = tlsConn + event.WriteSysEvent(ctx, op, "worker successfully authed", "name", workerInfo.Name) + return tlsConn, nil + + default: + return nil, nodeauth.NewTempError(errors.New("unable to authenticate incoming connection")) } - workerInfo := workerInfoRaw.(*workerAuthEntry) - workerInfo.conn = conn - event.WriteSysEvent(ctx, op, "worker successfully authed", "name", workerInfo.Name) - return conn, nil } func (m *interceptingListener) Close() error { diff --git a/internal/servers/controller/listeners.go b/internal/servers/controller/listeners.go index aa9df9ed6..e0be9cb64 100644 --- a/internal/servers/controller/listeners.go +++ b/internal/servers/controller/listeners.go @@ -17,6 +17,9 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers/workers" "github.com/hashicorp/boundary/internal/servers/controller/internal/metric" "github.com/hashicorp/go-multierror" + nodee "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/multihop" + "github.com/hashicorp/nodeenrollment/nodeauth" "google.golang.org/grpc" ) @@ -103,9 +106,21 @@ func (c *Controller) configureForApi(ln *base.ServerListener) ([]func(), error) } func (c *Controller) configureForCluster(ln *base.ServerListener) (func(), error) { - l := tls.NewListener(ln.ClusterListener, &tls.Config{ - GetConfigForClient: c.validateWorkerTls, - }) + l, err := nodeauth.NewInterceptingListener( + ln.ClusterListener, + &tls.Config{ + GetConfigForClient: c.validateWorkerTls, + }, + nodeauth.MakeCurrentParametersFactory( + c.baseContext, + nodee.NopTransactionStorage(c.NodeeFileStorage), + ), + nil, + nil, + ) + if err != nil { + return nil, fmt.Errorf("error instantiating node auth listener: %w", err) + } workerReqInterceptor, err := workerRequestInfoInterceptor(c.baseContext, c.conf.Eventer) if err != nil { @@ -129,6 +144,12 @@ func (c *Controller) configureForCluster(ln *base.ServerListener) (func(), error c.workerStatusUpdateTimes, c.kms) pbs.RegisterServerCoordinationServiceServer(workerServer, workerService) pbs.RegisterSessionServiceServer(workerServer, workerService) + + multihopService := workers.NewMultihopServiceServer( + nodeauth.MakeCurrentParametersFactory(c.baseContext, nodee.NopTransactionStorage(c.NodeeFileStorage)), + ) + multihop.RegisterMultihopServiceServer(workerServer, multihopService) + metric.InitializeClusterCollectors(c.conf.PrometheusRegisterer, workerServer) ln.GrpcServer = workerServer diff --git a/internal/servers/controller/worker_tls_config.go b/internal/servers/controller/worker_tls_config.go index 29e20e12b..703e75f60 100644 --- a/internal/servers/controller/worker_tls_config.go +++ b/internal/servers/controller/worker_tls_config.go @@ -27,6 +27,7 @@ type workerAuthEntry struct { // been replayed, then stores connection information into the auth cache, which // is used by the intercepting listener to log information. func (c Controller) validateWorkerTls(hello *tls.ClientHelloInfo) (*tls.Config, error) { + const op = "controller.(Controller).validateWorkerTls" for _, p := range hello.SupportedProtos { switch { case strings.HasPrefix(p, "v1workerauth-"): @@ -50,7 +51,7 @@ func (c Controller) validateWorkerTls(hello *tls.ClientHelloInfo) (*tls.Config, return tlsConf, err } } - return nil, nil + return nil, errors.New("no supported protos found in hello") } // v1WorkerAuthConfig: diff --git a/internal/servers/worker/controller_connection.go b/internal/servers/worker/controller_connection.go index 210f31936..a1e0435a2 100644 --- a/internal/servers/worker/controller_connection.go +++ b/internal/servers/worker/controller_connection.go @@ -23,7 +23,12 @@ import ( "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/servers/worker/internal/metric" "github.com/hashicorp/go-secure-stdlib/base62" + nodee "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/multihop" + "github.com/hashicorp/nodeenrollment/nodeauth" "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/resolver" "google.golang.org/protobuf/proto" ) @@ -62,41 +67,62 @@ func (w *Worker) startControllerConnections() error { } func (w *Worker) controllerDialerFunc() func(context.Context, string) (net.Conn, error) { - const op = "worker.(Worker).controllerDialerFunc" return func(ctx context.Context, addr string) (net.Conn, error) { - tlsConf, authInfo, err := w.workerAuthTLSConfig() - if err != nil { - return nil, fmt.Errorf("error creating tls config for worker auth: %w", err) - } - dialer := &net.Dialer{} - var nonTlsConn net.Conn + var conn net.Conn + var err error switch { - case strings.HasPrefix(addr, "/"): - nonTlsConn, err = dialer.DialContext(ctx, "unix", addr) + case w.conf.WorkerAuthKms != nil: + conn, err = w.v1KmsAuthDialFn(ctx, addr) default: - nonTlsConn, err = dialer.DialContext(ctx, "tcp", addr) + conn, err = nodeauth.AuthDialFn( + nodeauth.MakeCurrentParametersFactory( + ctx, + nodee.NopTransactionStorage(w.NodeeFileStorage), + ))(ctx, addr) } - if err != nil { - return nil, fmt.Errorf("unable to dial to controller: %w", err) + + if !w.everAuthenticated.Load() && err == nil && conn != nil { + w.everAuthenticated.Store(true) } - tlsConn := tls.Client(nonTlsConn, tlsConf) - written, err := tlsConn.Write([]byte(authInfo.ConnectionNonce)) - if err != nil { - if err := nonTlsConn.Close(); err != nil { - event.WriteError(ctx, op, err, event.WithInfoMsg("error closing connection after writing failure")) - } - return nil, fmt.Errorf("unable to write connection nonce: %w", err) - } - if written != len(authInfo.ConnectionNonce) { - if err := nonTlsConn.Close(); err != nil { - event.WriteError(ctx, op, err, event.WithInfoMsg("error closing connection after writing failure")) - } - return nil, fmt.Errorf("expected to write %d bytes of connection nonce, wrote %d", len(authInfo.ConnectionNonce), written) - } - return tlsConn, nil + + return conn, err } } +func (w *Worker) v1KmsAuthDialFn(ctx context.Context, addr string) (net.Conn, error) { + const op = "worker.(Worker).v1KmsAuthDialFn" + tlsConf, authInfo, err := w.workerAuthTLSConfig() + if err != nil { + return nil, fmt.Errorf("error creating tls config for worker auth: %w", err) + } + dialer := &net.Dialer{} + var nonTlsConn net.Conn + switch { + case strings.HasPrefix(addr, "/"): + nonTlsConn, err = dialer.DialContext(ctx, "unix", addr) + default: + nonTlsConn, err = dialer.DialContext(ctx, "tcp", addr) + } + if err != nil { + return nil, fmt.Errorf("unable to dial to controller: %w", err) + } + tlsConn := tls.Client(nonTlsConn, tlsConf) + written, err := tlsConn.Write([]byte(authInfo.ConnectionNonce)) + if err != nil { + if err := nonTlsConn.Close(); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error closing connection after writing failure")) + } + return nil, fmt.Errorf("unable to write connection nonce: %w", err) + } + if written != len(authInfo.ConnectionNonce) { + if err := nonTlsConn.Close(); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error closing connection after writing failure")) + } + return nil, fmt.Errorf("expected to write %d bytes of connection nonce, wrote %d", len(authInfo.ConnectionNonce), written) + } + return tlsConn, nil +} + func (w *Worker) createClientConn(addr string) error { const op = "worker.(Worker).createClientConn" defaultTimeout := (time.Second + time.Nanosecond).String() @@ -113,18 +139,29 @@ func (w *Worker) createClientConn(addr string) error { } `, defaultTimeout) res := w.Resolver() - cc, err := grpc.DialContext(w.baseContext, - fmt.Sprintf("%s:///%s", res.Scheme(), addr), + dialOpts := []grpc.DialOption{ grpc.WithResolvers(res), grpc.WithUnaryInterceptor(metric.InstrumentClusterClient()), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32)), grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(math.MaxInt32)), grpc.WithContextDialer(w.controllerDialerFunc()), - grpc.WithInsecure(), + grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(defServiceConfig), // Don't have the resolver reach out for a service config from the // resolver, use the one specified as default grpc.WithDisableServiceConfig(), + grpc.WithConnectParams(grpc.ConnectParams{ + Backoff: backoff.Config{ + BaseDelay: time.Second, + Multiplier: 1.2, + Jitter: 0.2, + MaxDelay: 3 * time.Second, + }, + }), + } + cc, err := grpc.DialContext(w.baseContext, + fmt.Sprintf("%s:///%s", res.Scheme(), addr), + dialOpts..., ) if err != nil { return fmt.Errorf("error dialing controller for worker auth: %w", err) @@ -132,8 +169,7 @@ func (w *Worker) createClientConn(addr string) error { w.controllerStatusConn.Store(pbs.NewServerCoordinationServiceClient(cc)) w.controllerSessionConn.Store(pbs.NewSessionServiceClient(cc)) - - event.WriteSysEvent(w.baseContext, op, "connected to controller", "address", addr) + w.controllerMultihopConn.Store(multihop.NewMultihopServiceClient(cc)) return nil } diff --git a/internal/servers/worker/listeners.go b/internal/servers/worker/listeners.go index ce259a700..f8a386247 100644 --- a/internal/servers/worker/listeners.go +++ b/internal/servers/worker/listeners.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "log" + "math" "net" "net/http" "os" @@ -13,7 +14,13 @@ import ( "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/observability/event" + "github.com/hashicorp/boundary/internal/servers/controller/handlers/workers" "github.com/hashicorp/go-multierror" + nodee "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/multihop" + "github.com/hashicorp/nodeenrollment/nodeauth" + "github.com/hashicorp/nodeenrollment/nodetypes" + "google.golang.org/grpc" ) func (w *Worker) startListeners() error { @@ -27,21 +34,17 @@ func (w *Worker) startListeners() error { if err != nil { return fmt.Errorf("%s: unable to initialize std logger: %w", op, err) } - - servers := make([]func(), 0, len(w.listeners)) - for i := range w.listeners { - ln := w.listeners[i] - workerServer, err := w.configureForWorker(ln, logger) - if err != nil { - return fmt.Errorf("%s: failed to configure for worker: %w", op, err) - } - servers = append(servers, workerServer) + if w.proxyListener == nil { + return fmt.Errorf("%s: nil proxy listener", op) } - for _, s := range servers { - s() + workerServer, err := w.configureForWorker(w.proxyListener, logger) + if err != nil { + return fmt.Errorf("%s: failed to configure for worker: %w", op, err) } + workerServer() + return nil } @@ -52,7 +55,7 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) } cancelCtx := w.baseContext - server := &http.Server{ + httpServer := &http.Server{ Handler: handler, ReadHeaderTimeout: 10 * time.Second, ReadTimeout: 30 * time.Second, @@ -61,80 +64,173 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) return cancelCtx }, } - ln.HTTPServer = server + ln.HTTPServer = httpServer if ln.Config.HTTPReadHeaderTimeout > 0 { - server.ReadHeaderTimeout = ln.Config.HTTPReadHeaderTimeout + httpServer.ReadHeaderTimeout = ln.Config.HTTPReadHeaderTimeout } if ln.Config.HTTPReadTimeout > 0 { - server.ReadTimeout = ln.Config.HTTPReadTimeout + httpServer.ReadTimeout = ln.Config.HTTPReadTimeout } if ln.Config.HTTPWriteTimeout > 0 { - server.WriteTimeout = ln.Config.HTTPWriteTimeout + httpServer.WriteTimeout = ln.Config.HTTPWriteTimeout } if ln.Config.HTTPIdleTimeout > 0 { - server.IdleTimeout = ln.Config.HTTPIdleTimeout + httpServer.IdleTimeout = ln.Config.HTTPIdleTimeout } - l := tls.NewListener(ln.ProxyListener, &tls.Config{ - GetConfigForClient: w.getSessionTls, - }) + fetchCredsFn := func( + ctx context.Context, + _ nodee.Storage, + req *nodetypes.FetchNodeCredentialsRequest, + _ ...nodee.Option, + ) (*nodetypes.FetchNodeCredentialsResponse, error) { + client := w.controllerMultihopConn.Load() + if client == nil { + return nil, nodeauth.NewTempError(errors.New("error fetching controller connection, client is nil")) + } + multihopClient, ok := client.(multihop.MultihopServiceClient) + if !ok { + return nil, nodeauth.NewTempError(errors.New("client could not be understood as a multihop service client")) + } + return multihopClient.FetchNodeCredentials(ctx, req) + } - return func() { go server.Serve(l) }, nil + generateServerCertificatesFn := func( + ctx context.Context, + _ nodee.Storage, + req *nodetypes.GenerateServerCertificatesRequest, + _ ...nodee.Option, + ) (*nodetypes.GenerateServerCertificatesResponse, error) { + client := w.controllerMultihopConn.Load() + if client == nil { + return nil, nodeauth.NewTempError(errors.New("error fetching controller connection, client is nil")) + } + multihopClient, ok := client.(multihop.MultihopServiceClient) + if !ok { + return nil, nodeauth.NewTempError(errors.New("client could not be understood as a multihop service client")) + } + return multihopClient.GenerateServerCertificates(ctx, req) + } + + interceptingListener, err := nodeauth.NewInterceptingListener( + ln.ProxyListener, + &tls.Config{ + GetConfigForClient: w.getSessionTls, + }, + nodeauth.MakeCurrentParametersFactory( + w.baseContext, + nodee.NopTransactionStorage(w.NodeeFileStorage), + ), + fetchCredsFn, + generateServerCertificatesFn, + ) + if err != nil { + return nil, fmt.Errorf("error instantiating node auth listener: %w", err) + } + + w.nodeeTeeListener = nodeauth.NewTeeListener(interceptingListener) + + downstreamServer := grpc.NewServer( + grpc.MaxRecvMsgSize(math.MaxInt32), + grpc.MaxSendMsgSize(math.MaxInt32), + ) + multihopService := workers.NewMultihopServiceServer( + nodeauth.MakeCurrentParametersFactory(w.baseContext, nodee.NopTransactionStorage(w.NodeeFileStorage)), + ) + multihop.RegisterMultihopServiceServer(downstreamServer, multihopService) + + ln.GrpcServer = downstreamServer + + return func() { + go w.nodeeTeeListener.Start() + go httpServer.Serve(w.nodeeTeeListener.OtherListener()) + go ln.GrpcServer.Serve(w.nodeeTeeListener.NodeeListener()) + }, nil } func (w *Worker) stopServersAndListeners() error { - var closeErrors *multierror.Error - err := w.stopHttpServersAndListeners() - if err != nil { - closeErrors = multierror.Append(closeErrors, err) + var mg multierror.Group + mg.Go(w.stopHttpServer) + mg.Go(w.stopClusterGrpcServer) + + // FIXME (jeff): For some reason, unlike the controller, the grpc server + // really likes to hang on closing. Maybe because it's never served a + // connection? This is a workaround to force it until I can dig in. + var cancel context.CancelFunc + if w.nodeeTeeListener != nil { + var ctx context.Context + ctx, cancel = context.WithTimeout(w.baseContext, 2*time.Second) + go func() { + <-ctx.Done() + w.nodeeTeeListener.Stop() + cancel() + }() } - err = w.stopAnyListeners() - if err != nil { - closeErrors = multierror.Append(closeErrors, err) + stopErrors := mg.Wait() + + if w.nodeeTeeListener != nil { + cancel() + err := w.nodeeTeeListener.Stop() + if err != nil { + stopErrors = multierror.Append(stopErrors, err) + } } - return closeErrors.ErrorOrNil() + err := w.stopAnyListeners() + if err != nil { + stopErrors = multierror.Append(stopErrors, err) + } + + return stopErrors.ErrorOrNil() } -func (w *Worker) stopHttpServersAndListeners() error { - var closeErrors *multierror.Error - for i := range w.listeners { - ln := w.listeners[i] - if ln.HTTPServer == nil { - continue - } - - ctx, cancel := context.WithTimeout(w.baseContext, ln.Config.MaxRequestDuration) - ln.HTTPServer.Shutdown(ctx) - cancel() - - err := ln.ProxyListener.Close() - err = listenerCloseErrorCheck(ln.Config.Type, err) - if err != nil { - closeErrors = multierror.Append(closeErrors, err) - } +func (w *Worker) stopHttpServer() error { + if w.proxyListener == nil { + return nil } - return closeErrors.ErrorOrNil() + if w.proxyListener.HTTPServer == nil { + return nil + } + + ctx, cancel := context.WithTimeout(w.baseContext, w.proxyListener.Config.MaxRequestDuration) + w.proxyListener.HTTPServer.Shutdown(ctx) + cancel() + + return nil +} + +func (w *Worker) stopClusterGrpcServer() error { + if w.proxyListener == nil { + return nil + } + if w.proxyListener.GrpcServer == nil { + return nil + } + + w.proxyListener.GrpcServer.GracefulStop() + return nil } // stopAnyListeners does a final once over the known // listeners to make sure we didn't miss any; // expected to run at the end of stopServersAndListeners. func (w *Worker) stopAnyListeners() error { + if w.proxyListener == nil { + return nil + } var closeErrors *multierror.Error - for _, ln := range w.listeners { - if ln == nil || ln.ProxyListener == nil { - continue - } - - err := ln.ProxyListener.Close() - err = listenerCloseErrorCheck(ln.Config.Type, err) - if err != nil { - closeErrors = multierror.Append(closeErrors, err) - } + var err error + if w.nodeeTeeListener != nil { + err = w.nodeeTeeListener.Stop() + } else if w.proxyListener.ProxyListener != nil { + err = w.proxyListener.ProxyListener.Close() + } + err = listenerCloseErrorCheck("proxy", err) + if err != nil { + closeErrors = multierror.Append(closeErrors, err) } return closeErrors.ErrorOrNil() diff --git a/internal/servers/worker/listeners_test.go b/internal/servers/worker/listeners_test.go index f8b6e2974..d48cb6827 100644 --- a/internal/servers/worker/listeners_test.go +++ b/internal/servers/worker/listeners_test.go @@ -3,13 +3,10 @@ package worker import ( "context" "fmt" - "io" "net" "net/http" "os" - "strconv" "testing" - "time" "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/go-secure-stdlib/listenerutil" @@ -18,30 +15,19 @@ import ( func TestStartListeners(t *testing.T) { testNonTlsRejected := func(t *testing.T, resp *http.Response, err error) { - require.NoError(t, err) - require.NotNil(t, resp) - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) - require.Equal(t, "Client sent an HTTP request to an HTTPS server.\n", string(body)) + require.Error(t, err) } tests := []struct { - name string - listeners []*listenerutil.ListenerConfig - expErr bool - expErrMsg string - assertions func(t *testing.T, w *Worker, addrs []string) + name string + proxyListeners []*listenerutil.ListenerConfig + expErr bool + expErrMsg string + assertions func(t *testing.T, w *Worker, addr string) }{ { - name: "one tcp listener, one unix listener", - listeners: []*listenerutil.ListenerConfig{ - { - Type: "tcp", - Purpose: []string{"proxy"}, - Address: "127.0.0.1:0", - TLSDisable: true, - }, + name: "unix listener", + proxyListeners: []*listenerutil.ListenerConfig{ { Type: "unix", Purpose: []string{"proxy"}, @@ -50,68 +36,30 @@ func TestStartListeners(t *testing.T) { }, }, expErr: false, - assertions: func(t *testing.T, w *Worker, addrs []string) { - require.Len(t, addrs, 2) - - resp, err := http.Get("http://" + addrs[0] + "/v1/proxy/") - testNonTlsRejected(t, resp, err) - + assertions: func(t *testing.T, w *Worker, addr string) { cl := http.Client{ Transport: &http.Transport{ - Dial: func(network, addr string) (net.Conn, error) { return net.Dial("unix", addrs[1]) }, + Dial: func(network, addr string) (net.Conn, error) { return net.Dial("unix", addr) }, }, } - resp, err = cl.Get("http://anything.domain/v1/proxy/") + resp, err := cl.Get("http://anything.domain/v1/proxy/") testNonTlsRejected(t, resp, err) }, }, { - name: "multiple tcp listeners, multiple unix listeners", - listeners: []*listenerutil.ListenerConfig{ + name: "tcp listener", + proxyListeners: []*listenerutil.ListenerConfig{ { Type: "tcp", Purpose: []string{"proxy"}, Address: "127.0.0.1:0", TLSDisable: true, }, - { - Type: "tcp", - Purpose: []string{"proxy"}, - Address: "127.0.0.1:0", - TLSDisable: true, - }, - { - Type: "unix", - Purpose: []string{"proxy"}, - Address: "/tmp/boundary-worker-test-start-listeners0.sock", - TLSDisable: true, - }, - { - Type: "unix", - Purpose: []string{"proxy"}, - Address: "/tmp/boundary-worker-test-start-listeners1.sock", - TLSDisable: true, - }, }, expErr: false, - assertions: func(t *testing.T, w *Worker, addrs []string) { - require.Len(t, addrs, 4) - - resp, err := http.Get("http://" + addrs[0] + "/v1/proxy/") + assertions: func(t *testing.T, w *Worker, addr string) { + resp, err := http.Get("http://" + addr + "/v1/proxy/") testNonTlsRejected(t, resp, err) - - resp, err = http.Get("http://" + addrs[1] + "/v1/proxy/") - testNonTlsRejected(t, resp, err) - - for _, proxyAddr := range []string{addrs[2], addrs[3]} { - cl := http.Client{ - Transport: &http.Transport{ - Dial: func(network, addr string) (net.Conn, error) { return net.Dial("unix", proxyAddr) }, - }, - } - resp, err = cl.Get("http://anything.domain/v1/proxy") - testNonTlsRejected(t, resp, err) - } }, }, } @@ -119,7 +67,7 @@ func TestStartListeners(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { conf := NewTestWorker(t, &TestWorkerOpts{DisableAutoStart: true}).w.conf - conf.RawConfig.SharedConfig.Listeners = tt.listeners + conf.RawConfig.SharedConfig.Listeners = tt.proxyListeners err := conf.SetupListeners(nil, conf.RawConfig.SharedConfig, []string{"proxy"}) require.NoError(t, err) @@ -136,12 +84,8 @@ func TestStartListeners(t *testing.T) { require.NoError(t, err) - addrs := make([]string, 0) - for _, l := range w.listeners { - addrs = append(addrs, l.ProxyListener.Addr().String()) - } if tt.assertions != nil { - tt.assertions(t, w, addrs) + tt.assertions(t, w, w.proxyListener.ProxyListener.Addr().String()) } }) } @@ -158,14 +102,14 @@ func TestStopHttpServersAndListeners(t *testing.T) { { name: "no listeners", workerFn: func(t *testing.T) *Worker { - return &Worker{listeners: []*base.ServerListener{}} + return &Worker{proxyListener: &base.ServerListener{}} }, expErr: false, }, { name: "nil listeners slice", workerFn: func(t *testing.T) *Worker { - return &Worker{listeners: nil} + return &Worker{proxyListener: nil} }, expErr: false, }, @@ -173,65 +117,42 @@ func TestStopHttpServersAndListeners(t *testing.T) { name: "listeners with nil http server", workerFn: func(t *testing.T) *Worker { return &Worker{ - listeners: []*base.ServerListener{ - {HTTPServer: nil}, - {HTTPServer: nil}, - {HTTPServer: nil}, + proxyListener: &base.ServerListener{ + HTTPServer: nil, }, } }, expErr: false, }, { - name: "multiple listeners", + name: "valid listener with http server", workerFn: func(t *testing.T) *Worker { l1, err := net.Listen("tcp", "127.0.0.1:0") require.NoError(t, err) - l2, err := net.Listen("unix", "/tmp/boundary-worker-TestStopHttpServersAndListeners-"+strconv.FormatInt(time.Now().UnixNano(), 10)) - require.NoError(t, err) s1 := &http.Server{} - s2 := &http.Server{} go s1.Serve(l1) - go s2.Serve(l2) // Make sure they're up _, err = http.Get("http://" + l1.Addr().String()) require.NoError(t, err) - c := http.Client{Transport: &http.Transport{ - Dial: func(network, addr string) (net.Conn, error) { - return net.Dial("unix", l2.Addr().String()) - }, - }} - _, err = c.Get("http://random.domain") - require.NoError(t, err) - return &Worker{ baseContext: context.Background(), - listeners: []*base.ServerListener{ - { - ProxyListener: l1, - HTTPServer: s1, - Config: &listenerutil.ListenerConfig{Type: "tcp"}, - }, - { - ProxyListener: l2, - HTTPServer: s2, - Config: &listenerutil.ListenerConfig{Type: "tcp"}, - }, + proxyListener: &base.ServerListener{ + ProxyListener: l1, + HTTPServer: s1, + Config: &listenerutil.ListenerConfig{Type: "tcp"}, }, } }, assertions: func(t *testing.T, w *Worker) { // Asserts the HTTP Servers are closed. - require.ErrorIs(t, w.listeners[0].HTTPServer.Serve(w.listeners[0].ProxyListener), http.ErrServerClosed) - require.ErrorIs(t, w.listeners[1].HTTPServer.Serve(w.listeners[1].ProxyListener), http.ErrServerClosed) + require.ErrorIs(t, w.proxyListener.HTTPServer.Serve(w.proxyListener.ProxyListener), http.ErrServerClosed) // Asserts the underlying listeners are closed. - require.ErrorIs(t, w.listeners[0].ProxyListener.Close(), net.ErrClosed) - require.ErrorIs(t, w.listeners[1].ProxyListener.Close(), net.ErrClosed) + require.ErrorIs(t, w.proxyListener.ProxyListener.Close(), net.ErrClosed) }, expErr: false, }, @@ -240,7 +161,7 @@ func TestStopHttpServersAndListeners(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := tt.workerFn(t) - err := w.stopHttpServersAndListeners() + err := w.stopServersAndListeners() if tt.expErr { require.EqualError(t, err, tt.expErrStr) return @@ -265,66 +186,14 @@ func TestStopAnyListeners(t *testing.T) { { name: "nil listeners", workerFn: func(t *testing.T) *Worker { - return &Worker{listeners: nil} + return &Worker{proxyListener: nil} }, expErr: false, }, { name: "no listeners", workerFn: func(t *testing.T) *Worker { - return &Worker{listeners: []*base.ServerListener{}} - }, - expErr: false, - }, - { - name: "listeners slice with nil server listeners", - workerFn: func(t *testing.T) *Worker { - return &Worker{listeners: []*base.ServerListener{nil, nil, nil}} - }, - expErr: false, - }, - { - name: "non-empty but nil listeners", - workerFn: func(t *testing.T) *Worker { - return &Worker{listeners: []*base.ServerListener{ - {ProxyListener: nil}, {ProxyListener: nil}, {ProxyListener: nil}, - }} - }, - expErr: false, - }, - { - name: "multiple listeners, including a closed one", - workerFn: func(t *testing.T) *Worker { - l1, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - - l2, err := net.Listen("unix", "/tmp/boundary-worker-TestStopAnyListeners-"+strconv.FormatInt(time.Now().UnixNano(), 10)) - require.NoError(t, err) - - l3, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - require.NoError(t, l3.Close()) - - return &Worker{listeners: []*base.ServerListener{ - { - Config: &listenerutil.ListenerConfig{Type: "tcp"}, - ProxyListener: l1, - }, - { - Config: &listenerutil.ListenerConfig{Type: "tcp"}, - ProxyListener: l2, - }, - { - Config: &listenerutil.ListenerConfig{Type: "tcp"}, - ProxyListener: l3, - }, - }} - }, - assertions: func(t *testing.T, w *Worker) { - for i := range w.listeners { - ln := w.listeners[i] - require.ErrorIs(t, ln.ProxyListener.Close(), net.ErrClosed) - } + return &Worker{proxyListener: &base.ServerListener{}} }, expErr: false, }, diff --git a/internal/servers/worker/status.go b/internal/servers/worker/status.go index 45cb4496c..88b349f2e 100644 --- a/internal/servers/worker/status.go +++ b/internal/servers/worker/status.go @@ -90,6 +90,14 @@ func (w *Worker) WaitForNextSuccessfulStatusUpdate() error { func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { const op = "worker.(Worker).sendWorkerStatus" + + // If we've never managed to successfully authenticate then we won't have + // any session information anyways and this will produce a ton of noise in + // observability, so suppress it + if !w.everAuthenticated.Load() { + return + } + // First send info as-is. We'll perform cleanup duties after we // get cancel/job change info back. var activeJobs []*pbs.JobStatus diff --git a/internal/servers/worker/worker.go b/internal/servers/worker/worker.go index beca2f74c..8f87cf731 100644 --- a/internal/servers/worker/worker.go +++ b/internal/servers/worker/worker.go @@ -25,6 +25,9 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-secure-stdlib/mlock" + "github.com/hashicorp/nodeenrollment/nodeauth" + "github.com/hashicorp/nodeenrollment/noderegistration" + nodeefile "github.com/hashicorp/nodeenrollment/nodestorage/file" ua "go.uber.org/atomic" "google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver/manual" @@ -43,6 +46,7 @@ type Worker struct { tickerWg sync.WaitGroup controllerStatusConn *atomic.Value + everAuthenticated *ua.Bool lastStatusSuccess *atomic.Value workerStartTime time.Time @@ -51,7 +55,9 @@ type Worker struct { controllerSessionConn *atomic.Value sessionInfoMap *sync.Map - listeners []*base.ServerListener + controllerMultihopConn *atomic.Value + + proxyListener *base.ServerListener // Used to generate a random nonce for Controller connections nonceFn randFn @@ -62,7 +68,12 @@ type Worker struct { // This stores whether or not to send updated tags on the next status // request. It can be set via startup in New below, or (eventually) via // SIGHUP. - updateTags ua.Bool + updateTags *ua.Bool + + // PoC: Testing bits for BYOW + NodeeFileStorage *nodeefile.FileStorage + NodeeKeyId string + nodeeTeeListener *nodeauth.TeeListener // Test-specific options TestOverrideX509VerifyDnsName string @@ -75,16 +86,19 @@ func New(conf *Config) (*Worker, error) { metric.InitializeClusterClientCollectors(conf.PrometheusRegisterer) w := &Worker{ - conf: conf, - logger: conf.Logger.Named("worker"), - started: ua.NewBool(false), - controllerStatusConn: new(atomic.Value), - lastStatusSuccess: new(atomic.Value), - controllerResolver: new(atomic.Value), - controllerSessionConn: new(atomic.Value), - sessionInfoMap: new(sync.Map), - tags: new(atomic.Value), - nonceFn: base62.Random, + conf: conf, + logger: conf.Logger.Named("worker"), + started: ua.NewBool(false), + controllerStatusConn: new(atomic.Value), + everAuthenticated: ua.NewBool(false), + lastStatusSuccess: new(atomic.Value), + controllerResolver: new(atomic.Value), + controllerSessionConn: new(atomic.Value), + sessionInfoMap: new(sync.Map), + controllerMultihopConn: new(atomic.Value), + tags: new(atomic.Value), + updateTags: ua.NewBool(false), + nonceFn: base62.Random, } w.lastStatusSuccess.Store((*LastStatusInformation)(nil)) @@ -121,6 +135,7 @@ func New(conf *Config) (*Worker, error) { } } + var listenerCount int for i := range conf.Listeners { l := conf.Listeners[i] if l == nil || l.Config == nil || l.Config.Purpose == nil { @@ -131,11 +146,14 @@ func New(conf *Config) (*Worker, error) { } switch l.Config.Purpose[0] { case "proxy": - w.listeners = append(w.listeners, l) + if w.proxyListener == nil { + w.proxyListener = l + } + listenerCount++ } } - if len(w.listeners) == 0 { - return nil, fmt.Errorf("no proxy listeners found") + if listenerCount != 1 { + return nil, fmt.Errorf("exactly one proxy listener is required") } return w, nil @@ -143,13 +161,14 @@ func New(conf *Config) (*Worker, error) { func (w *Worker) Start() error { const op = "worker.(Worker).Start" + + w.baseContext, w.baseCancel = context.WithCancel(context.Background()) + if w.started.Load() { event.WriteSysEvent(w.baseContext, op, "already started, skipping") return nil } - w.baseContext, w.baseCancel = context.WithCancel(context.Background()) - scheme := strconv.FormatInt(time.Now().UnixNano(), 36) controllerResolver := manual.NewBuilderWithScheme(scheme) w.controllerResolver.Store(controllerResolver) @@ -157,6 +176,18 @@ func (w *Worker) Start() error { if err := w.startListeners(); err != nil { return fmt.Errorf("error starting worker listeners: %w", err) } + + var err error + w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext) + if err != nil { + return err + } + + w.NodeeKeyId, err = noderegistration.GenerateNodeCredentialsForRegistration(w.baseContext, w.NodeeFileStorage) + if err != nil { + return err + } + if err := w.startControllerConnections(); err != nil { return fmt.Errorf("error making controller connections: %w", err) } diff --git a/internal/servers/worker/worker_test.go b/internal/servers/worker/worker_test.go index 8ae62a97d..2f04c2fc2 100644 --- a/internal/servers/worker/worker_test.go +++ b/internal/servers/worker/worker_test.go @@ -23,13 +23,13 @@ func TestWorkerNew(t *testing.T) { name: "nil listeners", in: &Config{Server: &base.Server{Listeners: nil}}, expErr: true, - expErrMsg: "no proxy listeners found", + expErrMsg: "exactly one proxy listener is required", }, { name: "zero listeners", in: &Config{Server: &base.Server{Listeners: []*base.ServerListener{}}}, expErr: true, - expErrMsg: "no proxy listeners found", + expErrMsg: "exactly one proxy listener is required", }, { name: "populated with nil values", @@ -43,7 +43,7 @@ func TestWorkerNew(t *testing.T) { }, }, expErr: true, - expErrMsg: "no proxy listeners found", + expErrMsg: "exactly one proxy listener is required", }, { name: "multiple purposes", @@ -60,7 +60,7 @@ func TestWorkerNew(t *testing.T) { expErrMsg: `found listener with multiple purposes "api,proxy"`, }, { - name: "valid listeners", + name: "too many proxy listeners", in: &Config{ Server: &base.Server{ Listeners: []*base.ServerListener{ @@ -71,10 +71,21 @@ func TestWorkerNew(t *testing.T) { }, }, }, - expErr: false, - assertions: func(t *testing.T, w *Worker) { - require.Len(t, w.listeners, 2) + expErr: true, + expErrMsg: "exactly one proxy listener is required", + }, + { + name: "valid listeners", + in: &Config{ + Server: &base.Server{ + Listeners: []*base.ServerListener{ + {Config: &listenerutil.ListenerConfig{Purpose: []string{"api"}}}, + {Config: &listenerutil.ListenerConfig{Purpose: []string{"proxy"}}}, + {Config: &listenerutil.ListenerConfig{Purpose: []string{"cluster"}}}, + }, + }, }, + expErr: false, }, { name: "worker nonce func is set", From 9e276052139b579bc20b62baa3a8c432f2fa240d Mon Sep 17 00:00:00 2001 From: irenarindos Date: Mon, 2 May 2022 10:32:33 -0400 Subject: [PATCH 002/100] refact(servers): Split server table into worker and controller --- Makefile | 4 +- .../server/worker_tags_reload_test.go | 2 +- .../99/01_worker_controller_tables.up.sql | 130 ++++++++ .../migrations/oss/postgres_24_01_test.go | 166 ---------- .../server_coordination_service.pb.go | 5 +- .../v1/server_coordination_service.proto | 7 +- .../storage/servers/store/v1/controller.proto | 25 ++ .../storage/servers/store/v1/worker.proto | 39 +++ internal/scheduler/job/testing.go | 14 +- internal/scheduler/testing.go | 12 +- internal/servers/controller/controller.go | 6 +- .../handlers/targets/target_service.go | 9 +- .../handlers/workers/worker_service.go | 39 ++- .../workers/worker_service_status_test.go | 62 ++-- internal/servers/controller/tickers.go | 21 +- internal/servers/gorm.go | 5 +- internal/servers/query.go | 2 +- internal/servers/repository.go | 170 ++++++---- internal/servers/repository_test.go | 76 +++-- internal/servers/store/controller.pb.go | 209 ++++++++++++ internal/servers/store/gorm.go | 11 + internal/servers/store/worker.pb.go | 308 ++++++++++++++++++ internal/servers/worker/status.go | 13 +- internal/servers/worker/worker.go | 3 +- internal/session/options.go | 30 +- internal/session/options_test.go | 6 +- internal/session/query.go | 28 +- internal/session/repository.go | 3 +- internal/session/repository_connection.go | 55 +++- .../session/repository_connection_test.go | 296 ++++++++++++++++- internal/session/repository_session.go | 27 +- internal/session/repository_session_test.go | 20 +- .../service_authorize_connection_test.go | 2 +- .../session/service_close_connections_test.go | 2 +- .../service_worker_status_report_test.go | 28 +- internal/session/session.go | 19 +- internal/session/session_cleanup_job.go | 2 +- internal/session/testing.go | 21 +- ...on_list_benchmarks_dump_generation_test.go | 10 +- 39 files changed, 1427 insertions(+), 460 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql delete mode 100644 internal/db/schema/migrations/oss/postgres_24_01_test.go create mode 100644 internal/proto/controller/storage/servers/store/v1/controller.proto create mode 100644 internal/proto/controller/storage/servers/store/v1/worker.proto create mode 100644 internal/servers/store/controller.pb.go create mode 100644 internal/servers/store/gorm.go create mode 100644 internal/servers/store/worker.pb.go diff --git a/Makefile b/Makefile index c4bd3d0cc..c4abd4552 100644 --- a/Makefile +++ b/Makefile @@ -146,7 +146,9 @@ protobuild: @protoc-go-inject-tag -input=./internal/kms/store/oplog_key.pb.go @protoc-go-inject-tag -input=./internal/kms/store/token_key.pb.go @protoc-go-inject-tag -input=./internal/kms/store/session_key.pb.go - @protoc-go-inject-tag -input=./internal/kms/store/oidc_key.pb.go + @protoc-go-inject-tag -input=./internal/kms/store/oidc_key.pb.go + @protoc-go-inject-tag -input=./internal/servers/store/controller.pb.go + @protoc-go-inject-tag -input=./internal/servers/store/worker.pb.go @protoc-go-inject-tag -input=./internal/target/store/target.pb.go @protoc-go-inject-tag -input=./internal/target/targettest/store/target.pb.go @protoc-go-inject-tag -input=./internal/target/tcp/store/target.pb.go diff --git a/internal/cmd/commands/server/worker_tags_reload_test.go b/internal/cmd/commands/server/worker_tags_reload_test.go index dc278af51..ceeb17d1d 100644 --- a/internal/cmd/commands/server/worker_tags_reload_test.go +++ b/internal/cmd/commands/server/worker_tags_reload_test.go @@ -140,7 +140,7 @@ func TestServer_ReloadWorkerTags(t *testing.T) { t.Helper() serversRepo, err := cmd.controller.ServersRepoFn() require.NoError(err) - tags, err := serversRepo.ListTagsForServers(cmd.Context, []string{name}) + tags, err := serversRepo.ListTagsForWorkers(cmd.Context, []string{name}) require.NoError(err) require.Len(tags, 2) require.Equal(key, tags[0].Key) diff --git a/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql new file mode 100644 index 000000000..cddd38dd5 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql @@ -0,0 +1,130 @@ +begin; + +-- Split the server table into two new tables: controller and worker +create table server_controller ( + private_id text primary key, + description wt_description, + address text not null, + create_time wt_timestamp, + update_time wt_timestamp +); + +create trigger immutable_columns before update on server_controller + for each row execute procedure immutable_columns('private_id','create_time'); + +create trigger default_create_time_column before insert on server_controller + for each row execute procedure default_create_time(); + +create trigger controller_insert_time_column before insert on server_controller + for each row execute procedure update_time_column(); + +create trigger controller_update_time_column before update on server_controller + for each row execute procedure update_time_column(); + +-- Worker table adds the field: name +create table server_worker ( + private_id text primary key, + description wt_description, + name wt_name unique, + address text not null, + create_time wt_timestamp, + update_time wt_timestamp +); + +create trigger immutable_columns before update on server_worker + for each row execute procedure immutable_columns('private_id','create_time'); + +create trigger default_create_time_column before insert on server_worker + for each row execute procedure default_create_time(); + +create trigger worker_insert_time_column before insert on server_worker + for each row execute procedure update_time_column(); + +create trigger worker_update_time_column before update on server_worker + for each row execute procedure update_time_column(); + +-- TODO: Migrate server entries to worker and controller tables, if necessary + +-- Create table worker tag +create table server_worker_tag ( + worker_id text + references server_worker(private_id) + on delete cascade + on update cascade, + key wt_tagpair, + value wt_tagpair, + primary key(worker_id, key, value) +); + +-- Aaand drop server_tag +drop table server_tag; + +-- Update session table to use worker_id instead of server_id +alter table session + drop constraint session_server_id_fkey; +drop trigger update_version_column + on session; +alter table session + drop column server_type cascade; + +alter table session + rename column server_id to worker_id; +alter table session + add constraint session_worker_id_fkey + foreign key (worker_id) + references server_worker(private_id) + on delete set null + on update cascade; + +create trigger + update_version_column + after update of version, termination_reason, key_id, tofu_token, worker_id on session + for each row execute procedure update_version_column(); + +-- Update session_connection table to use worker_id instead of server_id +alter table session_connection + drop constraint server_fkey; + +alter table session_connection + rename column server_id to worker_id; +alter table session_connection + add constraint worker_fkey + foreign key (worker_id) + references server_worker (private_id) + on delete set null + on update cascade; + +-- Update job run table so that server id references controller id +alter table job_run + drop constraint server_fkey; +alter table job_run + add constraint server_fkey + foreign key (server_id) + references server_controller (private_id) + on delete set null + on update cascade; + +-- Replaces the view created in 9/01 to include worker id instead of server id and server type +drop view if exists session_list; + +create view session_list as + + select + s.public_id, s.user_id, s.host_id, s.worker_id, s.target_id, + s.host_set_id, s.auth_token_id, s.scope_id, s.certificate,s.expiration_time, + s.connection_limit, s.tofu_token, s.key_id, s.termination_reason, s.version, + s.create_time, s.update_time, s.endpoint, s.worker_filter, + ss.state, ss.previous_end_time, ss.start_time, ss.end_time, sc.public_id as connection_id, + sc.client_tcp_address, sc.client_tcp_port, sc.endpoint_tcp_address, sc.endpoint_tcp_port, + sc.bytes_up, sc.bytes_down, sc.closed_reason + from session s + join session_state ss on + s.public_id = ss.session_id + left join session_connection sc on + s.public_id = sc.session_id; + +-- Drop the server and server_type_enm tables +drop table server; +drop table server_type_enm; + +commit; \ No newline at end of file diff --git a/internal/db/schema/migrations/oss/postgres_24_01_test.go b/internal/db/schema/migrations/oss/postgres_24_01_test.go deleted file mode 100644 index bb1893d4b..000000000 --- a/internal/db/schema/migrations/oss/postgres_24_01_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package oss_test - -import ( - "context" - "testing" - - "github.com/hashicorp/boundary/internal/authtoken" - "github.com/hashicorp/boundary/internal/db" - "github.com/hashicorp/boundary/internal/db/common" - "github.com/hashicorp/boundary/internal/db/schema" - "github.com/hashicorp/boundary/internal/host/static" - "github.com/hashicorp/boundary/internal/iam" - "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/session" - "github.com/hashicorp/boundary/internal/target" - "github.com/hashicorp/boundary/internal/target/tcp" - "github.com/hashicorp/boundary/testing/dbtest" - "github.com/stretchr/testify/require" -) - -func TestMigrations_SessionState(t *testing.T) { - t.Parallel() - require := require.New(t) - - const ( - priorMigration = 21002 - currentMigration = 24001 - ) - dialect := dbtest.Postgres - ctx := context.Background() - - c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1)) - require.NoError(err) - t.Cleanup(func() { - require.NoError(c()) - }) - d, err := common.SqlOpen(dialect, u) - require.NoError(err) - - // migration to the prior migration (before the one we want to test) - m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions( - schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}), - )) - require.NoError(err) - - require.NoError(m.ApplyMigrations(ctx)) - state, err := m.CurrentState(ctx) - require.NoError(err) - want := &schema.State{ - Initialized: true, - Editions: []schema.EditionState{ - { - Name: "oss", - BinarySchemaVersion: priorMigration, - DatabaseSchemaVersion: priorMigration, - DatabaseSchemaState: schema.Equal, - }, - }, - } - require.Equal(want, state) - - // Seed the database with test data - dbType, err := db.StringToDbType(dialect) - require.NoError(err) - - conn, err := db.Open(dbType, u) - require.NoError(err) - - rw := db.New(conn) - wrapper := db.TestWrapper(t) - - org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - require.NotNil(prj) - - hc := static.TestCatalogs(t, conn, prj.GetPublicId(), 1)[0] - hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] - h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] - static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - - tar := tcp.TestTarget(ctx, t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) - - kmsCache := kms.TestKms(t, conn, wrapper) - - { - at := authtoken.TestAuthToken(t, conn, kmsCache, org.GetPublicId()) - uId := at.GetIamUserId() - - sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ - UserId: uId, - HostId: h.GetPublicId(), - TargetId: tar.GetPublicId(), - HostSetId: hs.GetPublicId(), - AuthTokenId: at.GetPublicId(), - ScopeId: prj.GetPublicId(), - Endpoint: "tcp://127.0.0.1:22", - ConnectionLimit: -1, - }) - - session.TestConnection(t, conn, sess.PublicId, "127.0.0.1", 22, - "127.0.0.2", 23, "127.0.0.1") - } - - // now we're ready for the migration we want to test. - m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions( - schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}), - )) - require.NoError(err) - - require.NoError(m.ApplyMigrations(ctx)) - state, err = m.CurrentState(ctx) - require.NoError(err) - want = &schema.State{ - Initialized: true, - Editions: []schema.EditionState{ - { - Name: "oss", - BinarySchemaVersion: currentMigration, - DatabaseSchemaVersion: currentMigration, - DatabaseSchemaState: schema.Equal, - }, - }, - } - require.Equal(want, state) - - sessionRepo, err := session.NewRepository(rw, rw, kmsCache) - connectionRepo, err := session.NewConnectionRepository(ctx, rw, rw, kmsCache) - require.NoError(err) - - // Ensure session is cancelled - repoSessions, err := sessionRepo.ListSessions(ctx) - require.NoError(err) - var sessionTermReason []string - var repoSessionId string - for i := 0; i < len(repoSessions); i++ { - s := repoSessions[i] - repoSessionId = s.PublicId - sessionTermReason = append(sessionTermReason, s.TerminationReason) - } - require.Equal([]string{"canceled"}, sessionTermReason) - - // Ensure connection is also cancelled - connections, err := connectionRepo.ListConnectionsBySessionId(ctx, repoSessionId) - require.NoError(err) - - var connTermReason []string - for i := 0; i < len(connections); i++ { - c := connections[i] - connTermReason = append(connTermReason, c.ClosedReason) - } - require.Equal([]string{"canceled"}, connTermReason) - - // Validate new table contents - rows, err := d.QueryContext(ctx, "select * from session_valid_state") - require.NoError(err) - - var validStates []string - for rows.Next() { - var a, b string - require.NoError(rows.Scan(&a, &b)) - validStates = append(validStates, []string{a, b}...) - } - require.Equal([]string{ - "pending", "pending", "pending", "active", "pending", "terminated", - "pending", "canceling", "active", "canceling", "active", "terminated", "canceling", "terminated", - }, validStates) -} diff --git a/internal/gen/controller/servers/services/server_coordination_service.pb.go b/internal/gen/controller/servers/services/server_coordination_service.pb.go index bf6698ec1..c893c7c2b 100644 --- a/internal/gen/controller/servers/services/server_coordination_service.pb.go +++ b/internal/gen/controller/servers/services/server_coordination_service.pb.go @@ -468,7 +468,7 @@ type StatusRequest struct { unknownFields protoimpl.UnknownFields // The worker info. We could use information from the TLS connection but this - // is easier and going the other route doesn't provijde much benefit -- if you + // is easier and going the other route doesn't provide much benefit -- if you // get access to the key and spoof the connection, you're already compromised. Worker *servers.Server `protobuf:"bytes,10,opt,name=worker,proto3" json:"worker,omitempty"` // Jobs which this worker wants to report the status. @@ -593,8 +593,7 @@ type StatusResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Active controllers. This can be used (eventually) for connection - // management. + // Active controllers. This can be used for connection management. Controllers []*servers.Server `protobuf:"bytes,10,rep,name=controllers,proto3" json:"controllers,omitempty"` // List of jobs and the expected state changes. For example, this will // include jobs witch change type of canceled for jobs which are active on a diff --git a/internal/proto/controller/servers/services/v1/server_coordination_service.proto b/internal/proto/controller/servers/services/v1/server_coordination_service.proto index 098b2d7fc..6f8a0b009 100644 --- a/internal/proto/controller/servers/services/v1/server_coordination_service.proto +++ b/internal/proto/controller/servers/services/v1/server_coordination_service.proto @@ -56,9 +56,11 @@ message JobStatus { Job job = 1; } + + message StatusRequest { // The worker info. We could use information from the TLS connection but this - // is easier and going the other route doesn't provijde much benefit -- if you + // is easier and going the other route doesn't provide much benefit -- if you // get access to the key and spoof the connection, you're already compromised. servers.v1.Server worker = 10; @@ -85,8 +87,7 @@ message JobChangeRequest { } message StatusResponse { - // Active controllers. This can be used (eventually) for connection - // management. + // Active controllers. This can be used for connection management. repeated servers.v1.Server controllers = 10; // List of jobs and the expected state changes. For example, this will diff --git a/internal/proto/controller/storage/servers/store/v1/controller.proto b/internal/proto/controller/storage/servers/store/v1/controller.proto new file mode 100644 index 000000000..5d2be86f6 --- /dev/null +++ b/internal/proto/controller/storage/servers/store/v1/controller.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package controller.storage.servers.store.v1; +option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; + +import "controller/storage/timestamp/v1/timestamp.proto"; + +// Controller contains all fields related to a Controller resource +message Controller { + // Private ID of the resource + string private_id = 10; + + // Address for the controller + string address = 20; + + // Description of the resource (optional) + // @inject_tag: `gorm:"default:null"` + string description = 30; + + // First seen time from the RDBMS + storage.timestamp.v1.Timestamp create_time = 40; + + // Last time there was an update + storage.timestamp.v1.Timestamp update_time = 50; +} \ No newline at end of file diff --git a/internal/proto/controller/storage/servers/store/v1/worker.proto b/internal/proto/controller/storage/servers/store/v1/worker.proto new file mode 100644 index 000000000..6203a5f6a --- /dev/null +++ b/internal/proto/controller/storage/servers/store/v1/worker.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; + +package controller.storage.servers.store.v1; +option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; + +import "controller/storage/timestamp/v1/timestamp.proto"; + +// Worker contains all fields related to a Worker resource +message Worker { + // Private ID of the resource + string private_id = 10; + + // Name of the resource (optional) + // Description of the resource (optional) + // @inject_tag: `gorm:"default:null"` + string name = 20; + + // Description of the resource (optional) + // @inject_tag: `gorm:"default:null"` + string description = 30; + + // Address for the worker + string address = 40; + + // First seen time from the RDBMS + storage.timestamp.v1.Timestamp create_time = 50; + + // Last time there was an update + storage.timestamp.v1.Timestamp update_time = 60; + + // @gotags: `gorm:"-"` + map tags = 70; +} + +// TagValues is used because map fields cannot be repeated but can be a +// message +message TagValues { + repeated string values = 10; +} diff --git a/internal/scheduler/job/testing.go b/internal/scheduler/job/testing.go index 8a2fa6852..c1c43450b 100644 --- a/internal/scheduler/job/testing.go +++ b/internal/scheduler/job/testing.go @@ -6,6 +6,8 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" @@ -88,7 +90,7 @@ func testRunWithUpdateTime(conn *db.DB, pluginId, name, cId string, updateTime t return run, nil } -func testController(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper) *servers.Server { +func testController(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *store.Controller { t.Helper() rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) @@ -97,13 +99,11 @@ func testController(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper) *server id, err := uuid.GenerateUUID() require.NoError(t, err) - controller := &servers.Server{ - PrivateId: "test-job-server-" + id, - Type: servers.ServerTypeController.String(), - Description: "Test Job Controller", - Address: "127.0.0.1", + controller := &store.Controller{ + PrivateId: "test-job-server-" + id, + Address: "127.0.0.1", } - _, _, err = serversRepo.UpsertServer(context.Background(), controller) + _, err = serversRepo.UpsertController(context.Background(), controller) require.NoError(t, err) return controller } diff --git a/internal/scheduler/testing.go b/internal/scheduler/testing.go index e11672b0b..93c6a4752 100644 --- a/internal/scheduler/testing.go +++ b/internal/scheduler/testing.go @@ -6,6 +6,8 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" @@ -32,13 +34,11 @@ func TestScheduler(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, opt ...O id, err := uuid.GenerateUUID() require.NoError(t, err) - controller := &servers.Server{ - PrivateId: "test-job-server-" + id, - Type: servers.ServerTypeController.String(), - Description: "Test Job Controller", - Address: "127.0.0.1", + controller := &store.Controller{ + PrivateId: "test-job-server-" + id, + Address: "127.0.0.1", } - _, _, err = serversRepo.UpsertServer(context.Background(), controller) + _, err = serversRepo.UpsertController(context.Background(), controller) require.NoError(t, err) jobRepoFn := func() (*job.Repository, error) { diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index aeeb2b60f..a089694fc 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -334,9 +334,9 @@ func (c *Controller) Start() error { return fmt.Errorf("error starting controller listeners: %w", err) } - // Upsert server before starting tickers and scheduler to ensure the server exists - if err := c.upsertServer(c.baseContext); err != nil { - return fmt.Errorf("error upserting server: %w", err) + // Upsert controller before starting tickers and scheduler to ensure the controller exists + if err := c.upsertController(c.baseContext); err != nil { + return fmt.Errorf("error upserting controller: %w", err) } if err := c.scheduler.Start(c.baseContext, c.schedulerWg); err != nil { return fmt.Errorf("error starting scheduler: %w", err) diff --git a/internal/servers/controller/handlers/targets/target_service.go b/internal/servers/controller/handlers/targets/target_service.go index d90528da0..19798d438 100644 --- a/internal/servers/controller/handlers/targets/target_service.go +++ b/internal/servers/controller/handlers/targets/target_service.go @@ -23,7 +23,6 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/perms" "github.com/hashicorp/boundary/internal/requests" - "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/common/scopeids" @@ -898,7 +897,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession var workers []*pb.WorkerInfo var workerIds []string hasWorkerFilter := len(t.GetWorkerFilter()) > 0 - servers, err := serversRepo.ListServers(ctx, servers.ServerTypeWorker) + servers, err := serversRepo.ListWorkers(ctx) if err != nil { return nil, err } @@ -912,7 +911,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession if hasWorkerFilter && len(workerIds) > 0 { finalWorkers := make([]*pb.WorkerInfo, 0, len(workers)) // Fetch the tags for the given worker IDs - tags, err := serversRepo.ListTagsForServers(ctx, workerIds) + tags, err := serversRepo.ListTagsForWorkers(ctx, workerIds) if err != nil { return nil, err } @@ -921,10 +920,10 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession // worker's ID to its filter map. tagMap := make(map[string]map[string][]string) for _, tag := range tags { - currWorkerMap := tagMap[tag.ServerId] + currWorkerMap := tagMap[tag.WorkerId] if currWorkerMap == nil { currWorkerMap = make(map[string][]string) - tagMap[tag.ServerId] = currWorkerMap + tagMap[tag.WorkerId] = currWorkerMap } currWorkerMap[tag.Key] = append(currWorkerMap[tag.Key], tag.Value) // We don't need to reinsert after the fact because maps are diff --git a/internal/servers/controller/handlers/workers/worker_service.go b/internal/servers/controller/handlers/workers/worker_service.go index 29df3393c..45fbbb473 100644 --- a/internal/servers/controller/handlers/workers/worker_service.go +++ b/internal/servers/controller/handlers/workers/worker_service.go @@ -7,6 +7,8 @@ import ( "sync" "time" + "github.com/hashicorp/boundary/internal/servers/store" + pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/observability/event" @@ -14,7 +16,6 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/session" - "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" "github.com/hashicorp/go-bexpr" "google.golang.org/grpc/codes" @@ -70,14 +71,41 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques event.WriteError(ctx, op, err, event.WithInfoMsg("error getting sessions repo")) return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error acquiring repo to query session status: %v", err) } - req.Worker.Type = resource.Worker.String() - controllers, _, err := serverRepo.UpsertServer(ctx, req.Worker, servers.WithUpdateTags(req.GetUpdateTags())) + + reqServer := req.GetWorker() + // Convert API tags to storage tags + workerTags := make(map[string]*store.TagValues) + for k, v := range reqServer.Tags { + thisTag := &store.TagValues{ + Values: v.GetValues(), + } + workerTags[k] = thisTag + } + worker := &store.Worker{ + PrivateId: reqServer.PrivateId, + Address: reqServer.Address, + CreateTime: reqServer.CreateTime, + UpdateTime: reqServer.UpdateTime, + Tags: workerTags, + } + controllers, _, err := serverRepo.UpsertWorker(ctx, worker, servers.WithUpdateTags(req.GetUpdateTags())) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error storing worker status")) return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error storing worker status: %v", err) } + + responseControllers := []*servers.Server{} + for _, c := range controllers { + thisController := &servers.Server{ + PrivateId: c.PrivateId, + Address: c.Address, + CreateTime: c.CreateTime, + UpdateTime: c.UpdateTime, + } + responseControllers = append(responseControllers, thisController) + } ret := &pbs.StatusResponse{ - Controllers: controllers, + Controllers: responseControllers, } stateReport := make([]session.StateReport, 0, len(req.GetJobs())) @@ -171,7 +199,7 @@ func (ws *workerServiceServer) LookupSession(ctx context.Context, req *pbs.Looku event.WriteError(ctx, op, err, event.WithInfoMsg("error getting servers repo")) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error acquiring server repo when looking up session: %v", err) } - tags, err := serversRepo.ListTagsForServers(ctx, []string{req.ServerId}) + tags, err := serversRepo.ListTagsForWorkers(ctx, []string{req.ServerId}) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error looking up tags for server", "server_id", req.ServerId)) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error looking up tags for server: %v", err) @@ -295,7 +323,6 @@ func (ws *workerServiceServer) ActivateSession(ctx context.Context, req *pbs.Act req.GetSessionId(), req.GetVersion(), req.GetWorkerId(), - resource.Worker.String(), []byte(req.GetTofuToken())) if err != nil { return nil, status.Errorf(codes.Internal, "error looking up session: %v", err) diff --git a/internal/servers/controller/handlers/workers/worker_service_status_test.go b/internal/servers/controller/handlers/workers/worker_service_status_test.go index c1efb3ba7..b66dc3351 100644 --- a/internal/servers/controller/handlers/workers/worker_service_status_test.go +++ b/internal/servers/controller/handlers/workers/worker_service_status_test.go @@ -5,6 +5,8 @@ import ( "sync" "testing" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/boundary/internal/authtoken" @@ -31,14 +33,12 @@ func TestStatus(t *testing.T) { org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) serverRepo, _ := servers.NewRepository(rw, rw, kms) - serverRepo.UpsertServer(ctx, &servers.Server{ + serverRepo.UpsertController(ctx, &store.Controller{ PrivateId: "test_controller1", - Type: "controller", Address: "127.0.0.1", }) - serverRepo.UpsertServer(ctx, &servers.Server{ + serverRepo.UpsertWorker(ctx, &store.Worker{ PrivateId: "test_worker1", - Type: "worker", Address: "127.0.0.1", }) @@ -83,7 +83,7 @@ func TestStatus(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker1.PrivateId, worker1.Type, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker1.PrivateId, tofu) require.NoError(t, err) require.NoError(t, err) @@ -104,13 +104,17 @@ func TestStatus(t *testing.T) { name: "No Sessions", wantErr: false, req: &pbs.StatusRequest{ - Worker: worker1, + Worker: &servers.Server{ + PrivateId: worker1.PrivateId, + Address: worker1.Address, + CreateTime: worker1.CreateTime, + UpdateTime: worker1.UpdateTime, + }, }, want: &pbs.StatusResponse{ Controllers: []*servers.Server{ { PrivateId: "test_controller1", - Type: "controller", Address: "127.0.0.1", }, }, @@ -120,7 +124,12 @@ func TestStatus(t *testing.T) { name: "Still Active", wantErr: false, req: &pbs.StatusRequest{ - Worker: worker1, + Worker: &servers.Server{ + PrivateId: worker1.PrivateId, + Address: worker1.Address, + CreateTime: worker1.CreateTime, + UpdateTime: worker1.UpdateTime, + }, Jobs: []*pbs.JobStatus{ { Job: &pbs.Job{ @@ -145,7 +154,6 @@ func TestStatus(t *testing.T) { Controllers: []*servers.Server{ { PrivateId: "test_controller1", - Type: "controller", Address: "127.0.0.1", }, }, @@ -193,14 +201,12 @@ func TestStatusSessionClosed(t *testing.T) { org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) serverRepo, _ := servers.NewRepository(rw, rw, kms) - serverRepo.UpsertServer(ctx, &servers.Server{ + serverRepo.UpsertController(ctx, &store.Controller{ PrivateId: "test_controller1", - Type: "controller", Address: "127.0.0.1", }) - serverRepo.UpsertServer(ctx, &servers.Server{ + serverRepo.UpsertWorker(ctx, &store.Worker{ PrivateId: "test_worker1", - Type: "worker", Address: "127.0.0.1", }) @@ -245,7 +251,7 @@ func TestStatusSessionClosed(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker1.PrivateId, worker1.Type, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker1.PrivateId, tofu) require.NoError(t, err) sess2 := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -258,7 +264,7 @@ func TestStatusSessionClosed(t *testing.T) { ConnectionLimit: 10, }) tofu2 := session.TestTofu(t) - sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker1.PrivateId, worker1.Type, tofu2) + sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker1.PrivateId, tofu2) require.NoError(t, err) s := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) @@ -283,7 +289,12 @@ func TestStatusSessionClosed(t *testing.T) { require.NoError(t, err) }, req: &pbs.StatusRequest{ - Worker: worker1, + Worker: &servers.Server{ + PrivateId: worker1.PrivateId, + Address: worker1.Address, + CreateTime: worker1.CreateTime, + UpdateTime: worker1.UpdateTime, + }, Jobs: []*pbs.JobStatus{ { Job: &pbs.Job{ @@ -308,7 +319,6 @@ func TestStatusSessionClosed(t *testing.T) { Controllers: []*servers.Server{ { PrivateId: "test_controller1", - Type: "controller", Address: "127.0.0.1", }, }, @@ -373,14 +383,12 @@ func TestStatusDeadConnection(t *testing.T) { org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) serverRepo, _ := servers.NewRepository(rw, rw, kms) - serverRepo.UpsertServer(ctx, &servers.Server{ + serverRepo.UpsertController(ctx, &store.Controller{ PrivateId: "test_controller1", - Type: "controller", Address: "127.0.0.1", }) - serverRepo.UpsertServer(ctx, &servers.Server{ + serverRepo.UpsertWorker(ctx, &store.Worker{ PrivateId: "test_worker1", - Type: "worker", Address: "127.0.0.1", }) @@ -425,7 +433,7 @@ func TestStatusDeadConnection(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker1.PrivateId, worker1.Type, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker1.PrivateId, tofu) require.NoError(t, err) sess2 := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -438,7 +446,7 @@ func TestStatusDeadConnection(t *testing.T) { ConnectionLimit: 10, }) tofu2 := session.TestTofu(t) - sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker1.PrivateId, worker1.Type, tofu2) + sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker1.PrivateId, tofu2) require.NoError(t, err) s := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) @@ -451,7 +459,12 @@ func TestStatusDeadConnection(t *testing.T) { require.NotEqual(t, deadConn.PublicId, connection.PublicId) req := &pbs.StatusRequest{ - Worker: worker1, + Worker: &servers.Server{ + PrivateId: worker1.PrivateId, + Address: worker1.Address, + CreateTime: worker1.CreateTime, + UpdateTime: worker1.UpdateTime, + }, Jobs: []*pbs.JobStatus{ { Job: &pbs.Job{ @@ -476,7 +489,6 @@ func TestStatusDeadConnection(t *testing.T) { Controllers: []*servers.Server{ { PrivateId: "test_controller1", - Type: "controller", Address: "127.0.0.1", }, }, diff --git a/internal/servers/controller/tickers.go b/internal/servers/controller/tickers.go index 749195f50..3de6f52fd 100644 --- a/internal/servers/controller/tickers.go +++ b/internal/servers/controller/tickers.go @@ -5,10 +5,10 @@ import ( "math/rand" "time" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/observability/event" - "github.com/hashicorp/boundary/internal/servers" - "github.com/hashicorp/boundary/internal/types/resource" ) // In the future we could make this configurable @@ -30,7 +30,7 @@ func (c *Controller) startStatusTicking(cancelCtx context.Context) { return case <-timer.C: - if err := c.upsertServer(cancelCtx); err != nil { + if err := c.upsertController(cancelCtx); err != nil { event.WriteError(cancelCtx, op, err, event.WithInfoMsg("error fetching repository for status update")) } timer.Reset(statusInterval) @@ -38,21 +38,18 @@ func (c *Controller) startStatusTicking(cancelCtx context.Context) { } } -func (c *Controller) upsertServer(ctx context.Context) error { - const op = "controller.(Controller).upsertServer" - server := &servers.Server{ - PrivateId: c.conf.RawConfig.Controller.Name, - Name: c.conf.RawConfig.Controller.Name, - Type: resource.Controller.String(), - Description: c.conf.RawConfig.Controller.Description, - Address: c.conf.RawConfig.Controller.PublicClusterAddr, +func (c *Controller) upsertController(ctx context.Context) error { + const op = "controller.(Controller).upsertController" + controller := &store.Controller{ + PrivateId: c.conf.RawConfig.Controller.Name, + Address: c.conf.RawConfig.Controller.PublicClusterAddr, } repo, err := c.ServersRepoFn() if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("error fetching repository for status update")) } - _, _, err = repo.UpsertServer(ctx, server) + _, err = repo.UpsertController(ctx, controller) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("error performing status update")) } diff --git a/internal/servers/gorm.go b/internal/servers/gorm.go index 4ec277ef7..7588948f6 100644 --- a/internal/servers/gorm.go +++ b/internal/servers/gorm.go @@ -1,5 +1,6 @@ package servers -func (s *Server) TableName() string { - return "server" +// TableName overrides the table name used by WorkerTag to `worker_tag` +func (WorkerTag) TableName() string { + return "server_worker_tag" } diff --git a/internal/servers/query.go b/internal/servers/query.go index c0f865a17..81d52677e 100644 --- a/internal/servers/query.go +++ b/internal/servers/query.go @@ -2,5 +2,5 @@ package servers const ( deleteWhereCreateTimeSql = `create_time < ?` - deleteTagsSql = `server_id = ?` + deleteTagsSql = `worker_id = ?` ) diff --git a/internal/servers/repository.go b/internal/servers/repository.go index 4d71ae8b8..508968870 100644 --- a/internal/servers/repository.go +++ b/internal/servers/repository.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/types/resource" + "github.com/hashicorp/boundary/internal/servers/store" ) const ( @@ -62,16 +62,14 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms) (*Repository, error) }, nil } -// ListServers is a passthrough to listServersWithReader that uses the repo's -// normal reader. -func (r *Repository) ListServers(ctx context.Context, serverType ServerType, opt ...Option) ([]*Server, error) { - return r.listServersWithReader(ctx, r.reader, serverType, opt...) +// ListWorkers is a passthrough to listWorkersWithReader that uses the repo's normal reader. +func (r *Repository) ListWorkers(ctx context.Context, opt ...Option) ([]*store.Worker, error) { + return r.listWorkersWithReader(ctx, r.reader, opt...) } -// listServersWithReader will return a listing of resources and honor the -// WithLimit option or the repo defaultLimit. It accepts a reader, allowing it -// to be used within a transaction or without. -func (r *Repository) listServersWithReader(ctx context.Context, reader db.Reader, serverType ServerType, opt ...Option) ([]*Server, error) { +// listWorkersWithReader will return a listing of resources and honor the WithLimit option or the repo +// defaultLimit. It accepts a reader, allowing it to be used within a transaction or without. +func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader, opt ...Option) ([]*store.Worker, error) { opts := getOpts(opt...) liveness := opts.withLiveness if liveness == 0 { @@ -80,65 +78,89 @@ func (r *Repository) listServersWithReader(ctx context.Context, reader db.Reader var where string if liveness > 0 { - where = fmt.Sprintf("type = ? and update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) - } else { - where = "type = ?" + where = fmt.Sprintf("update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) } - var servers []*Server + var workers []*store.Worker if err := reader.SearchWhere( ctx, - &servers, + &workers, where, - []interface{}{serverType}, + []interface{}{}, db.WithLimit(-1), ); err != nil { - return nil, errors.Wrap(ctx, err, "servers.listServersWithReader") + return nil, errors.Wrap(ctx, err, "workers.listWorkersWithReader") } - return servers, nil + return workers, nil } -// ServerTag holds the information for the server_tag table for Gorm. -type ServerTag struct { - ServerId string +func (r *Repository) ListControllers(ctx context.Context, opt ...Option) ([]*store.Controller, error) { + return r.listControllersWithReader(ctx, r.reader, opt...) +} + +// listControllersWithReader will return a listing of resources and honor the +// WithLimit option or the repo defaultLimit. It accepts a reader, allowing it +// to be used within a transaction or without. +func (r *Repository) listControllersWithReader(ctx context.Context, reader db.Reader, opt ...Option) ([]*store.Controller, error) { + opts := getOpts(opt...) + liveness := opts.withLiveness + if liveness == 0 { + liveness = DefaultLiveness + } + + var where string + if liveness > 0 { + where = fmt.Sprintf("update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) + } + + var controllers []*store.Controller + if err := reader.SearchWhere( + ctx, + &controllers, + where, + []interface{}{}, + db.WithLimit(-1), + ); err != nil { + return nil, errors.Wrap(ctx, err, "workers.listControllersWithReader") + } + + return controllers, nil +} + +// WorkerTag holds the information for the worker_tag table for Gorm. +type WorkerTag struct { + WorkerId string Key string Value string } -// TableName overrides the table name used by ServerTag to `server_tag` -func (ServerTag) TableName() string { - return "server_tag" -} - -// ListTagsForServers pulls out tag tuples into ServerTag structs for the -// given server ID values. -func (r *Repository) ListTagsForServers(ctx context.Context, serverIds []string, opt ...Option) ([]*ServerTag, error) { - var serverTags []*ServerTag +// ListTagsForWorkers pulls out tag tuples into WorkerTag structs for the given worker +func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, opt ...Option) ([]*WorkerTag, error) { + var workerTags []*WorkerTag if err := r.reader.SearchWhere( ctx, - &serverTags, - "server_id in (?)", - []interface{}{serverIds}, + &workerTags, + "worker_id in (?)", + []interface{}{workerIds}, db.WithLimit(-1), ); err != nil { - return nil, errors.Wrap(ctx, err, "servers.ListTagsForServers", errors.WithMsg(fmt.Sprintf("server IDs %v", serverIds))) + return nil, errors.Wrap(ctx, err, "servers.ListTagsForWorkers", errors.WithMsg(fmt.Sprintf("worker IDs %v", workerIds))) } - return serverTags, nil + return workerTags, nil } -// UpsertServer adds or updates a server in the DB -func (r *Repository) UpsertServer(ctx context.Context, server *Server, opt ...Option) ([]*Server, int, error) { - const op = "servers.UpsertServer" +func (r *Repository) UpsertWorker(ctx context.Context, worker *store.Worker, opt ...Option) ([]*store.Controller, int, error) { + const op = "servers.UpsertWorker" - if server == nil { - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "server is nil") + if worker == nil { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "worker is nil") } opts := getOpts(opt...) var rowsUpdated int64 - var controllers []*Server + var controllers []*store.Controller _, err := r.writer.DoTx( ctx, db.StdRetryCnt, @@ -146,21 +168,18 @@ func (r *Repository) UpsertServer(ctx context.Context, server *Server, opt ...Op func(read db.Reader, w db.Writer) error { var err error onConflict := &db.OnConflict{ - Target: db.Constraint("server_pkey"), - Action: append(db.SetColumns([]string{"type", "description", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), + Target: db.Columns{"private_id"}, + Action: append(db.SetColumns([]string{"description", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), } - err = w.Create(ctx, server, db.WithOnConflict(onConflict), db.WithReturnRowsAffected(&rowsUpdated)) + err = w.Create(ctx, worker, db.WithOnConflict(onConflict), db.WithReturnRowsAffected(&rowsUpdated)) if err != nil { return errors.Wrap(ctx, err, op+":Upsert") } - // If it's a worker, fetch the current controllers to feed to them - if server.Type == resource.Worker.String() { - // Fetch current controllers to feed to the workers - controllers, err = r.listServersWithReader(ctx, read, ServerTypeController) - if err != nil { - return errors.Wrap(ctx, err, op+":ListServer") - } + // Fetch current controllers to feed to the workers + controllers, err = r.listControllersWithReader(ctx, read) + if err != nil { + return errors.Wrap(ctx, err, op+":ListController") } // If we've been told to update tags, we need to clean out old @@ -168,31 +187,31 @@ func (r *Repository) UpsertServer(ctx context.Context, server *Server, opt ...Op // delete all tags for the given worker, then add the new ones // we've been sent. if opts.withUpdateTags { - _, err = w.Delete(ctx, &ServerTag{}, db.WithWhere(deleteTagsSql, server.PrivateId)) + _, err = w.Delete(ctx, &WorkerTag{}, db.WithWhere(deleteTagsSql, worker.PrivateId)) if err != nil { - return errors.Wrap(ctx, err, op+":DeleteTags", errors.WithMsg(server.PrivateId)) + return errors.Wrap(ctx, err, op+":DeleteTags", errors.WithMsg(worker.PrivateId)) } // If tags were cleared out entirely, then we'll have nothing // to do here, e.g., it will result in deletion of all tags. // Otherwise, go through and stage each tuple for insertion // below. - if len(server.Tags) > 0 { - tags := make([]interface{}, 0, len(server.Tags)) - for k, v := range server.Tags { + if len(worker.Tags) > 0 { + tags := make([]interface{}, 0, len(worker.Tags)) + for k, v := range worker.Tags { if v == nil { - return errors.New(ctx, errors.InvalidParameter, op+":RangeTags", fmt.Sprintf("found nil tag value for worker %s and key %s", server.PrivateId, k)) + return errors.New(ctx, errors.InvalidParameter, op+":RangeTags", fmt.Sprintf("found nil tag value for worker %s and key %s", worker.PrivateId, k)) } for _, val := range v.Values { - tags = append(tags, ServerTag{ - ServerId: server.PrivateId, + tags = append(tags, WorkerTag{ + WorkerId: worker.PrivateId, Key: k, Value: val, }) } } if err = w.CreateItems(ctx, tags); err != nil { - return errors.Wrap(ctx, err, op+":CreateTags", errors.WithMsg(server.PrivateId)) + return errors.Wrap(ctx, err, op+":CreateTags", errors.WithMsg(worker.PrivateId)) } } } @@ -207,6 +226,39 @@ func (r *Repository) UpsertServer(ctx context.Context, server *Server, opt ...Op return controllers, int(rowsUpdated), nil } +func (r *Repository) UpsertController(ctx context.Context, controller *store.Controller) (int, error) { + const op = "servers.UpsertController" + + if controller == nil { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "controller is nil") + } + + var rowsUpdated int64 + _, err := r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(read db.Reader, w db.Writer) error { + var err error + onConflict := &db.OnConflict{ + Target: db.Columns{"private_id"}, + Action: append(db.SetColumns([]string{"description", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), + } + err = w.Create(ctx, controller, db.WithOnConflict(onConflict), db.WithReturnRowsAffected(&rowsUpdated)) + if err != nil { + return errors.Wrap(ctx, err, op+":Upsert") + } + + return nil + }, + ) + if err != nil { + return db.NoRowsAffected, err + } + + return int(rowsUpdated), nil +} + type Nonce struct { Nonce string Purpose string diff --git a/internal/servers/repository_test.go b/internal/servers/repository_test.go index 3ff57b655..74eba1f17 100644 --- a/internal/servers/repository_test.go +++ b/internal/servers/repository_test.go @@ -5,6 +5,8 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/api/recovery" "github.com/hashicorp/boundary/api/roles" "github.com/hashicorp/boundary/globals" @@ -93,55 +95,53 @@ func TestTagUpdatingListing(t *testing.T) { defer tc.Shutdown() repo := tc.ServersRepo() - srv := &servers.Server{ + srv := &store.Worker{ PrivateId: "test1", - Type: "worker", Address: "127.0.0.1", - Tags: map[string]*servers.TagValues{ + Tags: map[string]*store.TagValues{ "tag1": { Values: []string{"value1", "value2"}, }, }, } - _, _, err := repo.UpsertServer(tc.Context(), srv, servers.WithUpdateTags(true)) + _, _, err := repo.UpsertWorker(tc.Context(), srv, servers.WithUpdateTags(true)) require.NoError(err) - srv = &servers.Server{ + srv = &store.Worker{ PrivateId: "test2", - Type: "worker", Address: "127.0.0.1", - Tags: map[string]*servers.TagValues{ + Tags: map[string]*store.TagValues{ "tag2": { Values: []string{"value1", "value2"}, }, }, } - _, _, err = repo.UpsertServer(tc.Context(), srv, servers.WithUpdateTags(true)) + _, _, err = repo.UpsertWorker(tc.Context(), srv, servers.WithUpdateTags(true)) require.NoError(err) - tags, err := repo.ListTagsForServers(tc.Context(), []string{"test1", "test2"}) + tags, err := repo.ListTagsForWorkers(tc.Context(), []string{"test1", "test2"}) require.NoError(err) // Base case - exp := []*servers.ServerTag{ + exp := []*servers.WorkerTag{ { - ServerId: "test1", + WorkerId: "test1", Key: "tag1", Value: "value1", }, { - ServerId: "test1", + WorkerId: "test1", Key: "tag1", Value: "value2", }, { - ServerId: "test2", + WorkerId: "test2", Key: "tag2", Value: "value1", }, { - ServerId: "test2", + WorkerId: "test2", Key: "tag2", Value: "value2", }, @@ -149,36 +149,35 @@ func TestTagUpdatingListing(t *testing.T) { require.Equal(exp, tags) // Update without saying to update tags - srv = &servers.Server{ + srv = &store.Worker{ PrivateId: "test2", - Type: "worker", Address: "192.168.1.1", - Tags: map[string]*servers.TagValues{ + Tags: map[string]*store.TagValues{ "tag22": { Values: []string{"value21", "value22"}, }, }, } - _, _, err = repo.UpsertServer(tc.Context(), srv) + _, _, err = repo.UpsertWorker(tc.Context(), srv) require.NoError(err) - tags, err = repo.ListTagsForServers(tc.Context(), []string{"test1", "test2"}) + tags, err = repo.ListTagsForWorkers(tc.Context(), []string{"test1", "test2"}) require.NoError(err) require.Equal(exp, tags) // Update tags and test again - _, _, err = repo.UpsertServer(tc.Context(), srv, servers.WithUpdateTags(true)) + _, _, err = repo.UpsertWorker(tc.Context(), srv, servers.WithUpdateTags(true)) require.NoError(err) - tags, err = repo.ListTagsForServers(tc.Context(), []string{"test1", "test2"}) + tags, err = repo.ListTagsForWorkers(tc.Context(), []string{"test1", "test2"}) require.NoError(err) require.NotEqual(exp, tags) // Update and try again - exp[2] = &servers.ServerTag{ - ServerId: "test2", + exp[2] = &servers.WorkerTag{ + WorkerId: "test2", Key: "tag22", Value: "value21", } - exp[3] = &servers.ServerTag{ - ServerId: "test2", + exp[3] = &servers.WorkerTag{ + WorkerId: "test2", Key: "tag22", Value: "value22", } @@ -196,33 +195,32 @@ func TestListServersWithLiveness(t *testing.T) { require.NoError(err) ctx := context.Background() - newServer := func(privateId string) *servers.Server { - result := &servers.Server{ + newWorker := func(privateId string) *store.Worker { + result := &store.Worker{ PrivateId: privateId, - Type: "worker", Address: "127.0.0.1", } - _, rowsUpdated, err := serversRepo.UpsertServer(ctx, result) + _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, result) require.NoError(err) require.Equal(1, rowsUpdated) return result } - server1 := newServer("test1") - server2 := newServer("test2") - server3 := newServer("test3") + server1 := newWorker("test1") + server2 := newWorker("test2") + server3 := newWorker("test3") // Sleep the default liveness time (15sec currently) +1s time.Sleep(time.Second * 16) // Push an upsert to the first worker so that its status has been // updated. - _, rowsUpdated, err := serversRepo.UpsertServer(ctx, server1) + _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, server1) require.NoError(err) require.Equal(1, rowsUpdated) - requireIds := func(expected []string, actual []*servers.Server) { + requireIds := func(expected []string, actual []*store.Worker) { require.Len(expected, len(actual)) want := make(map[string]struct{}) for _, v := range expected { @@ -238,25 +236,25 @@ func TestListServersWithLiveness(t *testing.T) { } // Default liveness, should only list 1 - result, err := serversRepo.ListServers(ctx, servers.ServerTypeWorker) + result, err := serversRepo.ListWorkers(ctx) require.NoError(err) require.Len(result, 1) requireIds([]string{server1.PrivateId}, result) // Upsert second server. - _, rowsUpdated, err = serversRepo.UpsertServer(ctx, server2) + _, rowsUpdated, err = serversRepo.UpsertWorker(ctx, server2) require.NoError(err) require.Equal(1, rowsUpdated) // Static liveness. Should get two, so long as this did not take // more than 5s to execute. - result, err = serversRepo.ListServers(ctx, servers.ServerTypeWorker, servers.WithLiveness(time.Second*5)) + result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(time.Second*5)) require.NoError(err) require.Len(result, 2) requireIds([]string{server1.PrivateId, server2.PrivateId}, result) - // Liveness disabled, should get all three servers. - result, err = serversRepo.ListServers(ctx, servers.ServerTypeWorker, servers.WithLiveness(-1)) + // Liveness disabled, should get all three workers. + result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(-1)) require.NoError(err) require.Len(result, 3) requireIds([]string{server1.PrivateId, server2.PrivateId, server3.PrivateId}, result) diff --git a/internal/servers/store/controller.pb.go b/internal/servers/store/controller.pb.go new file mode 100644 index 000000000..c0d684a89 --- /dev/null +++ b/internal/servers/store/controller.pb.go @@ -0,0 +1,209 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/storage/servers/store/v1/controller.proto + +package store + +import ( + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Controller contains all fields related to a Controller resource +type Controller struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Private ID of the resource + PrivateId string `protobuf:"bytes,10,opt,name=private_id,json=privateId,proto3" json:"private_id,omitempty"` + // Address for the controller + Address string `protobuf:"bytes,20,opt,name=address,proto3" json:"address,omitempty"` + // Description of the resource (optional) + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,30,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // First seen time from the RDBMS + CreateTime *timestamp.Timestamp `protobuf:"bytes,40,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` + // Last time there was an update + UpdateTime *timestamp.Timestamp `protobuf:"bytes,50,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` +} + +func (x *Controller) Reset() { + *x = Controller{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_controller_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Controller) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Controller) ProtoMessage() {} + +func (x *Controller) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_controller_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Controller.ProtoReflect.Descriptor instead. +func (*Controller) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_controller_proto_rawDescGZIP(), []int{0} +} + +func (x *Controller) GetPrivateId() string { + if x != nil { + return x.PrivateId + } + return "" +} + +func (x *Controller) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *Controller) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Controller) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *Controller) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +var File_controller_storage_servers_store_v1_controller_proto protoreflect.FileDescriptor + +var file_controller_storage_servers_store_v1_controller_proto_rawDesc = []byte{ + 0x0a, 0x34, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x81, 0x02, 0x0a, + 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, + 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_storage_servers_store_v1_controller_proto_rawDescOnce sync.Once + file_controller_storage_servers_store_v1_controller_proto_rawDescData = file_controller_storage_servers_store_v1_controller_proto_rawDesc +) + +func file_controller_storage_servers_store_v1_controller_proto_rawDescGZIP() []byte { + file_controller_storage_servers_store_v1_controller_proto_rawDescOnce.Do(func() { + file_controller_storage_servers_store_v1_controller_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_servers_store_v1_controller_proto_rawDescData) + }) + return file_controller_storage_servers_store_v1_controller_proto_rawDescData +} + +var file_controller_storage_servers_store_v1_controller_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_storage_servers_store_v1_controller_proto_goTypes = []interface{}{ + (*Controller)(nil), // 0: controller.storage.servers.store.v1.Controller + (*timestamp.Timestamp)(nil), // 1: controller.storage.timestamp.v1.Timestamp +} +var file_controller_storage_servers_store_v1_controller_proto_depIdxs = []int32{ + 1, // 0: controller.storage.servers.store.v1.Controller.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 1, // 1: controller.storage.servers.store.v1.Controller.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_controller_storage_servers_store_v1_controller_proto_init() } +func file_controller_storage_servers_store_v1_controller_proto_init() { + if File_controller_storage_servers_store_v1_controller_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_servers_store_v1_controller_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Controller); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_storage_servers_store_v1_controller_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_servers_store_v1_controller_proto_goTypes, + DependencyIndexes: file_controller_storage_servers_store_v1_controller_proto_depIdxs, + MessageInfos: file_controller_storage_servers_store_v1_controller_proto_msgTypes, + }.Build() + File_controller_storage_servers_store_v1_controller_proto = out.File + file_controller_storage_servers_store_v1_controller_proto_rawDesc = nil + file_controller_storage_servers_store_v1_controller_proto_goTypes = nil + file_controller_storage_servers_store_v1_controller_proto_depIdxs = nil +} diff --git a/internal/servers/store/gorm.go b/internal/servers/store/gorm.go new file mode 100644 index 000000000..7ab3fc709 --- /dev/null +++ b/internal/servers/store/gorm.go @@ -0,0 +1,11 @@ +package store + +// TableName overrides the table name used by Worker to `server_worker` +func (Worker) TableName() string { + return "server_worker" +} + +// TableName overrides the table name used by Controller to `server_controller` +func (Controller) TableName() string { + return "server_controller" +} diff --git a/internal/servers/store/worker.pb.go b/internal/servers/store/worker.pb.go new file mode 100644 index 000000000..fd5504fb1 --- /dev/null +++ b/internal/servers/store/worker.pb.go @@ -0,0 +1,308 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/storage/servers/store/v1/worker.proto + +package store + +import ( + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Worker contains all fields related to a Worker resource +type Worker struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Private ID of the resource + PrivateId string `protobuf:"bytes,10,opt,name=private_id,json=privateId,proto3" json:"private_id,omitempty"` + // Name of the resource (optional) + // Description of the resource (optional) + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // Description of the resource (optional) + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,30,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // Address for the worker + Address string `protobuf:"bytes,40,opt,name=address,proto3" json:"address,omitempty"` + // First seen time from the RDBMS + CreateTime *timestamp.Timestamp `protobuf:"bytes,50,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` + // Last time there was an update + UpdateTime *timestamp.Timestamp `protobuf:"bytes,60,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` + // @gotags: `gorm:"-"` + Tags map[string]*TagValues `protobuf:"bytes,70,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" gorm:"-"` +} + +func (x *Worker) Reset() { + *x = Worker{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_worker_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Worker) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Worker) ProtoMessage() {} + +func (x *Worker) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_worker_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Worker.ProtoReflect.Descriptor instead. +func (*Worker) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_worker_proto_rawDescGZIP(), []int{0} +} + +func (x *Worker) GetPrivateId() string { + if x != nil { + return x.PrivateId + } + return "" +} + +func (x *Worker) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Worker) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Worker) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *Worker) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *Worker) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *Worker) GetTags() map[string]*TagValues { + if x != nil { + return x.Tags + } + return nil +} + +// TagValues is used because map fields cannot be repeated but can be a +// message +type TagValues struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Values []string `protobuf:"bytes,10,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *TagValues) Reset() { + *x = TagValues{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_worker_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TagValues) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TagValues) ProtoMessage() {} + +func (x *TagValues) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_worker_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TagValues.ProtoReflect.Descriptor instead. +func (*TagValues) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_worker_proto_rawDescGZIP(), []int{1} +} + +func (x *TagValues) GetValues() []string { + if x != nil { + return x.Values + } + return nil +} + +var File_controller_storage_servers_store_v1_worker_proto protoreflect.FileDescriptor + +var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ + 0x0a, 0x30, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc5, 0x03, 0x0a, 0x06, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x49, 0x0a, 0x04, + 0x74, 0x61, 0x67, 0x73, 0x18, 0x46, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x67, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x23, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, + 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_storage_servers_store_v1_worker_proto_rawDescOnce sync.Once + file_controller_storage_servers_store_v1_worker_proto_rawDescData = file_controller_storage_servers_store_v1_worker_proto_rawDesc +) + +func file_controller_storage_servers_store_v1_worker_proto_rawDescGZIP() []byte { + file_controller_storage_servers_store_v1_worker_proto_rawDescOnce.Do(func() { + file_controller_storage_servers_store_v1_worker_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_servers_store_v1_worker_proto_rawDescData) + }) + return file_controller_storage_servers_store_v1_worker_proto_rawDescData +} + +var file_controller_storage_servers_store_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_controller_storage_servers_store_v1_worker_proto_goTypes = []interface{}{ + (*Worker)(nil), // 0: controller.storage.servers.store.v1.Worker + (*TagValues)(nil), // 1: controller.storage.servers.store.v1.TagValues + nil, // 2: controller.storage.servers.store.v1.Worker.TagsEntry + (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp +} +var file_controller_storage_servers_store_v1_worker_proto_depIdxs = []int32{ + 3, // 0: controller.storage.servers.store.v1.Worker.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 1: controller.storage.servers.store.v1.Worker.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // 2: controller.storage.servers.store.v1.Worker.tags:type_name -> controller.storage.servers.store.v1.Worker.TagsEntry + 1, // 3: controller.storage.servers.store.v1.Worker.TagsEntry.value:type_name -> controller.storage.servers.store.v1.TagValues + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_controller_storage_servers_store_v1_worker_proto_init() } +func file_controller_storage_servers_store_v1_worker_proto_init() { + if File_controller_storage_servers_store_v1_worker_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_servers_store_v1_worker_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Worker); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_servers_store_v1_worker_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TagValues); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_storage_servers_store_v1_worker_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_servers_store_v1_worker_proto_goTypes, + DependencyIndexes: file_controller_storage_servers_store_v1_worker_proto_depIdxs, + MessageInfos: file_controller_storage_servers_store_v1_worker_proto_msgTypes, + }.Build() + File_controller_storage_servers_store_v1_worker_proto = out.File + file_controller_storage_servers_store_v1_worker_proto_rawDesc = nil + file_controller_storage_servers_store_v1_worker_proto_goTypes = nil + file_controller_storage_servers_store_v1_worker_proto_depIdxs = nil +} diff --git a/internal/servers/worker/status.go b/internal/servers/worker/status.go index 88b349f2e..4bb9481c5 100644 --- a/internal/servers/worker/status.go +++ b/internal/servers/worker/status.go @@ -6,12 +6,12 @@ import ( "math/rand" "time" + "github.com/hashicorp/boundary/internal/servers" + pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" - "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/servers/worker/common" "github.com/hashicorp/boundary/internal/servers/worker/session" - "github.com/hashicorp/boundary/internal/types/resource" "google.golang.org/grpc/resolver" ) @@ -143,14 +143,13 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { } statusCtx, statusCancel := context.WithTimeout(cancelCtx, common.StatusTimeout) defer statusCancel() + result, err := client.Status(statusCtx, &pbs.StatusRequest{ Jobs: activeJobs, Worker: &servers.Server{ - PrivateId: w.conf.RawConfig.Worker.Name, - Type: resource.Worker.String(), - Description: w.conf.RawConfig.Worker.Description, - Address: w.conf.RawConfig.Worker.PublicAddr, - Tags: tags, + PrivateId: w.conf.RawConfig.Worker.Name, + Address: w.conf.RawConfig.Worker.PublicAddr, + Tags: tags, }, UpdateTags: w.updateTags.Load(), }) diff --git a/internal/servers/worker/worker.go b/internal/servers/worker/worker.go index 8f87cf731..7a7a95c14 100644 --- a/internal/servers/worker/worker.go +++ b/internal/servers/worker/worker.go @@ -14,12 +14,13 @@ import ( "sync/atomic" "time" + "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/config" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" - "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/servers/worker/internal/metric" "github.com/hashicorp/boundary/internal/servers/worker/session" "github.com/hashicorp/go-hclog" diff --git a/internal/session/options.go b/internal/session/options.go index a4e5a335b..1ae61bbd9 100644 --- a/internal/session/options.go +++ b/internal/session/options.go @@ -21,17 +21,18 @@ type Option func(*options) // options = how options are represented type options struct { - withLimit int - withOrderByCreateTime db.OrderBy - withScopeIds []string - withUserId string - withExpirationTime *timestamp.Timestamp - withTestTofu []byte - withListingConvert bool - withSessionIds []string - withServerId string - withDbOpts []db.Option - withWorkerStateDelay time.Duration + withLimit int + withOrderByCreateTime db.OrderBy + withScopeIds []string + withUserId string + withExpirationTime *timestamp.Timestamp + withTestTofu []byte + withListingConvert bool + withSessionIds []string + withWorkerId string + withDbOpts []db.Option + withWorkerStateDelay time.Duration + withDeadWorkerConnCloseMinGrace time.Duration } func getDefaultOptions() options { @@ -99,11 +100,10 @@ func withListingConvert(withListingConvert bool) Option { } } -// WithServerId allows the specification of the server id to use for the -// operation. -func WithServerId(id string) Option { +// WithWorkerId allows the specification of the worker id to use +func WithWorkerId(id string) Option { return func(o *options) { - o.withServerId = id + o.withWorkerId = id } } diff --git a/internal/session/options_test.go b/internal/session/options_test.go index 975a2f93f..890d4c390 100644 --- a/internal/session/options_test.go +++ b/internal/session/options_test.go @@ -75,11 +75,11 @@ func Test_GetOpts(t *testing.T) { testOpts.withSessionIds = []string{"s_1", "s_2", "s_3"} assert.Equal(opts, testOpts) }) - t.Run("WithServerId", func(t *testing.T) { + t.Run("WithWorkerId", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithServerId("worker1")) + opts := getOpts(WithWorkerId("worker1")) testOpts := getDefaultOptions() - testOpts.withServerId = "worker1" + testOpts.withWorkerId = "worker1" assert.Equal(opts, testOpts) }) } diff --git a/internal/session/query.go b/internal/session/query.go index 7a10af70f..919980c55 100644 --- a/internal/session/query.go +++ b/internal/session/query.go @@ -81,7 +81,7 @@ active_session as ( select ss.session_id as session_id, @public_id as public_id, - @worker_id as server_id + @worker_id as worker_id from session_state ss where @@ -92,7 +92,7 @@ active_session as ( insert into session_connection ( session_id, public_id, - server_id + worker_id ) select * from active_session; ` @@ -323,26 +323,26 @@ where // each. closeConnectionsForDeadServersCte = ` with - dead_servers (server_id, last_update_time) as ( + dead_workers (worker_id, last_update_time) as ( select private_id, update_time - from server + from server_worker where update_time < wt_sub_seconds_from_now(@grace_period_seconds) ), - closed_connections (connection_id, server_id) as ( + closed_connections (connection_id, worker_id) as ( update session_connection set closed_reason = 'system error' - where server_id in (select server_id from dead_servers) + where worker_id in (select worker_id from dead_workers) and closed_reason is null - returning public_id, server_id + returning public_id, worker_id ) - select closed_connections.server_id, - dead_servers.last_update_time, + select closed_connections.worker_id, + dead_workers.last_update_time, count(closed_connections.connection_id) as number_connections_closed from closed_connections - join dead_servers - on closed_connections.server_id = dead_servers.server_id - group by closed_connections.server_id, dead_servers.last_update_time - order by closed_connections.server_id; + join dead_workers + on closed_connections.worker_id = dead_workers.worker_id + group by closed_connections.worker_id, dead_workers.last_update_time + order by closed_connections.worker_id; ` orphanedConnectionsCte = ` @@ -366,7 +366,7 @@ with from session_connection where -- Related to the worker that just reported to us - server_id = @server_id + worker_id = @worker_id -- Only unclosed ones and public_id in (select connection_id from unclosed_connections) -- These are connection IDs that just got reported to us by the given diff --git a/internal/session/repository.go b/internal/session/repository.go index 8735c8d7f..3e0948c30 100644 --- a/internal/session/repository.go +++ b/internal/session/repository.go @@ -110,8 +110,7 @@ func (r *Repository) convertToSessions(ctx context.Context, sessionList []*sessi PublicId: sv.PublicId, UserId: sv.UserId, HostId: sv.HostId, - ServerId: sv.ServerId, - ServerType: sv.ServerType, + WorkerId: sv.WorkerId, TargetId: sv.TargetId, HostSetId: sv.HostSetId, AuthTokenId: sv.AuthTokenId, diff --git a/internal/session/repository_connection.go b/internal/session/repository_connection.go index 808a2333a..6b3845b10 100644 --- a/internal/session/repository_connection.go +++ b/internal/session/repository_connection.go @@ -345,6 +345,59 @@ func (r *ConnectionRepository) DeleteConnection(ctx context.Context, publicId st return rowsDeleted, nil } +type CloseConnectionsForDeadWorkersResult struct { + WorkerId string + LastUpdateTime time.Time + NumberConnectionsClosed int +} + +// CloseConnectionsForDeadWorkers will run +// closeConnectionsForDeadServersCte to look for connections that +// should be marked because they are on a worker that is no longer +// sending status updates to the controller(s). +// +// The only input to the method is the grace period, in seconds. +func (r *ConnectionRepository) CloseConnectionsForDeadWorkers(ctx context.Context, gracePeriod time.Duration) ([]CloseConnectionsForDeadWorkersResult, error) { + const op = "session.(ConnectionRepository).CloseConnectionsForDeadWorkers" + if gracePeriod < r.deadWorkerConnCloseMinGrace { + return nil, errors.New(ctx, + errors.InvalidParameter, op, fmt.Sprintf("gracePeriod must be at least %s", r.deadWorkerConnCloseMinGrace)) + } + + args := []interface{}{ + sql.Named("grace_period_seconds", gracePeriod.Seconds()), + } + results := make([]CloseConnectionsForDeadWorkersResult, 0) + _, err := r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(reader db.Reader, w db.Writer) error { + rows, err := w.Query(ctx, closeConnectionsForDeadServersCte, args) + if err != nil { + return errors.Wrap(ctx, err, op) + } + defer rows.Close() + + for rows.Next() { + var result CloseConnectionsForDeadWorkersResult + if err := w.ScanRows(ctx, rows, &result); err != nil { + return errors.Wrap(ctx, err, op) + } + + results = append(results, result) + } + + return nil + }, + ) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + return results, nil +} + // closeOrphanedConnections looks for connections that are still active, but where not reported by the worker. func (r *ConnectionRepository) closeOrphanedConnections(ctx context.Context, workerId string, reportedConnections []string) ([]string, error) { const op = "session.(ConnectionRepository).closeOrphanedConnections" @@ -352,7 +405,7 @@ func (r *ConnectionRepository) closeOrphanedConnections(ctx context.Context, wor var orphanedConns []string args := make([]interface{}, 0, len(reportedConnections)+2) - args = append(args, sql.Named("server_id", workerId)) + args = append(args, sql.Named("worker_id", workerId)) args = append(args, sql.Named("worker_state_delay_seconds", r.workerStateDelay.Seconds())) var notInClause string diff --git a/internal/session/repository_connection_test.go b/internal/session/repository_connection_test.go index 164d0155c..4bb5cbbd4 100644 --- a/internal/session/repository_connection_test.go +++ b/internal/session/repository_connection_test.go @@ -6,6 +6,9 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" @@ -149,7 +152,7 @@ func TestRepository_ConnectConnection(t *testing.T) { s := TestDefaultSession(t, conn, wrapper, iamRepo) srv := TestWorker(t, conn, wrapper) tofu := TestTofu(t) - _, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, srv.Type, tofu) + _, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, tofu) require.NoError(t, err) c := TestConnection(t, conn, s.PublicId, "127.0.0.1", 22, "127.0.0.1", 2222, "127.0.0.1") return ConnectWith{ @@ -359,8 +362,8 @@ func TestRepository_orphanedConnections(t *testing.T) { // Create two "workers". One will remain untouched while the other "goes // away and comes back" (worker 2). - worker1 := TestWorker(t, conn, wrapper, WithServerId("worker1")) - worker2 := TestWorker(t, conn, wrapper, WithServerId("worker2")) + worker1 := TestWorker(t, conn, wrapper, WithWorkerId("worker1")) + worker2 := TestWorker(t, conn, wrapper, WithWorkerId("worker2")) // Create a few sessions on each, activate, and authorize a connection var connIds []string @@ -371,8 +374,8 @@ func TestRepository_orphanedConnections(t *testing.T) { if i%2 == 0 { serverId = worker2.PrivateId } - sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithServerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) - sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, "worker", []byte("foo")) + sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithWorkerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) + sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, []byte("foo")) require.NoError(err) c, cs, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), serverId) require.NoError(err) @@ -438,6 +441,287 @@ func TestRepository_orphanedConnections(t *testing.T) { assert.ElementsMatch(found, worker1ConnIds) } +func TestRepository_CloseConnectionsForDeadWorkers(t *testing.T) { + t.Parallel() + ctx := context.Background() + require := require.New(t) + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, wrapper) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + require.NoError(err) + deadWorkerConnCloseMinGrace := 1 * time.Second + connRepo, err := NewConnectionRepository(ctx, rw, rw, kms, WithDeadWorkerConnCloseMinGrace(deadWorkerConnCloseMinGrace)) + require.NoError(err) + serversRepo, err := servers.NewRepository(rw, rw, kms) + require.NoError(err) + + // connection count = 6 * states(authorized, connected, closed = 3) * servers_with_open_connections(3) + numConns := 54 + + // Create four "workers". This is similar to the setup in + // TestRepository_CloseDeadConnectionsOnWorker, but a bit more complex; + // firstly, the last worker will have no connections at all, and we will be + // closing the others in stages to test multiple servers being closed at + // once. + worker1 := TestWorker(t, conn, wrapper, WithWorkerId("worker1")) + worker2 := TestWorker(t, conn, wrapper, WithWorkerId("worker2")) + worker3 := TestWorker(t, conn, wrapper, WithWorkerId("worker3")) + worker4 := TestWorker(t, conn, wrapper, WithWorkerId("worker4")) + + // Create sessions on the first three, activate, and authorize connections + var worker1ConnIds, worker2ConnIds, worker3ConnIds []string + for i := 0; i < numConns; i++ { + var serverId string + if i%3 == 0 { + serverId = worker1.PrivateId + } else if i%3 == 1 { + serverId = worker2.PrivateId + } else { + serverId = worker3.PrivateId + } + sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithWorkerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) + sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, []byte("foo")) + require.NoError(err) + c, cs, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), serverId) + require.NoError(err) + require.Len(cs, 1) + require.Equal(StatusAuthorized, cs[0].Status) + if i%3 == 0 { + worker1ConnIds = append(worker1ConnIds, c.GetPublicId()) + } else if i%3 == 1 { + worker2ConnIds = append(worker2ConnIds, c.GetPublicId()) + } else { + worker3ConnIds = append(worker3ConnIds, c.GetPublicId()) + } + } + + // Mark a third of the connections connected, a third closed, and leave the + // others authorized. This is just to ensure we have a spread when we test it + // out. + for i, connId := range func() []string { + var s []string + s = append(s, worker1ConnIds...) + s = append(s, worker2ConnIds...) + s = append(s, worker3ConnIds...) + return s + }() { + if i%3 == 0 { + _, cs, err := connRepo.ConnectConnection(ctx, ConnectWith{ + ConnectionId: connId, + ClientTcpAddress: "127.0.0.1", + ClientTcpPort: 22, + EndpointTcpAddress: "127.0.0.1", + EndpointTcpPort: 22, + UserClientIp: "127.0.0.1", + }) + require.NoError(err) + require.Len(cs, 2) + var foundAuthorized, foundConnected bool + for _, status := range cs { + if status.Status == StatusAuthorized { + foundAuthorized = true + } + if status.Status == StatusConnected { + foundConnected = true + } + } + require.True(foundAuthorized) + require.True(foundConnected) + } else if i%3 == 1 { + resp, err := connRepo.closeConnections(ctx, []CloseWith{ + { + ConnectionId: connId, + ClosedReason: ConnectionCanceled, + }, + }) + require.NoError(err) + require.Len(resp, 1) + cs := resp[0].ConnectionStates + require.Len(cs, 2) + var foundAuthorized, foundClosed bool + for _, status := range cs { + if status.Status == StatusAuthorized { + foundAuthorized = true + } + if status.Status == StatusClosed { + foundClosed = true + } + } + require.True(foundAuthorized) + require.True(foundClosed) + } + } + + // updateWorker is a helper for updating the update time for our + // servers. The controller is read back so that we can reference + // the most up-to-date fields. + updateWorker := func(t *testing.T, w *store.Worker) *store.Worker { + t.Helper() + _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, w) + require.NoError(err) + require.Equal(1, rowsUpdated) + servers, err := serversRepo.ListWorkers(ctx) + require.NoError(err) + for _, server := range servers { + if server.PrivateId == w.PrivateId { + return server + } + } + + require.FailNowf("server %q not found after updating", w.PrivateId) + // Looks weird but needed to build, as we fail in testify instead + // of returning an error + return nil + } + + // requireConnectionStatus is a helper expecting all connections on a worker + // to be closed. + requireConnectionStatus := func(t *testing.T, connIds []string, expectAllClosed bool) { + t.Helper() + + var conns []*Connection + require.NoError(repo.list(ctx, &conns, "", nil)) + for i, connId := range connIds { + var expected ConnectionStatus + switch { + case expectAllClosed: + expected = StatusClosed + + case i%3 == 0: + expected = StatusConnected + + case i%3 == 1: + expected = StatusClosed + + case i%3 == 2: + expected = StatusAuthorized + } + + _, states, err := connRepo.LookupConnection(ctx, connId) + require.NoError(err) + require.Equal(expected, states[0].Status, "expected latest status for %q (index %d) to be %v", connId, i, expected) + } + } + + // We need this helper to fix the zone on protobuf timestamps + // versus what gets reported in the + // CloseConnectionsForDeadWorkersResult. + timestampPbAsUTC := func(t *testing.T, tm time.Time) time.Time { + t.Helper() + // utcLoc, err := time.LoadLocation("Etc/UTC") + // require.NoError(err) + return tm.In(time.Local) + } + + // Now try some scenarios. + { + // First, test the error/validation case. + result, err := connRepo.CloseConnectionsForDeadWorkers(ctx, -1) + require.Equal(err, errors.E(ctx, + errors.WithCode(errors.InvalidParameter), + errors.WithOp("session.(ConnectionRepository).CloseConnectionsForDeadWorkers"), + errors.WithMsg(fmt.Sprintf("gracePeriod must be at least %s", deadWorkerConnCloseMinGrace)), + )) + require.Nil(result) + } + + { + // Now, try the basis, or where all workers are reporting in. + worker1 = updateWorker(t, worker1) + worker2 = updateWorker(t, worker2) + worker3 = updateWorker(t, worker3) + updateWorker(t, worker4) // no re-assignment here because we never reference the server again + + result, err := connRepo.CloseConnectionsForDeadWorkers(ctx, deadWorkerConnCloseMinGrace) + require.NoError(err) + require.Empty(result) + // Expect appropriate split connection state on worker1 + requireConnectionStatus(t, worker1ConnIds, false) + // Expect appropriate split connection state on worker2 + requireConnectionStatus(t, worker2ConnIds, false) + // Expect appropriate split connection state on worker3 + requireConnectionStatus(t, worker3ConnIds, false) + } + + { + // Now try a zero case - similar to the basis, but only in that no results + // are expected to be returned for workers with no connections, even if + // they are dead. Here, the server with no connections is worker #4. + time.Sleep(deadWorkerConnCloseMinGrace) + worker1 = updateWorker(t, worker1) + worker2 = updateWorker(t, worker2) + worker3 = updateWorker(t, worker3) + + result, err := connRepo.CloseConnectionsForDeadWorkers(ctx, deadWorkerConnCloseMinGrace) + require.NoError(err) + require.Empty(result) + // Expect appropriate split connection state on worker1 + requireConnectionStatus(t, worker1ConnIds, false) + // Expect appropriate split connection state on worker2 + requireConnectionStatus(t, worker2ConnIds, false) + // Expect appropriate split connection state on worker3 + requireConnectionStatus(t, worker3ConnIds, false) + } + + { + // The first induction is letting the first worker "die" by not updating it + // too. All of its authorized and connected connections should be dead. + time.Sleep(deadWorkerConnCloseMinGrace) + worker2 = updateWorker(t, worker2) + worker3 = updateWorker(t, worker3) + + result, err := connRepo.CloseConnectionsForDeadWorkers(ctx, deadWorkerConnCloseMinGrace) + require.NoError(err) + // Assert that we have one result with the appropriate ID and + // number of connections closed. Due to how things are + require.Equal([]CloseConnectionsForDeadWorkersResult{ + { + WorkerId: worker1.PrivateId, + LastUpdateTime: timestampPbAsUTC(t, worker1.UpdateTime.AsTime()), + NumberConnectionsClosed: 12, // 18 per server, with 6 closed already + }, + }, result) + // Expect all connections closed on worker1 + requireConnectionStatus(t, worker1ConnIds, true) + // Expect appropriate split connection state on worker2 + requireConnectionStatus(t, worker2ConnIds, false) + // Expect appropriate split connection state on worker3 + requireConnectionStatus(t, worker3ConnIds, false) + } + + { + // The final case is having the other two workers die. After + // this, we should have all connections closed with the + // appropriate message from the next two servers acted on. + time.Sleep(deadWorkerConnCloseMinGrace) + + result, err := connRepo.CloseConnectionsForDeadWorkers(ctx, deadWorkerConnCloseMinGrace) + require.NoError(err) + // Assert that we have one result with the appropriate ID and number of connections closed. + require.Equal([]CloseConnectionsForDeadWorkersResult{ + { + WorkerId: worker2.PrivateId, + LastUpdateTime: timestampPbAsUTC(t, worker2.UpdateTime.AsTime()), + NumberConnectionsClosed: 12, // 18 per server, with 6 closed already + }, + { + WorkerId: worker3.PrivateId, + LastUpdateTime: timestampPbAsUTC(t, worker3.UpdateTime.AsTime()), + NumberConnectionsClosed: 12, // 18 per server, with 6 closed already + }, + }, result) + // Expect all connections closed on worker1 + requireConnectionStatus(t, worker1ConnIds, true) + // Expect all connections closed on worker2 + requireConnectionStatus(t, worker2ConnIds, true) + // Expect all connections closed on worker3 + requireConnectionStatus(t, worker3ConnIds, true) + } +} + func TestRepository_CloseConnections(t *testing.T) { t.Parallel() ctx := context.Background() @@ -455,7 +739,7 @@ func TestRepository_CloseConnections(t *testing.T) { s := TestDefaultSession(t, conn, wrapper, iamRepo) srv := TestWorker(t, conn, wrapper) tofu := TestTofu(t) - s, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, srv.Type, tofu) + s, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, tofu) require.NoError(t, err) cw := make([]CloseWith, 0, cnt) for i := 0; i < cnt; i++ { diff --git a/internal/session/repository_session.go b/internal/session/repository_session.go index d16edfa21..53448a806 100644 --- a/internal/session/repository_session.go +++ b/internal/session/repository_session.go @@ -18,7 +18,7 @@ import ( // CreateSession inserts into the repository and returns the new Session with // its State of "Pending". The following fields must be empty when creating a -// session: ServerId, ServerType, and PublicId. No options are +// session: WorkerId, and PublicId. No options are // currently supported. func (r *Repository) CreateSession(ctx context.Context, sessionWrapper wrapping.Wrapper, newSession *Session, workerAddresses []string, _ ...Option) (*Session, ed25519.PrivateKey, error) { const op = "session.(Repository).CreateSession" @@ -49,11 +49,8 @@ func (r *Repository) CreateSession(ctx context.Context, sessionWrapper wrapping. if newSession.ScopeId == "" { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") } - if newSession.ServerId != "" { - return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "server id is not empty") - } - if newSession.ServerType != "" { - return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "server type is not empty") + if newSession.WorkerId != "" { + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "worker id is not empty") } if newSession.CtTofuToken != nil { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "ct is not empty") @@ -251,7 +248,7 @@ func (r *Repository) FetchAuthzProtectedEntitiesByScope(ctx context.Context, sco return sessionsMap, nil } -// ListSessions will sessions. Supports the WithLimit, WithScopeId, WithSessionIds, and WithServerId options. +// ListSessions lists sessions. Supports the WithLimit, WithScopeId, WithSessionIds, and WithWorkerId options. func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Session, error) { const op = "session.(Repository).ListSessions" opts := getOpts(opt...) @@ -292,9 +289,9 @@ func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Sessio where = append(where, fmt.Sprintf("s.public_id in (%s)", strings.Join(idsInClause, ","))) } - if opts.withServerId != "" { + if opts.withWorkerId != "" { inClauseCnt += 1 - where, args = append(where, fmt.Sprintf("server_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.withServerId)) + where, args = append(where, fmt.Sprintf("worker_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.withWorkerId)) } var limit string @@ -495,7 +492,7 @@ func (r *Repository) sessionAuthzSummary(ctx context.Context, sessionId string) // activated. States are ordered by start time descending. Returns an // InvalidSessionState error code if a connection cannot be made because the session // was canceled or terminated. -func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sessionVersion uint32, serverId, serverType string, tofuToken []byte) (*Session, []*State, error) { +func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sessionVersion uint32, workerId string, tofuToken []byte) (*Session, []*State, error) { const op = "session.(Repository).ActivateSession" if sessionId == "" { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing session id") @@ -503,11 +500,8 @@ func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sess if sessionVersion == 0 { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing version") } - if serverId == "" { - return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing server id") - } - if serverType == "" { - return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing server type") + if workerId == "" { + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing worker id") } if len(tofuToken) == 0 { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing tofu token") @@ -545,8 +539,7 @@ func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sess } updatedSession.TofuToken = tofuToken - updatedSession.ServerId = serverId - updatedSession.ServerType = serverType + updatedSession.WorkerId = workerId if err := updatedSession.encrypt(ctx, databaseWrapper); err != nil { return errors.Wrap(ctx, err, op) } diff --git a/internal/session/repository_session_test.go b/internal/session/repository_session_test.go index cc9a770c6..1c9172351 100644 --- a/internal/session/repository_session_test.go +++ b/internal/session/repository_session_test.go @@ -215,10 +215,10 @@ func TestRepository_ListSession(t *testing.T) { db.TestDeleteWhere(t, conn, func() interface{} { i := AllocSession(); return &i }(), "1=1") for i := 0; i < 6; i++ { if i%2 == 0 { - TestWorker(t, conn, wrapper, WithServerId(fmt.Sprintf("server-%d", i/2))) + TestWorker(t, conn, wrapper, WithWorkerId(fmt.Sprintf("server-%d", i/2))) } _ = TestSession(t, conn, wrapper, composedOf, - WithServerId(fmt.Sprintf("server-%d", i/2)), + WithWorkerId(fmt.Sprintf("server-%d", i/2)), WithDbOpts(db.WithSkipVetForWrite(true)), ) } @@ -228,11 +228,11 @@ func TestRepository_ListSession(t *testing.T) { for i := 0; i < 3; i++ { serverId := fmt.Sprintf("server-%d", i) - got, err = repo.ListSessions(context.Background(), WithServerId(serverId)) + got, err = repo.ListSessions(context.Background(), WithWorkerId(serverId)) require.NoError(err) assert.Equal(2, len(got)) for _, item := range got { - assert.Equal(serverId, item.ServerId) + assert.Equal(serverId, item.WorkerId) } } }) @@ -683,7 +683,7 @@ func TestRepository_TerminateCompletedSessions(t *testing.T) { srv := TestWorker(t, conn, wrapper) tofu := TestTofu(t) - s, _, err = repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, srv.Type, tofu) + s, _, err = repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, tofu) require.NoError(t, err) c := TestConnection(t, conn, s.PublicId, "127.0.0.1", 22, "127.0.0.1", 222, "127.0.0.1") if !leaveOpen { @@ -1247,7 +1247,7 @@ func TestRepository_ActivateSession(t *testing.T) { name: "already-active", session: func() *Session { s := TestDefaultSession(t, conn, wrapper, iamRepo) - activeSession, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, worker.PrivateId, worker.Type, tofu) + activeSession, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, worker.PrivateId, tofu) require.NoError(t, err) return activeSession }(), @@ -1310,7 +1310,7 @@ func TestRepository_ActivateSession(t *testing.T) { default: version = tt.session.Version } - s, ss, err := repo.ActivateSession(context.Background(), id, version, worker.PrivateId, worker.Type, tofu) + s, ss, err := repo.ActivateSession(context.Background(), id, version, worker.PrivateId, tofu) if tt.wantErr { require.Error(err) assert.Truef(errors.Match(errors.T(tt.wantIsError), err), "unexpected error %s", err.Error()) @@ -1326,17 +1326,17 @@ func TestRepository_ActivateSession(t *testing.T) { t.Run("already active", func(t *testing.T) { assert, require := assert.New(t), require.New(t) session := TestDefaultSession(t, conn, wrapper, iamRepo) - s, ss, err := repo.ActivateSession(context.Background(), session.PublicId, 1, worker.PrivateId, worker.Type, tofu) + s, ss, err := repo.ActivateSession(context.Background(), session.PublicId, 1, worker.PrivateId, tofu) require.NoError(err) require.NotNil(s) require.NotNil(ss) assert.Equal(2, len(ss)) assert.Equal(StatusActive, ss[0].Status) - _, _, err = repo.ActivateSession(context.Background(), session.PublicId, 1, worker.PrivateId, worker.Type, tofu) + _, _, err = repo.ActivateSession(context.Background(), session.PublicId, 1, worker.PrivateId, tofu) require.Error(err) - _, _, err = repo.ActivateSession(context.Background(), session.PublicId, 2, worker.PrivateId, worker.Type, tofu) + _, _, err = repo.ActivateSession(context.Background(), session.PublicId, 2, worker.PrivateId, tofu) require.Error(err) }) } diff --git a/internal/session/service_authorize_connection_test.go b/internal/session/service_authorize_connection_test.go index 0cd6e86a2..a6f3ad381 100644 --- a/internal/session/service_authorize_connection_test.go +++ b/internal/session/service_authorize_connection_test.go @@ -38,7 +38,7 @@ func TestService_AuthorizeConnection(t *testing.T) { srv := TestWorker(t, conn, wrapper) testServer = srv.PrivateId tofu := TestTofu(t) - _, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, srv.Type, tofu) + _, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, tofu) require.NoError(t, err) return s } diff --git a/internal/session/service_close_connections_test.go b/internal/session/service_close_connections_test.go index 8eb5d3fa6..de0c0f007 100644 --- a/internal/session/service_close_connections_test.go +++ b/internal/session/service_close_connections_test.go @@ -32,7 +32,7 @@ func TestServiceCloseConnections(t *testing.T) { s := TestDefaultSession(t, conn, wrapper, iamRepo) srv := TestWorker(t, conn, wrapper) tofu := TestTofu(t) - s, _, err = repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, srv.Type, tofu) + s, _, err = repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, tofu) require.NoError(t, err) require.NoError(t, err) diff --git a/internal/session/service_worker_status_report_test.go b/internal/session/service_worker_status_report_test.go index 0589bdea3..64a29c252 100644 --- a/internal/session/service_worker_status_report_test.go +++ b/internal/session/service_worker_status_report_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/host/static" @@ -26,14 +28,12 @@ func TestWorkerStatusReport(t *testing.T) { org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) serverRepo, _ := servers.NewRepository(rw, rw, kms) - serverRepo.UpsertServer(ctx, &servers.Server{ + serverRepo.UpsertController(ctx, &store.Controller{ PrivateId: "test_controller1", - Type: "controller", Address: "127.0.0.1", }) - serverRepo.UpsertServer(ctx, &servers.Server{ + serverRepo.UpsertWorker(ctx, &store.Worker{ PrivateId: "test_worker1", - Type: "worker", Address: "127.0.0.1", }) @@ -56,7 +56,7 @@ func TestWorkerStatusReport(t *testing.T) { ) type testCase struct { - worker *servers.Server + worker *store.Worker req []session.StateReport want []session.StateReport orphanedConnections []string @@ -91,7 +91,7 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, worker.Type, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) require.NoError(t, err) require.NoError(t, err) @@ -123,7 +123,7 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, worker.Type, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) require.NoError(t, err) require.NoError(t, err) @@ -157,7 +157,7 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, worker.Type, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) require.NoError(t, err) connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PrivateId) require.NoError(t, err) @@ -197,7 +197,7 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, worker.Type, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) require.NoError(t, err) connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PrivateId) require.NoError(t, err) @@ -215,7 +215,7 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu2 := session.TestTofu(t) - sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker.PrivateId, worker.Type, tofu2) + sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker.PrivateId, tofu2) require.NoError(t, err) connection2, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PrivateId) require.NoError(t, err) @@ -264,7 +264,7 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, worker.Type, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) require.NoError(t, err) connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PrivateId) require.NoError(t, err) @@ -280,7 +280,7 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu2 := session.TestTofu(t) - sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker.PrivateId, worker.Type, tofu2) + sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker.PrivateId, tofu2) require.NoError(t, err) connection2, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PrivateId) require.NoError(t, err) @@ -315,7 +315,7 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, worker.Type, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) require.NoError(t, err) connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PrivateId) require.NoError(t, err) @@ -333,7 +333,7 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu2 := session.TestTofu(t) - sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker.PrivateId, worker.Type, tofu2) + sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker.PrivateId, tofu2) require.NoError(t, err) connection2, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PrivateId) require.NoError(t, err) diff --git a/internal/session/session.go b/internal/session/session.go index 7026656e7..7818aacc8 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -64,10 +64,8 @@ type Session struct { UserId string `json:"user_id,omitempty" gorm:"default:null"` // HostId of the session HostId string `json:"host_id,omitempty" gorm:"default:null"` - // ServerId that proxied the session - ServerId string `json:"server_id,omitempty" gorm:"default:null"` - // ServerType that proxied the session - ServerType string `json:"server_type,omitempty" gorm:"default:null"` + // WorkerId that proxied the session + WorkerId string `json:"worker_id,omitempty" gorm:"default:null"` // TargetId for the session TargetId string `json:"target_id,omitempty" gorm:"default:null"` // HostSetId for the session @@ -171,8 +169,7 @@ func (s *Session) Clone() interface{} { PublicId: s.PublicId, UserId: s.UserId, HostId: s.HostId, - ServerId: s.ServerId, - ServerType: s.ServerType, + WorkerId: s.WorkerId, TargetId: s.TargetId, HostSetId: s.HostSetId, AuthTokenId: s.AuthTokenId, @@ -337,11 +334,8 @@ func (s *Session) validateNewSession() error { if s.TerminationReason != "" { return errors.NewDeprecated(errors.InvalidParameter, op, "termination reason must be empty") } - if s.ServerId != "" { - return errors.NewDeprecated(errors.InvalidParameter, op, "server id must be empty") - } - if s.ServerType != "" { - return errors.NewDeprecated(errors.InvalidParameter, op, "server type must be empty") + if s.WorkerId != "" { + return errors.NewDeprecated(errors.InvalidParameter, op, "worker id must be empty") } if s.TofuToken != nil { return errors.NewDeprecated(errors.InvalidParameter, op, "tofu token must be empty") @@ -449,8 +443,7 @@ type sessionListView struct { PublicId string `json:"public_id,omitempty" gorm:"primary_key"` UserId string `json:"user_id,omitempty" gorm:"default:null"` HostId string `json:"host_id,omitempty" gorm:"default:null"` - ServerId string `json:"server_id,omitempty" gorm:"default:null"` - ServerType string `json:"server_type,omitempty" gorm:"default:null"` + WorkerId string `json:"worker_id,omitempty" gorm:"default:null"` TargetId string `json:"target_id,omitempty" gorm:"default:null"` HostSetId string `json:"host_set_id,omitempty" gorm:"default:null"` AuthTokenId string `json:"auth_token_id,omitempty" gorm:"default:null"` diff --git a/internal/session/session_cleanup_job.go b/internal/session/session_cleanup_job.go index c684ed1af..9ab10515a 100644 --- a/internal/session/session_cleanup_job.go +++ b/internal/session/session_cleanup_job.go @@ -107,7 +107,7 @@ func (j *sessionConnectionCleanupJob) Run(ctx context.Context) error { for _, result := range results { event.WriteError(ctx, op, stderrors.New("worker has not reported status within acceptable grace period, all connections closed"), event.WithInfo( - "private_id", result.ServerId, + "private_id", result.WorkerId, "update_time", result.LastUpdateTime, "grace_period_seconds", j.gracePeriod.Seconds(), "number_connections_closed", result.NumberConnectionsClosed, diff --git a/internal/session/testing.go b/internal/session/testing.go index 311a50b53..ced1f58c5 100644 --- a/internal/session/testing.go +++ b/internal/session/testing.go @@ -6,6 +6,8 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/db" @@ -88,7 +90,7 @@ func TestSession(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, c Composed _, certBytes, err := newCert(ctx, wrapper, c.UserId, id, []string{"127.0.0.1", "localhost"}, c.ExpirationTime.Timestamp.AsTime()) require.NoError(err) s.Certificate = certBytes - s.ServerId = opts.withServerId + s.WorkerId = opts.withWorkerId if len(s.TofuToken) != 0 { err = s.encrypt(ctx, wrapper) @@ -181,8 +183,7 @@ func TestTofu(t testing.TB) []byte { } // TestWorker inserts a worker into the db to satisfy foreign key constraints. -// Supports the WithServerId option. -func TestWorker(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *servers.Server { +func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *store.Worker { t.Helper() rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) @@ -190,19 +191,19 @@ func TestWorker(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, opt ...Opti require.NoError(t, err) opts := getOpts(opt...) - id := opts.withServerId + id := opts.withWorkerId if id == "" { id, err = uuid.GenerateUUID() require.NoError(t, err) id = "test-session-worker-" + id } - worker := &servers.Server{ - PrivateId: id, - Type: servers.ServerTypeWorker.String(), - Description: "Test Session Worker", - Address: "127.0.0.1", + name := "test-worker-" + id + worker := &store.Worker{ + PrivateId: id, + Name: name, + Address: "127.0.0.1", } - _, _, err = serversRepo.UpsertServer(context.Background(), worker) + _, _, err = serversRepo.UpsertWorker(context.Background(), worker) require.NoError(t, err) return worker } diff --git a/testing/dbtest/session_list_benchmarks_dump_generation_test.go b/testing/dbtest/session_list_benchmarks_dump_generation_test.go index 9178c559b..2455d2189 100644 --- a/testing/dbtest/session_list_benchmarks_dump_generation_test.go +++ b/testing/dbtest/session_list_benchmarks_dump_generation_test.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" @@ -131,12 +132,11 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) { require.NoError(err) serversRepo, err := servers.NewRepository(rw, rw, kms) require.NoError(err) - worker := &servers.Server{ + worker := &store.Worker{ PrivateId: "test1", - Type: "worker", Address: "127.0.0.1", } - _, _, err = serversRepo.UpsertServer(ctx, worker) + _, _, err = serversRepo.UpsertWorker(ctx, worker) require.NoError(err) usersStart := time.Now() @@ -220,8 +220,8 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) { } } -func cycleSessionStates(t testing.TB, ctx context.Context, sess *session.Session, sessRepo *session.Repository, connRepo *session.ConnectionRepository, conn *db.DB, worker *servers.Server, numConns int) { - sess, _, err := sessRepo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, worker.Type, []byte(`tofu`)) +func cycleSessionStates(t testing.TB, ctx context.Context, sess *session.Session, sessRepo *session.Repository, connRepo *session.ConnectionRepository, conn *db.DB, worker *store.Worker, numConns int) { + sess, _, err := sessRepo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, []byte(`tofu`)) require.NoError(t, err) var closeWiths []session.CloseWith for i := 0; i < numConns; i++ { From 8579a248d408a970de67e09c3cb8e0e6736d7190 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Thu, 5 May 2022 12:15:40 -0400 Subject: [PATCH 003/100] refactor(servers): minor sql adjustments to worker and controller tables --- .../99/01_worker_controller_tables.up.sql | 25 +++++++++++++------ internal/scheduler/job/repository_run_test.go | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql index cddd38dd5..76ff37b0b 100644 --- a/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql @@ -4,10 +4,14 @@ begin; create table server_controller ( private_id text primary key, description wt_description, - address text not null, + address text not null + constraint address_must_not_be_empty + check(length(trim(address)) > 0), create_time wt_timestamp, update_time wt_timestamp ); +comment on table server_controller is + 'server_controller is a table where each row represents a Boundary controller.'; create trigger immutable_columns before update on server_controller for each row execute procedure immutable_columns('private_id','create_time'); @@ -26,10 +30,14 @@ create table server_worker ( private_id text primary key, description wt_description, name wt_name unique, - address text not null, + address text not null + constraint address_must_not_be_empty + check(length(trim(address)) > 0), create_time wt_timestamp, update_time wt_timestamp ); +comment on table server_worker is + 'server_worker is a table where each row represents a Boundary worker.'; create trigger immutable_columns before update on server_worker for each row execute procedure immutable_columns('private_id','create_time'); @@ -48,9 +56,10 @@ create trigger worker_update_time_column before update on server_worker -- Create table worker tag create table server_worker_tag ( worker_id text - references server_worker(private_id) - on delete cascade - on update cascade, + constraint server_worker_fkey + references server_worker(private_id) + on delete cascade + on update cascade, key wt_tagpair, value wt_tagpair, primary key(worker_id, key, value) @@ -70,7 +79,7 @@ alter table session alter table session rename column server_id to worker_id; alter table session - add constraint session_worker_id_fkey + add constraint server_worker_fkey foreign key (worker_id) references server_worker(private_id) on delete set null @@ -88,7 +97,7 @@ alter table session_connection alter table session_connection rename column server_id to worker_id; alter table session_connection - add constraint worker_fkey + add constraint server_worker_fkey foreign key (worker_id) references server_worker (private_id) on delete set null @@ -98,7 +107,7 @@ alter table session_connection alter table job_run drop constraint server_fkey; alter table job_run - add constraint server_fkey + add constraint server_controller_fkey foreign key (server_id) references server_controller (private_id) on delete set null diff --git a/internal/scheduler/job/repository_run_test.go b/internal/scheduler/job/repository_run_test.go index 3647b3280..a89e24914 100644 --- a/internal/scheduler/job/repository_run_test.go +++ b/internal/scheduler/job/repository_run_test.go @@ -70,7 +70,7 @@ func TestRepository_RunJobs(t *testing.T) { }, wantErr: true, wantErrCode: errors.NotSpecificIntegrity, - wantErrMsg: "job.(Repository).RunJobs: db.DoTx: job.(Repository).RunJobs: db.Query: insert or update on table \"job_run\" violates foreign key constraint \"server_fkey\": integrity violation: error #1003", + wantErrMsg: "job.(Repository).RunJobs: db.DoTx: job.(Repository).RunJobs: db.Query: insert or update on table \"job_run\" violates foreign key constraint \"server_controller_fkey\": integrity violation: error #1003", }, } for _, tt := range tests { From ab4d542fad01367c445e679b99acbe987abef54f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 9 May 2022 13:23:38 -0400 Subject: [PATCH 004/100] Add multihop auth support (#32) --- go.mod | 4 +- go.sum | 2 - internal/servers/controller/controller.go | 4 +- internal/servers/controller/handler.go | 14 ++++- .../handlers/workers/multihop_service.go | 3 +- .../handlers/workers/worker_proxy_service.go | 59 +++++++++++++++++++ internal/servers/worker/listeners.go | 4 ++ internal/servers/worker/worker.go | 8 ++- internal/session/repository_connection.go | 4 +- internal/session/session_cleanup_job.go | 2 +- 10 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 internal/servers/controller/handlers/workers/worker_proxy_service.go diff --git a/go.mod b/go.mod index 90781b4ce..cbd778dfa 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ replace github.com/hashicorp/boundary/api => ./api replace github.com/hashicorp/boundary/sdk => ./sdk +replace github.com/hashicorp/nodeenrollment => ../nodeenrollment + require ( github.com/armon/go-metrics v0.3.9 // indirect github.com/fatih/color v1.13.0 @@ -87,7 +89,7 @@ require ( require github.com/hashicorp/go-dbw v0.0.0-20211215222256-2ff0d37184ff // this is a branch and should be updated before merging -require github.com/hashicorp/nodeenrollment v0.0.0-20220427165109-a2f5dad593c7 +require github.com/hashicorp/nodeenrollment v0.0.0-20220503175646-9114ff4a40e9 require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect diff --git a/go.sum b/go.sum index ebc320bf9..8f4201ebc 100644 --- a/go.sum +++ b/go.sum @@ -425,8 +425,6 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220427165109-a2f5dad593c7 h1:pOKDBZ6t9IZCL4xXWt0hJw0TM6Mn0BrRk4NAu7sJKS4= -github.com/hashicorp/nodeenrollment v0.0.0-20220427165109-a2f5dad593c7/go.mod h1:yOOykLWw4G7O1Z7+6X3gzXcQCb/5au1Zna4d1nlQLmM= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index a089694fc..1e66fac9e 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -38,7 +38,7 @@ import ( "github.com/hashicorp/go-secure-stdlib/pluginutil/v2" "github.com/hashicorp/nodeenrollment/noderegistration" nodeefile "github.com/hashicorp/nodeenrollment/nodestorage/file" - "github.com/hashicorp/nodeenrollment/nodetypes" + "github.com/hashicorp/nodeenrollment/rotation" ua "go.uber.org/atomic" "google.golang.org/grpc" ) @@ -322,7 +322,7 @@ func (c *Controller) Start() error { if err != nil { return err } - _, err = nodetypes.RotateRootCertificates(c.baseContext, c.NodeeFileStorage) + _, err = rotation.RotateRootCertificates(c.baseContext, c.NodeeFileStorage) if err != nil { return err } diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index 4a32ea81d..a97401dcb 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -573,12 +573,24 @@ func handleNodes(c *Controller) http.Handler { } var err error var currVals vals - currVals.WaitingNodes, err = c.NodeeFileStorage.List(c.baseContext, (*nodetypes.NodeInformation)(nil)) + waitingNodes, err := c.NodeeFileStorage.List(c.baseContext, (*nodetypes.NodeInformation)(nil)) if err != nil { _, _ = w.Write([]byte(err.Error())) w.WriteHeader(500) return } + for _, keyId := range waitingNodes { + ni, err := nodetypes.LoadNodeInformation(c.baseContext, c.NodeeFileStorage, keyId) + if err != nil { + _, _ = w.Write([]byte(err.Error())) + w.WriteHeader(500) + return + } + if ni.Authorized { + continue + } + currVals.WaitingNodes = append(currVals.WaitingNodes, keyId) + } ret, err := json.Marshal(currVals) if err != nil { _, _ = w.Write([]byte(err.Error())) diff --git a/internal/servers/controller/handlers/workers/multihop_service.go b/internal/servers/controller/handlers/workers/multihop_service.go index 9b3fa4dbf..af2c82c21 100644 --- a/internal/servers/controller/handlers/workers/multihop_service.go +++ b/internal/servers/controller/handlers/workers/multihop_service.go @@ -7,6 +7,7 @@ import ( pbs "github.com/hashicorp/nodeenrollment/multihop" "github.com/hashicorp/nodeenrollment/nodeauth" "github.com/hashicorp/nodeenrollment/noderegistration" + "github.com/hashicorp/nodeenrollment/nodetls" "github.com/hashicorp/nodeenrollment/nodetypes" ) @@ -41,5 +42,5 @@ func (m *multihopServiceServer) GenerateServerCertificates(ctx context.Context, if err != nil { return nil, fmt.Errorf("%s: error getting current parameters: %w", op, err) } - return noderegistration.GenerateServerCertificates(ctx, storage, req, opt...) + return nodetls.GenerateServerCertificates(ctx, storage, req, opt...) } diff --git a/internal/servers/controller/handlers/workers/worker_proxy_service.go b/internal/servers/controller/handlers/workers/worker_proxy_service.go new file mode 100644 index 000000000..198030dc2 --- /dev/null +++ b/internal/servers/controller/handlers/workers/worker_proxy_service.go @@ -0,0 +1,59 @@ +package workers + +import ( + "context" + "sync/atomic" + + pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" +) + +type workerProxyServiceServer struct { + pbs.UnimplementedServerCoordinationServiceServer + pbs.UnimplementedSessionServiceServer + + scsClient *atomic.Value + ssClient *atomic.Value +} + +func NewWorkerProxyServiceServer( + scsClient *atomic.Value, + ssClient *atomic.Value, +) *workerProxyServiceServer { + return &workerProxyServiceServer{ + scsClient: scsClient, + ssClient: ssClient, + } +} + +var ( + _ pbs.ServerCoordinationServiceServer = &workerProxyServiceServer{} + _ pbs.SessionServiceServer = &workerProxyServiceServer{} +) + +func (ws *workerProxyServiceServer) Status(ctx context.Context, req *pbs.StatusRequest) (*pbs.StatusResponse, error) { + return ws.scsClient.Load().(pbs.ServerCoordinationServiceClient).Status(ctx, req) +} + +func (ws *workerProxyServiceServer) LookupSession(ctx context.Context, req *pbs.LookupSessionRequest) (*pbs.LookupSessionResponse, error) { + return ws.ssClient.Load().(pbs.SessionServiceClient).LookupSession(ctx, req) +} + +func (ws *workerProxyServiceServer) CancelSession(ctx context.Context, req *pbs.CancelSessionRequest) (*pbs.CancelSessionResponse, error) { + return ws.ssClient.Load().(pbs.SessionServiceClient).CancelSession(ctx, req) +} + +func (ws *workerProxyServiceServer) ActivateSession(ctx context.Context, req *pbs.ActivateSessionRequest) (*pbs.ActivateSessionResponse, error) { + return ws.ssClient.Load().(pbs.SessionServiceClient).ActivateSession(ctx, req) +} + +func (ws *workerProxyServiceServer) AuthorizeConnection(ctx context.Context, req *pbs.AuthorizeConnectionRequest) (*pbs.AuthorizeConnectionResponse, error) { + return ws.ssClient.Load().(pbs.SessionServiceClient).AuthorizeConnection(ctx, req) +} + +func (ws *workerProxyServiceServer) ConnectConnection(ctx context.Context, req *pbs.ConnectConnectionRequest) (*pbs.ConnectConnectionResponse, error) { + return ws.ssClient.Load().(pbs.SessionServiceClient).ConnectConnection(ctx, req) +} + +func (ws *workerProxyServiceServer) CloseConnection(ctx context.Context, req *pbs.CloseConnectionRequest) (*pbs.CloseConnectionResponse, error) { + return ws.ssClient.Load().(pbs.SessionServiceClient).CloseConnection(ctx, req) +} diff --git a/internal/servers/worker/listeners.go b/internal/servers/worker/listeners.go index f8a386247..7dc71a2fd 100644 --- a/internal/servers/worker/listeners.go +++ b/internal/servers/worker/listeners.go @@ -13,6 +13,7 @@ import ( "time" "github.com/hashicorp/boundary/internal/cmd/base" + pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/servers/controller/handlers/workers" "github.com/hashicorp/go-multierror" @@ -139,6 +140,9 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) nodeauth.MakeCurrentParametersFactory(w.baseContext, nodee.NopTransactionStorage(w.NodeeFileStorage)), ) multihop.RegisterMultihopServiceServer(downstreamServer, multihopService) + statusSessionService := workers.NewWorkerProxyServiceServer(w.controllerStatusConn, w.controllerSessionConn) + pbs.RegisterServerCoordinationServiceServer(downstreamServer, statusSessionService) + pbs.RegisterSessionServiceServer(downstreamServer, statusSessionService) ln.GrpcServer = downstreamServer diff --git a/internal/servers/worker/worker.go b/internal/servers/worker/worker.go index 7a7a95c14..05979268b 100644 --- a/internal/servers/worker/worker.go +++ b/internal/servers/worker/worker.go @@ -26,9 +26,10 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-secure-stdlib/mlock" + nodee "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/nodeauth" - "github.com/hashicorp/nodeenrollment/noderegistration" nodeefile "github.com/hashicorp/nodeenrollment/nodestorage/file" + "github.com/hashicorp/nodeenrollment/nodetypes" ua "go.uber.org/atomic" "google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver/manual" @@ -184,10 +185,11 @@ func (w *Worker) Start() error { return err } - w.NodeeKeyId, err = noderegistration.GenerateNodeCredentialsForRegistration(w.baseContext, w.NodeeFileStorage) - if err != nil { + var nodeCreds nodetypes.NodeCredentials + if err := nodeCreds.GenerateRegistrationParameters(w.baseContext, w.NodeeFileStorage); err != nil { return err } + w.NodeeKeyId = nodee.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) if err := w.startControllerConnections(); err != nil { return fmt.Errorf("error making controller connections: %w", err) diff --git a/internal/session/repository_connection.go b/internal/session/repository_connection.go index 6b3845b10..696e62fe8 100644 --- a/internal/session/repository_connection.go +++ b/internal/session/repository_connection.go @@ -359,9 +359,9 @@ type CloseConnectionsForDeadWorkersResult struct { // The only input to the method is the grace period, in seconds. func (r *ConnectionRepository) CloseConnectionsForDeadWorkers(ctx context.Context, gracePeriod time.Duration) ([]CloseConnectionsForDeadWorkersResult, error) { const op = "session.(ConnectionRepository).CloseConnectionsForDeadWorkers" - if gracePeriod < r.deadWorkerConnCloseMinGrace { + if gracePeriod < deadWorkerConnCloseMinGrace { return nil, errors.New(ctx, - errors.InvalidParameter, op, fmt.Sprintf("gracePeriod must be at least %s", r.deadWorkerConnCloseMinGrace)) + errors.InvalidParameter, op, fmt.Sprintf("gracePeriod must be at least %s", deadWorkerConnCloseMinGrace)) } args := []interface{}{ diff --git a/internal/session/session_cleanup_job.go b/internal/session/session_cleanup_job.go index 9ab10515a..100c58900 100644 --- a/internal/session/session_cleanup_job.go +++ b/internal/session/session_cleanup_job.go @@ -20,7 +20,7 @@ import ( const deadWorkerConnCloseMinGrace = servers.DefaultLiveness type closeConnectionsForDeadWorkersResult struct { - ServerId string + WorkerId string LastUpdateTime time.Time NumberConnectionsClosed int } From 2a8e179692ecab15459947e866858816f40d5186 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 9 May 2022 14:28:25 -0400 Subject: [PATCH 005/100] Update against changes from merge --- .../migrations/oss/postgres_28_02_test.go | 151 ------------------ .../session/repository_connection_test.go | 4 +- internal/session/repository_session_test.go | 2 +- internal/session/session_cleanup_job_test.go | 35 ++-- 4 files changed, 21 insertions(+), 171 deletions(-) delete mode 100644 internal/db/schema/migrations/oss/postgres_28_02_test.go diff --git a/internal/db/schema/migrations/oss/postgres_28_02_test.go b/internal/db/schema/migrations/oss/postgres_28_02_test.go deleted file mode 100644 index d10967cd7..000000000 --- a/internal/db/schema/migrations/oss/postgres_28_02_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package oss_test - -import ( - "context" - "testing" - - "github.com/hashicorp/boundary/internal/authtoken" - "github.com/hashicorp/boundary/internal/db" - "github.com/hashicorp/boundary/internal/db/common" - "github.com/hashicorp/boundary/internal/db/schema" - "github.com/hashicorp/boundary/internal/host/static" - "github.com/hashicorp/boundary/internal/iam" - "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/session" - "github.com/hashicorp/boundary/internal/target" - "github.com/hashicorp/boundary/internal/target/tcp" - "github.com/hashicorp/boundary/internal/types/resource" - "github.com/hashicorp/boundary/testing/dbtest" - "github.com/stretchr/testify/require" -) - -func TestMigrations_SessionStateTrigger(t *testing.T) { - t.Parallel() - require := require.New(t) - - const ( - priorMigration = 27002 - currentMigration = 28002 - ) - dialect := dbtest.Postgres - ctx := context.Background() - - c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1)) - require.NoError(err) - t.Cleanup(func() { - require.NoError(c()) - }) - d, err := common.SqlOpen(dialect, u) - require.NoError(err) - - // migration to the prior migration (before the one we want to test) - m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions( - schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}), - )) - require.NoError(err) - - require.NoError(m.ApplyMigrations(ctx)) - state, err := m.CurrentState(ctx) - require.NoError(err) - want := &schema.State{ - Initialized: true, - Editions: []schema.EditionState{ - { - Name: "oss", - BinarySchemaVersion: priorMigration, - DatabaseSchemaVersion: priorMigration, - DatabaseSchemaState: schema.Equal, - }, - }, - } - require.Equal(want, state) - - // Seed the database with test data - dbType, err := db.StringToDbType(dialect) - require.NoError(err) - - conn, err := db.Open(dbType, u) - require.NoError(err) - - rw := db.New(conn) - wrapper := db.TestWrapper(t) - - org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - require.NotNil(prj) - - hc := static.TestCatalogs(t, conn, prj.GetPublicId(), 1)[0] - hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] - h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] - static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := tcp.TestTarget(ctx, t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) - kmsCache := kms.TestKms(t, conn, wrapper) - - serverId := "worker" - tofu := session.TestTofu(t) - session.TestWorker(t, conn, wrapper, session.WithServerId(serverId)) - at := authtoken.TestAuthToken(t, conn, kmsCache, org.GetPublicId()) - uId := at.GetIamUserId() - sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ - UserId: uId, - HostId: h.GetPublicId(), - TargetId: tar.GetPublicId(), - HostSetId: hs.GetPublicId(), - AuthTokenId: at.GetPublicId(), - ScopeId: prj.GetPublicId(), - Endpoint: "tcp://127.0.0.1:22", - ConnectionLimit: 1, - }) - - sessionRepo, err := session.NewRepository(rw, rw, kmsCache) - require.NoError(err) - - // Make and transition a valid session through pending, active, canceling, and terminated - // Unfortunately, could not recreate an invalid session P-A-T-C using the session repo - _, _, err = sessionRepo.ActivateSession(ctx, sess.PublicId, sess.Version, serverId, resource.Worker.String(), tofu) - require.NoError(err) - connection := session.TestConnection(t, conn, sess.PublicId, "127.0.0.1", 22, - "127.0.0.2", 23, "127.0.0.1") - session.TestConnectionState(t, conn, connection.PublicId, session.StatusConnected) - session.TestConnectionState(t, conn, connection.PublicId, session.StatusClosed) - _, err = sessionRepo.CancelSession(ctx, sess.PublicId, sess.Version+1) - require.NoError(err) - sessionRepo.TerminateCompletedSessions(ctx) - - repoSessions, err := sessionRepo.ListSessions(ctx) - require.NoError(err) - var numStates int - for _, s := range repoSessions { - numStates = len(s.States) - } - require.Equal(4, numStates) - - // now we're ready for the migration we want to test. - m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions( - schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}), - )) - require.NoError(err) - - require.NoError(m.ApplyMigrations(ctx)) - state, err = m.CurrentState(ctx) - require.NoError(err) - want = &schema.State{ - Initialized: true, - Editions: []schema.EditionState{ - { - Name: "oss", - BinarySchemaVersion: currentMigration, - DatabaseSchemaVersion: currentMigration, - DatabaseSchemaState: schema.Equal, - }, - }, - } - require.Equal(want, state) - - // Check that we haven't removed a state - repoSessions, err = sessionRepo.ListSessions(ctx) - require.NoError(err) - for _, s := range repoSessions { - numStates = len(s.States) - } - require.Equal(4, numStates) -} diff --git a/internal/session/repository_connection_test.go b/internal/session/repository_connection_test.go index 4bb5cbbd4..454a7ff1d 100644 --- a/internal/session/repository_connection_test.go +++ b/internal/session/repository_connection_test.go @@ -452,8 +452,8 @@ func TestRepository_CloseConnectionsForDeadWorkers(t *testing.T) { kms := kms.TestKms(t, conn, wrapper) repo, err := NewRepository(rw, rw, kms) require.NoError(err) - deadWorkerConnCloseMinGrace := 1 * time.Second - connRepo, err := NewConnectionRepository(ctx, rw, rw, kms, WithDeadWorkerConnCloseMinGrace(deadWorkerConnCloseMinGrace)) + deadWorkerConnCloseMinGrace := 15 * time.Second + connRepo, err := NewConnectionRepository(ctx, rw, rw, kms) require.NoError(err) serversRepo, err := servers.NewRepository(rw, rw, kms) require.NoError(err) diff --git a/internal/session/repository_session_test.go b/internal/session/repository_session_test.go index 1c9172351..f81207b70 100644 --- a/internal/session/repository_session_test.go +++ b/internal/session/repository_session_test.go @@ -642,7 +642,7 @@ func TestRepository_transitionState(t *testing.T) { var ss []*State var err error if status == StatusActive { - s, ss, err = repo.ActivateSession(context.Background(), id, version, srv.PrivateId, srv.Type, tofu) + s, ss, err = repo.ActivateSession(context.Background(), id, version, srv.PrivateId, tofu) } else { s, ss, err = repo.updateState(context.Background(), id, version, status) } diff --git a/internal/session/session_cleanup_job_test.go b/internal/session/session_cleanup_job_test.go index b9ff65d35..b9a7886e4 100644 --- a/internal/session/session_cleanup_job_test.go +++ b/internal/session/session_cleanup_job_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -44,8 +45,8 @@ func TestSessionConnectionCleanupJob(t *testing.T) { // Create two "workers". One will remain untouched while the other "goes // away and comes back" (worker 2). - worker1 := TestWorker(t, conn, wrapper, WithServerId("worker1")) - worker2 := TestWorker(t, conn, wrapper, WithServerId("worker2")) + worker1 := TestWorker(t, conn, wrapper, WithWorkerId("worker1")) + worker2 := TestWorker(t, conn, wrapper, WithWorkerId("worker2")) // Create a few sessions on each, activate, and authorize a connection var connIds []string @@ -55,8 +56,8 @@ func TestSessionConnectionCleanupJob(t *testing.T) { if i%2 == 0 { serverId = worker2.PrivateId } - sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithServerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) - sess, _, err = sessionRepo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, "worker", []byte("foo")) + sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithWorkerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) + sess, _, err = sessionRepo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, []byte("foo")) require.NoError(err) c, cs, _, err := AuthorizeConnection(ctx, sessionRepo, connectionRepo, sess.GetPublicId(), serverId) require.NoError(err) @@ -108,7 +109,7 @@ func TestSessionConnectionCleanupJob(t *testing.T) { // Push an upsert to the first worker so that its status has been // updated. - _, rowsUpdated, err := serversRepo.UpsertServer(ctx, worker1, []servers.Option{}...) + _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, worker1, []servers.Option{}...) require.NoError(err) require.Equal(1, rowsUpdated) @@ -196,10 +197,10 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // firstly, the last worker will have no connections at all, and we will be // closing the others in stages to test multiple servers being closed at // once. - worker1 := TestWorker(t, conn, wrapper, WithServerId("worker1")) - worker2 := TestWorker(t, conn, wrapper, WithServerId("worker2")) - worker3 := TestWorker(t, conn, wrapper, WithServerId("worker3")) - worker4 := TestWorker(t, conn, wrapper, WithServerId("worker4")) + worker1 := TestWorker(t, conn, wrapper, WithWorkerId("worker1")) + worker2 := TestWorker(t, conn, wrapper, WithWorkerId("worker2")) + worker3 := TestWorker(t, conn, wrapper, WithWorkerId("worker3")) + worker4 := TestWorker(t, conn, wrapper, WithWorkerId("worker4")) // Create sessions on the first three, activate, and authorize connections var worker1ConnIds, worker2ConnIds, worker3ConnIds []string @@ -212,8 +213,8 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { } else { serverId = worker3.PrivateId } - sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithServerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) - sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, "worker", []byte("foo")) + sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithWorkerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) + sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, []byte("foo")) require.NoError(err) c, cs, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), serverId) require.NoError(err) @@ -288,12 +289,12 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // updateServer is a helper for updating the update time for our // servers. The controller is read back so that we can reference // the most up-to-date fields. - updateServer := func(t *testing.T, w *servers.Server) *servers.Server { + updateServer := func(t *testing.T, w *store.Worker) *store.Worker { t.Helper() - _, rowsUpdated, err := serversRepo.UpsertServer(ctx, w) + _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, w) require.NoError(err) require.Equal(1, rowsUpdated) - servers, err := serversRepo.ListServers(ctx, servers.ServerTypeWorker) + servers, err := serversRepo.ListWorkers(ctx) require.NoError(err) for _, server := range servers { if server.PrivateId == w.PrivateId { @@ -398,7 +399,7 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // number of connections closed. Due to how things are require.Equal([]closeConnectionsForDeadWorkersResult{ { - ServerId: worker1.PrivateId, + WorkerId: worker1.PrivateId, LastUpdateTime: timestampPbAsUTC(t, worker1.UpdateTime.AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, @@ -422,12 +423,12 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // Assert that we have one result with the appropriate ID and number of connections closed. require.Equal([]closeConnectionsForDeadWorkersResult{ { - ServerId: worker2.PrivateId, + WorkerId: worker2.PrivateId, LastUpdateTime: timestampPbAsUTC(t, worker2.UpdateTime.AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, { - ServerId: worker3.PrivateId, + WorkerId: worker3.PrivateId, LastUpdateTime: timestampPbAsUTC(t, worker3.UpdateTime.AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, From b83f4ece395ca2501fe09be066cb06ff7538b9b5 Mon Sep 17 00:00:00 2001 From: Todd Date: Mon, 9 May 2022 14:45:30 -0700 Subject: [PATCH 006/100] Manual merge of main into llb-byow. --- internal/daemon/controller/handlers/targets/target_service.go | 1 - internal/daemon/worker/listeners.go | 2 +- internal/daemon/worker/status.go | 1 - internal/daemon/worker/worker.go | 3 --- 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index 81fdfef9f..5c98b087e 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -27,7 +27,6 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/perms" "github.com/hashicorp/boundary/internal/requests" - "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/types/action" diff --git a/internal/daemon/worker/listeners.go b/internal/daemon/worker/listeners.go index 7dc71a2fd..a2bfdaad2 100644 --- a/internal/daemon/worker/listeners.go +++ b/internal/daemon/worker/listeners.go @@ -13,9 +13,9 @@ import ( "time" "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" - "github.com/hashicorp/boundary/internal/servers/controller/handlers/workers" "github.com/hashicorp/go-multierror" nodee "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/multihop" diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index a7986d8ea..1a29642b3 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -12,7 +12,6 @@ import ( "github.com/hashicorp/boundary/internal/daemon/worker/session" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" - "github.com/hashicorp/boundary/internal/types/resource" "google.golang.org/grpc/resolver" ) diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 84da2eb3e..9cd3e7f6f 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -23,9 +23,6 @@ import ( "github.com/hashicorp/boundary/internal/daemon/worker/session" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" - "github.com/hashicorp/boundary/internal/servers" - "github.com/hashicorp/boundary/internal/servers/worker/internal/metric" - "github.com/hashicorp/boundary/internal/servers/worker/session" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-secure-stdlib/mlock" From 837eb9e6f12c93f4d16b1533caccac2d3d108969 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 10 May 2022 16:33:35 -0400 Subject: [PATCH 007/100] Update nodeenrollment dep against new dev branch --- go.mod | 4 ++-- go.sum | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index cbd778dfa..99b4f6fd0 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ replace github.com/hashicorp/boundary/api => ./api replace github.com/hashicorp/boundary/sdk => ./sdk -replace github.com/hashicorp/nodeenrollment => ../nodeenrollment +//replace github.com/hashicorp/nodeenrollment => ../nodeenrollment require ( github.com/armon/go-metrics v0.3.9 // indirect @@ -89,7 +89,7 @@ require ( require github.com/hashicorp/go-dbw v0.0.0-20211215222256-2ff0d37184ff // this is a branch and should be updated before merging -require github.com/hashicorp/nodeenrollment v0.0.0-20220503175646-9114ff4a40e9 +require github.com/hashicorp/nodeenrollment v0.0.0-20220510203232-823dc46c13f6 require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect diff --git a/go.sum b/go.sum index 8f4201ebc..274db287a 100644 --- a/go.sum +++ b/go.sum @@ -425,6 +425,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/nodeenrollment v0.0.0-20220510203232-823dc46c13f6 h1:q/Q2txuhRC13sPEU/iRx5fcex60igDpLiFs7f8Izcr4= +github.com/hashicorp/nodeenrollment v0.0.0-20220510203232-823dc46c13f6/go.mod h1:yOOykLWw4G7O1Z7+6X3gzXcQCb/5au1Zna4d1nlQLmM= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= From 0ef523ba773ebb6fe3ee536683c70de5130ca67e Mon Sep 17 00:00:00 2001 From: irenarindos Date: Wed, 11 May 2022 12:30:30 -0400 Subject: [PATCH 008/100] Remove column cascade --- .../oss/postgres/99/01_worker_controller_tables.up.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql index 76ff37b0b..b213b451b 100644 --- a/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql @@ -68,13 +68,15 @@ create table server_worker_tag ( -- Aaand drop server_tag drop table server_tag; --- Update session table to use worker_id instead of server_id +-- Update session table to use worker_id instead of server_id, drop view first because of dependency on server type +drop view session_list; + alter table session drop constraint session_server_id_fkey; drop trigger update_version_column on session; alter table session - drop column server_type cascade; + drop column server_type; alter table session rename column server_id to worker_id; @@ -114,8 +116,6 @@ alter table job_run on update cascade; -- Replaces the view created in 9/01 to include worker id instead of server id and server type -drop view if exists session_list; - create view session_list as select From 898da56a7765b5011808f013acf9ab4b957e85e9 Mon Sep 17 00:00:00 2001 From: Haotian Date: Wed, 11 May 2022 17:15:38 -0700 Subject: [PATCH 009/100] feat(workers): Add storage path parameter to worker config --- internal/cmd/config/config.go | 3 +++ internal/daemon/worker/worker.go | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index 832782a4e..f79840f98 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -187,6 +187,9 @@ type Worker struct { // // TODO: This field is currently internal. StatusGracePeriodDuration time.Duration `hcl:"-"` + + // StoragePath represents the location a pki worker stores its node credentials, if set. + StoragePath string `hcl:"storage_path"` } func (w *Worker) InitNameIfEmpty() (string, error) { diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 9cd3e7f6f..b4573220a 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -180,7 +180,8 @@ func (w *Worker) Start() error { } var err error - w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext) + w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext, + nodeefile.WithFileStorageBaseDirectory(w.conf.RawConfig.Worker.StoragePath)) if err != nil { return err } From 00e57db60f407fb075d9824e1a9577d280594eb6 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 13 May 2022 10:06:21 -0400 Subject: [PATCH 010/100] Adapt to library changes --- go.mod | 3 ++- go.sum | 6 ++++-- internal/daemon/controller/intercepting_listener.go | 9 ++++++++- internal/daemon/worker/worker.go | 5 ++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 99b4f6fd0..7e131b4b4 100644 --- a/go.mod +++ b/go.mod @@ -89,7 +89,7 @@ require ( require github.com/hashicorp/go-dbw v0.0.0-20211215222256-2ff0d37184ff // this is a branch and should be updated before merging -require github.com/hashicorp/nodeenrollment v0.0.0-20220510203232-823dc46c13f6 +require github.com/hashicorp/nodeenrollment v0.0.0-20220513140413-5b932ff6843f require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect @@ -170,6 +170,7 @@ require ( github.com/prometheus/procfs v0.7.3 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/sethvargo/go-diceware v0.3.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/sirupsen/logrus v1.7.0 // indirect github.com/urfave/cli/v2 v2.3.0 // indirect diff --git a/go.sum b/go.sum index 274db287a..29a6525fd 100644 --- a/go.sum +++ b/go.sum @@ -425,8 +425,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220510203232-823dc46c13f6 h1:q/Q2txuhRC13sPEU/iRx5fcex60igDpLiFs7f8Izcr4= -github.com/hashicorp/nodeenrollment v0.0.0-20220510203232-823dc46c13f6/go.mod h1:yOOykLWw4G7O1Z7+6X3gzXcQCb/5au1Zna4d1nlQLmM= +github.com/hashicorp/nodeenrollment v0.0.0-20220513140413-5b932ff6843f h1:HZzIMCecyDlRB8uWy/UIIC5RjfdORDKf6bpi0KLhcu0= +github.com/hashicorp/nodeenrollment v0.0.0-20220513140413-5b932ff6843f/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= @@ -734,6 +734,8 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sethvargo/go-diceware v0.3.0 h1:UVVEfmN/uF50JfWAN7nbY6CiAlp5xeSx+5U0lWKkMCQ= +github.com/sethvargo/go-diceware v0.3.0/go.mod h1:lH5Q/oSPMivseNdhMERAC7Ti5oOPqsaVddU1BcN1CY0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= diff --git a/internal/daemon/controller/intercepting_listener.go b/internal/daemon/controller/intercepting_listener.go index 55306f150..cf97474d0 100644 --- a/internal/daemon/controller/intercepting_listener.go +++ b/internal/daemon/controller/intercepting_listener.go @@ -80,7 +80,14 @@ func (m *interceptingListener) Accept() (net.Conn, error) { tlsConn := conn.(*tls.Conn) switch { case nodeauth.ContainsNodeAuthAlpnProto(tlsConn.ConnectionState().NegotiatedProtocol): - event.WriteSysEvent(ctx, op, "worker successfully authed", "key_id", nodee.KeyIdFromPkix(tlsConn.ConnectionState().PeerCertificates[0].SubjectKeyId)) + keyId, err := nodee.KeyIdFromPkix(tlsConn.ConnectionState().PeerCertificates[0].SubjectKeyId) + if err != nil { + if err := conn.Close(); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error closing worker connection")) + } + return nil, newTempError(fmt.Errorf("error deriving key id from newly-authenticated node: %w", err)) + } + event.WriteSysEvent(ctx, op, "worker successfully authed", "key_id", keyId) return conn, nil case strings.HasPrefix(tlsConn.ConnectionState().NegotiatedProtocol, "v1workerauth"): diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index b4573220a..3ec5c7616 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -190,7 +190,10 @@ func (w *Worker) Start() error { if err := nodeCreds.GenerateRegistrationParameters(w.baseContext, w.NodeeFileStorage); err != nil { return err } - w.NodeeKeyId = nodee.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) + w.NodeeKeyId, err = nodee.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) + if err != nil { + return fmt.Errorf("error deriving key id: %w", err) + } if err := w.startControllerConnections(); err != nil { return fmt.Errorf("error making controller connections: %w", err) From 0581e164f10ba91b1533dd2a4f58d917e22f984b Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 16 May 2022 09:02:09 -0400 Subject: [PATCH 011/100] Update nodeenrollment dep --- go.mod | 2 +- go.sum | 4 ++-- internal/cmd/commands/dev/dev.go | 4 ++-- internal/daemon/controller/controller.go | 2 +- internal/daemon/controller/handler.go | 6 +++--- .../controller/handlers/workers/multihop_service.go | 6 +++--- internal/daemon/worker/listeners.go | 10 +++++----- internal/daemon/worker/worker.go | 8 ++++---- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 945290b18..2c0dd9b26 100644 --- a/go.mod +++ b/go.mod @@ -91,7 +91,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220512190651-85bdcdd684e6 - github.com/hashicorp/nodeenrollment v0.0.0-20220513140413-5b932ff6843f + github.com/hashicorp/nodeenrollment v0.0.0-20220516125915-9a115b0fb887 ) require ( diff --git a/go.sum b/go.sum index 02f489c32..7c4204729 100644 --- a/go.sum +++ b/go.sum @@ -741,8 +741,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220513140413-5b932ff6843f h1:HZzIMCecyDlRB8uWy/UIIC5RjfdORDKf6bpi0KLhcu0= -github.com/hashicorp/nodeenrollment v0.0.0-20220513140413-5b932ff6843f/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.0.0-20220516125915-9a115b0fb887 h1:Ov/lPq1YFKt6CLPCriEL21NqMndrJEKNLIZW9qJYpaE= +github.com/hashicorp/nodeenrollment v0.0.0-20220516125915-9a115b0fb887/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index fcbfba79d..1b301907f 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -29,7 +29,7 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/strutil" - "github.com/hashicorp/nodeenrollment/nodetypes" + "github.com/hashicorp/nodeenrollment/types" "github.com/mitchellh/cli" "github.com/posener/complete" "go.uber.org/atomic" @@ -689,7 +689,7 @@ func (c *Command) Run(args []string) int { case <-c.Context.Done(): return case <-time.After(time.Second): - waitingNodes, err := c.controller.NodeeFileStorage.List(c.Context, (*nodetypes.NodeInformation)(nil)) + waitingNodes, err := c.controller.NodeeFileStorage.List(c.Context, (*types.NodeInformation)(nil)) if err != nil { c.UI.Error(fmt.Errorf("Error listing dev worker for authentication: %w", err).Error()) errorEncountered.Store(true) diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index f284db66f..066cd5df5 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -37,8 +37,8 @@ import ( "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/hashicorp/go-secure-stdlib/pluginutil/v2" "github.com/hashicorp/nodeenrollment/noderegistration" - nodeefile "github.com/hashicorp/nodeenrollment/nodestorage/file" "github.com/hashicorp/nodeenrollment/rotation" + nodeefile "github.com/hashicorp/nodeenrollment/storage/file" ua "go.uber.org/atomic" "google.golang.org/grpc" ) diff --git a/internal/daemon/controller/handler.go b/internal/daemon/controller/handler.go index 0bcef2c92..bbe9e7822 100644 --- a/internal/daemon/controller/handler.go +++ b/internal/daemon/controller/handler.go @@ -43,7 +43,7 @@ import ( "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-secure-stdlib/listenerutil" "github.com/hashicorp/go-secure-stdlib/strutil" - "github.com/hashicorp/nodeenrollment/nodetypes" + "github.com/hashicorp/nodeenrollment/types" "github.com/mr-tron/base58" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -573,14 +573,14 @@ func handleNodes(c *Controller) http.Handler { } var err error var currVals vals - waitingNodes, err := c.NodeeFileStorage.List(c.baseContext, (*nodetypes.NodeInformation)(nil)) + waitingNodes, err := c.NodeeFileStorage.List(c.baseContext, (*types.NodeInformation)(nil)) if err != nil { _, _ = w.Write([]byte(err.Error())) w.WriteHeader(500) return } for _, keyId := range waitingNodes { - ni, err := nodetypes.LoadNodeInformation(c.baseContext, c.NodeeFileStorage, keyId) + ni, err := types.LoadNodeInformation(c.baseContext, c.NodeeFileStorage, keyId) if err != nil { _, _ = w.Write([]byte(err.Error())) w.WriteHeader(500) diff --git a/internal/daemon/controller/handlers/workers/multihop_service.go b/internal/daemon/controller/handlers/workers/multihop_service.go index af2c82c21..a840abeee 100644 --- a/internal/daemon/controller/handlers/workers/multihop_service.go +++ b/internal/daemon/controller/handlers/workers/multihop_service.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/nodeenrollment/nodeauth" "github.com/hashicorp/nodeenrollment/noderegistration" "github.com/hashicorp/nodeenrollment/nodetls" - "github.com/hashicorp/nodeenrollment/nodetypes" + "github.com/hashicorp/nodeenrollment/types" ) type multihopServiceServer struct { @@ -27,7 +27,7 @@ func NewMultihopServiceServer( var _ pbs.MultihopServiceServer = (*multihopServiceServer)(nil) -func (m *multihopServiceServer) FetchNodeCredentials(ctx context.Context, req *nodetypes.FetchNodeCredentialsRequest) (*nodetypes.FetchNodeCredentialsResponse, error) { +func (m *multihopServiceServer) FetchNodeCredentials(ctx context.Context, req *types.FetchNodeCredentialsRequest) (*types.FetchNodeCredentialsResponse, error) { const op = "workers.(multihopServiceServer).FetchNodeCredentials" _, storage, opt, err := m.currentParams() if err != nil { @@ -36,7 +36,7 @@ func (m *multihopServiceServer) FetchNodeCredentials(ctx context.Context, req *n return noderegistration.FetchNodeCredentials(ctx, storage, req, opt...) } -func (m *multihopServiceServer) GenerateServerCertificates(ctx context.Context, req *nodetypes.GenerateServerCertificatesRequest) (*nodetypes.GenerateServerCertificatesResponse, error) { +func (m *multihopServiceServer) GenerateServerCertificates(ctx context.Context, req *types.GenerateServerCertificatesRequest) (*types.GenerateServerCertificatesResponse, error) { const op = "workers.(multihopServiceServer).GenerateServerCertificates" _, storage, opt, err := m.currentParams() if err != nil { diff --git a/internal/daemon/worker/listeners.go b/internal/daemon/worker/listeners.go index a2bfdaad2..3f83115b1 100644 --- a/internal/daemon/worker/listeners.go +++ b/internal/daemon/worker/listeners.go @@ -20,7 +20,7 @@ import ( nodee "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/multihop" "github.com/hashicorp/nodeenrollment/nodeauth" - "github.com/hashicorp/nodeenrollment/nodetypes" + "github.com/hashicorp/nodeenrollment/types" "google.golang.org/grpc" ) @@ -83,9 +83,9 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) fetchCredsFn := func( ctx context.Context, _ nodee.Storage, - req *nodetypes.FetchNodeCredentialsRequest, + req *types.FetchNodeCredentialsRequest, _ ...nodee.Option, - ) (*nodetypes.FetchNodeCredentialsResponse, error) { + ) (*types.FetchNodeCredentialsResponse, error) { client := w.controllerMultihopConn.Load() if client == nil { return nil, nodeauth.NewTempError(errors.New("error fetching controller connection, client is nil")) @@ -100,9 +100,9 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) generateServerCertificatesFn := func( ctx context.Context, _ nodee.Storage, - req *nodetypes.GenerateServerCertificatesRequest, + req *types.GenerateServerCertificatesRequest, _ ...nodee.Option, - ) (*nodetypes.GenerateServerCertificatesResponse, error) { + ) (*types.GenerateServerCertificatesResponse, error) { client := w.controllerMultihopConn.Load() if client == nil { return nil, nodeauth.NewTempError(errors.New("error fetching controller connection, client is nil")) diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 3ec5c7616..3b410b82d 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -28,8 +28,8 @@ import ( "github.com/hashicorp/go-secure-stdlib/mlock" nodee "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/nodeauth" - nodeefile "github.com/hashicorp/nodeenrollment/nodestorage/file" - "github.com/hashicorp/nodeenrollment/nodetypes" + nodeefile "github.com/hashicorp/nodeenrollment/storage/file" + "github.com/hashicorp/nodeenrollment/types" ua "go.uber.org/atomic" "google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver/manual" @@ -181,12 +181,12 @@ func (w *Worker) Start() error { var err error w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext, - nodeefile.WithFileStorageBaseDirectory(w.conf.RawConfig.Worker.StoragePath)) + nodeefile.WithBaseDirectory(w.conf.RawConfig.Worker.StoragePath)) if err != nil { return err } - var nodeCreds nodetypes.NodeCredentials + var nodeCreds types.NodeCredentials if err := nodeCreds.GenerateRegistrationParameters(w.baseContext, w.NodeeFileStorage); err != nil { return err } From 472d7d520a7689389e9512966d0392cd1efab001 Mon Sep 17 00:00:00 2001 From: Todd Date: Mon, 16 May 2022 15:14:12 -0700 Subject: [PATCH 012/100] Remove the server_id from session table, change it to worker_id on session_connection (#2070) * Remove the server_id from session table, change it to worker_id on session_connection This is step 1 in updating the worker table. The worker is becoming a public resource so it must have a public id. We drop the server_id column on the session_connection table and add the worker_id column so we can use the wt_public_key type. Since db migrations are applied while the controller is offline, it should be safe to assume all session connections are not in a running state, thus allowing us to null out all the worker ids in the session_connection table. For now we don't have very good enforcement of the wt_public_id constraints, since the id is currently defined in the worker config, but this will soon change and the worker's id will be generated by the controller. --- api/sessions/session.gen.go | 1 - api/sessions/workers.gen.go | 6 - internal/api/genapi/input.go | 4 - .../server/worker_shutdown_reload_test.go | 101 ++---- .../server/worker_tags_reload_test.go | 112 +++---- internal/cmd/commands/sessionscmd/funcs.go | 25 -- internal/cmd/config/config.go | 6 +- internal/cmd/config/config_test.go | 24 +- .../handlers/targets/target_service.go | 2 +- .../targets/tcp/target_service_test.go | 6 +- .../handlers/workers/worker_service.go | 9 +- .../workers/worker_service_status_test.go | 38 +-- internal/daemon/worker/testing.go | 4 +- .../99/01_worker_controller_tables.up.sql | 52 ++- internal/gen/controller.swagger.json | 17 - internal/proto/buf.yaml | 9 + .../api/resources/sessions/v1/session.proto | 15 +- .../v1/server_coordination_service.proto | 2 - .../storage/servers/store/v1/controller.proto | 5 +- .../storage/servers/store/v1/worker.proto | 8 +- internal/servers/repository.go | 12 +- internal/servers/repository_test.go | 50 +-- internal/servers/store/worker.pb.go | 81 ++--- internal/session/options.go | 29 +- internal/session/options_test.go | 7 - internal/session/query.go | 12 +- internal/session/repository.go | 1 - internal/session/repository_connection.go | 53 --- .../session/repository_connection_test.go | 306 +----------------- internal/session/repository_session.go | 16 +- internal/session/repository_session_test.go | 44 +-- .../service_authorize_connection_test.go | 4 +- .../session/service_close_connections_test.go | 3 +- .../service_worker_status_report_test.go | 44 +-- internal/session/session.go | 7 - internal/session/session_cleanup_job.go | 41 ++- internal/session/session_cleanup_job_test.go | 144 +++++++-- internal/session/testing.go | 19 +- .../tests/api/sessions/classification_test.go | 14 +- internal/tests/cluster/worker_tagging_test.go | 10 +- internal/tests/helper/testing_helper.go | 2 +- .../api/resources/sessions/session.pb.go | 306 +++++++----------- ...on_list_benchmarks_dump_generation_test.go | 6 +- 43 files changed, 578 insertions(+), 1079 deletions(-) delete mode 100644 api/sessions/workers.gen.go diff --git a/api/sessions/session.gen.go b/api/sessions/session.gen.go index b8f91ef0d..301ff994f 100644 --- a/api/sessions/session.gen.go +++ b/api/sessions/session.gen.go @@ -28,7 +28,6 @@ type Session struct { Endpoint string `json:"endpoint,omitempty"` States []*SessionState `json:"states,omitempty"` Status string `json:"status,omitempty"` - WorkerInfo []*WorkerInfo `json:"worker_info,omitempty"` Certificate []byte `json:"certificate,omitempty"` TerminationReason string `json:"termination_reason,omitempty"` AuthorizedActions []string `json:"authorized_actions,omitempty"` diff --git a/api/sessions/workers.gen.go b/api/sessions/workers.gen.go deleted file mode 100644 index 113654da8..000000000 --- a/api/sessions/workers.gen.go +++ /dev/null @@ -1,6 +0,0 @@ -// Code generated by "make api"; DO NOT EDIT. -package sessions - -type WorkerInfo struct { - Address string `json:"address,omitempty"` -} diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index 9574ae296..b7eaab89e 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -685,10 +685,6 @@ var inputStructs = []*structInfo{ inProto: &sessions.SessionState{}, outFile: "sessions/state.gen.go", }, - { - inProto: &sessions.WorkerInfo{}, - outFile: "sessions/workers.gen.go", - }, { inProto: &sessions.Connection{}, outFile: "sessions/connection.gen.go", diff --git a/internal/cmd/commands/server/worker_shutdown_reload_test.go b/internal/cmd/commands/server/worker_shutdown_reload_test.go index 656c921d8..0afc6efb4 100644 --- a/internal/cmd/commands/server/worker_shutdown_reload_test.go +++ b/internal/cmd/commands/server/worker_shutdown_reload_test.go @@ -14,9 +14,10 @@ import ( "github.com/hashicorp/boundary/api/authmethods" "github.com/hashicorp/boundary/api/authtokens" "github.com/hashicorp/boundary/api/targets" - "github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/tests/helper" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/boundary/testing/controller" "github.com/mitchellh/cli" "github.com/stretchr/testify/require" "go.uber.org/atomic" @@ -24,54 +25,13 @@ import ( _ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets/tcp" ) -const shutdownReloadApiAddr = "http://127.0.0.1:9203" - -const shutdownReloadControllerConfig = ` -disable_mlock = true - -controller { - name = "dev-controller" - description = "A default controller created in dev mode" - database { - url = "%s" - } -} - -kms "aead" { - purpose = "root" - aead_type = "aes-gcm" - key = "%s" - key_id = "global_root" -} - -kms "aead" { - purpose = "worker-auth" - aead_type = "aes-gcm" - key = "%s" - key_id = "global_worker-auth" -} - -listener "tcp" { - purpose = "api" - address = "127.0.0.1:9203" - tls_disable = true - cors_enabled = true - cors_allowed_origins = ["*"] -} - -listener "tcp" { - purpose = "cluster" - address = "127.0.0.1:9204" -} -` - const shutdownReloadWorkerConfig = ` disable_mlock = true worker { - name = "dev-worker" + name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["127.0.0.1:9204"] + controllers = ["%s"] } kms "aead" { @@ -89,35 +49,16 @@ listener "tcp" { func TestServer_ShutdownWorker(t *testing.T) { require := require.New(t) - controllerKey, workerAuthKey, _ := config.DevKeyGeneration() - // Start the controller - controllerCmd := testServerCommand(t, testServerCommandOpts{ - CreateDevDatabase: true, - ControllerKey: controllerKey, - UseDevAuthMethod: true, - UseDevTarget: true, - }) - t.Cleanup(func() { - if controllerCmd.DevDatabaseCleanupFunc != nil { - require.NoError(controllerCmd.DevDatabaseCleanupFunc()) - } - }) - controllerCmd.presetConfig = atomic.NewString(fmt.Sprintf(shutdownReloadControllerConfig, controllerCmd.DatabaseUrl, controllerKey, workerAuthKey)) - - // Use code channel so that we can use test assertions on the returned integer. - // It is illegal to call `t.FailNow()` from a goroutine. - // https://pkg.go.dev/testing#T.FailNow - controllerCodeChan := make(chan int) - go func() { - controllerCodeChan <- controllerCmd.Run(nil) - }() - - waitCh(t, controllerCmd.startedCh) + rootWrapper, _ := wrapperWithKey(t) + recoveryWrapper, _ := wrapperWithKey(t) + workerAuthWrapper, key := wrapperWithKey(t) + testController := controller.NewTestController(t, controller.WithWorkerAuthKms(workerAuthWrapper), controller.WithRootKms(rootWrapper), controller.WithRecoveryKms(recoveryWrapper)) + defer testController.Shutdown() // Start the worker workerCmd := testServerCommand(t, testServerCommandOpts{}) - workerCmd.presetConfig = atomic.NewString(fmt.Sprintf(shutdownReloadWorkerConfig, workerAuthKey)) + workerCmd.presetConfig = atomic.NewString(fmt.Sprintf(shutdownReloadWorkerConfig, testController.ClusterAddrs()[0], key)) workerCodeChan := make(chan int) go func() { @@ -131,24 +72,27 @@ func TestServer_ShutdownWorker(t *testing.T) { // Set up the target ctx := context.Background() - client := buildClient(t, shutdownReloadApiAddr) + client := buildClient(t, testController.ApiAddrs()[0]) setAuthToken(ctx, t, client) tcl := targets.NewClient(client) - tgt, err := tcl.Read(ctx, "ttcp_1234567890") + tgtL, err := tcl.List(ctx, scope.Global.String(), targets.WithRecursive(true)) + // tgt, err := tcl.Read(ctx, "ttcp_1234567890") require.NoError(err) + require.Len(tgtL.Items, 1) + tgt := tgtL.Items[0] require.NotNil(tgt) // Create test server, update default port on target ts := helper.NewTestTcpServer(t) require.NotNil(ts) defer ts.Close() - tgt, err = tcl.Update(ctx, tgt.Item.Id, tgt.Item.Version, targets.WithTcpTargetDefaultPort(ts.Port())) + tgtR, err := tcl.Update(ctx, tgt.Id, tgt.Version, targets.WithTcpTargetDefaultPort(ts.Port())) require.NoError(err) - require.NotNil(tgt) + require.NotNil(tgtR) // Authorize and connect - sess := helper.NewTestSession(ctx, t, tcl, "ttcp_1234567890") + sess := helper.NewTestSession(ctx, t, tcl, tgt.Id) sConn := sess.Connect(ctx, t) // Run initial send/receive test, make sure things are working @@ -164,14 +108,7 @@ func TestServer_ShutdownWorker(t *testing.T) { // Connection should fail, and the session should be closed on the DB. sConn.TestSendRecvFail(t) - sess.ExpectConnectionStateOnController(ctx, t, controllerCmd.controller.ConnectionRepoFn, session.StatusClosed) - - // We're done! Shutdown the controller, and that's it. - close(controllerCmd.ShutdownCh) - if <-controllerCodeChan != 0 { - output := controllerCmd.UI.(*cli.MockUi).ErrorWriter.String() + controllerCmd.UI.(*cli.MockUi).OutputWriter.String() - require.FailNow(output, "command exited with non-zero error code") - } + sess.ExpectConnectionStateOnController(ctx, t, testController.Controller().ConnectionRepoFn, session.StatusClosed) } // largely copied from controller/testing.go diff --git a/internal/cmd/commands/server/worker_tags_reload_test.go b/internal/cmd/commands/server/worker_tags_reload_test.go index ceeb17d1d..f650e58fb 100644 --- a/internal/cmd/commands/server/worker_tags_reload_test.go +++ b/internal/cmd/commands/server/worker_tags_reload_test.go @@ -10,12 +10,17 @@ package server import ( + "context" + "crypto/rand" + "encoding/base64" "fmt" "sync" "testing" "time" - "github.com/hashicorp/boundary/internal/cmd/config" + "github.com/hashicorp/boundary/testing/controller" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" + "github.com/hashicorp/go-kms-wrapping/v2/aead" "github.com/mitchellh/cli" "github.com/stretchr/testify/require" "go.uber.org/atomic" @@ -24,26 +29,6 @@ import ( const workerBaseConfig = ` disable_mlock = true -telemetry { - prometheus_retention_time = "24h" - disable_hostname = true -} - -controller { - name = "dev-controller" - description = "A default controller created in dev mode" - database { - url = "%s" - } -} - -kms "aead" { - purpose = "root" - aead_type = "aes-gcm" - key = "%s" - key_id = "global_root" -} - kms "aead" { purpose = "worker-auth" aead_type = "aes-gcm" @@ -51,38 +36,17 @@ kms "aead" { key_id = "global_worker-auth" } -kms "aead" { - purpose = "recovery" - aead_type = "aes-gcm" - key = "%s" - key_id = "global_recovery" -} - -listener "tcp" { - purpose = "api" - address = "127.0.0.1:9403" - tls_disable = true - cors_enabled = true - cors_allowed_origins = ["*"] -} - -listener "tcp" { - purpose = "cluster" - address = "127.0.0.1:9404" -} - listener "tcp" { purpose = "proxy" address = "127.0.0.1:9405" } - ` const tag1Config = ` worker { - name = "dev-worker" + name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["127.0.0.1:9404"] + controllers = ["%s"] tags { type = ["dev", "local"] } @@ -91,9 +55,9 @@ worker { const tag2Config = ` worker { - name = "dev-worker" + name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["127.0.0.1:9404"] + controllers = ["%s"] tags { foo = ["bar", "baz"] } @@ -103,23 +67,17 @@ worker { func TestServer_ReloadWorkerTags(t *testing.T) { t.Parallel() require := require.New(t) + + rootWrapper, _ := wrapperWithKey(t) + recoveryWrapper, _ := wrapperWithKey(t) + workerAuthWrapper, key := wrapperWithKey(t) + testController := controller.NewTestController(t, controller.WithWorkerAuthKms(workerAuthWrapper), controller.WithRootKms(rootWrapper), controller.WithRecoveryKms(recoveryWrapper)) + defer testController.Shutdown() + wg := &sync.WaitGroup{} - controllerKey, workerAuthKey, recoveryKey := config.DevKeyGeneration() - - cmd := testServerCommand(t, testServerCommandOpts{ - CreateDevDatabase: true, - ControllerKey: controllerKey, - UseDevAuthMethod: true, - UseDevTarget: true, - }) - defer func() { - if cmd.DevDatabaseCleanupFunc != nil { - require.NoError(cmd.DevDatabaseCleanupFunc()) - } - }() - - cmd.presetConfig = atomic.NewString(fmt.Sprintf(workerBaseConfig+tag1Config, cmd.DatabaseUrl, controllerKey, workerAuthKey, recoveryKey)) + cmd := testServerCommand(t, testServerCommandOpts{}) + cmd.presetConfig = atomic.NewString(fmt.Sprintf(workerBaseConfig+tag1Config, key, testController.ClusterAddrs()[0])) wg.Add(1) go func() { @@ -138,9 +96,9 @@ func TestServer_ReloadWorkerTags(t *testing.T) { fetchWorkerTags := func(name string, key string, values []string) { t.Helper() - serversRepo, err := cmd.controller.ServersRepoFn() + serversRepo, err := testController.Controller().ServersRepoFn() require.NoError(err) - tags, err := serversRepo.ListTagsForWorkers(cmd.Context, []string{name}) + tags, err := serversRepo.ListTagsForWorkers(testController.Context(), []string{name}) require.NoError(err) require.Len(tags, 2) require.Equal(key, tags[0].Key) @@ -151,9 +109,9 @@ func TestServer_ReloadWorkerTags(t *testing.T) { // Give time to populate up to the controller time.Sleep(10 * time.Second) - fetchWorkerTags("dev-worker", "type", []string{"dev", "local"}) + fetchWorkerTags("w_1234567890", "type", []string{"dev", "local"}) - cmd.presetConfig.Store(fmt.Sprintf(workerBaseConfig+tag2Config, cmd.DatabaseUrl, controllerKey, workerAuthKey, recoveryKey)) + cmd.presetConfig.Store(fmt.Sprintf(workerBaseConfig+tag2Config, key, testController.ClusterAddrs()[0])) cmd.SighupCh <- struct{}{} select { @@ -163,9 +121,31 @@ func TestServer_ReloadWorkerTags(t *testing.T) { } time.Sleep(10 * time.Second) - fetchWorkerTags("dev-worker", "foo", []string{"bar", "baz"}) + fetchWorkerTags("w_1234567890", "foo", []string{"bar", "baz"}) close(cmd.ShutdownCh) wg.Wait() } + +// TestWrapper initializes an AEAD wrapping.Wrapper for testing the oplog +func wrapperWithKey(t testing.TB) (wrapping.Wrapper, string) { + t.Helper() + rootKey := make([]byte, 32) + n, err := rand.Read(rootKey) + if err != nil { + t.Fatal(err) + } + if n != 32 { + t.Fatal(n) + } + root := aead.NewWrapper() + _, err = root.SetConfig(context.Background(), wrapping.WithKeyId(base64.StdEncoding.EncodeToString(rootKey))) + if err != nil { + t.Fatal(err) + } + if err := root.SetAesGcmKeyBytes(rootKey); err != nil { + t.Fatal(err) + } + return root, base64.StdEncoding.EncodeToString(rootKey) +} diff --git a/internal/cmd/commands/sessionscmd/funcs.go b/internal/cmd/commands/sessionscmd/funcs.go index cccfecb51..117940fcf 100644 --- a/internal/cmd/commands/sessionscmd/funcs.go +++ b/internal/cmd/commands/sessionscmd/funcs.go @@ -196,19 +196,6 @@ func printItemTable(result api.GenericResult) string { } } - var workerInfoMaps []map[string]interface{} - if len(item.WorkerInfo) > 0 { - for _, wi := range item.WorkerInfo { - m := map[string]interface{}{ - "Address": wi.Address, - } - workerInfoMaps = append(workerInfoMaps, m) - } - if l := len("Address"); l > maxLength { - maxLength = l - } - } - ret := []string{ "", "Session information:", @@ -244,17 +231,5 @@ func printItemTable(result api.GenericResult) string { } } - if len(item.WorkerInfo) > 0 { - ret = append(ret, - fmt.Sprintf(" Worker Info: %s", ""), - ) - for _, m := range workerInfoMaps { - ret = append(ret, - base.WrapMap(4, maxLength, m), - "", - ) - } - } - return base.WrapForHelpText(ret) } diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index f79840f98..0c753f5f9 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -14,9 +14,9 @@ import ( "strings" "time" + "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/observability/event" wrapping "github.com/hashicorp/go-kms-wrapping/v2" - "github.com/hashicorp/go-secure-stdlib/base62" configutil "github.com/hashicorp/go-secure-stdlib/configutil/v2" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/strutil" @@ -87,7 +87,7 @@ listener "tcp" { } worker { - name = "dev-worker" + name = "w_1234567890" description = "A default worker created in dev mode" controllers = ["127.0.0.1"] tags { @@ -205,7 +205,7 @@ func (w *Worker) InitNameIfEmpty() (string, error) { func initNameIfEmpty(name *string) error { if *name == "" { var err error - if *name, err = base62.Random(10); err != nil { + if *name, err = db.NewPublicId("w"); err != nil { return err } } diff --git a/internal/cmd/config/config_test.go b/internal/cmd/config/config_test.go index 9a0210f24..99ba8486a 100644 --- a/internal/cmd/config/config_test.go +++ b/internal/cmd/config/config_test.go @@ -180,7 +180,7 @@ func TestDevWorker(t *testing.T) { }, }, Worker: &Worker{ - Name: "dev-worker", + Name: "w_1234567890", Description: "A default worker created in dev mode", Controllers: []string{"127.0.0.1"}, ControllersRaw: []interface{}{"127.0.0.1"}, @@ -201,7 +201,7 @@ func TestDevWorker(t *testing.T) { } worker { - name = "dev-worker" + name = "w_1234567890" description = "A default worker created in dev mode" controllers = ["127.0.0.1"] tags = ["type=dev", "type=local"] @@ -221,7 +221,7 @@ func TestDevWorker(t *testing.T) { } worker { - name = "dev-worker" + name = "w_1234567890" description = "A default worker created in dev mode" controllers = ["127.0.0.1"] tags { @@ -246,7 +246,7 @@ func TestDevWorker(t *testing.T) { } worker { - name = "dev-worker" + name = "w_1234567890" description = "A default worker created in dev mode" controllers = ["127.0.0.1"] tags = ["tyPe=dev", "type=local"] @@ -263,7 +263,7 @@ func TestDevWorker(t *testing.T) { } worker { - name = "dev-worker" + name = "w_1234567890" description = "A default worker created in dev mode" controllers = ["127.0.0.1"] tags = ["type=dev", "type=loCal"] @@ -315,18 +315,18 @@ func TestParsingName(t *testing.T) { { name: "no env", templateController: "foobar", - templateWorker: "barfoo", + templateWorker: "test_worker_barfoo", expectedController: "foobar", - expectedWorker: "barfoo", + expectedWorker: "test_worker_barfoo", }, { name: "env", templateController: fmt.Sprintf("env://%s", controllerEnv), templateWorker: fmt.Sprintf("env://%s", workerEnv), envController: "foobar2", - envWorker: "barfoo2", + envWorker: "env_name_barfoo2", expectedController: "foobar2", - expectedWorker: "barfoo2", + expectedWorker: "env_name_barfoo2", }, } for _, tt := range cases { @@ -390,7 +390,7 @@ func TestWorkerTags(t *testing.T) { name: "no tags", in: ` worker { - name = "testworker" + name = "w_1234567890" } `, expWorkerTags: nil, @@ -400,7 +400,7 @@ func TestWorkerTags(t *testing.T) { name: "empty tags", in: ` worker { - name = "testworker" + name = "w_1234567890" tags = {} } `, @@ -411,7 +411,7 @@ func TestWorkerTags(t *testing.T) { name: "empty tags 2", in: ` worker { - name = "testworker" + name = "w_1234567890" tags = [] } `, diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index 5c98b087e..e1713c421 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -903,7 +903,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession } for _, v := range servers { if hasWorkerFilter { - workerIds = append(workerIds, v.GetPrivateId()) + workerIds = append(workerIds, v.GetPublicId()) } workers = append(workers, &pb.WorkerInfo{Address: v.Address}) } diff --git a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go index 46e256643..1a526d127 100644 --- a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go @@ -2790,7 +2790,7 @@ func TestAuthorizeSession(t *testing.T) { workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) _, err = workerService.Status(ctx, &spbs.StatusRequest{ Worker: &spb.Server{ - PrivateId: "testworker", + PrivateId: "w_1234567890", Address: "localhost:8457", }, }) @@ -3078,7 +3078,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) _, err = workerService.Status(ctx, &spbs.StatusRequest{ Worker: &spb.Server{ - PrivateId: "testworker", + PrivateId: "w_1234567890", Address: "localhost:8457", }, }) @@ -3209,7 +3209,7 @@ func TestAuthorizeSession_Errors(t *testing.T) { workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) _, err := workerService.Status(context.Background(), &spbs.StatusRequest{ Worker: &spb.Server{ - PrivateId: "testworker", + PrivateId: "w_1234567890", Address: "localhost:123", }, }) diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index 2c897886b..f14fd004a 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -82,7 +82,7 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques workerTags[k] = thisTag } worker := &store.Worker{ - PrivateId: reqServer.PrivateId, + PublicId: reqServer.PrivateId, Address: reqServer.Address, CreateTime: reqServer.CreateTime, UpdateTime: reqServer.UpdateTime, @@ -318,12 +318,7 @@ func (ws *workerServiceServer) ActivateSession(ctx context.Context, req *pbs.Act return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err) } - sessionInfo, sessionStates, err := sessRepo.ActivateSession( - ctx, - req.GetSessionId(), - req.GetVersion(), - req.GetWorkerId(), - []byte(req.GetTofuToken())) + sessionInfo, sessionStates, err := sessRepo.ActivateSession(ctx, req.GetSessionId(), req.GetVersion(), []byte(req.GetTofuToken())) if err != nil { return nil, status.Errorf(codes.Internal, "error looking up session: %v", err) } diff --git a/internal/daemon/controller/handlers/workers/worker_service_status_test.go b/internal/daemon/controller/handlers/workers/worker_service_status_test.go index 878fefcce..85b104e18 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_status_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_status_test.go @@ -38,8 +38,8 @@ func TestStatus(t *testing.T) { Address: "127.0.0.1", }) serverRepo.UpsertWorker(ctx, &store.Worker{ - PrivateId: "test_worker1", - Address: "127.0.0.1", + PublicId: "test_worker1", + Address: "127.0.0.1", }) serversRepoFn := func() (*servers.Repository, error) { @@ -83,14 +83,14 @@ func TestStatus(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker1.PrivateId, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, tofu) require.NoError(t, err) require.NoError(t, err) s := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) require.NotNil(t, s) - connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PrivateId) + connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PublicId) require.NoError(t, err) cases := []struct { @@ -105,7 +105,7 @@ func TestStatus(t *testing.T) { wantErr: false, req: &pbs.StatusRequest{ Worker: &servers.Server{ - PrivateId: worker1.PrivateId, + PrivateId: worker1.PublicId, Address: worker1.Address, CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, @@ -125,7 +125,7 @@ func TestStatus(t *testing.T) { wantErr: false, req: &pbs.StatusRequest{ Worker: &servers.Server{ - PrivateId: worker1.PrivateId, + PrivateId: worker1.PublicId, Address: worker1.Address, CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, @@ -206,8 +206,8 @@ func TestStatusSessionClosed(t *testing.T) { Address: "127.0.0.1", }) serverRepo.UpsertWorker(ctx, &store.Worker{ - PrivateId: "test_worker1", - Address: "127.0.0.1", + PublicId: "test_worker1", + Address: "127.0.0.1", }) serversRepoFn := func() (*servers.Repository, error) { @@ -251,7 +251,7 @@ func TestStatusSessionClosed(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker1.PrivateId, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, tofu) require.NoError(t, err) sess2 := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -264,13 +264,13 @@ func TestStatusSessionClosed(t *testing.T) { ConnectionLimit: 10, }) tofu2 := session.TestTofu(t) - sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker1.PrivateId, tofu2) + sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, tofu2) require.NoError(t, err) s := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) require.NotNil(t, s) - connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PrivateId) + connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PublicId) require.NoError(t, err) cases := []struct { @@ -290,7 +290,7 @@ func TestStatusSessionClosed(t *testing.T) { }, req: &pbs.StatusRequest{ Worker: &servers.Server{ - PrivateId: worker1.PrivateId, + PrivateId: worker1.PublicId, Address: worker1.Address, CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, @@ -388,8 +388,8 @@ func TestStatusDeadConnection(t *testing.T) { Address: "127.0.0.1", }) serverRepo.UpsertWorker(ctx, &store.Worker{ - PrivateId: "test_worker1", - Address: "127.0.0.1", + PublicId: "test_worker1", + Address: "127.0.0.1", }) serversRepoFn := func() (*servers.Repository, error) { @@ -433,7 +433,7 @@ func TestStatusDeadConnection(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker1.PrivateId, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, tofu) require.NoError(t, err) sess2 := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -446,21 +446,21 @@ func TestStatusDeadConnection(t *testing.T) { ConnectionLimit: 10, }) tofu2 := session.TestTofu(t) - sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker1.PrivateId, tofu2) + sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, tofu2) require.NoError(t, err) s := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) require.NotNil(t, s) - connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PrivateId) + connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PublicId) require.NoError(t, err) - deadConn, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker1.PrivateId) + deadConn, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker1.PublicId) require.NoError(t, err) require.NotEqual(t, deadConn.PublicId, connection.PublicId) req := &pbs.StatusRequest{ Worker: &servers.Server{ - PrivateId: worker1.PrivateId, + PrivateId: worker1.PublicId, Address: worker1.Address, CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, diff --git a/internal/daemon/worker/testing.go b/internal/daemon/worker/testing.go index 9a8cf1c1c..a394e72d1 100644 --- a/internal/daemon/worker/testing.go +++ b/internal/daemon/worker/testing.go @@ -10,11 +10,11 @@ import ( "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/daemon/worker/session" + "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/go-hclog" wrapping "github.com/hashicorp/go-kms-wrapping/v2" - "github.com/hashicorp/go-secure-stdlib/base62" "github.com/prometheus/client_golang/prometheus" ) @@ -316,7 +316,7 @@ func (tw *TestWorker) AddClusterWorkerMember(t testing.TB, opts *TestWorkerOpts) } if nextOpts.Name == "" { var err error - nextOpts.Name, err = base62.Random(5) + nextOpts.Name, err = db.NewPublicId("w") if err != nil { t.Fatal(err) } diff --git a/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql index b213b451b..22e4b8437 100644 --- a/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql @@ -25,9 +25,11 @@ create trigger controller_insert_time_column before insert on server_controller create trigger controller_update_time_column before update on server_controller for each row execute procedure update_time_column(); --- Worker table adds the field: name +-- Worker table takes the place of the server table. +-- instead of the private_id we use a wt_public_id field named public_id since +-- workers will now be exposed as resources in boundary. create table server_worker ( - private_id text primary key, + public_id wt_public_id primary key, description wt_description, name wt_name unique, address text not null @@ -40,7 +42,7 @@ comment on table server_worker is 'server_worker is a table where each row represents a Boundary worker.'; create trigger immutable_columns before update on server_worker - for each row execute procedure immutable_columns('private_id','create_time'); + for each row execute procedure immutable_columns('public_id','create_time'); create trigger default_create_time_column before insert on server_worker for each row execute procedure default_create_time(); @@ -51,13 +53,11 @@ create trigger worker_insert_time_column before insert on server_worker create trigger worker_update_time_column before update on server_worker for each row execute procedure update_time_column(); --- TODO: Migrate server entries to worker and controller tables, if necessary - -- Create table worker tag create table server_worker_tag ( - worker_id text + worker_id wt_public_id constraint server_worker_fkey - references server_worker(private_id) + references server_worker(public_id) on delete cascade on update cascade, key wt_tagpair, @@ -71,55 +71,43 @@ drop table server_tag; -- Update session table to use worker_id instead of server_id, drop view first because of dependency on server type drop view session_list; -alter table session - drop constraint session_server_id_fkey; +-- Update session table to use worker_id instead of server_id +-- Updating the session table modified in 01/01_server_tags_migrations.up.sql drop trigger update_version_column on session; alter table session - drop column server_type; - -alter table session - rename column server_id to worker_id; -alter table session - add constraint server_worker_fkey - foreign key (worker_id) - references server_worker(private_id) - on delete set null - on update cascade; - + drop constraint session_server_id_fkey, + drop column server_type, + drop column server_id; create trigger update_version_column - after update of version, termination_reason, key_id, tofu_token, worker_id on session + after update of version, termination_reason, key_id, tofu_token on session for each row execute procedure update_version_column(); -- Update session_connection table to use worker_id instead of server_id +-- Table last updated in 21/02_session.up.sql alter table session_connection - drop constraint server_fkey; - -alter table session_connection - rename column server_id to worker_id; -alter table session_connection + drop column server_id, + add column worker_id wt_public_id, add constraint server_worker_fkey foreign key (worker_id) - references server_worker (private_id) + references server_worker (public_id) on delete set null on update cascade; -- Update job run table so that server id references controller id alter table job_run - drop constraint server_fkey; -alter table job_run + drop constraint server_fkey, add constraint server_controller_fkey foreign key (server_id) references server_controller (private_id) on delete set null on update cascade; --- Replaces the view created in 9/01 to include worker id instead of server id and server type +-- Replaces the view created in 9/01 to remove references to the server/worker id. create view session_list as - select - s.public_id, s.user_id, s.host_id, s.worker_id, s.target_id, + s.public_id, s.user_id, s.host_id, s.target_id, s.host_set_id, s.auth_token_id, s.scope_id, s.certificate,s.expiration_time, s.connection_limit, s.tofu_token, s.key_id, s.termination_reason, s.version, s.create_time, s.update_time, s.endpoint, s.worker_filter, diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 3c80e7784..5edeba6aa 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -4836,14 +4836,6 @@ "description": "Output only. The current status of this Session.", "readOnly": true }, - "worker_info": { - "type": "array", - "items": { - "$ref": "#/definitions/controller.api.resources.sessions.v1.WorkerInfo" - }, - "description": "Output only. Worker information given to the client.", - "readOnly": true - }, "certificate": { "type": "string", "format": "byte", @@ -4895,15 +4887,6 @@ } } }, - "controller.api.resources.sessions.v1.WorkerInfo": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "The address of the worker." - } - } - }, "controller.api.resources.targets.v1.CredentialLibrary": { "type": "object", "properties": { diff --git a/internal/proto/buf.yaml b/internal/proto/buf.yaml index 6aa5d8858..c951d7b3c 100644 --- a/internal/proto/buf.yaml +++ b/internal/proto/buf.yaml @@ -6,6 +6,15 @@ deps: breaking: use: - WIRE_JSON + ignore_only: + FIELD_NO_DELETE_UNLESS_NUMBER_RESERVED: + # The proto files in controller/api are only exposed as json files so enforcing + # proto backward compatability doesn't make sense, except for maybe the names. + - controller/api + # Storage protos are used by gorm. Gorm relies on go tags and the json + # names to determine what columns to update so we don't care of the field + # numbers remain the same. + - controller/storage lint: use: - DEFAULT diff --git a/internal/proto/controller/api/resources/sessions/v1/session.proto b/internal/proto/controller/api/resources/sessions/v1/session.proto index d05233766..f30967a37 100644 --- a/internal/proto/controller/api/resources/sessions/v1/session.proto +++ b/internal/proto/controller/api/resources/sessions/v1/session.proto @@ -7,11 +7,6 @@ import "google/protobuf/timestamp.proto"; option go_package = "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/sessions;sessions"; -message WorkerInfo { - // The address of the worker. - string address = 10; // @gotags: `class:"public"` -} - message SessionState { // The status of the Session, e.g. "pending", "active", "canceling", "terminated". string status = 10; // @gotags: `class:"public"` @@ -49,6 +44,13 @@ message Connection { // Session contains all fields related to a Session resource message Session { + // Worker info previously contained only the worker's address but was never + // populated either in the database or on the API. While worker id information + // may become helpful on a session in the future, the worker id seems the most + // relevant information since the address can change between the time the + // session is created and a session connection is made through that worker. + reserved "worker_info"; + // Output only. The ID of the Session. string id = 10; // @gotags: `class:"public"` @@ -97,9 +99,6 @@ message Session { // Output only. The current status of this Session. string status = 180; // @gotags: `class:"public"` - // Output only. Worker information given to the client. - repeated WorkerInfo worker_info = 190 [json_name = "worker_info"]; - // Output only. The certificate generated for the session. Raw DER bytes. bytes certificate = 200; // @gotags: `class:"public"` diff --git a/internal/proto/controller/servers/services/v1/server_coordination_service.proto b/internal/proto/controller/servers/services/v1/server_coordination_service.proto index 6f8a0b009..f571efe4a 100644 --- a/internal/proto/controller/servers/services/v1/server_coordination_service.proto +++ b/internal/proto/controller/servers/services/v1/server_coordination_service.proto @@ -56,8 +56,6 @@ message JobStatus { Job job = 1; } - - message StatusRequest { // The worker info. We could use information from the TLS connection but this // is easier and going the other route doesn't provide much benefit -- if you diff --git a/internal/proto/controller/storage/servers/store/v1/controller.proto b/internal/proto/controller/storage/servers/store/v1/controller.proto index 5d2be86f6..f952ae983 100644 --- a/internal/proto/controller/storage/servers/store/v1/controller.proto +++ b/internal/proto/controller/storage/servers/store/v1/controller.proto @@ -1,10 +1,11 @@ syntax = "proto3"; package controller.storage.servers.store.v1; -option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; import "controller/storage/timestamp/v1/timestamp.proto"; +option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; + // Controller contains all fields related to a Controller resource message Controller { // Private ID of the resource @@ -22,4 +23,4 @@ message Controller { // Last time there was an update storage.timestamp.v1.Timestamp update_time = 50; -} \ No newline at end of file +} diff --git a/internal/proto/controller/storage/servers/store/v1/worker.proto b/internal/proto/controller/storage/servers/store/v1/worker.proto index 6203a5f6a..a5063074e 100644 --- a/internal/proto/controller/storage/servers/store/v1/worker.proto +++ b/internal/proto/controller/storage/servers/store/v1/worker.proto @@ -1,14 +1,16 @@ syntax = "proto3"; package controller.storage.servers.store.v1; -option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; import "controller/storage/timestamp/v1/timestamp.proto"; +option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; + // Worker contains all fields related to a Worker resource message Worker { - // Private ID of the resource - string private_id = 10; + // Public ID of the resource + // @inject_tag: `gorm:"primary_key"` + string public_id = 10; // Name of the resource (optional) // Description of the resource (optional) diff --git a/internal/servers/repository.go b/internal/servers/repository.go index 508968870..b089d3b16 100644 --- a/internal/servers/repository.go +++ b/internal/servers/repository.go @@ -168,7 +168,7 @@ func (r *Repository) UpsertWorker(ctx context.Context, worker *store.Worker, opt func(read db.Reader, w db.Writer) error { var err error onConflict := &db.OnConflict{ - Target: db.Columns{"private_id"}, + Target: db.Columns{"public_id"}, Action: append(db.SetColumns([]string{"description", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), } err = w.Create(ctx, worker, db.WithOnConflict(onConflict), db.WithReturnRowsAffected(&rowsUpdated)) @@ -187,9 +187,9 @@ func (r *Repository) UpsertWorker(ctx context.Context, worker *store.Worker, opt // delete all tags for the given worker, then add the new ones // we've been sent. if opts.withUpdateTags { - _, err = w.Delete(ctx, &WorkerTag{}, db.WithWhere(deleteTagsSql, worker.PrivateId)) + _, err = w.Delete(ctx, &WorkerTag{}, db.WithWhere(deleteTagsSql, worker.PublicId)) if err != nil { - return errors.Wrap(ctx, err, op+":DeleteTags", errors.WithMsg(worker.PrivateId)) + return errors.Wrap(ctx, err, op+":DeleteTags", errors.WithMsg(worker.PublicId)) } // If tags were cleared out entirely, then we'll have nothing @@ -200,18 +200,18 @@ func (r *Repository) UpsertWorker(ctx context.Context, worker *store.Worker, opt tags := make([]interface{}, 0, len(worker.Tags)) for k, v := range worker.Tags { if v == nil { - return errors.New(ctx, errors.InvalidParameter, op+":RangeTags", fmt.Sprintf("found nil tag value for worker %s and key %s", worker.PrivateId, k)) + return errors.New(ctx, errors.InvalidParameter, op+":RangeTags", fmt.Sprintf("found nil tag value for worker %s and key %s", worker.PublicId, k)) } for _, val := range v.Values { tags = append(tags, WorkerTag{ - WorkerId: worker.PrivateId, + WorkerId: worker.PublicId, Key: k, Value: val, }) } } if err = w.CreateItems(ctx, tags); err != nil { - return errors.Wrap(ctx, err, op+":CreateTags", errors.WithMsg(worker.PrivateId)) + return errors.Wrap(ctx, err, op+":CreateTags", errors.WithMsg(worker.PublicId)) } } } diff --git a/internal/servers/repository_test.go b/internal/servers/repository_test.go index 20e074f1d..173b2169e 100644 --- a/internal/servers/repository_test.go +++ b/internal/servers/repository_test.go @@ -100,8 +100,8 @@ func TestTagUpdatingListing(t *testing.T) { repo := tc.ServersRepo() srv := &store.Worker{ - PrivateId: "test1", - Address: "127.0.0.1", + PublicId: "test_worker_1", + Address: "127.0.0.1", Tags: map[string]*store.TagValues{ "tag1": { Values: []string{"value1", "value2"}, @@ -113,8 +113,8 @@ func TestTagUpdatingListing(t *testing.T) { require.NoError(err) srv = &store.Worker{ - PrivateId: "test2", - Address: "127.0.0.1", + PublicId: "test_worker_2", + Address: "127.0.0.1", Tags: map[string]*store.TagValues{ "tag2": { Values: []string{"value1", "value2"}, @@ -124,28 +124,28 @@ func TestTagUpdatingListing(t *testing.T) { _, _, err = repo.UpsertWorker(tc.Context(), srv, servers.WithUpdateTags(true)) require.NoError(err) - tags, err := repo.ListTagsForWorkers(tc.Context(), []string{"test1", "test2"}) + tags, err := repo.ListTagsForWorkers(tc.Context(), []string{"test_worker_1", "test_worker_2"}) require.NoError(err) // Base case exp := []*servers.WorkerTag{ { - WorkerId: "test1", + WorkerId: "test_worker_1", Key: "tag1", Value: "value1", }, { - WorkerId: "test1", + WorkerId: "test_worker_1", Key: "tag1", Value: "value2", }, { - WorkerId: "test2", + WorkerId: "test_worker_2", Key: "tag2", Value: "value1", }, { - WorkerId: "test2", + WorkerId: "test_worker_2", Key: "tag2", Value: "value2", }, @@ -154,8 +154,8 @@ func TestTagUpdatingListing(t *testing.T) { // Update without saying to update tags srv = &store.Worker{ - PrivateId: "test2", - Address: "192.168.1.1", + PublicId: "test_worker_2", + Address: "192.168.1.1", Tags: map[string]*store.TagValues{ "tag22": { Values: []string{"value21", "value22"}, @@ -164,24 +164,24 @@ func TestTagUpdatingListing(t *testing.T) { } _, _, err = repo.UpsertWorker(tc.Context(), srv) require.NoError(err) - tags, err = repo.ListTagsForWorkers(tc.Context(), []string{"test1", "test2"}) + tags, err = repo.ListTagsForWorkers(tc.Context(), []string{"test_worker_1", "test_worker_2"}) require.NoError(err) require.Equal(exp, tags) // Update tags and test again _, _, err = repo.UpsertWorker(tc.Context(), srv, servers.WithUpdateTags(true)) require.NoError(err) - tags, err = repo.ListTagsForWorkers(tc.Context(), []string{"test1", "test2"}) + tags, err = repo.ListTagsForWorkers(tc.Context(), []string{"test_worker_1", "test_worker_2"}) require.NoError(err) require.NotEqual(exp, tags) // Update and try again exp[2] = &servers.WorkerTag{ - WorkerId: "test2", + WorkerId: "test_worker_2", Key: "tag22", Value: "value21", } exp[3] = &servers.WorkerTag{ - WorkerId: "test2", + WorkerId: "test_worker_2", Key: "tag22", Value: "value22", } @@ -199,10 +199,10 @@ func TestListServersWithLiveness(t *testing.T) { require.NoError(err) ctx := context.Background() - newWorker := func(privateId string) *store.Worker { + newWorker := func(publicId string) *store.Worker { result := &store.Worker{ - PrivateId: privateId, - Address: "127.0.0.1", + PublicId: publicId, + Address: "127.0.0.1", } _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, result) require.NoError(err) @@ -211,9 +211,9 @@ func TestListServersWithLiveness(t *testing.T) { return result } - server1 := newWorker("test1") - server2 := newWorker("test2") - server3 := newWorker("test3") + server1 := newWorker("test_worker_1") + server2 := newWorker("test_worker_2") + server3 := newWorker("test_worker_3") // Sleep the default liveness time (15sec currently) +1s time.Sleep(time.Second * 16) @@ -233,7 +233,7 @@ func TestListServersWithLiveness(t *testing.T) { got := make(map[string]struct{}) for _, v := range actual { - got[v.PrivateId] = struct{}{} + got[v.PublicId] = struct{}{} } require.Equal(want, got) @@ -243,7 +243,7 @@ func TestListServersWithLiveness(t *testing.T) { result, err := serversRepo.ListWorkers(ctx) require.NoError(err) require.Len(result, 1) - requireIds([]string{server1.PrivateId}, result) + requireIds([]string{server1.PublicId}, result) // Upsert second server. _, rowsUpdated, err = serversRepo.UpsertWorker(ctx, server2) @@ -255,11 +255,11 @@ func TestListServersWithLiveness(t *testing.T) { result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(time.Second*5)) require.NoError(err) require.Len(result, 2) - requireIds([]string{server1.PrivateId, server2.PrivateId}, result) + requireIds([]string{server1.PublicId, server2.PublicId}, result) // Liveness disabled, should get all three workers. result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(-1)) require.NoError(err) require.Len(result, 3) - requireIds([]string{server1.PrivateId, server2.PrivateId, server3.PrivateId}, result) + requireIds([]string{server1.PublicId, server2.PublicId, server3.PublicId}, result) } diff --git a/internal/servers/store/worker.pb.go b/internal/servers/store/worker.pb.go index fd5504fb1..4f78c2685 100644 --- a/internal/servers/store/worker.pb.go +++ b/internal/servers/store/worker.pb.go @@ -27,8 +27,9 @@ type Worker struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Private ID of the resource - PrivateId string `protobuf:"bytes,10,opt,name=private_id,json=privateId,proto3" json:"private_id,omitempty"` + // Public ID of the resource + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` // Name of the resource (optional) // Description of the resource (optional) // @inject_tag: `gorm:"default:null"` @@ -78,9 +79,9 @@ func (*Worker) Descriptor() ([]byte, []int) { return file_controller_storage_servers_store_v1_worker_proto_rawDescGZIP(), []int{0} } -func (x *Worker) GetPrivateId() string { +func (x *Worker) GetPublicId() string { if x != nil { - return x.PrivateId + return x.PublicId } return "" } @@ -187,42 +188,42 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc5, 0x03, 0x0a, 0x06, 0x57, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x49, 0x0a, 0x04, - 0x74, 0x61, 0x67, 0x73, 0x18, 0x46, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x67, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x23, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, - 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc3, 0x03, 0x0a, 0x06, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, + 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x49, 0x0a, 0x04, 0x74, 0x61, + 0x67, 0x73, 0x18, 0x46, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x67, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, + 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x23, + 0x0a, 0x09, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/session/options.go b/internal/session/options.go index 1ae61bbd9..1fdaaeb06 100644 --- a/internal/session/options.go +++ b/internal/session/options.go @@ -21,18 +21,16 @@ type Option func(*options) // options = how options are represented type options struct { - withLimit int - withOrderByCreateTime db.OrderBy - withScopeIds []string - withUserId string - withExpirationTime *timestamp.Timestamp - withTestTofu []byte - withListingConvert bool - withSessionIds []string - withWorkerId string - withDbOpts []db.Option - withWorkerStateDelay time.Duration - withDeadWorkerConnCloseMinGrace time.Duration + withLimit int + withOrderByCreateTime db.OrderBy + withScopeIds []string + withUserId string + withExpirationTime *timestamp.Timestamp + withTestTofu []byte + withListingConvert bool + withSessionIds []string + withDbOpts []db.Option + withWorkerStateDelay time.Duration } func getDefaultOptions() options { @@ -100,13 +98,6 @@ func withListingConvert(withListingConvert bool) Option { } } -// WithWorkerId allows the specification of the worker id to use -func WithWorkerId(id string) Option { - return func(o *options) { - o.withWorkerId = id - } -} - // WithDbOpts passes through given DB options to the DB layer func WithDbOpts(opts ...db.Option) Option { return func(o *options) { diff --git a/internal/session/options_test.go b/internal/session/options_test.go index 890d4c390..a32c08772 100644 --- a/internal/session/options_test.go +++ b/internal/session/options_test.go @@ -75,11 +75,4 @@ func Test_GetOpts(t *testing.T) { testOpts.withSessionIds = []string{"s_1", "s_2", "s_3"} assert.Equal(opts, testOpts) }) - t.Run("WithWorkerId", func(t *testing.T) { - assert := assert.New(t) - opts := getOpts(WithWorkerId("worker1")) - testOpts := getDefaultOptions() - testOpts.withWorkerId = "worker1" - assert.Equal(opts, testOpts) - }) } diff --git a/internal/session/query.go b/internal/session/query.go index 919980c55..8ede00ad8 100644 --- a/internal/session/query.go +++ b/internal/session/query.go @@ -324,7 +324,7 @@ where closeConnectionsForDeadServersCte = ` with dead_workers (worker_id, last_update_time) as ( - select private_id, update_time + select public_id, update_time from server_worker where update_time < wt_sub_seconds_from_now(@grace_period_seconds) ), @@ -345,6 +345,16 @@ where order by closed_connections.worker_id; ` + // closeWorkerlessConnections closes any open connections which has the + // worker id set to null. + closeWorkerlessConnections = ` + update session_connection + set closed_reason = 'system error' + where worker_id is null + and closed_reason is null + returning public_id; +` + orphanedConnectionsCte = ` -- Find connections that are not closed so we can reference those IDs with diff --git a/internal/session/repository.go b/internal/session/repository.go index 3e0948c30..7ded1414e 100644 --- a/internal/session/repository.go +++ b/internal/session/repository.go @@ -110,7 +110,6 @@ func (r *Repository) convertToSessions(ctx context.Context, sessionList []*sessi PublicId: sv.PublicId, UserId: sv.UserId, HostId: sv.HostId, - WorkerId: sv.WorkerId, TargetId: sv.TargetId, HostSetId: sv.HostSetId, AuthTokenId: sv.AuthTokenId, diff --git a/internal/session/repository_connection.go b/internal/session/repository_connection.go index 696e62fe8..6c8c6c4a8 100644 --- a/internal/session/repository_connection.go +++ b/internal/session/repository_connection.go @@ -345,59 +345,6 @@ func (r *ConnectionRepository) DeleteConnection(ctx context.Context, publicId st return rowsDeleted, nil } -type CloseConnectionsForDeadWorkersResult struct { - WorkerId string - LastUpdateTime time.Time - NumberConnectionsClosed int -} - -// CloseConnectionsForDeadWorkers will run -// closeConnectionsForDeadServersCte to look for connections that -// should be marked because they are on a worker that is no longer -// sending status updates to the controller(s). -// -// The only input to the method is the grace period, in seconds. -func (r *ConnectionRepository) CloseConnectionsForDeadWorkers(ctx context.Context, gracePeriod time.Duration) ([]CloseConnectionsForDeadWorkersResult, error) { - const op = "session.(ConnectionRepository).CloseConnectionsForDeadWorkers" - if gracePeriod < deadWorkerConnCloseMinGrace { - return nil, errors.New(ctx, - errors.InvalidParameter, op, fmt.Sprintf("gracePeriod must be at least %s", deadWorkerConnCloseMinGrace)) - } - - args := []interface{}{ - sql.Named("grace_period_seconds", gracePeriod.Seconds()), - } - results := make([]CloseConnectionsForDeadWorkersResult, 0) - _, err := r.writer.DoTx( - ctx, - db.StdRetryCnt, - db.ExpBackoff{}, - func(reader db.Reader, w db.Writer) error { - rows, err := w.Query(ctx, closeConnectionsForDeadServersCte, args) - if err != nil { - return errors.Wrap(ctx, err, op) - } - defer rows.Close() - - for rows.Next() { - var result CloseConnectionsForDeadWorkersResult - if err := w.ScanRows(ctx, rows, &result); err != nil { - return errors.Wrap(ctx, err, op) - } - - results = append(results, result) - } - - return nil - }, - ) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - - return results, nil -} - // closeOrphanedConnections looks for connections that are still active, but where not reported by the worker. func (r *ConnectionRepository) closeOrphanedConnections(ctx context.Context, workerId string, reportedConnections []string) ([]string, error) { const op = "session.(ConnectionRepository).closeOrphanedConnections" diff --git a/internal/session/repository_connection_test.go b/internal/session/repository_connection_test.go index 454a7ff1d..ba5d38029 100644 --- a/internal/session/repository_connection_test.go +++ b/internal/session/repository_connection_test.go @@ -6,9 +6,6 @@ import ( "testing" "time" - "github.com/hashicorp/boundary/internal/servers" - "github.com/hashicorp/boundary/internal/servers/store" - "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" @@ -150,9 +147,8 @@ func TestRepository_ConnectConnection(t *testing.T) { setupFn := func() ConnectWith { s := TestDefaultSession(t, conn, wrapper, iamRepo) - srv := TestWorker(t, conn, wrapper) tofu := TestTofu(t) - _, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, tofu) + _, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, tofu) require.NoError(t, err) c := TestConnection(t, conn, s.PublicId, "127.0.0.1", 22, "127.0.0.1", 2222, "127.0.0.1") return ConnectWith{ @@ -362,20 +358,20 @@ func TestRepository_orphanedConnections(t *testing.T) { // Create two "workers". One will remain untouched while the other "goes // away and comes back" (worker 2). - worker1 := TestWorker(t, conn, wrapper, WithWorkerId("worker1")) - worker2 := TestWorker(t, conn, wrapper, WithWorkerId("worker2")) + worker1 := TestWorker(t, conn, wrapper) + worker2 := TestWorker(t, conn, wrapper) // Create a few sessions on each, activate, and authorize a connection var connIds []string var worker1ConnIds []string var worker2ConnIds []string for i := 0; i < numConns; i++ { - serverId := worker1.PrivateId + serverId := worker1.PublicId if i%2 == 0 { - serverId = worker2.PrivateId + serverId = worker2.PublicId } - sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithWorkerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) - sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, []byte("foo")) + sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithDbOpts(db.WithSkipVetForWrite(true))) + sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, []byte("foo")) require.NoError(err) c, cs, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), serverId) require.NoError(err) @@ -421,7 +417,7 @@ func TestRepository_orphanedConnections(t *testing.T) { // all connection IDs for worker 1 should be showing as non-closed, and // the ones for worker 2 not advertised should be closed. shouldStayOpen := worker2ConnIds[0:2] - found, err := connRepo.closeOrphanedConnections(ctx, worker2.GetPrivateId(), shouldStayOpen) + found, err := connRepo.closeOrphanedConnections(ctx, worker2.GetPublicId(), shouldStayOpen) require.NoError(err) fmt.Printf("shouldstate: %v\nfound: %v\n", shouldStayOpen, found) require.Equal(4, len(found)) @@ -435,293 +431,12 @@ func TestRepository_orphanedConnections(t *testing.T) { // Now, advertise none of the connection IDs for worker 2. This is mainly to // test that handling the case where we do not include IDs works properly as // it changes the where clause. - found, err = connRepo.closeOrphanedConnections(ctx, worker1.GetPrivateId(), nil) + found, err = connRepo.closeOrphanedConnections(ctx, worker1.GetPublicId(), nil) require.NoError(err) assert.Equal(6, len(found)) assert.ElementsMatch(found, worker1ConnIds) } -func TestRepository_CloseConnectionsForDeadWorkers(t *testing.T) { - t.Parallel() - ctx := context.Background() - require := require.New(t) - conn, _ := db.TestSetup(t, "postgres") - rw := db.New(conn) - wrapper := db.TestWrapper(t) - iamRepo := iam.TestRepo(t, conn, wrapper) - kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms) - require.NoError(err) - deadWorkerConnCloseMinGrace := 15 * time.Second - connRepo, err := NewConnectionRepository(ctx, rw, rw, kms) - require.NoError(err) - serversRepo, err := servers.NewRepository(rw, rw, kms) - require.NoError(err) - - // connection count = 6 * states(authorized, connected, closed = 3) * servers_with_open_connections(3) - numConns := 54 - - // Create four "workers". This is similar to the setup in - // TestRepository_CloseDeadConnectionsOnWorker, but a bit more complex; - // firstly, the last worker will have no connections at all, and we will be - // closing the others in stages to test multiple servers being closed at - // once. - worker1 := TestWorker(t, conn, wrapper, WithWorkerId("worker1")) - worker2 := TestWorker(t, conn, wrapper, WithWorkerId("worker2")) - worker3 := TestWorker(t, conn, wrapper, WithWorkerId("worker3")) - worker4 := TestWorker(t, conn, wrapper, WithWorkerId("worker4")) - - // Create sessions on the first three, activate, and authorize connections - var worker1ConnIds, worker2ConnIds, worker3ConnIds []string - for i := 0; i < numConns; i++ { - var serverId string - if i%3 == 0 { - serverId = worker1.PrivateId - } else if i%3 == 1 { - serverId = worker2.PrivateId - } else { - serverId = worker3.PrivateId - } - sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithWorkerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) - sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, []byte("foo")) - require.NoError(err) - c, cs, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), serverId) - require.NoError(err) - require.Len(cs, 1) - require.Equal(StatusAuthorized, cs[0].Status) - if i%3 == 0 { - worker1ConnIds = append(worker1ConnIds, c.GetPublicId()) - } else if i%3 == 1 { - worker2ConnIds = append(worker2ConnIds, c.GetPublicId()) - } else { - worker3ConnIds = append(worker3ConnIds, c.GetPublicId()) - } - } - - // Mark a third of the connections connected, a third closed, and leave the - // others authorized. This is just to ensure we have a spread when we test it - // out. - for i, connId := range func() []string { - var s []string - s = append(s, worker1ConnIds...) - s = append(s, worker2ConnIds...) - s = append(s, worker3ConnIds...) - return s - }() { - if i%3 == 0 { - _, cs, err := connRepo.ConnectConnection(ctx, ConnectWith{ - ConnectionId: connId, - ClientTcpAddress: "127.0.0.1", - ClientTcpPort: 22, - EndpointTcpAddress: "127.0.0.1", - EndpointTcpPort: 22, - UserClientIp: "127.0.0.1", - }) - require.NoError(err) - require.Len(cs, 2) - var foundAuthorized, foundConnected bool - for _, status := range cs { - if status.Status == StatusAuthorized { - foundAuthorized = true - } - if status.Status == StatusConnected { - foundConnected = true - } - } - require.True(foundAuthorized) - require.True(foundConnected) - } else if i%3 == 1 { - resp, err := connRepo.closeConnections(ctx, []CloseWith{ - { - ConnectionId: connId, - ClosedReason: ConnectionCanceled, - }, - }) - require.NoError(err) - require.Len(resp, 1) - cs := resp[0].ConnectionStates - require.Len(cs, 2) - var foundAuthorized, foundClosed bool - for _, status := range cs { - if status.Status == StatusAuthorized { - foundAuthorized = true - } - if status.Status == StatusClosed { - foundClosed = true - } - } - require.True(foundAuthorized) - require.True(foundClosed) - } - } - - // updateWorker is a helper for updating the update time for our - // servers. The controller is read back so that we can reference - // the most up-to-date fields. - updateWorker := func(t *testing.T, w *store.Worker) *store.Worker { - t.Helper() - _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, w) - require.NoError(err) - require.Equal(1, rowsUpdated) - servers, err := serversRepo.ListWorkers(ctx) - require.NoError(err) - for _, server := range servers { - if server.PrivateId == w.PrivateId { - return server - } - } - - require.FailNowf("server %q not found after updating", w.PrivateId) - // Looks weird but needed to build, as we fail in testify instead - // of returning an error - return nil - } - - // requireConnectionStatus is a helper expecting all connections on a worker - // to be closed. - requireConnectionStatus := func(t *testing.T, connIds []string, expectAllClosed bool) { - t.Helper() - - var conns []*Connection - require.NoError(repo.list(ctx, &conns, "", nil)) - for i, connId := range connIds { - var expected ConnectionStatus - switch { - case expectAllClosed: - expected = StatusClosed - - case i%3 == 0: - expected = StatusConnected - - case i%3 == 1: - expected = StatusClosed - - case i%3 == 2: - expected = StatusAuthorized - } - - _, states, err := connRepo.LookupConnection(ctx, connId) - require.NoError(err) - require.Equal(expected, states[0].Status, "expected latest status for %q (index %d) to be %v", connId, i, expected) - } - } - - // We need this helper to fix the zone on protobuf timestamps - // versus what gets reported in the - // CloseConnectionsForDeadWorkersResult. - timestampPbAsUTC := func(t *testing.T, tm time.Time) time.Time { - t.Helper() - // utcLoc, err := time.LoadLocation("Etc/UTC") - // require.NoError(err) - return tm.In(time.Local) - } - - // Now try some scenarios. - { - // First, test the error/validation case. - result, err := connRepo.CloseConnectionsForDeadWorkers(ctx, -1) - require.Equal(err, errors.E(ctx, - errors.WithCode(errors.InvalidParameter), - errors.WithOp("session.(ConnectionRepository).CloseConnectionsForDeadWorkers"), - errors.WithMsg(fmt.Sprintf("gracePeriod must be at least %s", deadWorkerConnCloseMinGrace)), - )) - require.Nil(result) - } - - { - // Now, try the basis, or where all workers are reporting in. - worker1 = updateWorker(t, worker1) - worker2 = updateWorker(t, worker2) - worker3 = updateWorker(t, worker3) - updateWorker(t, worker4) // no re-assignment here because we never reference the server again - - result, err := connRepo.CloseConnectionsForDeadWorkers(ctx, deadWorkerConnCloseMinGrace) - require.NoError(err) - require.Empty(result) - // Expect appropriate split connection state on worker1 - requireConnectionStatus(t, worker1ConnIds, false) - // Expect appropriate split connection state on worker2 - requireConnectionStatus(t, worker2ConnIds, false) - // Expect appropriate split connection state on worker3 - requireConnectionStatus(t, worker3ConnIds, false) - } - - { - // Now try a zero case - similar to the basis, but only in that no results - // are expected to be returned for workers with no connections, even if - // they are dead. Here, the server with no connections is worker #4. - time.Sleep(deadWorkerConnCloseMinGrace) - worker1 = updateWorker(t, worker1) - worker2 = updateWorker(t, worker2) - worker3 = updateWorker(t, worker3) - - result, err := connRepo.CloseConnectionsForDeadWorkers(ctx, deadWorkerConnCloseMinGrace) - require.NoError(err) - require.Empty(result) - // Expect appropriate split connection state on worker1 - requireConnectionStatus(t, worker1ConnIds, false) - // Expect appropriate split connection state on worker2 - requireConnectionStatus(t, worker2ConnIds, false) - // Expect appropriate split connection state on worker3 - requireConnectionStatus(t, worker3ConnIds, false) - } - - { - // The first induction is letting the first worker "die" by not updating it - // too. All of its authorized and connected connections should be dead. - time.Sleep(deadWorkerConnCloseMinGrace) - worker2 = updateWorker(t, worker2) - worker3 = updateWorker(t, worker3) - - result, err := connRepo.CloseConnectionsForDeadWorkers(ctx, deadWorkerConnCloseMinGrace) - require.NoError(err) - // Assert that we have one result with the appropriate ID and - // number of connections closed. Due to how things are - require.Equal([]CloseConnectionsForDeadWorkersResult{ - { - WorkerId: worker1.PrivateId, - LastUpdateTime: timestampPbAsUTC(t, worker1.UpdateTime.AsTime()), - NumberConnectionsClosed: 12, // 18 per server, with 6 closed already - }, - }, result) - // Expect all connections closed on worker1 - requireConnectionStatus(t, worker1ConnIds, true) - // Expect appropriate split connection state on worker2 - requireConnectionStatus(t, worker2ConnIds, false) - // Expect appropriate split connection state on worker3 - requireConnectionStatus(t, worker3ConnIds, false) - } - - { - // The final case is having the other two workers die. After - // this, we should have all connections closed with the - // appropriate message from the next two servers acted on. - time.Sleep(deadWorkerConnCloseMinGrace) - - result, err := connRepo.CloseConnectionsForDeadWorkers(ctx, deadWorkerConnCloseMinGrace) - require.NoError(err) - // Assert that we have one result with the appropriate ID and number of connections closed. - require.Equal([]CloseConnectionsForDeadWorkersResult{ - { - WorkerId: worker2.PrivateId, - LastUpdateTime: timestampPbAsUTC(t, worker2.UpdateTime.AsTime()), - NumberConnectionsClosed: 12, // 18 per server, with 6 closed already - }, - { - WorkerId: worker3.PrivateId, - LastUpdateTime: timestampPbAsUTC(t, worker3.UpdateTime.AsTime()), - NumberConnectionsClosed: 12, // 18 per server, with 6 closed already - }, - }, result) - // Expect all connections closed on worker1 - requireConnectionStatus(t, worker1ConnIds, true) - // Expect all connections closed on worker2 - requireConnectionStatus(t, worker2ConnIds, true) - // Expect all connections closed on worker3 - requireConnectionStatus(t, worker3ConnIds, true) - } -} - func TestRepository_CloseConnections(t *testing.T) { t.Parallel() ctx := context.Background() @@ -737,9 +452,8 @@ func TestRepository_CloseConnections(t *testing.T) { setupFn := func(cnt int) []CloseWith { s := TestDefaultSession(t, conn, wrapper, iamRepo) - srv := TestWorker(t, conn, wrapper) tofu := TestTofu(t) - s, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, tofu) + s, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, tofu) require.NoError(t, err) cw := make([]CloseWith, 0, cnt) for i := 0; i < cnt; i++ { diff --git a/internal/session/repository_session.go b/internal/session/repository_session.go index 53448a806..bc89c7183 100644 --- a/internal/session/repository_session.go +++ b/internal/session/repository_session.go @@ -49,9 +49,6 @@ func (r *Repository) CreateSession(ctx context.Context, sessionWrapper wrapping. if newSession.ScopeId == "" { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") } - if newSession.WorkerId != "" { - return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "worker id is not empty") - } if newSession.CtTofuToken != nil { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "ct is not empty") } @@ -248,7 +245,7 @@ func (r *Repository) FetchAuthzProtectedEntitiesByScope(ctx context.Context, sco return sessionsMap, nil } -// ListSessions lists sessions. Supports the WithLimit, WithScopeId, WithSessionIds, and WithWorkerId options. +// ListSessions lists sessions. Supports the WithLimit, WithScopeId, and WithSessionIds options. func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Session, error) { const op = "session.(Repository).ListSessions" opts := getOpts(opt...) @@ -289,11 +286,6 @@ func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Sessio where = append(where, fmt.Sprintf("s.public_id in (%s)", strings.Join(idsInClause, ","))) } - if opts.withWorkerId != "" { - inClauseCnt += 1 - where, args = append(where, fmt.Sprintf("worker_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.withWorkerId)) - } - var limit string switch { case opts.withLimit < 0: // any negative number signals unlimited results @@ -492,7 +484,7 @@ func (r *Repository) sessionAuthzSummary(ctx context.Context, sessionId string) // activated. States are ordered by start time descending. Returns an // InvalidSessionState error code if a connection cannot be made because the session // was canceled or terminated. -func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sessionVersion uint32, workerId string, tofuToken []byte) (*Session, []*State, error) { +func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sessionVersion uint32, tofuToken []byte) (*Session, []*State, error) { const op = "session.(Repository).ActivateSession" if sessionId == "" { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing session id") @@ -500,9 +492,6 @@ func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sess if sessionVersion == 0 { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing version") } - if workerId == "" { - return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing worker id") - } if len(tofuToken) == 0 { return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing tofu token") } @@ -539,7 +528,6 @@ func (r *Repository) ActivateSession(ctx context.Context, sessionId string, sess } updatedSession.TofuToken = tofuToken - updatedSession.WorkerId = workerId if err := updatedSession.encrypt(ctx, databaseWrapper); err != nil { return errors.Wrap(ctx, err, op) } diff --git a/internal/session/repository_session_test.go b/internal/session/repository_session_test.go index 3f0aba921..0dd8d440c 100644 --- a/internal/session/repository_session_test.go +++ b/internal/session/repository_session_test.go @@ -2,7 +2,6 @@ package session import ( "context" - "fmt" "testing" "time" @@ -210,32 +209,6 @@ func TestRepository_ListSession(t *testing.T) { assert.Equal(StatusActive, got[0].States[0].Status) assert.Equal(StatusPending, got[0].States[1].Status) }) - t.Run("withServerId", func(t *testing.T) { - assert, require := assert.New(t), require.New(t) - db.TestDeleteWhere(t, conn, func() interface{} { i := AllocSession(); return &i }(), "1=1") - for i := 0; i < 6; i++ { - if i%2 == 0 { - TestWorker(t, conn, wrapper, WithWorkerId(fmt.Sprintf("server-%d", i/2))) - } - _ = TestSession(t, conn, wrapper, composedOf, - WithWorkerId(fmt.Sprintf("server-%d", i/2)), - WithDbOpts(db.WithSkipVetForWrite(true)), - ) - } - got, err := repo.ListSessions(context.Background()) - require.NoError(err) - assert.Equal(6, len(got)) - - for i := 0; i < 3; i++ { - serverId := fmt.Sprintf("server-%d", i) - got, err = repo.ListSessions(context.Background(), WithWorkerId(serverId)) - require.NoError(err) - assert.Equal(2, len(got)) - for _, item := range got { - assert.Equal(serverId, item.WorkerId) - } - } - }) } func TestRepository_ListSessions_Multiple_Scopes(t *testing.T) { @@ -574,7 +547,6 @@ func TestRepository_transitionState(t *testing.T) { repo, err := NewRepository(rw, rw, kms) require.NoError(t, err) tofu := TestTofu(t) - srv := TestWorker(t, conn, wrapper) tests := []struct { name string @@ -642,7 +614,7 @@ func TestRepository_transitionState(t *testing.T) { var ss []*State var err error if status == StatusActive { - s, ss, err = repo.ActivateSession(context.Background(), id, version, srv.PrivateId, tofu) + s, ss, err = repo.ActivateSession(context.Background(), id, version, tofu) } else { s, ss, err = repo.updateState(context.Background(), id, version, status) } @@ -681,9 +653,8 @@ func TestRepository_TerminateCompletedSessions(t *testing.T) { composedOf.ExpirationTime = ×tamp.Timestamp{Timestamp: exp} s := TestSession(t, conn, wrapper, composedOf) - srv := TestWorker(t, conn, wrapper) tofu := TestTofu(t) - s, _, err = repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, tofu) + s, _, err = repo.ActivateSession(context.Background(), s.PublicId, s.Version, tofu) require.NoError(t, err) c := TestConnection(t, conn, s.PublicId, "127.0.0.1", 22, "127.0.0.1", 222, "127.0.0.1") if !leaveOpen { @@ -1227,7 +1198,6 @@ func TestRepository_ActivateSession(t *testing.T) { kms := kms.TestKms(t, conn, wrapper) repo, err := NewRepository(rw, rw, kms) require.NoError(t, err) - worker := TestWorker(t, conn, wrapper) tofu := TestTofu(t) tests := []struct { @@ -1247,7 +1217,7 @@ func TestRepository_ActivateSession(t *testing.T) { name: "already-active", session: func() *Session { s := TestDefaultSession(t, conn, wrapper, iamRepo) - activeSession, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, worker.PrivateId, tofu) + activeSession, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, tofu) require.NoError(t, err) return activeSession }(), @@ -1310,7 +1280,7 @@ func TestRepository_ActivateSession(t *testing.T) { default: version = tt.session.Version } - s, ss, err := repo.ActivateSession(context.Background(), id, version, worker.PrivateId, tofu) + s, ss, err := repo.ActivateSession(context.Background(), id, version, tofu) if tt.wantErr { require.Error(err) assert.Truef(errors.Match(errors.T(tt.wantIsError), err), "unexpected error %s", err.Error()) @@ -1326,17 +1296,17 @@ func TestRepository_ActivateSession(t *testing.T) { t.Run("already active", func(t *testing.T) { assert, require := assert.New(t), require.New(t) session := TestDefaultSession(t, conn, wrapper, iamRepo) - s, ss, err := repo.ActivateSession(context.Background(), session.PublicId, 1, worker.PrivateId, tofu) + s, ss, err := repo.ActivateSession(context.Background(), session.PublicId, 1, tofu) require.NoError(err) require.NotNil(s) require.NotNil(ss) assert.Equal(2, len(ss)) assert.Equal(StatusActive, ss[0].Status) - _, _, err = repo.ActivateSession(context.Background(), session.PublicId, 1, worker.PrivateId, tofu) + _, _, err = repo.ActivateSession(context.Background(), session.PublicId, 1, tofu) require.Error(err) - _, _, err = repo.ActivateSession(context.Background(), session.PublicId, 2, worker.PrivateId, tofu) + _, _, err = repo.ActivateSession(context.Background(), session.PublicId, 2, tofu) require.Error(err) }) } diff --git a/internal/session/service_authorize_connection_test.go b/internal/session/service_authorize_connection_test.go index a6f3ad381..b6d88580c 100644 --- a/internal/session/service_authorize_connection_test.go +++ b/internal/session/service_authorize_connection_test.go @@ -36,9 +36,9 @@ func TestService_AuthorizeConnection(t *testing.T) { } s := TestSession(t, conn, wrapper, composedOf) srv := TestWorker(t, conn, wrapper) - testServer = srv.PrivateId + testServer = srv.PublicId tofu := TestTofu(t) - _, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, tofu) + _, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, tofu) require.NoError(t, err) return s } diff --git a/internal/session/service_close_connections_test.go b/internal/session/service_close_connections_test.go index de0c0f007..d54fafa92 100644 --- a/internal/session/service_close_connections_test.go +++ b/internal/session/service_close_connections_test.go @@ -30,9 +30,8 @@ func TestServiceCloseConnections(t *testing.T) { setupFn := func(cnt int, addtlConn int) sessionAndCloseWiths { s := TestDefaultSession(t, conn, wrapper, iamRepo) - srv := TestWorker(t, conn, wrapper) tofu := TestTofu(t) - s, _, err = repo.ActivateSession(context.Background(), s.PublicId, s.Version, srv.PrivateId, tofu) + s, _, err = repo.ActivateSession(context.Background(), s.PublicId, s.Version, tofu) require.NoError(t, err) require.NoError(t, err) diff --git a/internal/session/service_worker_status_report_test.go b/internal/session/service_worker_status_report_test.go index 64a29c252..52ed0bb64 100644 --- a/internal/session/service_worker_status_report_test.go +++ b/internal/session/service_worker_status_report_test.go @@ -33,8 +33,8 @@ func TestWorkerStatusReport(t *testing.T) { Address: "127.0.0.1", }) serverRepo.UpsertWorker(ctx, &store.Worker{ - PrivateId: "test_worker1", - Address: "127.0.0.1", + PublicId: "test_worker1", + Address: "127.0.0.1", }) repo, err := session.NewRepository(rw, rw, kms) @@ -91,11 +91,11 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, tofu) require.NoError(t, err) require.NoError(t, err) - _, _, err = connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PrivateId) + _, _, err = connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PublicId) require.NoError(t, err) _, err = repo.CancelSession(ctx, sess.PublicId, sess.Version) @@ -123,11 +123,11 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, tofu) require.NoError(t, err) require.NoError(t, err) - connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PrivateId) + connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PublicId) require.NoError(t, err) return testCase{ worker: worker, @@ -157,9 +157,9 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, tofu) require.NoError(t, err) - connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PrivateId) + connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PublicId) require.NoError(t, err) _, err = repo.CancelSession(ctx, sess.PublicId, sess.Version) require.NoError(t, err) @@ -197,9 +197,9 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, tofu) require.NoError(t, err) - connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PrivateId) + connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PublicId) require.NoError(t, err) _, err = repo.CancelSession(ctx, sess.PublicId, sess.Version) require.NoError(t, err) @@ -215,9 +215,9 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu2 := session.TestTofu(t) - sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker.PrivateId, tofu2) + sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, tofu2) require.NoError(t, err) - connection2, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PrivateId) + connection2, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PublicId) require.NoError(t, err) _, err = repo.CancelSession(ctx, sess2.PublicId, sess2.Version) require.NoError(t, err) @@ -264,9 +264,9 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, tofu) require.NoError(t, err) - connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PrivateId) + connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PublicId) require.NoError(t, err) sess2 := session.TestSession(t, conn, wrapper, session.ComposedOf{ @@ -280,9 +280,9 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu2 := session.TestTofu(t) - sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker.PrivateId, tofu2) + sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, tofu2) require.NoError(t, err) - connection2, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PrivateId) + connection2, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PublicId) require.NoError(t, err) require.NotEqual(t, connection.PublicId, connection2.PublicId) @@ -315,9 +315,9 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu := session.TestTofu(t) - sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, tofu) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, tofu) require.NoError(t, err) - connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PrivateId) + connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker.PublicId) require.NoError(t, err) _, err = repo.CancelSession(ctx, sess.PublicId, sess.Version) require.NoError(t, err) @@ -333,11 +333,11 @@ func TestWorkerStatusReport(t *testing.T) { ConnectionLimit: 10, }) tofu2 := session.TestTofu(t) - sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, worker.PrivateId, tofu2) + sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, tofu2) require.NoError(t, err) - connection2, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PrivateId) + connection2, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PublicId) require.NoError(t, err) - connection3, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PrivateId) + connection3, _, err := connRepo.AuthorizeConnection(ctx, sess2.PublicId, worker.PublicId) require.NoError(t, err) _, err = repo.CancelSession(ctx, sess2.PublicId, sess2.Version) require.NoError(t, err) @@ -378,7 +378,7 @@ func TestWorkerStatusReport(t *testing.T) { tc := tt.caseFn(t) - got, err := session.WorkerStatusReport(ctx, repo, connRepo, tc.worker.PrivateId, tc.req) + got, err := session.WorkerStatusReport(ctx, repo, connRepo, tc.worker.PublicId, tc.req) require.NoError(err) assert.ElementsMatch(tc.want, got) for _, dc := range tc.orphanedConnections { diff --git a/internal/session/session.go b/internal/session/session.go index 7818aacc8..2c9e51579 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -64,8 +64,6 @@ type Session struct { UserId string `json:"user_id,omitempty" gorm:"default:null"` // HostId of the session HostId string `json:"host_id,omitempty" gorm:"default:null"` - // WorkerId that proxied the session - WorkerId string `json:"worker_id,omitempty" gorm:"default:null"` // TargetId for the session TargetId string `json:"target_id,omitempty" gorm:"default:null"` // HostSetId for the session @@ -169,7 +167,6 @@ func (s *Session) Clone() interface{} { PublicId: s.PublicId, UserId: s.UserId, HostId: s.HostId, - WorkerId: s.WorkerId, TargetId: s.TargetId, HostSetId: s.HostSetId, AuthTokenId: s.AuthTokenId, @@ -334,9 +331,6 @@ func (s *Session) validateNewSession() error { if s.TerminationReason != "" { return errors.NewDeprecated(errors.InvalidParameter, op, "termination reason must be empty") } - if s.WorkerId != "" { - return errors.NewDeprecated(errors.InvalidParameter, op, "worker id must be empty") - } if s.TofuToken != nil { return errors.NewDeprecated(errors.InvalidParameter, op, "tofu token must be empty") } @@ -443,7 +437,6 @@ type sessionListView struct { PublicId string `json:"public_id,omitempty" gorm:"primary_key"` UserId string `json:"user_id,omitempty" gorm:"default:null"` HostId string `json:"host_id,omitempty" gorm:"default:null"` - WorkerId string `json:"worker_id,omitempty" gorm:"default:null"` TargetId string `json:"target_id,omitempty" gorm:"default:null"` HostSetId string `json:"host_set_id,omitempty" gorm:"default:null"` AuthTokenId string `json:"auth_token_id,omitempty" gorm:"default:null"` diff --git a/internal/session/session_cleanup_job.go b/internal/session/session_cleanup_job.go index 100c58900..3947cc6b2 100644 --- a/internal/session/session_cleanup_job.go +++ b/internal/session/session_cleanup_job.go @@ -107,7 +107,7 @@ func (j *sessionConnectionCleanupJob) Run(ctx context.Context) error { for _, result := range results { event.WriteError(ctx, op, stderrors.New("worker has not reported status within acceptable grace period, all connections closed"), event.WithInfo( - "private_id", result.WorkerId, + "public_id", result.WorkerId, "update_time", result.LastUpdateTime, "grace_period_seconds", j.gracePeriod.Seconds(), "number_connections_closed", result.NumberConnectionsClosed, @@ -116,9 +116,48 @@ func (j *sessionConnectionCleanupJob) Run(ctx context.Context) error { j.totalClosed += result.NumberConnectionsClosed } + { + count, err := j.closeWorkerlessConnections(ctx) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if count > 0 { + event.WriteError(ctx, op, stderrors.New("connections with no worker closed"), + event.WithInfo( + "number_connections_closed", count, + )) + j.totalClosed += count + } + } + return nil } +// closeWorkerlessConnections will close all connections which do not have a +// worker id associated with them. +func (j *sessionConnectionCleanupJob) closeWorkerlessConnections(ctx context.Context) (int, error) { + const op = "session.(sessionConnectionCleanupJob).closeWorkerlessConnections" + count := 0 + _, err := j.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(reader db.Reader, w db.Writer) error { + var err error + count, err = w.Exec(ctx, closeWorkerlessConnections, []interface{}{}) + if err != nil { + return errors.Wrap(ctx, err, op) + } + return nil + }, + ) + if err != nil { + return count, errors.Wrap(ctx, err, op) + } + + return count, nil +} + // closeConnectionsForDeadWorkers will run // closeConnectionsForDeadServersCte to look for connections that // should be marked because they are on a server that is no longer diff --git a/internal/session/session_cleanup_job_test.go b/internal/session/session_cleanup_job_test.go index b9a7886e4..5b63c37ce 100644 --- a/internal/session/session_cleanup_job_test.go +++ b/internal/session/session_cleanup_job_test.go @@ -45,19 +45,19 @@ func TestSessionConnectionCleanupJob(t *testing.T) { // Create two "workers". One will remain untouched while the other "goes // away and comes back" (worker 2). - worker1 := TestWorker(t, conn, wrapper, WithWorkerId("worker1")) - worker2 := TestWorker(t, conn, wrapper, WithWorkerId("worker2")) + worker1 := TestWorker(t, conn, wrapper) + worker2 := TestWorker(t, conn, wrapper) // Create a few sessions on each, activate, and authorize a connection var connIds []string connIdsByWorker := make(map[string][]string) for i := 0; i < numConns; i++ { - serverId := worker1.PrivateId + serverId := worker1.PublicId if i%2 == 0 { - serverId = worker2.PrivateId + serverId = worker2.PublicId } - sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithWorkerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) - sess, _, err = sessionRepo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, []byte("foo")) + sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithDbOpts(db.WithSkipVetForWrite(true))) + sess, _, err = sessionRepo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, []byte("foo")) require.NoError(err) c, cs, _, err := AuthorizeConnection(ctx, sessionRepo, connectionRepo, sess.GetPublicId(), serverId) require.NoError(err) @@ -65,9 +65,9 @@ func TestSessionConnectionCleanupJob(t *testing.T) { require.Equal(StatusAuthorized, cs[0].Status) connIds = append(connIds, c.GetPublicId()) if i%2 == 0 { - connIdsByWorker[worker2.PrivateId] = append(connIdsByWorker[worker2.PrivateId], c.GetPublicId()) + connIdsByWorker[worker2.PublicId] = append(connIdsByWorker[worker2.PublicId], c.GetPublicId()) } else { - connIdsByWorker[worker1.PrivateId] = append(connIdsByWorker[worker1.PrivateId], c.GetPublicId()) + connIdsByWorker[worker1.PublicId] = append(connIdsByWorker[worker1.PublicId], c.GetPublicId()) } } @@ -109,7 +109,7 @@ func TestSessionConnectionCleanupJob(t *testing.T) { // Push an upsert to the first worker so that its status has been // updated. - _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, worker1, []servers.Option{}...) + _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, worker1) require.NoError(err) require.Equal(1, rowsUpdated) @@ -136,9 +136,9 @@ func TestSessionConnectionCleanupJob(t *testing.T) { } // Assert that all connections on the second worker are closed - assertConnections(worker2.PrivateId, true) + assertConnections(worker2.PublicId, true) // Assert that all connections on the first worker are still open - assertConnections(worker1.PrivateId, false) + assertConnections(worker1.PublicId, false) } func TestSessionConnectionCleanupJobNewJobErr(t *testing.T) { @@ -197,24 +197,24 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // firstly, the last worker will have no connections at all, and we will be // closing the others in stages to test multiple servers being closed at // once. - worker1 := TestWorker(t, conn, wrapper, WithWorkerId("worker1")) - worker2 := TestWorker(t, conn, wrapper, WithWorkerId("worker2")) - worker3 := TestWorker(t, conn, wrapper, WithWorkerId("worker3")) - worker4 := TestWorker(t, conn, wrapper, WithWorkerId("worker4")) + worker1 := TestWorker(t, conn, wrapper) + worker2 := TestWorker(t, conn, wrapper) + worker3 := TestWorker(t, conn, wrapper) + worker4 := TestWorker(t, conn, wrapper) // Create sessions on the first three, activate, and authorize connections var worker1ConnIds, worker2ConnIds, worker3ConnIds []string for i := 0; i < numConns; i++ { var serverId string if i%3 == 0 { - serverId = worker1.PrivateId + serverId = worker1.PublicId } else if i%3 == 1 { - serverId = worker2.PrivateId + serverId = worker2.PublicId } else { - serverId = worker3.PrivateId + serverId = worker3.PublicId } - sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithWorkerId(serverId), WithDbOpts(db.WithSkipVetForWrite(true))) - sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, serverId, []byte("foo")) + sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithDbOpts(db.WithSkipVetForWrite(true))) + sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, []byte("foo")) require.NoError(err) c, cs, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), serverId) require.NoError(err) @@ -297,12 +297,12 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { servers, err := serversRepo.ListWorkers(ctx) require.NoError(err) for _, server := range servers { - if server.PrivateId == w.PrivateId { + if server.PublicId == w.PublicId { return server } } - require.FailNowf("server %q not found after updating", w.PrivateId) + require.FailNowf("server %q not found after updating", w.PublicId) // Looks weird but needed to build, as we fail in testify instead // of returning an error return nil @@ -399,7 +399,7 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // number of connections closed. Due to how things are require.Equal([]closeConnectionsForDeadWorkersResult{ { - WorkerId: worker1.PrivateId, + WorkerId: worker1.PublicId, LastUpdateTime: timestampPbAsUTC(t, worker1.UpdateTime.AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, @@ -421,14 +421,14 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { result, err := job.closeConnectionsForDeadWorkers(ctx, gracePeriod) require.NoError(err) // Assert that we have one result with the appropriate ID and number of connections closed. - require.Equal([]closeConnectionsForDeadWorkersResult{ + require.ElementsMatch([]closeConnectionsForDeadWorkersResult{ { - WorkerId: worker2.PrivateId, + WorkerId: worker2.PublicId, LastUpdateTime: timestampPbAsUTC(t, worker2.UpdateTime.AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, { - WorkerId: worker3.PrivateId, + WorkerId: worker3.PublicId, LastUpdateTime: timestampPbAsUTC(t, worker3.UpdateTime.AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, @@ -441,3 +441,95 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { requireConnectionStatus(t, worker3ConnIds, true) } } + +func TestCloseWorkerlessConnections(t *testing.T) { + t.Parallel() + ctx := context.Background() + require := require.New(t) + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, wrapper) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + require.NoError(err) + connRepo, err := NewConnectionRepository(ctx, rw, rw, kms) + require.NoError(err) + + job, err := newSessionConnectionCleanupJob(rw, time.Hour) + require.NoError(err) + + createConnection := func(workerId string) *Connection { + sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithDbOpts(db.WithSkipVetForWrite(true))) + sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, []byte("foo")) + require.NoError(err) + + conn, cs, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), workerId) + require.NoError(err) + require.Len(cs, 1) + require.Equal(StatusAuthorized, cs[0].Status) + return conn + } + + // Setup deleted worker connections + deletedWorker := TestWorker(t, conn, wrapper) + dActiveConn := createConnection(deletedWorker.GetPublicId()) + dClosedConn := createConnection(deletedWorker.GetPublicId()) + connRepo.closeConnections(ctx, []CloseWith{{ + ConnectionId: dClosedConn.PublicId, + BytesUp: 1, + BytesDown: 2, + ClosedReason: ConnectionClosedByUser, + }}) + _, err = rw.Delete(ctx, deletedWorker) + require.NoError(err) + + // Non deleted worker case + activeWorker := TestWorker(t, conn, wrapper) + activeConn := createConnection(activeWorker.GetPublicId()) + closedConn := createConnection(activeWorker.GetPublicId()) + connRepo.closeConnections(ctx, []CloseWith{{ + ConnectionId: closedConn.PublicId, + BytesUp: 1, + BytesDown: 2, + ClosedReason: ConnectionClosedByUser, + }}) + + _, st, err := connRepo.LookupConnection(ctx, dActiveConn.GetPublicId()) + require.NoError(err) + require.Equal(StatusAuthorized, st[0].Status) + + _, st, err = connRepo.LookupConnection(ctx, dClosedConn.GetPublicId()) + require.NoError(err) + require.Equal(StatusClosed, st[0].Status) + + _, st, err = connRepo.LookupConnection(ctx, activeConn.GetPublicId()) + require.NoError(err) + require.Equal(StatusAuthorized, st[0].Status) + + _, st, err = connRepo.LookupConnection(ctx, closedConn.GetPublicId()) + require.NoError(err) + require.Equal(StatusClosed, st[0].Status) + + // Run the job + numClosed, err := job.closeWorkerlessConnections(ctx) + require.NoError(err) + assert.Equal(t, 1, numClosed) + + // This is the only one that the job should have actually closed. + _, st, err = connRepo.LookupConnection(ctx, dActiveConn.GetPublicId()) + require.NoError(err) + require.Equal(StatusClosed, st[0].Status) + + _, st, err = connRepo.LookupConnection(ctx, dClosedConn.GetPublicId()) + require.NoError(err) + require.Equal(StatusClosed, st[0].Status) + + _, st, err = connRepo.LookupConnection(ctx, activeConn.GetPublicId()) + require.NoError(err) + require.Equal(StatusAuthorized, st[0].Status) + + _, st, err = connRepo.LookupConnection(ctx, closedConn.GetPublicId()) + require.NoError(err) + require.Equal(StatusClosed, st[0].Status) +} diff --git a/internal/session/testing.go b/internal/session/testing.go index ced1f58c5..21e04af94 100644 --- a/internal/session/testing.go +++ b/internal/session/testing.go @@ -90,8 +90,6 @@ func TestSession(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, c Composed _, certBytes, err := newCert(ctx, wrapper, c.UserId, id, []string{"127.0.0.1", "localhost"}, c.ExpirationTime.Timestamp.AsTime()) require.NoError(err) s.Certificate = certBytes - s.WorkerId = opts.withWorkerId - if len(s.TofuToken) != 0 { err = s.encrypt(ctx, wrapper) } @@ -190,18 +188,15 @@ func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Opti serversRepo, err := servers.NewRepository(rw, rw, kms) require.NoError(t, err) - opts := getOpts(opt...) - id := opts.withWorkerId - if id == "" { - id, err = uuid.GenerateUUID() - require.NoError(t, err) - id = "test-session-worker-" + id - } + id, err := uuid.GenerateUUID() + require.NoError(t, err) + id = "test_session_worker-" + id + name := "test-worker-" + id worker := &store.Worker{ - PrivateId: id, - Name: name, - Address: "127.0.0.1", + PublicId: id, + Name: name, + Address: "127.0.0.1", } _, _, err = serversRepo.UpsertWorker(context.Background(), worker) require.NoError(t, err) diff --git a/internal/tests/api/sessions/classification_test.go b/internal/tests/api/sessions/classification_test.go index 3136a44e9..8ee4e56d5 100644 --- a/internal/tests/api/sessions/classification_test.go +++ b/internal/tests/api/sessions/classification_test.go @@ -66,12 +66,7 @@ func TestSessions(t *testing.T) { EndTime: pbNow, }, }, - Status: "status", - WorkerInfo: []*pb.WorkerInfo{ - { - Address: "worker-address", - }, - }, + Status: "status", Certificate: []byte("certificate"), TerminationReason: "termination_reason", AuthorizedActions: []string{"action-1"}, @@ -119,12 +114,7 @@ func TestSessions(t *testing.T) { EndTime: pbNow, }, }, - Status: "status", - WorkerInfo: []*pb.WorkerInfo{ - { - Address: "worker-address", - }, - }, + Status: "status", Certificate: []byte("certificate"), TerminationReason: "termination_reason", AuthorizedActions: []string{"action-1"}, diff --git a/internal/tests/cluster/worker_tagging_test.go b/internal/tests/cluster/worker_tagging_test.go index 8c217b6e1..7c6132810 100644 --- a/internal/tests/cluster/worker_tagging_test.go +++ b/internal/tests/cluster/worker_tagging_test.go @@ -47,7 +47,7 @@ func TestWorkerTagging(t *testing.T) { // Worker 1 conf, err = config.DevWorker() require.NoError(t, err) - conf.Worker.Name = "w1" + conf.Worker.Name = "test_worker_1" conf.Worker.Tags = map[string][]string{ "region": {"east"}, "foo": {"bar"}, @@ -64,7 +64,7 @@ func TestWorkerTagging(t *testing.T) { // Worker 2 conf, err = config.DevWorker() require.NoError(t, err) - conf.Worker.Name = "w2" + conf.Worker.Name = "test_worker_2" conf.Worker.Tags = map[string][]string{ "region": {"west"}, "az": {"one", "two", "three"}, @@ -81,7 +81,7 @@ func TestWorkerTagging(t *testing.T) { // Worker 3 conf, err = config.DevWorker() require.NoError(t, err) - conf.Worker.Name = "w3" + conf.Worker.Name = "test_worker_3" conf.Worker.Tags = map[string][]string{ "region": {"west"}, "az": {"one", "three"}, @@ -109,12 +109,12 @@ func TestWorkerTagging(t *testing.T) { }, { name: "name and region", - filter: `"/name" matches "w[13]" and "west" in "/tags/region"`, + filter: `"/name" matches "test_worker_[13]" and "west" in "/tags/region"`, expWorkers: []string{w3Addr}, }, { name: "name and az", - filter: `"/name" matches "w[23]" and "three" in "/tags/az"`, + filter: `"/name" matches "test_worker_[23]" and "three" in "/tags/az"`, expWorkers: []string{w2Addr, w3Addr}, }, { diff --git a/internal/tests/helper/testing_helper.go b/internal/tests/helper/testing_helper.go index 4fab08943..05bacb66a 100644 --- a/internal/tests/helper/testing_helper.go +++ b/internal/tests/helper/testing_helper.go @@ -70,7 +70,7 @@ func NewTestSession( ) *TestSession { t.Helper() require := require.New(t) - sar, err := tcl.AuthorizeSession(ctx, "ttcp_1234567890") + sar, err := tcl.AuthorizeSession(ctx, targetId) require.NoError(err) require.NotNil(sar) diff --git a/sdk/pbs/controller/api/resources/sessions/session.pb.go b/sdk/pbs/controller/api/resources/sessions/session.pb.go index 9b50dd7fd..09e092cea 100644 --- a/sdk/pbs/controller/api/resources/sessions/session.pb.go +++ b/sdk/pbs/controller/api/resources/sessions/session.pb.go @@ -22,54 +22,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type WorkerInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The address of the worker. - Address string `protobuf:"bytes,10,opt,name=address,proto3" json:"address,omitempty" class:"public"` // @gotags: `class:"public"` -} - -func (x *WorkerInfo) Reset() { - *x = WorkerInfo{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WorkerInfo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WorkerInfo) ProtoMessage() {} - -func (x *WorkerInfo) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WorkerInfo.ProtoReflect.Descriptor instead. -func (*WorkerInfo) Descriptor() ([]byte, []int) { - return file_controller_api_resources_sessions_v1_session_proto_rawDescGZIP(), []int{0} -} - -func (x *WorkerInfo) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - type SessionState struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -86,7 +38,7 @@ type SessionState struct { func (x *SessionState) Reset() { *x = SessionState{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[1] + mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -99,7 +51,7 @@ func (x *SessionState) String() string { func (*SessionState) ProtoMessage() {} func (x *SessionState) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[1] + mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -112,7 +64,7 @@ func (x *SessionState) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionState.ProtoReflect.Descriptor instead. func (*SessionState) Descriptor() ([]byte, []int) { - return file_controller_api_resources_sessions_v1_session_proto_rawDescGZIP(), []int{1} + return file_controller_api_resources_sessions_v1_session_proto_rawDescGZIP(), []int{0} } func (x *SessionState) GetStatus() string { @@ -161,7 +113,7 @@ type Connection struct { func (x *Connection) Reset() { *x = Connection{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[2] + mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -174,7 +126,7 @@ func (x *Connection) String() string { func (*Connection) ProtoMessage() {} func (x *Connection) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[2] + mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -187,7 +139,7 @@ func (x *Connection) ProtoReflect() protoreflect.Message { // Deprecated: Use Connection.ProtoReflect.Descriptor instead. func (*Connection) Descriptor() ([]byte, []int) { - return file_controller_api_resources_sessions_v1_session_proto_rawDescGZIP(), []int{2} + return file_controller_api_resources_sessions_v1_session_proto_rawDescGZIP(), []int{1} } func (x *Connection) GetClientTcpAddress() string { @@ -277,8 +229,6 @@ type Session struct { States []*SessionState `protobuf:"bytes,170,rep,name=states,proto3" json:"states,omitempty"` // Output only. The current status of this Session. Status string `protobuf:"bytes,180,opt,name=status,proto3" json:"status,omitempty" class:"public"` // @gotags: `class:"public"` - // Output only. Worker information given to the client. - WorkerInfo []*WorkerInfo `protobuf:"bytes,190,rep,name=worker_info,proto3" json:"worker_info,omitempty"` // Output only. The certificate generated for the session. Raw DER bytes. Certificate []byte `protobuf:"bytes,200,opt,name=certificate,proto3" json:"certificate,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. If the session is terminated, this provides a short description as to why. @@ -292,7 +242,7 @@ type Session struct { func (x *Session) Reset() { *x = Session{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[3] + mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -305,7 +255,7 @@ func (x *Session) String() string { func (*Session) ProtoMessage() {} func (x *Session) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[3] + mi := &file_controller_api_resources_sessions_v1_session_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -318,7 +268,7 @@ func (x *Session) ProtoReflect() protoreflect.Message { // Deprecated: Use Session.ProtoReflect.Descriptor instead. func (*Session) Descriptor() ([]byte, []int) { - return file_controller_api_resources_sessions_v1_session_proto_rawDescGZIP(), []int{3} + return file_controller_api_resources_sessions_v1_session_proto_rawDescGZIP(), []int{2} } func (x *Session) GetId() string { @@ -433,13 +383,6 @@ func (x *Session) GetStatus() string { return "" } -func (x *Session) GetWorkerInfo() []*WorkerInfo { - if x != nil { - return x.WorkerInfo - } - return nil -} - func (x *Session) GetCertificate() []byte { if x != nil { return x.Certificate @@ -481,98 +424,91 @@ var file_controller_api_resources_sessions_v1_session_proto_rawDesc = []byte{ 0x72, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x26, 0x0a, 0x0a, 0x57, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3a, 0x0a, 0x0a, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x22, 0x9f, 0x02, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x2c, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x54, 0x63, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x26, 0x0a, - 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x70, 0x6f, 0x72, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x63, - 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x12, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x63, 0x70, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x63, 0x70, 0x50, - 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x75, 0x70, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x55, 0x70, 0x12, 0x1d, - 0x0a, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x09, 0x62, 0x79, 0x74, 0x65, 0x73, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x23, 0x0a, - 0x0d, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x22, 0xbd, 0x07, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, - 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, - 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, - 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x44, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x01, 0x0a, 0x0c, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x75, - 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x75, - 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x78, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x6f, 0x73, - 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x6f, 0x73, 0x74, - 0x5f, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x96, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, - 0x12, 0x1b, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0xa0, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x4b, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0xaa, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0xb4, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x53, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0xbe, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x77, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, - 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x12, 0x74, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x18, 0xd2, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x12, - 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x53, 0x0a, - 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb6, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x12, 0x36, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, + 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x9f, 0x02, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x63, 0x70, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, + 0x74, 0x63, 0x70, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x63, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, + 0x14, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x65, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x63, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x2a, 0x0a, 0x11, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, + 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x65, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x63, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x5f, 0x75, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x55, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, + 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, + 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, + 0x6f, 0x73, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xf5, 0x06, 0x0a, 0x07, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x44, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, + 0x0d, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x6e, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x5f, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x78, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x12, 0x21, 0x0a, + 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x82, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, + 0x12, 0x19, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x8c, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x08, 0x73, + 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x96, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x18, 0xa0, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, + 0xaa, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x73, 0x12, 0x17, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0xb4, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0b, 0x63, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2f, + 0x0a, 0x12, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x18, 0xd2, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, + 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x53, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0xb6, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x42, 0x52, 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, @@ -592,30 +528,28 @@ func file_controller_api_resources_sessions_v1_session_proto_rawDescGZIP() []byt return file_controller_api_resources_sessions_v1_session_proto_rawDescData } -var file_controller_api_resources_sessions_v1_session_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_controller_api_resources_sessions_v1_session_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_controller_api_resources_sessions_v1_session_proto_goTypes = []interface{}{ - (*WorkerInfo)(nil), // 0: controller.api.resources.sessions.v1.WorkerInfo - (*SessionState)(nil), // 1: controller.api.resources.sessions.v1.SessionState - (*Connection)(nil), // 2: controller.api.resources.sessions.v1.Connection - (*Session)(nil), // 3: controller.api.resources.sessions.v1.Session - (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp - (*scopes.ScopeInfo)(nil), // 5: controller.api.resources.scopes.v1.ScopeInfo + (*SessionState)(nil), // 0: controller.api.resources.sessions.v1.SessionState + (*Connection)(nil), // 1: controller.api.resources.sessions.v1.Connection + (*Session)(nil), // 2: controller.api.resources.sessions.v1.Session + (*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp + (*scopes.ScopeInfo)(nil), // 4: controller.api.resources.scopes.v1.ScopeInfo } var file_controller_api_resources_sessions_v1_session_proto_depIdxs = []int32{ - 4, // 0: controller.api.resources.sessions.v1.SessionState.start_time:type_name -> google.protobuf.Timestamp - 4, // 1: controller.api.resources.sessions.v1.SessionState.end_time:type_name -> google.protobuf.Timestamp - 5, // 2: controller.api.resources.sessions.v1.Session.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 4, // 3: controller.api.resources.sessions.v1.Session.created_time:type_name -> google.protobuf.Timestamp - 4, // 4: controller.api.resources.sessions.v1.Session.updated_time:type_name -> google.protobuf.Timestamp - 4, // 5: controller.api.resources.sessions.v1.Session.expiration_time:type_name -> google.protobuf.Timestamp - 1, // 6: controller.api.resources.sessions.v1.Session.states:type_name -> controller.api.resources.sessions.v1.SessionState - 0, // 7: controller.api.resources.sessions.v1.Session.worker_info:type_name -> controller.api.resources.sessions.v1.WorkerInfo - 2, // 8: controller.api.resources.sessions.v1.Session.connections:type_name -> controller.api.resources.sessions.v1.Connection - 9, // [9:9] is the sub-list for method output_type - 9, // [9:9] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 3, // 0: controller.api.resources.sessions.v1.SessionState.start_time:type_name -> google.protobuf.Timestamp + 3, // 1: controller.api.resources.sessions.v1.SessionState.end_time:type_name -> google.protobuf.Timestamp + 4, // 2: controller.api.resources.sessions.v1.Session.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 3, // 3: controller.api.resources.sessions.v1.Session.created_time:type_name -> google.protobuf.Timestamp + 3, // 4: controller.api.resources.sessions.v1.Session.updated_time:type_name -> google.protobuf.Timestamp + 3, // 5: controller.api.resources.sessions.v1.Session.expiration_time:type_name -> google.protobuf.Timestamp + 0, // 6: controller.api.resources.sessions.v1.Session.states:type_name -> controller.api.resources.sessions.v1.SessionState + 1, // 7: controller.api.resources.sessions.v1.Session.connections:type_name -> controller.api.resources.sessions.v1.Connection + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_controller_api_resources_sessions_v1_session_proto_init() } @@ -625,18 +559,6 @@ func file_controller_api_resources_sessions_v1_session_proto_init() { } if !protoimpl.UnsafeEnabled { file_controller_api_resources_sessions_v1_session_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkerInfo); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_resources_sessions_v1_session_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SessionState); i { case 0: return &v.state @@ -648,7 +570,7 @@ func file_controller_api_resources_sessions_v1_session_proto_init() { return nil } } - file_controller_api_resources_sessions_v1_session_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_resources_sessions_v1_session_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Connection); i { case 0: return &v.state @@ -660,7 +582,7 @@ func file_controller_api_resources_sessions_v1_session_proto_init() { return nil } } - file_controller_api_resources_sessions_v1_session_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_resources_sessions_v1_session_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Session); i { case 0: return &v.state @@ -679,7 +601,7 @@ func file_controller_api_resources_sessions_v1_session_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_api_resources_sessions_v1_session_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/testing/dbtest/session_list_benchmarks_dump_generation_test.go b/testing/dbtest/session_list_benchmarks_dump_generation_test.go index 2455d2189..939f794f8 100644 --- a/testing/dbtest/session_list_benchmarks_dump_generation_test.go +++ b/testing/dbtest/session_list_benchmarks_dump_generation_test.go @@ -133,8 +133,8 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) { serversRepo, err := servers.NewRepository(rw, rw, kms) require.NoError(err) worker := &store.Worker{ - PrivateId: "test1", - Address: "127.0.0.1", + PublicId: "test_worker_1", + Address: "127.0.0.1", } _, _, err = serversRepo.UpsertWorker(ctx, worker) require.NoError(err) @@ -221,7 +221,7 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) { } func cycleSessionStates(t testing.TB, ctx context.Context, sess *session.Session, sessRepo *session.Repository, connRepo *session.ConnectionRepository, conn *db.DB, worker *store.Worker, numConns int) { - sess, _, err := sessRepo.ActivateSession(ctx, sess.PublicId, sess.Version, worker.PrivateId, []byte(`tofu`)) + sess, _, err := sessRepo.ActivateSession(ctx, sess.PublicId, sess.Version, []byte(`tofu`)) require.NoError(t, err) var closeWiths []session.CloseWith for i := 0; i < numConns; i++ { From 2ecd550e276a09b84baf1a9436d75e685731f445 Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 18 May 2022 17:03:24 -0700 Subject: [PATCH 013/100] Add fields to server_worker to be a boundary resource (#2083) * Remove the server_id from session table, change it to worker_id on session_connection This is step 1 in updating the worker table. The worker is becoming a public resource so it must have a public id. We drop the server_id column on the session_connection table and add the worker_id column so we can use the wt_public_key type. Since db migrations are applied while the controller is offline, it should be safe to assume all session connections are not in a running state, thus allowing us to null out all the worker ids in the session_connection table. For now we don't have very good enforcement of the wt_public_id contraints, since the id is currently defined in the worker config, but this will soon change and the worker's id will be generated by the controller. * Updating server_id references to controller_id. * Adding worker tag db writing test. * Move the server table back to the top. * Update comment for the worker object. * Update comment for the worker table. * Update the newWorkerId call to take a context and fix its error. * Correct comment for the wt_network_address domain. --- .../server/worker_tags_reload_test.go | 10 +- .../handlers/targets/target_service.go | 12 +- .../handlers/workers/worker_service.go | 37 +-- .../workers/worker_service_status_test.go | 25 +- .../oss/postgres/99/01_domain_types.up.sql | 15 ++ ...sql => 02_worker_controller_tables.up.sql} | 51 +++- .../controller/storage/job/store/v1/job.proto | 14 +- .../storage/servers/store/v1/worker.proto | 43 ++- internal/scheduler/job/options.go | 8 +- internal/scheduler/job/options_test.go | 6 +- internal/scheduler/job/query.go | 2 +- internal/scheduler/job/repository_run.go | 12 +- internal/scheduler/job/repository_run_test.go | 244 +++++++++--------- internal/scheduler/job/store/job.pb.go | 75 +++--- internal/scheduler/job/testing.go | 4 +- internal/scheduler/job/testing_test.go | 2 +- internal/scheduler/scheduler.go | 2 +- internal/servers/gorm.go | 6 - internal/servers/options.go | 53 +++- internal/servers/options_test.go | 63 ++++- internal/servers/public_ids.go | 21 ++ internal/servers/repository.go | 74 +++--- internal/servers/repository_test.go | 125 ++++----- internal/servers/store/gorm.go | 10 +- internal/servers/store/worker.pb.go | 151 ++++++----- internal/servers/worker.go | 55 ++++ internal/servers/worker_tags_test.go | 78 ++++++ internal/servers/worker_test.go | 185 +++++++++++++ .../session/service_worker_status_report.go | 2 +- .../service_worker_status_report_test.go | 10 +- internal/session/session_cleanup_job_test.go | 3 +- internal/session/testing.go | 13 +- ...on_list_benchmarks_dump_generation_test.go | 13 +- 33 files changed, 967 insertions(+), 457 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/99/01_domain_types.up.sql rename internal/db/schema/migrations/oss/postgres/99/{01_worker_controller_tables.up.sql => 02_worker_controller_tables.up.sql} (70%) delete mode 100644 internal/servers/gorm.go create mode 100644 internal/servers/public_ids.go create mode 100644 internal/servers/worker.go create mode 100644 internal/servers/worker_tags_test.go create mode 100644 internal/servers/worker_test.go diff --git a/internal/cmd/commands/server/worker_tags_reload_test.go b/internal/cmd/commands/server/worker_tags_reload_test.go index f650e58fb..5229e8cd7 100644 --- a/internal/cmd/commands/server/worker_tags_reload_test.go +++ b/internal/cmd/commands/server/worker_tags_reload_test.go @@ -100,11 +100,11 @@ func TestServer_ReloadWorkerTags(t *testing.T) { require.NoError(err) tags, err := serversRepo.ListTagsForWorkers(testController.Context(), []string{name}) require.NoError(err) - require.Len(tags, 2) - require.Equal(key, tags[0].Key) - require.Equal(values[0], tags[0].Value) - require.Equal(key, tags[1].Key) - require.Equal(values[1], tags[1].Value) + require.Len(tags[name], 2) + require.Equal(key, tags[name][0].Key) + require.Equal(values[0], tags[name][0].Value) + require.Equal(key, tags[name][1].Key) + require.Equal(values[1], tags[name][1].Value) } // Give time to populate up to the controller diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index e1713c421..5427a0691 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -911,7 +911,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession if hasWorkerFilter && len(workerIds) > 0 { finalWorkers := make([]*pb.WorkerInfo, 0, len(workers)) // Fetch the tags for the given worker IDs - tags, err := serversRepo.ListTagsForWorkers(ctx, workerIds) + workerTags, err := serversRepo.ListTagsForWorkers(ctx, workerIds) if err != nil { return nil, err } @@ -919,13 +919,15 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession // built from the worker config, but with one extra level: a map of the // worker's ID to its filter map. tagMap := make(map[string]map[string][]string) - for _, tag := range tags { - currWorkerMap := tagMap[tag.WorkerId] + for wId, tags := range workerTags { + currWorkerMap := tagMap[wId] if currWorkerMap == nil { currWorkerMap = make(map[string][]string) - tagMap[tag.WorkerId] = currWorkerMap + tagMap[wId] = currWorkerMap + } + for _, tag := range tags { + currWorkerMap[tag.Key] = append(currWorkerMap[tag.Key], tag.Value) } - currWorkerMap[tag.Key] = append(currWorkerMap[tag.Key], tag.Value) // We don't need to reinsert after the fact because maps are // reference types, so we don't need to re-insert into tagMap } diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index f14fd004a..20f5235ec 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -7,8 +7,6 @@ import ( "sync" "time" - "github.com/hashicorp/boundary/internal/servers/store" - "github.com/hashicorp/boundary/internal/daemon/controller/common" "github.com/hashicorp/boundary/internal/daemon/controller/handlers" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" @@ -16,6 +14,7 @@ import ( "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/session" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" "github.com/hashicorp/go-bexpr" "google.golang.org/grpc/codes" @@ -74,20 +73,20 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques reqServer := req.GetWorker() // Convert API tags to storage tags - workerTags := make(map[string]*store.TagValues) + workerTags := make([]*servers.Tag, 0, len(reqServer.Tags)) for k, v := range reqServer.Tags { - thisTag := &store.TagValues{ - Values: v.GetValues(), + for _, val := range v.GetValues() { + workerTags = append(workerTags, &servers.Tag{ + Key: k, + Value: val, + }) } - workerTags[k] = thisTag - } - worker := &store.Worker{ - PublicId: reqServer.PrivateId, - Address: reqServer.Address, - CreateTime: reqServer.CreateTime, - UpdateTime: reqServer.UpdateTime, - Tags: workerTags, } + + worker := servers.NewWorker(scope.Global.String(), + servers.WithPublicId(reqServer.PrivateId), + servers.WithAddress(reqServer.Address), + servers.WithWorkerTags(workerTags...)) controllers, _, err := serverRepo.UpsertWorker(ctx, worker, servers.WithUpdateTags(req.GetUpdateTags())) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error storing worker status")) @@ -199,17 +198,19 @@ func (ws *workerServiceServer) LookupSession(ctx context.Context, req *pbs.Looku event.WriteError(ctx, op, err, event.WithInfoMsg("error getting servers repo")) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error acquiring server repo when looking up session: %v", err) } - tags, err := serversRepo.ListTagsForWorkers(ctx, []string{req.ServerId}) + workerTags, err := serversRepo.ListTagsForWorkers(ctx, []string{req.ServerId}) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error looking up tags for server", "server_id", req.ServerId)) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error looking up tags for server: %v", err) } // Build the map for filtering. tagMap := make(map[string][]string) - for _, tag := range tags { - tagMap[tag.Key] = append(tagMap[tag.Key], tag.Value) - // We don't need to reinsert after the fact because maps are - // reference types, so we don't need to re-insert into tagMap + for _, tags := range workerTags { + for _, tag := range tags { + tagMap[tag.Key] = append(tagMap[tag.Key], tag.Value) + // We don't need to reinsert after the fact because maps are + // reference types, so we don't need to re-insert into tagMap + } } // Create the evaluator diff --git a/internal/daemon/controller/handlers/workers/worker_service_status_test.go b/internal/daemon/controller/handlers/workers/worker_service_status_test.go index 85b104e18..9c8854fa2 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_status_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_status_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -37,10 +38,10 @@ func TestStatus(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorker(ctx, &store.Worker{ - PublicId: "test_worker1", - Address: "127.0.0.1", - }) + serverRepo.UpsertWorker(ctx, servers.NewWorker( + scope.Global.String(), + servers.WithPublicId("test_worker1"), + servers.WithAddress("127.0.0.1"))) serversRepoFn := func() (*servers.Repository, error) { return serverRepo, nil @@ -205,10 +206,10 @@ func TestStatusSessionClosed(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorker(ctx, &store.Worker{ - PublicId: "test_worker1", - Address: "127.0.0.1", - }) + serverRepo.UpsertWorker(ctx, servers.NewWorker( + scope.Global.String(), + servers.WithPublicId("test_worker1"), + servers.WithAddress("127.0.0.1"))) serversRepoFn := func() (*servers.Repository, error) { return serverRepo, nil @@ -387,10 +388,10 @@ func TestStatusDeadConnection(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorker(ctx, &store.Worker{ - PublicId: "test_worker1", - Address: "127.0.0.1", - }) + serverRepo.UpsertWorker(ctx, servers.NewWorker( + scope.Global.String(), + servers.WithPublicId("test_worker1"), + servers.WithAddress("127.0.0.1"))) serversRepoFn := func() (*servers.Repository, error) { return serverRepo, nil diff --git a/internal/db/schema/migrations/oss/postgres/99/01_domain_types.up.sql b/internal/db/schema/migrations/oss/postgres/99/01_domain_types.up.sql new file mode 100644 index 000000000..504274797 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/99/01_domain_types.up.sql @@ -0,0 +1,15 @@ +begin; + +-- wt_network_address defines a type for a network address. +-- While wt_dns_names does exist, a wt_network_address can be either a dns name +-- or an ip address. +create domain wt_network_address as text + constraint wt_network_address_too_short + check (length(trim(value)) > 0) + constraint wt_network_address_too_long + check (length(trim(value)) < 256); +comment on domain wt_network_address is + 'standard column for a network address.'; + + +commit; \ No newline at end of file diff --git a/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql similarity index 70% rename from internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql rename to internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index 22e4b8437..88f97444b 100644 --- a/internal/db/schema/migrations/oss/postgres/99/01_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -1,12 +1,11 @@ begin; -- Split the server table into two new tables: controller and worker + create table server_controller ( private_id text primary key, description wt_description, - address text not null - constraint address_must_not_be_empty - check(length(trim(address)) > 0), + address wt_network_address not null, create_time wt_timestamp, update_time wt_timestamp ); @@ -30,19 +29,25 @@ create trigger controller_update_time_column before update on server_controller -- workers will now be exposed as resources in boundary. create table server_worker ( public_id wt_public_id primary key, + scope_id wt_scope_id not null + references iam_scope_global(scope_id) + on delete cascade + on update cascade, description wt_description, name wt_name unique, - address text not null - constraint address_must_not_be_empty - check(length(trim(address)) > 0), + -- The address can be null since it is an optional value from the API. + address wt_network_address, create_time wt_timestamp, - update_time wt_timestamp + update_time wt_timestamp, + version wt_version, + constraint server_worker_scope_id_name_uq + unique(scope_id, name) ); comment on table server_worker is 'server_worker is a table where each row represents a Boundary worker.'; create trigger immutable_columns before update on server_worker - for each row execute procedure immutable_columns('public_id','create_time'); + for each row execute procedure immutable_columns('public_id', 'scope_id', 'create_time'); create trigger default_create_time_column before insert on server_worker for each row execute procedure default_create_time(); @@ -96,15 +101,39 @@ alter table session_connection on update cascade; -- Update job run table so that server id references controller id +-- We are not migrating the values from server_id to controller_id. The fkey +-- constraint says that server_id can be set to null when the server is deleted +-- which is what this migration does (removing all records from the server table). +-- Not migrating values make it easier to change types in the server_worker and +-- server_controller tables (like from text to wt_public_id or text to wt_address) +-- without having to worry about old values being valid in the new types. +-- Finally, neither jobs nor servers are exposed out of boundary so the risk of +-- losing data that would be useful later on is diminished. +alter table job_run + add column controller_id wt_private_id, + drop column server_id; alter table job_run - drop constraint server_fkey, add constraint server_controller_fkey - foreign key (server_id) + foreign key (controller_id) references server_controller (private_id) on delete set null on update cascade; --- Replaces the view created in 9/01 to remove references to the server/worker id. +-- Since the above alter tables sets all controller_ids to null running jobs +-- can no longer be reclaimed by any controller and should be considered +-- interrupted. +update job_run +set + status = 'interrupted', + end_time = current_timestamp +where + status = 'running'; + + +-- Replaces the view created in 9/01. +-- Remove the worker id from this view. In actuality this is almost a no-op +-- because no server information was ever getting populated here due to a bug +-- in the update mask when updating a session at the time we activate a session. create view session_list as select s.public_id, s.user_id, s.host_id, s.target_id, diff --git a/internal/proto/controller/storage/job/store/v1/job.proto b/internal/proto/controller/storage/job/store/v1/job.proto index dd8c3caca..f5cdb1638 100644 --- a/internal/proto/controller/storage/job/store/v1/job.proto +++ b/internal/proto/controller/storage/job/store/v1/job.proto @@ -26,6 +26,12 @@ message Job { } message JobRun { + // server_id referenced an server table which was replaced with both a + // server_controller table and a server_worker table. For the purpose of + // jobs, only the controller table is relevant thus we replaced this column + // with controller_id. + reserved "server_id"; + // private_id is used to access the job run but not intended to be available via the API // @inject_tag: `gorm:"primary_key;default:null"` string private_id = 1; @@ -38,10 +44,6 @@ message JobRun { // @inject_tag: `gorm:"not_null"` string job_name = 3; - // The server_id of the server running the job and must be set. - // @inject_tag: `gorm:"not_null"` - string server_id = 4; - // The create_time is set by the database. // @inject_tag: `gorm:"default:current_timestamp"` timestamp.v1.Timestamp create_time = 5; @@ -65,4 +67,8 @@ message JobRun { // status of the job run (running, completed, failed or interrupted). // @inject_tag: `gorm:"not_null"` string status = 10; + + // The controller_id of the controller running the job and must be set. + // @inject_tag: `gorm:"not_null"` + string controller_id = 11; } diff --git a/internal/proto/controller/storage/servers/store/v1/worker.proto b/internal/proto/controller/storage/servers/store/v1/worker.proto index a5063074e..3898131d2 100644 --- a/internal/proto/controller/storage/servers/store/v1/worker.proto +++ b/internal/proto/controller/storage/servers/store/v1/worker.proto @@ -8,12 +8,11 @@ option go_package = "github.com/hashicorp/boundary/internal/servers/store;store" // Worker contains all fields related to a Worker resource message Worker { - // Public ID of the resource + // public_id is a surrogate key suitable for use in a public API // @inject_tag: `gorm:"primary_key"` string public_id = 10; // Name of the resource (optional) - // Description of the resource (optional) // @inject_tag: `gorm:"default:null"` string name = 20; @@ -21,21 +20,39 @@ message Worker { // @inject_tag: `gorm:"default:null"` string description = 30; - // Address for the worker + // Address for the worker. This must be set + // @inject_tag: `gorm:"default:null"` string address = 40; - // First seen time from the RDBMS - storage.timestamp.v1.Timestamp create_time = 50; + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 50; - // Last time there was an update - storage.timestamp.v1.Timestamp update_time = 60; + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 60; - // @gotags: `gorm:"-"` - map tags = 70; + // The scope_id of the owning scope and must be set. + // @inject_tag: `gorm:"not_null"` + string scope_id = 70; + + // version allows optimistic locking of the resource. + // @inject_tag: `gorm:"default:null"` + uint32 version = 80; } -// TagValues is used because map fields cannot be repeated but can be a -// message -message TagValues { - repeated string values = 10; +// WorkerTag is a tag for a worker. The primary key is comprised of the +// worker_id, key, value, and source. +message WorkerTag { + // worker_id is the public key that key of the worker this tag is for. + // @inject_tag: `gorm:"default:null"` + string worker_id = 10; + + // key is the key of the tag. This must be set. + // @inject_tag: `gorm:"default:null"` + string key = 20; + + // value is the value + // @inject_tag: `gorm:"default:null"` + string value = 30; } diff --git a/internal/scheduler/job/options.go b/internal/scheduler/job/options.go index 5b0cc64cf..493ae0837 100644 --- a/internal/scheduler/job/options.go +++ b/internal/scheduler/job/options.go @@ -27,7 +27,7 @@ type options struct { withRunJobsLimit uint withLimit int withName string - withServerId string + withControllerId string } func getDefaultOptions() options { @@ -72,9 +72,9 @@ func WithName(n string) Option { } } -// WithServerId provides an option to provide the server id to match when calling InterruptRuns -func WithServerId(id string) Option { +// WithControllerId provides an option to provide the server id to match when calling InterruptRuns +func WithControllerId(id string) Option { return func(o *options) { - o.withServerId = id + o.withControllerId = id } } diff --git a/internal/scheduler/job/options_test.go b/internal/scheduler/job/options_test.go index 1b7e4e78d..ff6c0484c 100644 --- a/internal/scheduler/job/options_test.go +++ b/internal/scheduler/job/options_test.go @@ -49,12 +49,12 @@ func Test_GetOpts(t *testing.T) { testOpts.withName = "name" assert.Equal(opts, testOpts) }) - t.Run("WithServerId", func(t *testing.T) { + t.Run("WithControllerId", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithServerId("server_id")) + opts := getOpts(WithControllerId("controller_id")) testOpts := getDefaultOptions() assert.NotEqual(opts, testOpts) - testOpts.withServerId = "server_id" + testOpts.withControllerId = "controller_id" assert.Equal(opts, testOpts) }) } diff --git a/internal/scheduler/job/query.go b/internal/scheduler/job/query.go index 48f5e185e..4fd9c9aa4 100644 --- a/internal/scheduler/job/query.go +++ b/internal/scheduler/job/query.go @@ -2,7 +2,7 @@ package job const runJobsQuery = ` insert into job_run ( - job_plugin_id, job_name, server_id + job_plugin_id, job_name, controller_id ) select job_plugin_id, job_name, ? diff --git a/internal/scheduler/job/repository_run.go b/internal/scheduler/job/repository_run.go index 0c630b332..5288faf25 100644 --- a/internal/scheduler/job/repository_run.go +++ b/internal/scheduler/job/repository_run.go @@ -255,7 +255,7 @@ func (r *Repository) FailRun(ctx context.Context, runId string, completed, total // Once a run has been persisted with a final run status (completed, failed // or interrupted), any future calls to InterruptRuns will return an error with Code // errors.InvalidJobRunState. -// WithServerId is the only valid option +// WithControllerId is the only valid option func (r *Repository) InterruptRuns(ctx context.Context, interruptThreshold time.Duration, opt ...Option) ([]*Run, error) { const op = "job.(Repository).InterruptRuns" @@ -263,12 +263,12 @@ func (r *Repository) InterruptRuns(ctx context.Context, interruptThreshold time. // interruptThreshold is seconds in past so * -1 args := []interface{}{-1 * int(interruptThreshold.Round(time.Second).Seconds())} - var whereServerId string - if opts.withServerId != "" { - whereServerId = "and server_id = ?" - args = append(args, opts.withServerId) + var whereControllerId string + if opts.withControllerId != "" { + whereControllerId = "and controller_id = ?" + args = append(args, opts.withControllerId) } - query := fmt.Sprintf(interruptRunsQuery, whereServerId) + query := fmt.Sprintf(interruptRunsQuery, whereControllerId) var runs []*Run _, err := r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, diff --git a/internal/scheduler/job/repository_run_test.go b/internal/scheduler/job/repository_run_test.go index a89e24914..69b8e8808 100644 --- a/internal/scheduler/job/repository_run_test.go +++ b/internal/scheduler/job/repository_run_test.go @@ -27,30 +27,30 @@ func TestRepository_RunJobs(t *testing.T) { server := testController(t, conn, wrapper) tests := []struct { - name string - serverId string - job *Job - wantRun bool - wantErr bool - wantErrCode errors.Code - wantErrMsg string + name string + ControllerId string + job *Job + wantRun bool + wantErr bool + wantErrCode errors.Code + wantErrMsg string }{ { - name: "missing-server-id", - serverId: "", - wantErr: true, - wantErrCode: errors.InvalidParameter, - wantErrMsg: "job.(Repository).RunJobs: missing server id: parameter violation: error #100", + name: "missing-server-id", + ControllerId: "", + wantErr: true, + wantErrCode: errors.InvalidParameter, + wantErrMsg: "job.(Repository).RunJobs: missing server id: parameter violation: error #100", }, { - name: "no-work", - serverId: server.PrivateId, - wantRun: false, - wantErr: false, + name: "no-work", + ControllerId: server.PrivateId, + wantRun: false, + wantErr: false, }, { - name: "valid", - serverId: server.PrivateId, + name: "valid", + ControllerId: server.PrivateId, job: &Job{ Job: &store.Job{ Name: "valid-test", @@ -60,8 +60,8 @@ func TestRepository_RunJobs(t *testing.T) { wantRun: true, }, { - name: "fake-server-id", - serverId: "fake-server-id", + name: "fake-server-id", + ControllerId: "fake-server-id", job: &Job{ Job: &store.Job{ Name: "fake-server-id-test", @@ -84,7 +84,7 @@ func TestRepository_RunJobs(t *testing.T) { assert.NoError(err) require.NotNil(repo) - got, err := repo.RunJobs(context.Background(), tt.serverId) + got, err := repo.RunJobs(context.Background(), tt.ControllerId) if tt.wantErr { require.Error(err) assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "Unexpected error %s", err) @@ -253,10 +253,10 @@ func TestRepository_UpdateProgress(t *testing.T) { name: "status-already-interrupted", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Interrupted.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Interrupted.string(), }, }, wantErr: true, @@ -267,10 +267,10 @@ func TestRepository_UpdateProgress(t *testing.T) { name: "status-already-failed", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Failed.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Failed.string(), }, }, wantErr: true, @@ -281,10 +281,10 @@ func TestRepository_UpdateProgress(t *testing.T) { name: "status-already-completed", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Completed.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Completed.string(), }, }, wantErr: true, @@ -295,10 +295,10 @@ func TestRepository_UpdateProgress(t *testing.T) { name: "valid-no-changes", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Running.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Running.string(), }, }, }, @@ -306,10 +306,10 @@ func TestRepository_UpdateProgress(t *testing.T) { name: "valid-update-total", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Running.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Running.string(), }, }, args: args{ @@ -323,11 +323,11 @@ func TestRepository_UpdateProgress(t *testing.T) { name: "valid-update-completed", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Running.string(), - TotalCount: 10, + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Running.string(), + TotalCount: 10, }, }, args: args{ @@ -343,10 +343,10 @@ func TestRepository_UpdateProgress(t *testing.T) { name: "valid-update-completed-and-total", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Running.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Running.string(), }, }, args: args{ @@ -362,10 +362,10 @@ func TestRepository_UpdateProgress(t *testing.T) { name: "invalid-completed-greater-than-total", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Running.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Running.string(), }, }, args: args{ @@ -467,10 +467,10 @@ func TestRepository_CompleteRun(t *testing.T) { name: "status-already-interrupted", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Interrupted.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Interrupted.string(), }, }, wantErr: true, @@ -481,10 +481,10 @@ func TestRepository_CompleteRun(t *testing.T) { name: "status-already-failed", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Failed.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Failed.string(), }, }, wantErr: true, @@ -495,10 +495,10 @@ func TestRepository_CompleteRun(t *testing.T) { name: "status-already-completed", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Completed.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Completed.string(), }, }, wantErr: true, @@ -509,10 +509,10 @@ func TestRepository_CompleteRun(t *testing.T) { name: "valid", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Running.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Running.string(), }, }, nextRunIn: time.Hour, @@ -521,10 +521,10 @@ func TestRepository_CompleteRun(t *testing.T) { name: "valid-with-progress", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Running.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Running.string(), }, }, args: args{completed: 10, total: 20}, @@ -631,10 +631,10 @@ func TestRepository_FailRun(t *testing.T) { name: "status-already-interrupted", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Interrupted.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Interrupted.string(), }, }, wantErr: true, @@ -645,10 +645,10 @@ func TestRepository_FailRun(t *testing.T) { name: "status-already-failed", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Failed.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Failed.string(), }, }, wantErr: true, @@ -659,10 +659,10 @@ func TestRepository_FailRun(t *testing.T) { name: "status-already-completed", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Completed.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Completed.string(), }, }, wantErr: true, @@ -673,10 +673,10 @@ func TestRepository_FailRun(t *testing.T) { name: "valid", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Running.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Running.string(), }, }, }, @@ -684,10 +684,10 @@ func TestRepository_FailRun(t *testing.T) { name: "valid-with-progress", orig: &Run{ JobRun: &store.JobRun{ - JobName: job.Name, - JobPluginId: job.PluginId, - ServerId: server.PrivateId, - Status: Running.string(), + JobName: job.Name, + JobPluginId: job.PluginId, + ControllerId: server.PrivateId, + Status: Running.string(), }, }, args: args{completed: 10, total: 20}, @@ -859,7 +859,7 @@ func TestRepository_InterruptServerRuns(t *testing.T) { job3 := testJob(t, conn, "job3", "description", wrapper) type args struct { - serverId string + ControllerId string opts []Option expectedJobs []*Job } @@ -872,7 +872,7 @@ func TestRepository_InterruptServerRuns(t *testing.T) { name: "all-runs", runs: []args{ { - serverId: server1.PrivateId, + ControllerId: server1.PrivateId, opts: []Option{WithRunJobsLimit(3)}, expectedJobs: []*Job{job1, job2, job3}, }, @@ -887,22 +887,22 @@ func TestRepository_InterruptServerRuns(t *testing.T) { name: "all-runs-on-single-server-with-server-id", runs: []args{ { - serverId: server2.PrivateId, + ControllerId: server2.PrivateId, opts: []Option{WithRunJobsLimit(3)}, expectedJobs: []*Job{job1, job2, job3}, }, }, interrupts: []args{ { - opts: []Option{WithServerId(server1.PrivateId)}, + opts: []Option{WithControllerId(server1.PrivateId)}, expectedJobs: []*Job{}, }, { - opts: []Option{WithServerId(server2.PrivateId)}, + opts: []Option{WithControllerId(server2.PrivateId)}, expectedJobs: []*Job{job1, job2, job3}, }, { - opts: []Option{WithServerId(server3.PrivateId)}, + opts: []Option{WithControllerId(server3.PrivateId)}, expectedJobs: []*Job{}, }, }, @@ -921,15 +921,15 @@ func TestRepository_InterruptServerRuns(t *testing.T) { runs: []args{}, interrupts: []args{ { - opts: []Option{WithServerId(server1.PrivateId)}, + opts: []Option{WithControllerId(server1.PrivateId)}, expectedJobs: []*Job{}, }, { - opts: []Option{WithServerId(server2.PrivateId)}, + opts: []Option{WithControllerId(server2.PrivateId)}, expectedJobs: []*Job{}, }, { - opts: []Option{WithServerId(server3.PrivateId)}, + opts: []Option{WithControllerId(server3.PrivateId)}, expectedJobs: []*Job{}, }, }, @@ -938,17 +938,17 @@ func TestRepository_InterruptServerRuns(t *testing.T) { name: "multiple-servers-interrupt-all", runs: []args{ { - serverId: server1.PrivateId, + ControllerId: server1.PrivateId, opts: []Option{WithRunJobsLimit(1)}, expectedJobs: []*Job{job1}, }, { - serverId: server2.PrivateId, + ControllerId: server2.PrivateId, opts: []Option{WithRunJobsLimit(1)}, expectedJobs: []*Job{job2}, }, { - serverId: server3.PrivateId, + ControllerId: server3.PrivateId, opts: []Option{WithRunJobsLimit(1)}, expectedJobs: []*Job{job3}, }, @@ -963,32 +963,32 @@ func TestRepository_InterruptServerRuns(t *testing.T) { name: "multiple-servers-with-server-id", runs: []args{ { - serverId: server1.PrivateId, + ControllerId: server1.PrivateId, opts: []Option{WithRunJobsLimit(1)}, expectedJobs: []*Job{job1}, }, { - serverId: server2.PrivateId, + ControllerId: server2.PrivateId, opts: []Option{WithRunJobsLimit(1)}, expectedJobs: []*Job{job2}, }, { - serverId: server3.PrivateId, + ControllerId: server3.PrivateId, opts: []Option{WithRunJobsLimit(1)}, expectedJobs: []*Job{job3}, }, }, interrupts: []args{ { - opts: []Option{WithServerId(server1.PrivateId)}, + opts: []Option{WithControllerId(server1.PrivateId)}, expectedJobs: []*Job{job1}, }, { - opts: []Option{WithServerId(server2.PrivateId)}, + opts: []Option{WithControllerId(server2.PrivateId)}, expectedJobs: []*Job{job2}, }, { - opts: []Option{WithServerId(server3.PrivateId)}, + opts: []Option{WithControllerId(server3.PrivateId)}, expectedJobs: []*Job{job3}, }, }, @@ -997,32 +997,32 @@ func TestRepository_InterruptServerRuns(t *testing.T) { name: "multiple-servers-distributed-runs", runs: []args{ { - serverId: server1.PrivateId, + ControllerId: server1.PrivateId, opts: []Option{WithRunJobsLimit(2)}, expectedJobs: []*Job{job1, job2}, }, { - serverId: server2.PrivateId, + ControllerId: server2.PrivateId, opts: []Option{WithRunJobsLimit(1)}, expectedJobs: []*Job{job3}, }, { - serverId: server3.PrivateId, + ControllerId: server3.PrivateId, opts: []Option{WithRunJobsLimit(1)}, expectedJobs: []*Job{}, }, }, interrupts: []args{ { - opts: []Option{WithServerId(server1.PrivateId)}, + opts: []Option{WithControllerId(server1.PrivateId)}, expectedJobs: []*Job{job1, job2}, }, { - opts: []Option{WithServerId(server2.PrivateId)}, + opts: []Option{WithControllerId(server2.PrivateId)}, expectedJobs: []*Job{job3}, }, { - opts: []Option{WithServerId(server3.PrivateId)}, + opts: []Option{WithControllerId(server3.PrivateId)}, expectedJobs: []*Job{}, }, }, @@ -1031,17 +1031,17 @@ func TestRepository_InterruptServerRuns(t *testing.T) { name: "multiple-servers-distributed-runs-interrupt-all", runs: []args{ { - serverId: server1.PrivateId, + ControllerId: server1.PrivateId, opts: []Option{WithRunJobsLimit(2)}, expectedJobs: []*Job{job1, job2}, }, { - serverId: server2.PrivateId, + ControllerId: server2.PrivateId, opts: []Option{WithRunJobsLimit(1)}, expectedJobs: []*Job{job3}, }, { - serverId: server3.PrivateId, + ControllerId: server3.PrivateId, opts: []Option{WithRunJobsLimit(1)}, expectedJobs: []*Job{}, }, @@ -1060,7 +1060,7 @@ func TestRepository_InterruptServerRuns(t *testing.T) { require.NoError(err) for _, r := range tt.runs { - runs, err := repo.RunJobs(context.Background(), r.serverId, r.opts...) + runs, err := repo.RunJobs(context.Background(), r.ControllerId, r.opts...) require.NoError(err) assert.Len(runs, len(r.expectedJobs)) sort.Slice(runs, func(i, j int) bool { return runs[i].JobName < runs[j].JobName }) diff --git a/internal/scheduler/job/store/job.pb.go b/internal/scheduler/job/store/job.pb.go index 77c96a057..e04d18c12 100644 --- a/internal/scheduler/job/store/job.pb.go +++ b/internal/scheduler/job/store/job.pb.go @@ -116,9 +116,6 @@ type JobRun struct { // The job_name is the name of the job and must be set. // @inject_tag: `gorm:"not_null"` JobName string `protobuf:"bytes,3,opt,name=job_name,json=jobName,proto3" json:"job_name,omitempty" gorm:"not_null"` - // The server_id of the server running the job and must be set. - // @inject_tag: `gorm:"not_null"` - ServerId string `protobuf:"bytes,4,opt,name=server_id,json=serverId,proto3" json:"server_id,omitempty" gorm:"not_null"` // The create_time is set by the database. // @inject_tag: `gorm:"default:current_timestamp"` CreateTime *timestamp.Timestamp `protobuf:"bytes,5,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` @@ -137,6 +134,9 @@ type JobRun struct { // status of the job run (running, completed, failed or interrupted). // @inject_tag: `gorm:"not_null"` Status string `protobuf:"bytes,10,opt,name=status,proto3" json:"status,omitempty" gorm:"not_null"` + // The controller_id of the controller running the job and must be set. + // @inject_tag: `gorm:"not_null"` + ControllerId string `protobuf:"bytes,11,opt,name=controller_id,json=controllerId,proto3" json:"controller_id,omitempty" gorm:"not_null"` } func (x *JobRun) Reset() { @@ -192,13 +192,6 @@ func (x *JobRun) GetJobName() string { return "" } -func (x *JobRun) GetServerId() string { - if x != nil { - return x.ServerId - } - return "" -} - func (x *JobRun) GetCreateTime() *timestamp.Timestamp { if x != nil { return x.CreateTime @@ -241,6 +234,13 @@ func (x *JobRun) GetStatus() string { return "" } +func (x *JobRun) GetControllerId() string { + if x != nil { + return x.ControllerId + } + return "" +} + var File_controller_storage_job_store_v1_job_proto protoreflect.FileDescriptor var file_controller_storage_job_store_v1_job_proto_rawDesc = []byte{ @@ -263,40 +263,41 @@ var file_controller_storage_job_store_v1_job_proto_rawDesc = []byte{ 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6e, 0x65, 0x78, 0x74, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x52, - 0x75, 0x6e, 0x22, 0xc6, 0x03, 0x0a, 0x06, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x12, 0x1d, 0x0a, + 0x75, 0x6e, 0x22, 0xd9, 0x03, 0x0a, 0x06, 0x4a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6a, 0x6f, 0x62, 0x5f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6a, 0x6f, 0x62, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, - 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, - 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x42, 0x5a, 0x40, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x2f, - 0x6a, 0x6f, 0x62, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, + 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, + 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x49, 0x64, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x42, 0x42, + 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, + 0x65, 0x72, 0x2f, 0x6a, 0x6f, 0x62, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/scheduler/job/testing.go b/internal/scheduler/job/testing.go index c1c43450b..9d00670e9 100644 --- a/internal/scheduler/job/testing.go +++ b/internal/scheduler/job/testing.go @@ -35,7 +35,7 @@ func testJob(t testing.TB, conn *db.DB, name, description string, wrapper wrappi func testRun(conn *db.DB, pluginId, name, cId string) (*Run, error) { query := ` insert into job_run ( - job_plugin_id, job_name, server_id + job_plugin_id, job_name, controller_id ) values (?,?,?) on conflict (job_plugin_id, job_name) where status = 'running' @@ -65,7 +65,7 @@ func testRun(conn *db.DB, pluginId, name, cId string) (*Run, error) { func testRunWithUpdateTime(conn *db.DB, pluginId, name, cId string, updateTime time.Time) (*Run, error) { query := ` insert into job_run ( - job_plugin_id, job_name, server_id, update_time + job_plugin_id, job_name, controller_id, update_time ) values (?,?,?,?) returning *; diff --git a/internal/scheduler/job/testing_test.go b/internal/scheduler/job/testing_test.go index cd3e6ddd4..4abd3f74a 100644 --- a/internal/scheduler/job/testing_test.go +++ b/internal/scheduler/job/testing_test.go @@ -68,7 +68,7 @@ func Test_TestRun(t *testing.T) { var got Run err = rw.LookupWhere(context.Background(), &got, "private_id = ?", []interface{}{run.PrivateId}) require.NoError(err) - assert.Equal(server.PrivateId, got.ServerId) + assert.Equal(server.PrivateId, got.ControllerId) assert.Equal(job.Name, got.JobName) assert.Equal(job.PluginId, got.JobPluginId) assert.NotEmpty(got.CreateTime) diff --git a/internal/scheduler/scheduler.go b/internal/scheduler/scheduler.go index 313a2ce55..7b6d8371d 100644 --- a/internal/scheduler/scheduler.go +++ b/internal/scheduler/scheduler.go @@ -154,7 +154,7 @@ func (s *Scheduler) Start(ctx context.Context, wg *sync.WaitGroup) error { } // Interrupt all runs previously allocated to this server - _, err = repo.InterruptRuns(ctx, 0, job.WithServerId(s.serverId)) + _, err = repo.InterruptRuns(ctx, 0, job.WithControllerId(s.serverId)) if err != nil { return errors.Wrap(ctx, err, op) } diff --git a/internal/servers/gorm.go b/internal/servers/gorm.go deleted file mode 100644 index 7588948f6..000000000 --- a/internal/servers/gorm.go +++ /dev/null @@ -1,6 +0,0 @@ -package servers - -// TableName overrides the table name used by WorkerTag to `worker_tag` -func (WorkerTag) TableName() string { - return "server_worker_tag" -} diff --git a/internal/servers/options.go b/internal/servers/options.go index f82f5a52d..d58d9dd84 100644 --- a/internal/servers/options.go +++ b/internal/servers/options.go @@ -1,6 +1,8 @@ package servers -import "time" +import ( + "time" +) // getOpts - iterate the inbound Options and return a struct func getOpts(opt ...Option) options { @@ -16,15 +18,45 @@ type Option func(*options) // options = how options are represented type options struct { - withLimit int - withLiveness time.Duration - withUpdateTags bool + withName string + withDescription string + withAddress string + withPublicId string + withLimit int + withLiveness time.Duration + withUpdateTags bool + withWorkerTags []*Tag } func getDefaultOptions() options { - return options{ - withLimit: 0, - withLiveness: 0, + return options{} +} + +// WithDescription provides an optional description. +func WithDescription(desc string) Option { + return func(o *options) { + o.withDescription = desc + } +} + +// WithName provides an optional name. +func WithName(name string) Option { + return func(o *options) { + o.withName = name + } +} + +// WithAddress provides an optional address. +func WithAddress(address string) Option { + return func(o *options) { + o.withAddress = address + } +} + +// WithPublicId provides an optional public id +func WithPublicId(id string) Option { + return func(o *options) { + o.withPublicId = id } } @@ -54,3 +86,10 @@ func WithUpdateTags(updateTags bool) Option { o.withUpdateTags = updateTags } } + +// WithWorkerTags provides worker tags. +func WithWorkerTags(tags ...*Tag) Option { + return func(o *options) { + o.withWorkerTags = tags + } +} diff --git a/internal/servers/options_test.go b/internal/servers/options_test.go index 6ceedfc52..05b9d4070 100644 --- a/internal/servers/options_test.go +++ b/internal/servers/options_test.go @@ -13,15 +13,56 @@ func Test_GetOpts(t *testing.T) { opts := getOpts() assert.Equal(t, options{}, opts) - opts = getOpts( - WithLimit(5), - WithLiveness(time.Hour), - WithUpdateTags(true), - ) - exp := options{ - withLimit: 5, - withLiveness: time.Hour, - withUpdateTags: true, - } - assert.Equal(t, exp, opts) + t.Run("WithName", func(t *testing.T) { + opts := getOpts(WithName("test")) + testOpts := getDefaultOptions() + testOpts.withName = "test" + assert.Equal(t, opts, testOpts) + }) + t.Run("WithDescription", func(t *testing.T) { + opts := getOpts(WithDescription("test desc")) + testOpts := getDefaultOptions() + testOpts.withDescription = "test desc" + assert.Equal(t, opts, testOpts) + }) + t.Run("WithLimit", func(t *testing.T) { + opts := getOpts(WithLimit(5)) + testOpts := getDefaultOptions() + testOpts.withLimit = 5 + assert.Equal(t, opts, testOpts) + }) + t.Run("WithAddress", func(t *testing.T) { + opts := getOpts(WithAddress("test")) + testOpts := getDefaultOptions() + testOpts.withAddress = "test" + assert.Equal(t, opts, testOpts) + }) + t.Run("WithPublicId", func(t *testing.T) { + opts := getOpts(WithPublicId("test")) + testOpts := getDefaultOptions() + testOpts.withPublicId = "test" + assert.Equal(t, opts, testOpts) + }) + t.Run("WithLiveness", func(t *testing.T) { + opts := getOpts(WithLiveness(time.Hour)) + testOpts := getDefaultOptions() + testOpts.withLiveness = time.Hour + assert.Equal(t, opts, testOpts) + }) + t.Run("WithUpdateTags", func(t *testing.T) { + opts := getOpts(WithUpdateTags(true)) + testOpts := getDefaultOptions() + testOpts.withUpdateTags = true + assert.Equal(t, opts, testOpts) + }) + t.Run("WithWorkerTags", func(t *testing.T) { + tags := []*Tag{ + {Key: "key1", Value: "val1"}, + {Key: "key2", Value: "val2"}, + } + opts := getOpts(WithWorkerTags(tags...)) + testOpts := getDefaultOptions() + testOpts.withWorkerTags = tags + assert.Equal(t, opts, testOpts) + }) } diff --git a/internal/servers/public_ids.go b/internal/servers/public_ids.go new file mode 100644 index 000000000..16d9a7829 --- /dev/null +++ b/internal/servers/public_ids.go @@ -0,0 +1,21 @@ +package servers + +import ( + "context" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" +) + +// PublicId prefixes for the resources in the servers package. +const ( + WorkerPrefix = "w" +) + +func newWorkerId(ctx context.Context) (string, error) { + id, err := db.NewPublicId(WorkerPrefix) + if err != nil { + return "", errors.Wrap(ctx, err, "servers.newWorkerId") + } + return id, nil +} diff --git a/internal/servers/repository.go b/internal/servers/repository.go index b089d3b16..908a6beee 100644 --- a/internal/servers/repository.go +++ b/internal/servers/repository.go @@ -24,17 +24,6 @@ const ( DefaultLiveness = 15 * time.Second ) -type ServerType string - -const ( - ServerTypeController ServerType = "controller" - ServerTypeWorker ServerType = "worker" -) - -func (s ServerType) String() string { - return string(s) -} - // Repository is the servers database repository type Repository struct { reader db.Reader @@ -63,13 +52,13 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms) (*Repository, error) } // ListWorkers is a passthrough to listWorkersWithReader that uses the repo's normal reader. -func (r *Repository) ListWorkers(ctx context.Context, opt ...Option) ([]*store.Worker, error) { +func (r *Repository) ListWorkers(ctx context.Context, opt ...Option) ([]*Worker, error) { return r.listWorkersWithReader(ctx, r.reader, opt...) } // listWorkersWithReader will return a listing of resources and honor the WithLimit option or the repo // defaultLimit. It accepts a reader, allowing it to be used within a transaction or without. -func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader, opt ...Option) ([]*store.Worker, error) { +func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader, opt ...Option) ([]*Worker, error) { opts := getOpts(opt...) liveness := opts.withLiveness if liveness == 0 { @@ -81,7 +70,7 @@ func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader where = fmt.Sprintf("update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) } - var workers []*store.Worker + var workers []*Worker if err := reader.SearchWhere( ctx, &workers, @@ -128,16 +117,10 @@ func (r *Repository) listControllersWithReader(ctx context.Context, reader db.Re return controllers, nil } -// WorkerTag holds the information for the worker_tag table for Gorm. -type WorkerTag struct { - WorkerId string - Key string - Value string -} - -// ListTagsForWorkers pulls out tag tuples into WorkerTag structs for the given worker -func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, opt ...Option) ([]*WorkerTag, error) { - var workerTags []*WorkerTag +// ListTagsForWorkers returns a map from the worker's id to the list of Tags +// that are for that worker. All options are ignored. +func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, opt ...Option) (map[string][]*Tag, error) { + var workerTags []*store.WorkerTag if err := r.reader.SearchWhere( ctx, &workerTags, @@ -147,14 +130,29 @@ func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, ); err != nil { return nil, errors.Wrap(ctx, err, "servers.ListTagsForWorkers", errors.WithMsg(fmt.Sprintf("worker IDs %v", workerIds))) } - return workerTags, nil + + ret := make(map[string][]*Tag, len(workerIds)) + for _, t := range workerTags { + ret[t.WorkerId] = append(ret[t.WorkerId], &Tag{Key: t.Key, Value: t.Value}) + } + return ret, nil } -func (r *Repository) UpsertWorker(ctx context.Context, worker *store.Worker, opt ...Option) ([]*store.Controller, int, error) { +// UpsertWorker creates a new worker if one with the provided public id doesn't +// already exist. If it does, UpsertWorker updates the worker. The +// WithUpdateTags option is the only one used. All others are ignored. +func (r *Repository) UpsertWorker(ctx context.Context, worker *Worker, opt ...Option) ([]*store.Controller, int, error) { const op = "servers.UpsertWorker" - if worker == nil { + switch { + case worker == nil: return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "worker is nil") + case worker.Address == "": + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "address is empty") + case worker.ScopeId == "": + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "scope id is empty") + case worker.PublicId == "": + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "public id is empty") } opts := getOpts(opt...) @@ -187,9 +185,9 @@ func (r *Repository) UpsertWorker(ctx context.Context, worker *store.Worker, opt // delete all tags for the given worker, then add the new ones // we've been sent. if opts.withUpdateTags { - _, err = w.Delete(ctx, &WorkerTag{}, db.WithWhere(deleteTagsSql, worker.PublicId)) + _, err = w.Delete(ctx, &store.WorkerTag{}, db.WithWhere(deleteTagsSql, worker.GetPublicId())) if err != nil { - return errors.Wrap(ctx, err, op+":DeleteTags", errors.WithMsg(worker.PublicId)) + return errors.Wrap(ctx, err, op+":DeleteTags", errors.WithMsg(worker.GetPublicId())) } // If tags were cleared out entirely, then we'll have nothing @@ -198,20 +196,18 @@ func (r *Repository) UpsertWorker(ctx context.Context, worker *store.Worker, opt // below. if len(worker.Tags) > 0 { tags := make([]interface{}, 0, len(worker.Tags)) - for k, v := range worker.Tags { + for _, v := range worker.Tags { if v == nil { - return errors.New(ctx, errors.InvalidParameter, op+":RangeTags", fmt.Sprintf("found nil tag value for worker %s and key %s", worker.PublicId, k)) - } - for _, val := range v.Values { - tags = append(tags, WorkerTag{ - WorkerId: worker.PublicId, - Key: k, - Value: val, - }) + return errors.New(ctx, errors.InvalidParameter, op+":RangeTags", fmt.Sprintf("found nil tag value for worker %s", worker.GetPublicId())) } + tags = append(tags, &store.WorkerTag{ + WorkerId: worker.GetPublicId(), + Key: v.Key, + Value: v.Value, + }) } if err = w.CreateItems(ctx, tags); err != nil { - return errors.Wrap(ctx, err, op+":CreateTags", errors.WithMsg(worker.PublicId)) + return errors.Wrap(ctx, err, op+":CreateTags", errors.WithMsg(worker.GetPublicId())) } } } diff --git a/internal/servers/repository_test.go b/internal/servers/repository_test.go index 173b2169e..8f46ebe7e 100644 --- a/internal/servers/repository_test.go +++ b/internal/servers/repository_test.go @@ -5,8 +5,6 @@ import ( "testing" "time" - "github.com/hashicorp/boundary/internal/servers/store" - "github.com/hashicorp/boundary/api/recovery" "github.com/hashicorp/boundary/api/roles" "github.com/hashicorp/boundary/globals" @@ -99,28 +97,34 @@ func TestTagUpdatingListing(t *testing.T) { defer tc.Shutdown() repo := tc.ServersRepo() - srv := &store.Worker{ - PublicId: "test_worker_1", - Address: "127.0.0.1", - Tags: map[string]*store.TagValues{ - "tag1": { - Values: []string{"value1", "value2"}, + srv := servers.NewWorker(scope.Global.String(), + servers.WithPublicId("test_worker_1"), + servers.WithAddress("127.0.0.1"), + servers.WithWorkerTags( + &servers.Tag{ + Key: "tag1", + Value: "value1", }, - }, - } + &servers.Tag{ + Key: "tag1", + Value: "value2", + })) _, _, err := repo.UpsertWorker(tc.Context(), srv, servers.WithUpdateTags(true)) require.NoError(err) - srv = &store.Worker{ - PublicId: "test_worker_2", - Address: "127.0.0.1", - Tags: map[string]*store.TagValues{ - "tag2": { - Values: []string{"value1", "value2"}, + srv = servers.NewWorker(scope.Global.String(), + servers.WithPublicId("test_worker_2"), + servers.WithAddress("127.0.0.1"), + servers.WithWorkerTags( + &servers.Tag{ + Key: "tag2", + Value: "value1", }, - }, - } + &servers.Tag{ + Key: "tag2", + Value: "value2", + })) _, _, err = repo.UpsertWorker(tc.Context(), srv, servers.WithUpdateTags(true)) require.NoError(err) @@ -128,40 +132,42 @@ func TestTagUpdatingListing(t *testing.T) { require.NoError(err) // Base case - exp := []*servers.WorkerTag{ - { - WorkerId: "test_worker_1", - Key: "tag1", - Value: "value1", + exp := map[string][]*servers.Tag{ + "test_worker_1": { + { + Key: "tag1", + Value: "value1", + }, { + Key: "tag1", + Value: "value2", + }, }, - { - WorkerId: "test_worker_1", - Key: "tag1", - Value: "value2", - }, - { - WorkerId: "test_worker_2", - Key: "tag2", - Value: "value1", - }, - { - WorkerId: "test_worker_2", - Key: "tag2", - Value: "value2", + "test_worker_2": { + { + Key: "tag2", + Value: "value1", + }, + { + Key: "tag2", + Value: "value2", + }, }, } require.Equal(exp, tags) // Update without saying to update tags - srv = &store.Worker{ - PublicId: "test_worker_2", - Address: "192.168.1.1", - Tags: map[string]*store.TagValues{ - "tag22": { - Values: []string{"value21", "value22"}, + srv = servers.NewWorker(scope.Global.String(), + servers.WithPublicId("test_worker_2"), + servers.WithAddress("192.168.1.1"), + servers.WithWorkerTags( + &servers.Tag{ + Key: "tag22", + Value: "value21", }, - }, - } + &servers.Tag{ + Key: "tag22", + Value: "value22", + })) _, _, err = repo.UpsertWorker(tc.Context(), srv) require.NoError(err) tags, err = repo.ListTagsForWorkers(tc.Context(), []string{"test_worker_1", "test_worker_2"}) @@ -175,15 +181,15 @@ func TestTagUpdatingListing(t *testing.T) { require.NoError(err) require.NotEqual(exp, tags) // Update and try again - exp[2] = &servers.WorkerTag{ - WorkerId: "test_worker_2", - Key: "tag22", - Value: "value21", - } - exp[3] = &servers.WorkerTag{ - WorkerId: "test_worker_2", - Key: "tag22", - Value: "value22", + exp["test_worker_2"] = []*servers.Tag{ + { + Key: "tag22", + Value: "value21", + }, + { + Key: "tag22", + Value: "value22", + }, } require.Equal(exp, tags) } @@ -199,11 +205,10 @@ func TestListServersWithLiveness(t *testing.T) { require.NoError(err) ctx := context.Background() - newWorker := func(publicId string) *store.Worker { - result := &store.Worker{ - PublicId: publicId, - Address: "127.0.0.1", - } + newWorker := func(publicId string) *servers.Worker { + result := servers.NewWorker(scope.Global.String(), + servers.WithPublicId(publicId), + servers.WithAddress("127.0.0.1")) _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, result) require.NoError(err) require.Equal(1, rowsUpdated) @@ -224,7 +229,7 @@ func TestListServersWithLiveness(t *testing.T) { require.NoError(err) require.Equal(1, rowsUpdated) - requireIds := func(expected []string, actual []*store.Worker) { + requireIds := func(expected []string, actual []*servers.Worker) { require.Len(expected, len(actual)) want := make(map[string]struct{}) for _, v := range expected { diff --git a/internal/servers/store/gorm.go b/internal/servers/store/gorm.go index 7ab3fc709..2da1ceae9 100644 --- a/internal/servers/store/gorm.go +++ b/internal/servers/store/gorm.go @@ -1,11 +1,11 @@ package store -// TableName overrides the table name used by Worker to `server_worker` -func (Worker) TableName() string { - return "server_worker" -} - // TableName overrides the table name used by Controller to `server_controller` func (Controller) TableName() string { return "server_controller" } + +// TableName overrides the table name used by WorkerTag to `worker_tag` +func (WorkerTag) TableName() string { + return "server_worker_tag" +} diff --git a/internal/servers/store/worker.pb.go b/internal/servers/store/worker.pb.go index 4f78c2685..3ad103fc6 100644 --- a/internal/servers/store/worker.pb.go +++ b/internal/servers/store/worker.pb.go @@ -27,24 +27,30 @@ type Worker struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Public ID of the resource + // public_id is a surrogate key suitable for use in a public API // @inject_tag: `gorm:"primary_key"` PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` // Name of the resource (optional) - // Description of the resource (optional) // @inject_tag: `gorm:"default:null"` Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` // Description of the resource (optional) // @inject_tag: `gorm:"default:null"` Description string `protobuf:"bytes,30,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` - // Address for the worker - Address string `protobuf:"bytes,40,opt,name=address,proto3" json:"address,omitempty"` - // First seen time from the RDBMS - CreateTime *timestamp.Timestamp `protobuf:"bytes,50,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` - // Last time there was an update - UpdateTime *timestamp.Timestamp `protobuf:"bytes,60,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` - // @gotags: `gorm:"-"` - Tags map[string]*TagValues `protobuf:"bytes,70,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" gorm:"-"` + // Address for the worker. This must be set + // @inject_tag: `gorm:"default:null"` + Address string `protobuf:"bytes,40,opt,name=address,proto3" json:"address,omitempty" gorm:"default:null"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,50,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,60,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // The scope_id of the owning scope and must be set. + // @inject_tag: `gorm:"not_null"` + ScopeId string `protobuf:"bytes,70,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"not_null"` + // version allows optimistic locking of the resource. + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` } func (x *Worker) Reset() { @@ -121,25 +127,40 @@ func (x *Worker) GetUpdateTime() *timestamp.Timestamp { return nil } -func (x *Worker) GetTags() map[string]*TagValues { +func (x *Worker) GetScopeId() string { if x != nil { - return x.Tags + return x.ScopeId } - return nil + return "" } -// TagValues is used because map fields cannot be repeated but can be a -// message -type TagValues struct { +func (x *Worker) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +// WorkerTag is a tag for a worker. The primary key is comprised of the +// worker_id, key, value, and source. +type WorkerTag struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Values []string `protobuf:"bytes,10,rep,name=values,proto3" json:"values,omitempty"` + // worker_id is the public key that key of the worker this tag is for. + // @inject_tag: `gorm:"default:null"` + WorkerId string `protobuf:"bytes,10,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty" gorm:"default:null"` + // key is the key of the tag. This must be set. + // @inject_tag: `gorm:"default:null"` + Key string `protobuf:"bytes,20,opt,name=key,proto3" json:"key,omitempty" gorm:"default:null"` + // value is the value + // @inject_tag: `gorm:"default:null"` + Value string `protobuf:"bytes,30,opt,name=value,proto3" json:"value,omitempty" gorm:"default:null"` } -func (x *TagValues) Reset() { - *x = TagValues{} +func (x *WorkerTag) Reset() { + *x = WorkerTag{} if protoimpl.UnsafeEnabled { mi := &file_controller_storage_servers_store_v1_worker_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -147,13 +168,13 @@ func (x *TagValues) Reset() { } } -func (x *TagValues) String() string { +func (x *WorkerTag) String() string { return protoimpl.X.MessageStringOf(x) } -func (*TagValues) ProtoMessage() {} +func (*WorkerTag) ProtoMessage() {} -func (x *TagValues) ProtoReflect() protoreflect.Message { +func (x *WorkerTag) ProtoReflect() protoreflect.Message { mi := &file_controller_storage_servers_store_v1_worker_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -165,16 +186,30 @@ func (x *TagValues) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use TagValues.ProtoReflect.Descriptor instead. -func (*TagValues) Descriptor() ([]byte, []int) { +// Deprecated: Use WorkerTag.ProtoReflect.Descriptor instead. +func (*WorkerTag) Descriptor() ([]byte, []int) { return file_controller_storage_servers_store_v1_worker_proto_rawDescGZIP(), []int{1} } -func (x *TagValues) GetValues() []string { +func (x *WorkerTag) GetWorkerId() string { if x != nil { - return x.Values + return x.WorkerId } - return nil + return "" +} + +func (x *WorkerTag) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *WorkerTag) GetValue() string { + if x != nil { + return x.Value + } + return "" } var File_controller_storage_servers_store_v1_worker_proto protoreflect.FileDescriptor @@ -188,7 +223,7 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc3, 0x03, 0x0a, 0x06, 0x57, 0x6f, 0x72, + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc4, 0x02, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, @@ -205,25 +240,20 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x49, 0x0a, 0x04, 0x74, 0x61, - 0x67, 0x73, 0x18, 0x46, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x67, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, - 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x23, - 0x0a, 0x09, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x73, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, - 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, + 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, + 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, + 0x50, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x09, + 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, + 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -238,23 +268,20 @@ func file_controller_storage_servers_store_v1_worker_proto_rawDescGZIP() []byte return file_controller_storage_servers_store_v1_worker_proto_rawDescData } -var file_controller_storage_servers_store_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_controller_storage_servers_store_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_controller_storage_servers_store_v1_worker_proto_goTypes = []interface{}{ (*Worker)(nil), // 0: controller.storage.servers.store.v1.Worker - (*TagValues)(nil), // 1: controller.storage.servers.store.v1.TagValues - nil, // 2: controller.storage.servers.store.v1.Worker.TagsEntry - (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp + (*WorkerTag)(nil), // 1: controller.storage.servers.store.v1.WorkerTag + (*timestamp.Timestamp)(nil), // 2: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_servers_store_v1_worker_proto_depIdxs = []int32{ - 3, // 0: controller.storage.servers.store.v1.Worker.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 1: controller.storage.servers.store.v1.Worker.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 2, // 2: controller.storage.servers.store.v1.Worker.tags:type_name -> controller.storage.servers.store.v1.Worker.TagsEntry - 1, // 3: controller.storage.servers.store.v1.Worker.TagsEntry.value:type_name -> controller.storage.servers.store.v1.TagValues - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 2, // 0: controller.storage.servers.store.v1.Worker.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // 1: controller.storage.servers.store.v1.Worker.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_controller_storage_servers_store_v1_worker_proto_init() } @@ -276,7 +303,7 @@ func file_controller_storage_servers_store_v1_worker_proto_init() { } } file_controller_storage_servers_store_v1_worker_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TagValues); i { + switch v := v.(*WorkerTag); i { case 0: return &v.state case 1: @@ -294,7 +321,7 @@ func file_controller_storage_servers_store_v1_worker_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_servers_store_v1_worker_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/servers/worker.go b/internal/servers/worker.go new file mode 100644 index 000000000..2b1a68af4 --- /dev/null +++ b/internal/servers/worker.go @@ -0,0 +1,55 @@ +package servers + +import ( + "github.com/hashicorp/boundary/internal/servers/store" + "google.golang.org/protobuf/proto" +) + +// 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 + Tags []*Tag `gorm:"-"` +} + +// A Tag is a custom key/value pair which can be attached to a Worker. +// Multiple Tags may contain the same key and different values in which +// case both key/value pairs are valid. +type Tag struct { + Key string + Value string +} + +// NewWorker returns a new Worker. Valid options are WithName, WithDescription +// WithAddress, and WithWorkerTags. All other options are ignored. +func NewWorker(scopeId string, opt ...Option) *Worker { + opts := getOpts(opt...) + return &Worker{ + Worker: &store.Worker{ + ScopeId: scopeId, + PublicId: opts.withPublicId, + Name: opts.withName, + Description: opts.withDescription, + Address: opts.withAddress, + }, + Tags: opts.withWorkerTags, + } +} + +func (w *Worker) clone() *Worker { + tags := make([]*Tag, 0, len(w.Tags)) + for _, t := range w.Tags { + tags = append(tags, &Tag{Key: t.Key, Value: t.Value}) + } + cw := proto.Clone(w.Worker) + return &Worker{ + Worker: cw.(*store.Worker), + Tags: tags, + } +} + +// TableName overrides the table name used by Worker to `server_worker` +func (Worker) TableName() string { + return "server_worker" +} diff --git a/internal/servers/worker_tags_test.go b/internal/servers/worker_tags_test.go new file mode 100644 index 000000000..bd7e16019 --- /dev/null +++ b/internal/servers/worker_tags_test.go @@ -0,0 +1,78 @@ +package servers + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestWorkerTags_Create(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + + workerPublicid, err := newWorkerId(context.Background()) + require.NoError(t, err) + worker := NewWorker(scope.Global.String(), WithPublicId(workerPublicid)) + + tests := []struct { + name string + want *store.WorkerTag + wantCreateErr bool + }{ + { + name: "success", + want: &store.WorkerTag{ + WorkerId: worker.GetPublicId(), + Key: "key", + Value: "value", + }, + }, + { + name: "bad worker id", + want: &store.WorkerTag{ + WorkerId: "w_badworkeridthatdoesntexist", + Key: "key", + Value: "value", + }, + wantCreateErr: true, + }, + { + name: "missing worker id", + want: &store.WorkerTag{ + Key: "key", + Value: "value", + }, + wantCreateErr: true, + }, + { + name: "missing key", + want: &store.WorkerTag{ + WorkerId: worker.GetPublicId(), + Value: "value", + }, + }, + { + name: "missing value", + want: &store.WorkerTag{ + WorkerId: worker.GetPublicId(), + Key: "key", + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + err := rw.Create(context.Background(), tt.want) + if tt.wantCreateErr { + assert.Error(t, err) + } + }) + } +} diff --git a/internal/servers/worker_test.go b/internal/servers/worker_test.go new file mode 100644 index 000000000..4b4bf2acd --- /dev/null +++ b/internal/servers/worker_test.go @@ -0,0 +1,185 @@ +package servers + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/testing/protocmp" +) + +func TestWorker_New(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + rw := db.New(conn) + + org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + type args struct { + scopeId string + opts []Option + } + + tests := []struct { + name string + args args + want *Worker + wantCreateErr bool + }{ + { + name: "missing-scope-id", + args: args{}, + want: &Worker{ + Worker: &store.Worker{}, + }, + wantCreateErr: true, + }, + { + name: "global-scope-id", + args: args{ + scopeId: scope.Global.String(), + }, + want: &Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + }, + }, + }, + { + name: "org-scope-id", + args: args{ + scopeId: org.GetPublicId(), + }, + want: &Worker{ + Worker: &store.Worker{ + ScopeId: org.GetPublicId(), + }, + }, + wantCreateErr: true, + }, + { + name: "project-scope-id", + args: args{ + scopeId: prj.GetPublicId(), + }, + want: &Worker{ + Worker: &store.Worker{ + ScopeId: prj.GetPublicId(), + }, + }, + wantCreateErr: true, + }, + { + name: "with public id", + args: args{ + scopeId: scope.Global.String(), + opts: []Option{ + WithPublicId("w_test_public_id"), + }, + }, + want: &Worker{ + Worker: &store.Worker{ + PublicId: "w_test_public_id", + ScopeId: scope.Global.String(), + }, + }, + }, + { + name: "with name", + args: args{ + scopeId: scope.Global.String(), + opts: []Option{ + WithName("foo"), + }, + }, + want: &Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + Name: "foo", + }, + }, + }, + { + name: "with description", + args: args{ + scopeId: scope.Global.String(), + opts: []Option{ + WithDescription("foo"), + }, + }, + want: &Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + Description: "foo", + }, + }, + }, + { + name: "with address", + args: args{ + scopeId: scope.Global.String(), + opts: []Option{ + WithAddress("foo"), + }, + }, + want: &Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + Address: "foo", + }, + }, + }, + { + name: "with Tags", + args: args{ + scopeId: scope.Global.String(), + opts: []Option{ + WithWorkerTags(&Tag{ + Key: "key", + Value: "val", + }), + }, + }, + want: &Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + }, + Tags: []*Tag{ + { + Key: "key", + Value: "val", + }, + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + got := NewWorker(tt.args.scopeId, tt.args.opts...) + assert.Empty(cmp.Diff(tt.want, got.clone(), protocmp.Transform())) + + id, err := newWorkerId(context.Background()) + assert.NoError(err) + + tt.want.PublicId = id + got.PublicId = id + + err2 := rw.Create(context.Background(), got) + if tt.wantCreateErr { + assert.Error(err2) + } else { + assert.NoError(err2) + assert.Equal(uint32(1), got.Version) + } + }) + } +} diff --git a/internal/session/service_worker_status_report.go b/internal/session/service_worker_status_report.go index 0cc62a426..0ed4820a3 100644 --- a/internal/session/service_worker_status_report.go +++ b/internal/session/service_worker_status_report.go @@ -42,7 +42,7 @@ func WorkerStatusReport(ctx context.Context, repo *Repository, connRepo *Connect return notActive, errors.New(ctx, errors.Internal, op, fmt.Sprintf("Error closing orphaned connections for worker %s: %v", workerId, err)) } if len(closed) > 0 { - event.WriteSysEvent(ctx, op, "marked unclaimed connections as closed", "server_id", workerId, "count", len(closed)) + event.WriteSysEvent(ctx, op, "marked unclaimed connections as closed", "controller_id", workerId, "count", len(closed)) } return notActive, err } diff --git a/internal/session/service_worker_status_report_test.go b/internal/session/service_worker_status_report_test.go index 52ed0bb64..ff5b2b6d7 100644 --- a/internal/session/service_worker_status_report_test.go +++ b/internal/session/service_worker_status_report_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/db" @@ -32,10 +33,9 @@ func TestWorkerStatusReport(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorker(ctx, &store.Worker{ - PublicId: "test_worker1", - Address: "127.0.0.1", - }) + serverRepo.UpsertWorker(ctx, servers.NewWorker(scope.Global.String(), + servers.WithPublicId("test_worker1"), + servers.WithAddress("127.0.0.1"))) repo, err := session.NewRepository(rw, rw, kms) require.NoError(t, err) @@ -56,7 +56,7 @@ func TestWorkerStatusReport(t *testing.T) { ) type testCase struct { - worker *store.Worker + worker *servers.Worker req []session.StateReport want []session.StateReport orphanedConnections []string diff --git a/internal/session/session_cleanup_job_test.go b/internal/session/session_cleanup_job_test.go index 5b63c37ce..fb8d819b3 100644 --- a/internal/session/session_cleanup_job_test.go +++ b/internal/session/session_cleanup_job_test.go @@ -12,7 +12,6 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/servers" - "github.com/hashicorp/boundary/internal/servers/store" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -289,7 +288,7 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // updateServer is a helper for updating the update time for our // servers. The controller is read back so that we can reference // the most up-to-date fields. - updateServer := func(t *testing.T, w *store.Worker) *store.Worker { + updateServer := func(t *testing.T, w *servers.Worker) *servers.Worker { t.Helper() _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, w) require.NoError(err) diff --git a/internal/session/testing.go b/internal/session/testing.go index 21e04af94..dfc783025 100644 --- a/internal/session/testing.go +++ b/internal/session/testing.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/authtoken" @@ -181,7 +181,7 @@ func TestTofu(t testing.TB) []byte { } // TestWorker inserts a worker into the db to satisfy foreign key constraints. -func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *store.Worker { +func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *servers.Worker { t.Helper() rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) @@ -193,11 +193,10 @@ func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Opti id = "test_session_worker-" + id name := "test-worker-" + id - worker := &store.Worker{ - PublicId: id, - Name: name, - Address: "127.0.0.1", - } + worker := servers.NewWorker(scope.Global.String(), + servers.WithPublicId(id), + servers.WithName(name), + servers.WithAddress("127.0.0.1")) _, _, err = serversRepo.UpsertWorker(context.Background(), worker) require.NoError(t, err) return worker diff --git a/testing/dbtest/session_list_benchmarks_dump_generation_test.go b/testing/dbtest/session_list_benchmarks_dump_generation_test.go index 939f794f8..5adf48a79 100644 --- a/testing/dbtest/session_list_benchmarks_dump_generation_test.go +++ b/testing/dbtest/session_list_benchmarks_dump_generation_test.go @@ -19,10 +19,10 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" - "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/testing/dbtest" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" @@ -132,10 +132,9 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) { require.NoError(err) serversRepo, err := servers.NewRepository(rw, rw, kms) require.NoError(err) - worker := &store.Worker{ - PublicId: "test_worker_1", - Address: "127.0.0.1", - } + worker := servers.NewWorker(scope.Global.String(), + servers.WithPublicId("test_worker_1"), + servers.WithAddress("127.0.0.1")) _, _, err = serversRepo.UpsertWorker(ctx, worker) require.NoError(err) @@ -196,7 +195,7 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) { ScopeId: users[userIndex].scopeId, Endpoint: "tcp://127.0.0.1:22", }) - cycleSessionStates(t, ctx, sess, sessRepo, connRepo, conn, worker, scenario.connsPerSession) + cycleSessionStates(t, ctx, sess, sessRepo, connRepo, conn, scenario.connsPerSession) return nil }) } @@ -220,7 +219,7 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) { } } -func cycleSessionStates(t testing.TB, ctx context.Context, sess *session.Session, sessRepo *session.Repository, connRepo *session.ConnectionRepository, conn *db.DB, worker *store.Worker, numConns int) { +func cycleSessionStates(t testing.TB, ctx context.Context, sess *session.Session, sessRepo *session.Repository, connRepo *session.ConnectionRepository, conn *db.DB, numConns int) { sess, _, err := sessRepo.ActivateSession(ctx, sess.PublicId, sess.Version, []byte(`tofu`)) require.NoError(t, err) var closeWiths []session.CloseWith From 1f550722a2aadace5ffbf13fcb343465d38ed830 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 19 May 2022 14:17:01 -0400 Subject: [PATCH 014/100] Sync against current library state --- go.mod | 2 +- go.sum | 4 ++++ internal/daemon/controller/controller.go | 4 ++-- .../controller/handlers/workers/multihop_service.go | 8 ++++---- internal/daemon/worker/worker.go | 6 +++--- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 2c0dd9b26..a00e1c979 100644 --- a/go.mod +++ b/go.mod @@ -91,7 +91,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220512190651-85bdcdd684e6 - github.com/hashicorp/nodeenrollment v0.0.0-20220516125915-9a115b0fb887 + github.com/hashicorp/nodeenrollment v0.0.0-20220519180751-27ee40a67a33 ) require ( diff --git a/go.sum b/go.sum index 7c4204729..32481aff2 100644 --- a/go.sum +++ b/go.sum @@ -743,6 +743,10 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/nodeenrollment v0.0.0-20220516125915-9a115b0fb887 h1:Ov/lPq1YFKt6CLPCriEL21NqMndrJEKNLIZW9qJYpaE= github.com/hashicorp/nodeenrollment v0.0.0-20220516125915-9a115b0fb887/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.0.0-20220519052605-83a44d379ae6 h1:NrZnIV4TYGDymhzcx/xNIe02obXSrJsPJrPm+OZdXug= +github.com/hashicorp/nodeenrollment v0.0.0-20220519052605-83a44d379ae6/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.0.0-20220519180751-27ee40a67a33 h1:kVIAR0iBLc224QqU4l9QQuJBN8MuEjanc7rHzuCIUVM= +github.com/hashicorp/nodeenrollment v0.0.0-20220519180751-27ee40a67a33/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index 066cd5df5..a1da547a9 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -36,7 +36,7 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/hashicorp/go-secure-stdlib/pluginutil/v2" - "github.com/hashicorp/nodeenrollment/noderegistration" + "github.com/hashicorp/nodeenrollment/registration" "github.com/hashicorp/nodeenrollment/rotation" nodeefile "github.com/hashicorp/nodeenrollment/storage/file" ua "go.uber.org/atomic" @@ -407,5 +407,5 @@ func (c *Controller) WorkerStatusUpdateTimes() *sync.Map { } func (c *Controller) AuthorizeNodeeWorker(keyId string) error { - return noderegistration.AuthorizeNode(c.baseContext, c.NodeeFileStorage, keyId) + return registration.AuthorizeNode(c.baseContext, c.NodeeFileStorage, keyId) } diff --git a/internal/daemon/controller/handlers/workers/multihop_service.go b/internal/daemon/controller/handlers/workers/multihop_service.go index a840abeee..9a84ea9f9 100644 --- a/internal/daemon/controller/handlers/workers/multihop_service.go +++ b/internal/daemon/controller/handlers/workers/multihop_service.go @@ -6,8 +6,8 @@ import ( pbs "github.com/hashicorp/nodeenrollment/multihop" "github.com/hashicorp/nodeenrollment/nodeauth" - "github.com/hashicorp/nodeenrollment/noderegistration" - "github.com/hashicorp/nodeenrollment/nodetls" + "github.com/hashicorp/nodeenrollment/registration" + "github.com/hashicorp/nodeenrollment/tls" "github.com/hashicorp/nodeenrollment/types" ) @@ -33,7 +33,7 @@ func (m *multihopServiceServer) FetchNodeCredentials(ctx context.Context, req *t if err != nil { return nil, fmt.Errorf("%s: error getting current parameters: %w", op, err) } - return noderegistration.FetchNodeCredentials(ctx, storage, req, opt...) + return registration.FetchNodeCredentials(ctx, storage, req, opt...) } func (m *multihopServiceServer) GenerateServerCertificates(ctx context.Context, req *types.GenerateServerCertificatesRequest) (*types.GenerateServerCertificatesResponse, error) { @@ -42,5 +42,5 @@ func (m *multihopServiceServer) GenerateServerCertificates(ctx context.Context, if err != nil { return nil, fmt.Errorf("%s: error getting current parameters: %w", op, err) } - return nodetls.GenerateServerCertificates(ctx, storage, req, opt...) + return tls.GenerateServerCertificates(ctx, storage, req, opt...) } diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 3b410b82d..182c7832b 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -186,9 +186,9 @@ func (w *Worker) Start() error { return err } - var nodeCreds types.NodeCredentials - if err := nodeCreds.GenerateRegistrationParameters(w.baseContext, w.NodeeFileStorage); err != nil { - return err + nodeCreds, err := types.NewNodeCredentials(w.baseContext, w.NodeeFileStorage) + if err != nil { + return fmt.Errorf("error generating new node creds: %w", err) } w.NodeeKeyId, err = nodee.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) if err != nil { From 88a6ca036f60511dc1ff6ecd488405ecaf343ff1 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Tue, 10 May 2022 10:03:08 -0400 Subject: [PATCH 015/100] feat(pki): Add PKI schemas and protos --- Makefile | 2 + .../99/02_worker_authentication.up.sql | 96 ++++ .../servers/store/v1/root_certificate.proto | 42 ++ .../servers/store/v1/worker_auth.proto | 53 ++ internal/servers/options.go | 51 +- internal/servers/options_test.go | 45 ++ internal/servers/root_certificate.go | 125 +++++ internal/servers/state.go | 20 + internal/servers/store/root_certificate.pb.go | 251 +++++++++ internal/servers/store/worker_auth.pb.go | 326 +++++++++++ internal/servers/testing.go | 103 ++++ internal/servers/worker_auth.go | 183 ++++++ internal/servers/workerauth_store_test.go | 524 ++++++++++++++++++ 13 files changed, 1813 insertions(+), 8 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/99/02_worker_authentication.up.sql create mode 100644 internal/proto/controller/storage/servers/store/v1/root_certificate.proto create mode 100644 internal/proto/controller/storage/servers/store/v1/worker_auth.proto create mode 100644 internal/servers/root_certificate.go create mode 100644 internal/servers/state.go create mode 100644 internal/servers/store/root_certificate.pb.go create mode 100644 internal/servers/store/worker_auth.pb.go create mode 100644 internal/servers/testing.go create mode 100644 internal/servers/worker_auth.go create mode 100644 internal/servers/workerauth_store_test.go diff --git a/Makefile b/Makefile index 8bf0244ae..bdd427988 100644 --- a/Makefile +++ b/Makefile @@ -149,6 +149,8 @@ protobuild: @protoc-go-inject-tag -input=./internal/kms/store/oidc_key.pb.go @protoc-go-inject-tag -input=./internal/servers/store/controller.pb.go @protoc-go-inject-tag -input=./internal/servers/store/worker.pb.go + @protoc-go-inject-tag -input=./internal/servers/store/root_certificate.pb.go + @protoc-go-inject-tag -input=./internal/servers/store/worker_auth.pb.go @protoc-go-inject-tag -input=./internal/target/store/target.pb.go @protoc-go-inject-tag -input=./internal/target/targettest/store/target.pb.go @protoc-go-inject-tag -input=./internal/target/tcp/store/target.pb.go diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_authentication.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_authentication.up.sql new file mode 100644 index 000000000..d64967532 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_authentication.up.sql @@ -0,0 +1,96 @@ +begin; + +create table worker_auth_operational_indicator_enm( + state text primary key + constraint only_predefined_worker_auth_states_allowed + check ( + state in ('next','current') + ) +); + +comment on table worker_auth_operational_indicator_enm is + 'worker_auth_operational_indicator_enm is an enumeration table representing the state of certificates used for worker authentication.'; + +insert into worker_auth_operational_indicator_enm (state) +values + ('next'), + ('current'); + +create table worker_auth_ca_certificate( + serial_number numeric, + certificate bytea not null + constraint certificate_must_not_be_empty + check(length(certificate) > 0), + not_valid_before wt_timestamp not null, + not_valid_after wt_timestamp not null + constraint not_valid_before_must_be_before_not_valid_after + check(not_valid_before < not_valid_after), + public_key bytea primary key + constraint public_key_must_not_be_empty + check(length(public_key) > 0), + private_key bytea not null -- encrypted PEM encoded private key + constraint private_key_must_not_be_empty + check(length(private_key) > 0), + key_id kms_private_id not null -- key used to encrypt entries via wrapping wrapper. + constraint kms_data_key_version_fkey + references kms_data_key_version (private_id) + on delete restrict + on update cascade, + state text unique not null + constraint worker_auth_operational_indicator_enm_fkey + references worker_auth_operational_indicator_enm(state) + on delete restrict + on update cascade +); + +comment on table worker_auth_ca_certificate is + 'worker_auth_ca_certificate is a table where each row represents a root certificate for used for worker authentication.'; + +create table worker_auth_authorized( + worker_key_identifier text primary key, -- The public key id for this WorkerAuth entry, generated from the signing pub key + worker_id wt_public_id not null + constraint server_worker_fkey + references server_worker (public_id) + on delete cascade + on update cascade, + worker_signing_pub_key bytea not null + constraint worker_signing_pub_key_must_not_be_empty + check(length(worker_signing_pub_key) > 0), + worker_encryption_pub_key bytea not null + constraint worker_encryption_pub_key_must_not_be_empty + check(length(worker_encryption_pub_key) > 0), + controller_encryption_priv_key bytea not null -- encrypted PEM encoded private key + constraint controller_encryption_priv_key_must_not_be_empty + check(length(controller_encryption_priv_key) > 0), + key_id kms_private_id not null -- key used to encrypt entries via wrapping wrapper. + constraint kms_data_key_version_fkey + references kms_data_key_version (private_id) + on delete restrict + on update cascade, + nonce bytea +); + +comment on table worker_auth_authorized is + 'worker_auth_authorized is a table where each row represents key and cert data associated with an authorized worker.'; + +create table worker_auth_certificate_bundle( + certificate_public_key bytea not null + constraint worker_auth_ca_certificate_fkey + references worker_auth_ca_certificate(public_key) + on delete cascade + on update cascade, + worker_key_identifier text not null + constraint worker_auth_authorized_fkey + references worker_auth_authorized(worker_key_identifier) + on delete cascade + on update cascade, + cert_bundle bytea + constraint current_cert_bundle_must_not_be_empty + check(length(cert_bundle) > 0), + primary key(certificate_public_key, worker_key_identifier) +); + +comment on table worker_auth_certificate_bundle is + 'worker_auth_certificate_bundle is a table where each row represents a cert bundle issued by a ca certificate for a worker.'; + +commit; \ No newline at end of file diff --git a/internal/proto/controller/storage/servers/store/v1/root_certificate.proto b/internal/proto/controller/storage/servers/store/v1/root_certificate.proto new file mode 100644 index 000000000..16a4d2907 --- /dev/null +++ b/internal/proto/controller/storage/servers/store/v1/root_certificate.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +// Package store provides protobufs for storing types in the pki package. +package controller.storage.servers.store.v1; + +import "controller/storage/timestamp/v1/timestamp.proto"; + +option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; + +// RootCertificate contains all fields related to a RootCertificate resource +message RootCertificate { + // The serial number of the root certificate + // @inject_tag: `gorm:"not_null"` + uint64 serial_number = 10; + + // Certificate is the PEM encoded certificate. + // @inject_tag: `gorm:"not_null"` + bytes certificate = 20; + + // Not valid before is the timestamp at which this certificate's validity period starts + storage.timestamp.v1.Timestamp not_valid_before = 30; + + // Not valid after is the timestamp at which this certificate's validity period ends + storage.timestamp.v1.Timestamp not_valid_after = 40; + + // The public key associated with this certificate + // @inject_tag: `gorm:"not_null"` + bytes public_key = 50; + + // The private key associated with this certificate + // This is a ciphertext field + // @inject_tag: `gorm:"not_null"` + bytes private_key = 60; + + // The id of the kms database key used for encrypting this entry. + // @inject_tag: `gorm:"not_null"` + string key_id = 70; + + // State is an enum value indicating if this is the next or current root cert + // @inject_tag: `gorm:"not_null"` + string state = 80; +} diff --git a/internal/proto/controller/storage/servers/store/v1/worker_auth.proto b/internal/proto/controller/storage/servers/store/v1/worker_auth.proto new file mode 100644 index 000000000..197ea4005 --- /dev/null +++ b/internal/proto/controller/storage/servers/store/v1/worker_auth.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +// Package store provides protobufs for storing types in the pki package. +package controller.storage.servers.store.v1; + +option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; + +// WorkerAuth contains all fields related to an authorized Worker resource +message WorkerAuth { + // The key id for this worker auth record, generated from the signing pub key + // @inject_tag: `gorm:"primary_key"` + string worker_key_identifier = 10; + + // The worker id this worker authentication record is for + // @inject_tag: `gorm:"not_null"` + string worker_id = 20; + + // The worker's public signing key + // @inject_tag: `gorm:"not_null"` + bytes worker_signing_pub_key = 30; + + // The worker's public encryption key + // @inject_tag: `gorm:"not_null"` + bytes worker_encryption_pub_key = 40; + + // The private key associated with this certificate + // This is a ciphertext field + // @inject_tag: `gorm:"not_null"` + bytes controller_encryption_priv_key = 50; + + // The id of the kms database key used for encrypting this entry. + // @inject_tag: `gorm:"not_null"` + string key_id = 60; + + // Nonce used by a worker in authenticating + // @inject_tag: `gorm:"default:null"` + bytes nonce = 70; +} + +// WorkerCertBundle contains all fields related to a WorkerCertBundle resource +message WorkerCertBundle { + // The public key of the issuing root certificate + // @inject_tag: `gorm:"not_null"` + bytes certificate_public_key = 10; + + // The WorkerAuth worker_key_identifier this cert bundle record is for + // @inject_tag: `gorm:"not_null"` + string worker_key_identifier = 20; + + // CertBundle is the marshaled protobuf certificate bundle for a WorkerAuth + // @inject_tag: `gorm:"not_null"` + bytes cert_bundle = 30; +} diff --git a/internal/servers/options.go b/internal/servers/options.go index d58d9dd84..6d1e274f3 100644 --- a/internal/servers/options.go +++ b/internal/servers/options.go @@ -18,14 +18,19 @@ type Option func(*options) // options = how options are represented type options struct { - withName string - withDescription string - withAddress string - withPublicId string - withLimit int - withLiveness time.Duration - withUpdateTags bool - withWorkerTags []*Tag + withName string + withDescription string + withAddress string + withPublicId string + withLimit int + withLiveness time.Duration + withUpdateTags bool + withWorkerTags []*Tag + withWorkerKeyIdentifier string + withWorkerKeys WorkerKeys + withControllerEncryptionPrivateKey []byte + withKeyId string + withNonce []byte } func getDefaultOptions() options { @@ -93,3 +98,33 @@ func WithWorkerTags(tags ...*Tag) Option { o.withWorkerTags = tags } } + +func WithWorkerKeyIdentifier(workerKeyIdentifier string) Option { + return func(o *options) { + o.withWorkerKeyIdentifier = workerKeyIdentifier + } +} + +func WithWorkerKeys(workerKeys WorkerKeys) Option { + return func(o *options) { + o.withWorkerKeys = workerKeys + } +} + +func WithControllerEncryptionPrivateKey(controllerKey []byte) Option { + return func(o *options) { + o.withControllerEncryptionPrivateKey = controllerKey + } +} + +func WithKeyId(keyId string) Option { + return func(o *options) { + o.withKeyId = keyId + } +} + +func WithNonce(nonce []byte) Option { + return func(o *options) { + o.withNonce = nonce + } +} diff --git a/internal/servers/options_test.go b/internal/servers/options_test.go index 05b9d4070..2e4e21381 100644 --- a/internal/servers/options_test.go +++ b/internal/servers/options_test.go @@ -65,4 +65,49 @@ func Test_GetOpts(t *testing.T) { testOpts.withWorkerTags = tags assert.Equal(t, opts, testOpts) }) + t.Run("WithWorkerKeys", func(t *testing.T) { + assert := assert.New(t) + testOpts := getDefaultOptions() + + keys := WorkerKeys{ + workerEncryptionPubKey: populateBytes(20), + workerSigningPubKey: populateBytes(20), + } + + opts := getOpts(WithWorkerKeys(keys)) + testOpts = getDefaultOptions() + testOpts.withWorkerKeys = keys + assert.Equal(opts, testOpts) + }) + t.Run("WithControllerEncryptionPrivateKey", func(t *testing.T) { + assert := assert.New(t) + testOpts := getDefaultOptions() + + key := populateBytes(20) + + opts := getOpts(WithControllerEncryptionPrivateKey(key)) + testOpts = getDefaultOptions() + testOpts.withControllerEncryptionPrivateKey = key + assert.Equal(opts, testOpts) + }) + t.Run("WithKeyId", func(t *testing.T) { + assert := assert.New(t) + testOpts := getDefaultOptions() + + opts := getOpts(WithKeyId("hi i'm another key id")) + testOpts = getDefaultOptions() + testOpts.withKeyId = "hi i'm another key id" + assert.Equal(opts, testOpts) + }) + t.Run("WithNonce", func(t *testing.T) { + assert := assert.New(t) + testOpts := getDefaultOptions() + + nonce := populateBytes(20) + + opts := getOpts(WithNonce(nonce)) + testOpts = getDefaultOptions() + testOpts.withNonce = nonce + assert.Equal(opts, testOpts) + }) } diff --git a/internal/servers/root_certificate.go b/internal/servers/root_certificate.go new file mode 100644 index 000000000..4e935fa95 --- /dev/null +++ b/internal/servers/root_certificate.go @@ -0,0 +1,125 @@ +package servers + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/internal/errors" + + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/servers/store" + "google.golang.org/protobuf/proto" +) + +// RootCertificate contains fields related to a RootCertificate resource +// This includes public/ private keys, the PEM encoded certificate, and the certificate validity period +type RootCertificate struct { + *store.RootCertificate + tableName string `gorm:"-"` +} + +type RootCertificateKeys struct { + publicKey []byte + privateKey []byte +} + +func newRootCertificate(ctx context.Context, serialNumber uint64, certificate []byte, notValidBefore, notValidAfter *timestamp.Timestamp, + rootCertificateKeys RootCertificateKeys, keyId string, state CertificateState, +) (*RootCertificate, error) { + const op = "servers.newRootCertificate" + + if &serialNumber == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no serialNumber") + } + if certificate == nil || len(certificate) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty certificate") + } + if notValidAfter == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no notValidAfter") + } + if notValidBefore == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no notValidBefore") + } + if rootCertificateKeys.publicKey == nil || len(rootCertificateKeys.publicKey) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty publicKey") + } + if rootCertificateKeys.privateKey == nil || len(rootCertificateKeys.privateKey) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty privateKey") + } + if keyId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no keyId") + } + if !validState(state) { + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is not a valid certificate state", state)) + } + + l := &RootCertificate{ + RootCertificate: &store.RootCertificate{ + SerialNumber: serialNumber, + Certificate: certificate, + NotValidAfter: notValidAfter, + NotValidBefore: notValidBefore, + PublicKey: rootCertificateKeys.publicKey, + PrivateKey: rootCertificateKeys.privateKey, + KeyId: keyId, + State: string(state), + }, + } + return l, nil +} + +func AllocRootCertificate() *RootCertificate { + return &RootCertificate{ + RootCertificate: &store.RootCertificate{}, + } +} + +func (r *RootCertificate) clone() *RootCertificate { + cp := proto.Clone(r.RootCertificate) + return &RootCertificate{ + RootCertificate: cp.(*store.RootCertificate), + } +} + +func (r *RootCertificate) ValidateNewRootCertificate(ctx context.Context) error { + const op = "servers.(RootCertificate).ValidateNewRootCertificate" + if &r.SerialNumber == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing SerialNumber") + } + if r.Certificate == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing Certificate") + } + if r.NotValidBefore.GetTimestamp().AsTime().IsZero() { + return errors.New(ctx, errors.InvalidParameter, op, "missing not valid before timestamp") + } + if r.NotValidAfter.GetTimestamp().AsTime().IsZero() { + return errors.New(ctx, errors.InvalidParameter, op, "missing not valid after timestamp") + } + if r.PublicKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing public key") + } + if r.PrivateKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing private key") + } + if r.KeyId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing key id") + } + if r.State == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing state") + } + + return nil +} + +// TableName returns the table name. +func (r *RootCertificate) TableName() string { + if r.tableName != "" { + return r.tableName + } + return "worker_auth_ca_certificate" +} + +// SetTableName sets the table name. +func (r *RootCertificate) SetTableName(n string) { + r.tableName = n +} diff --git a/internal/servers/state.go b/internal/servers/state.go new file mode 100644 index 000000000..2cbf6c295 --- /dev/null +++ b/internal/servers/state.go @@ -0,0 +1,20 @@ +package servers + +// CertificateState defines the possible states for a workerauth certificate +type CertificateState string + +const ( + UnknownState CertificateState = "unknown" + CurrentState CertificateState = "current" + NextState CertificateState = "next" +) + +func validState(s CertificateState) bool { + st := CertificateState(s) + switch st { + case CurrentState, NextState: + return true + default: + return false + } +} diff --git a/internal/servers/store/root_certificate.pb.go b/internal/servers/store/root_certificate.pb.go new file mode 100644 index 000000000..7e8feee6a --- /dev/null +++ b/internal/servers/store/root_certificate.pb.go @@ -0,0 +1,251 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/storage/servers/store/v1/root_certificate.proto + +// Package store provides protobufs for storing types in the pki package. + +package store + +import ( + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// RootCertificate contains all fields related to a RootCertificate resource +type RootCertificate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The serial number of the root certificate + // @inject_tag: `gorm:"not_null"` + SerialNumber uint64 `protobuf:"varint,10,opt,name=serial_number,json=serialNumber,proto3" json:"serial_number,omitempty" gorm:"not_null"` + // Certificate is the PEM encoded certificate. + // @inject_tag: `gorm:"not_null"` + Certificate []byte `protobuf:"bytes,20,opt,name=certificate,proto3" json:"certificate,omitempty" gorm:"not_null"` + // Not valid before is the timestamp at which this certificate's validity period starts + NotValidBefore *timestamp.Timestamp `protobuf:"bytes,30,opt,name=not_valid_before,json=notValidBefore,proto3" json:"not_valid_before,omitempty"` + // Not valid after is the timestamp at which this certificate's validity period ends + NotValidAfter *timestamp.Timestamp `protobuf:"bytes,40,opt,name=not_valid_after,json=notValidAfter,proto3" json:"not_valid_after,omitempty"` + // The public key associated with this certificate + // @inject_tag: `gorm:"not_null"` + PublicKey []byte `protobuf:"bytes,50,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" gorm:"not_null"` + // The private key associated with this certificate + // This is a ciphertext field + // @inject_tag: `gorm:"not_null"` + PrivateKey []byte `protobuf:"bytes,60,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty" gorm:"not_null"` + // The id of the kms database key used for encrypting this entry. + // @inject_tag: `gorm:"not_null"` + KeyId string `protobuf:"bytes,70,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"` + // State is an enum value indicating if this is the next or current root cert + // @inject_tag: `gorm:"not_null"` + State string `protobuf:"bytes,80,opt,name=state,proto3" json:"state,omitempty" gorm:"not_null"` +} + +func (x *RootCertificate) Reset() { + *x = RootCertificate{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RootCertificate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RootCertificate) ProtoMessage() {} + +func (x *RootCertificate) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RootCertificate.ProtoReflect.Descriptor instead. +func (*RootCertificate) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_root_certificate_proto_rawDescGZIP(), []int{0} +} + +func (x *RootCertificate) GetSerialNumber() uint64 { + if x != nil { + return x.SerialNumber + } + return 0 +} + +func (x *RootCertificate) GetCertificate() []byte { + if x != nil { + return x.Certificate + } + return nil +} + +func (x *RootCertificate) GetNotValidBefore() *timestamp.Timestamp { + if x != nil { + return x.NotValidBefore + } + return nil +} + +func (x *RootCertificate) GetNotValidAfter() *timestamp.Timestamp { + if x != nil { + return x.NotValidAfter + } + return nil +} + +func (x *RootCertificate) GetPublicKey() []byte { + if x != nil { + return x.PublicKey + } + return nil +} + +func (x *RootCertificate) GetPrivateKey() []byte { + if x != nil { + return x.PrivateKey + } + return nil +} + +func (x *RootCertificate) GetKeyId() string { + if x != nil { + return x.KeyId + } + return "" +} + +func (x *RootCertificate) GetState() string { + if x != nil { + return x.State + } + return "" +} + +var File_controller_storage_servers_store_v1_root_certificate_proto protoreflect.FileDescriptor + +var file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc = []byte{ + 0x0a, 0x3a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, + 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xef, 0x02, 0x0a, 0x0f, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x73, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x63, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x54, 0x0a, + 0x10, 0x6e, 0x6f, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, + 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6e, 0x6f, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x42, 0x65, 0x66, + 0x6f, 0x72, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, + 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x14, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_storage_servers_store_v1_root_certificate_proto_rawDescOnce sync.Once + file_controller_storage_servers_store_v1_root_certificate_proto_rawDescData = file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc +) + +func file_controller_storage_servers_store_v1_root_certificate_proto_rawDescGZIP() []byte { + file_controller_storage_servers_store_v1_root_certificate_proto_rawDescOnce.Do(func() { + file_controller_storage_servers_store_v1_root_certificate_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_servers_store_v1_root_certificate_proto_rawDescData) + }) + return file_controller_storage_servers_store_v1_root_certificate_proto_rawDescData +} + +var file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_storage_servers_store_v1_root_certificate_proto_goTypes = []interface{}{ + (*RootCertificate)(nil), // 0: controller.storage.servers.store.v1.RootCertificate + (*timestamp.Timestamp)(nil), // 1: controller.storage.timestamp.v1.Timestamp +} +var file_controller_storage_servers_store_v1_root_certificate_proto_depIdxs = []int32{ + 1, // 0: controller.storage.servers.store.v1.RootCertificate.not_valid_before:type_name -> controller.storage.timestamp.v1.Timestamp + 1, // 1: controller.storage.servers.store.v1.RootCertificate.not_valid_after:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_controller_storage_servers_store_v1_root_certificate_proto_init() } +func file_controller_storage_servers_store_v1_root_certificate_proto_init() { + if File_controller_storage_servers_store_v1_root_certificate_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RootCertificate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_servers_store_v1_root_certificate_proto_goTypes, + DependencyIndexes: file_controller_storage_servers_store_v1_root_certificate_proto_depIdxs, + MessageInfos: file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes, + }.Build() + File_controller_storage_servers_store_v1_root_certificate_proto = out.File + file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc = nil + file_controller_storage_servers_store_v1_root_certificate_proto_goTypes = nil + file_controller_storage_servers_store_v1_root_certificate_proto_depIdxs = nil +} diff --git a/internal/servers/store/worker_auth.pb.go b/internal/servers/store/worker_auth.pb.go new file mode 100644 index 000000000..119db8fee --- /dev/null +++ b/internal/servers/store/worker_auth.pb.go @@ -0,0 +1,326 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/storage/servers/store/v1/worker_auth.proto + +// Package store provides protobufs for storing types in the pki package. + +package store + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// WorkerAuth contains all fields related to an authorized Worker resource +type WorkerAuth struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The key id for this worker auth record, generated from the signing pub key + // @inject_tag: `gorm:"primary_key"` + WorkerKeyIdentifier string `protobuf:"bytes,10,opt,name=worker_key_identifier,json=workerKeyIdentifier,proto3" json:"worker_key_identifier,omitempty" gorm:"primary_key"` + // The worker id this worker authentication record is for + // @inject_tag: `gorm:"not_null"` + WorkerId string `protobuf:"bytes,20,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty" gorm:"not_null"` + // The worker's public signing key + // @inject_tag: `gorm:"not_null"` + WorkerSigningPubKey []byte `protobuf:"bytes,30,opt,name=worker_signing_pub_key,json=workerSigningPubKey,proto3" json:"worker_signing_pub_key,omitempty" gorm:"not_null"` + // The worker's public encryption key + // @inject_tag: `gorm:"not_null"` + WorkerEncryptionPubKey []byte `protobuf:"bytes,40,opt,name=worker_encryption_pub_key,json=workerEncryptionPubKey,proto3" json:"worker_encryption_pub_key,omitempty" gorm:"not_null"` + // The private key associated with this certificate + // This is a ciphertext field + // @inject_tag: `gorm:"not_null"` + ControllerEncryptionPrivKey []byte `protobuf:"bytes,50,opt,name=controller_encryption_priv_key,json=controllerEncryptionPrivKey,proto3" json:"controller_encryption_priv_key,omitempty" gorm:"not_null"` + // The id of the kms database key used for encrypting this entry. + // @inject_tag: `gorm:"not_null"` + KeyId string `protobuf:"bytes,60,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"` + // Nonce used by a worker in authenticating + // @inject_tag: `gorm:"default:null"` + Nonce []byte `protobuf:"bytes,70,opt,name=nonce,proto3" json:"nonce,omitempty" gorm:"default:null"` +} + +func (x *WorkerAuth) Reset() { + *x = WorkerAuth{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WorkerAuth) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkerAuth) ProtoMessage() {} + +func (x *WorkerAuth) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkerAuth.ProtoReflect.Descriptor instead. +func (*WorkerAuth) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_worker_auth_proto_rawDescGZIP(), []int{0} +} + +func (x *WorkerAuth) GetWorkerKeyIdentifier() string { + if x != nil { + return x.WorkerKeyIdentifier + } + return "" +} + +func (x *WorkerAuth) GetWorkerId() string { + if x != nil { + return x.WorkerId + } + return "" +} + +func (x *WorkerAuth) GetWorkerSigningPubKey() []byte { + if x != nil { + return x.WorkerSigningPubKey + } + return nil +} + +func (x *WorkerAuth) GetWorkerEncryptionPubKey() []byte { + if x != nil { + return x.WorkerEncryptionPubKey + } + return nil +} + +func (x *WorkerAuth) GetControllerEncryptionPrivKey() []byte { + if x != nil { + return x.ControllerEncryptionPrivKey + } + return nil +} + +func (x *WorkerAuth) GetKeyId() string { + if x != nil { + return x.KeyId + } + return "" +} + +func (x *WorkerAuth) GetNonce() []byte { + if x != nil { + return x.Nonce + } + return nil +} + +// WorkerCertBundle contains all fields related to a WorkerCertBundle resource +type WorkerCertBundle struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The public key of the issuing root certificate + // @inject_tag: `gorm:"not_null"` + CertificatePublicKey []byte `protobuf:"bytes,10,opt,name=certificate_public_key,json=certificatePublicKey,proto3" json:"certificate_public_key,omitempty" gorm:"not_null"` + // The WorkerAuth worker_key_identifier this cert bundle record is for + // @inject_tag: `gorm:"not_null"` + WorkerKeyIdentifier string `protobuf:"bytes,20,opt,name=worker_key_identifier,json=workerKeyIdentifier,proto3" json:"worker_key_identifier,omitempty" gorm:"not_null"` + // CertBundle is the marshaled protobuf certificate bundle for a WorkerAuth + // @inject_tag: `gorm:"not_null"` + CertBundle []byte `protobuf:"bytes,30,opt,name=cert_bundle,json=certBundle,proto3" json:"cert_bundle,omitempty" gorm:"not_null"` +} + +func (x *WorkerCertBundle) Reset() { + *x = WorkerCertBundle{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WorkerCertBundle) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkerCertBundle) ProtoMessage() {} + +func (x *WorkerCertBundle) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkerCertBundle.ProtoReflect.Descriptor instead. +func (*WorkerCertBundle) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_worker_auth_proto_rawDescGZIP(), []int{1} +} + +func (x *WorkerCertBundle) GetCertificatePublicKey() []byte { + if x != nil { + return x.CertificatePublicKey + } + return nil +} + +func (x *WorkerCertBundle) GetWorkerKeyIdentifier() string { + if x != nil { + return x.WorkerKeyIdentifier + } + return "" +} + +func (x *WorkerCertBundle) GetCertBundle() []byte { + if x != nil { + return x.CertBundle + } + return nil +} + +var File_controller_storage_servers_store_v1_worker_auth_proto protoreflect.FileDescriptor + +var file_controller_storage_servers_store_v1_worker_auth_proto_rawDesc = []byte{ + 0x0a, 0x35, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x61, 0x75, 0x74, + 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x73, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x22, 0xbf, 0x02, 0x0a, + 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x12, 0x32, 0x0a, 0x15, 0x77, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, + 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x16, + 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x70, + 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x77, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, + 0x79, 0x12, 0x39, 0x0a, 0x19, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x28, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x45, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x43, 0x0a, 0x1e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x32, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x1b, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x69, 0x76, 0x4b, 0x65, + 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x9d, + 0x01, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x42, 0x75, 0x6e, + 0x64, 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x14, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1f, 0x0a, + 0x0b, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x65, 0x72, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x42, 0x3c, + 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, + 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_storage_servers_store_v1_worker_auth_proto_rawDescOnce sync.Once + file_controller_storage_servers_store_v1_worker_auth_proto_rawDescData = file_controller_storage_servers_store_v1_worker_auth_proto_rawDesc +) + +func file_controller_storage_servers_store_v1_worker_auth_proto_rawDescGZIP() []byte { + file_controller_storage_servers_store_v1_worker_auth_proto_rawDescOnce.Do(func() { + file_controller_storage_servers_store_v1_worker_auth_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_servers_store_v1_worker_auth_proto_rawDescData) + }) + return file_controller_storage_servers_store_v1_worker_auth_proto_rawDescData +} + +var file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_controller_storage_servers_store_v1_worker_auth_proto_goTypes = []interface{}{ + (*WorkerAuth)(nil), // 0: controller.storage.servers.store.v1.WorkerAuth + (*WorkerCertBundle)(nil), // 1: controller.storage.servers.store.v1.WorkerCertBundle +} +var file_controller_storage_servers_store_v1_worker_auth_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_controller_storage_servers_store_v1_worker_auth_proto_init() } +func file_controller_storage_servers_store_v1_worker_auth_proto_init() { + if File_controller_storage_servers_store_v1_worker_auth_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkerAuth); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkerCertBundle); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_storage_servers_store_v1_worker_auth_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_servers_store_v1_worker_auth_proto_goTypes, + DependencyIndexes: file_controller_storage_servers_store_v1_worker_auth_proto_depIdxs, + MessageInfos: file_controller_storage_servers_store_v1_worker_auth_proto_msgTypes, + }.Build() + File_controller_storage_servers_store_v1_worker_auth_proto = out.File + file_controller_storage_servers_store_v1_worker_auth_proto_rawDesc = nil + file_controller_storage_servers_store_v1_worker_auth_proto_goTypes = nil + file_controller_storage_servers_store_v1_worker_auth_proto_depIdxs = nil +} diff --git a/internal/servers/testing.go b/internal/servers/testing.go new file mode 100644 index 000000000..0c12e7a62 --- /dev/null +++ b/internal/servers/testing.go @@ -0,0 +1,103 @@ +package servers + +import ( + "context" + "math/rand" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/servers/store" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" + "github.com/hashicorp/go-uuid" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" +) + +const defaultLength = 20 + +// Generate random bytes for byte fields +func populateBytes(length int) []byte { + fieldBytes := make([]byte, length) + rand.Read(fieldBytes) + return fieldBytes +} + +func TestKkmsKey(ctx context.Context, t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) string { + t.Helper() + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + kmsCache := kms.TestKms(t, conn, wrapper) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + testKey, err := databaseWrapper.KeyId(ctx) + require.NoError(t, err) + + return testKey +} + +func TestRootCertificate(ctx context.Context, t *testing.T, conn *db.DB, kmsKey string) *store.RootCertificate { + t.Helper() + rw := db.New(conn) + + beforeTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(-1 * time.Hour))} + afterTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(1 * time.Hour))} + + rootCertKeys := RootCertificateKeys{ + publicKey: populateBytes(defaultLength), + privateKey: populateBytes(defaultLength), + } + + cert, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + rootCertKeys, kmsKey, CurrentState) + err = rw.Create(ctx, cert) + require.NoError(t, err) + return cert.RootCertificate +} + +func TestWorkerAuth(ctx context.Context, t *testing.T, conn *db.DB, worker *store.Worker, kmsKey string) *store.WorkerAuth { + t.Helper() + rw := db.New(conn) + wSignPubKey := populateBytes(defaultLength) + wEncPubKey := populateBytes(defaultLength) + workerKeys := WorkerKeys{workerSigningPubKey: wSignPubKey, workerEncryptionPubKey: wEncPubKey} + controllerKey := populateBytes(defaultLength) + nonce := populateBytes(defaultLength) + opt := []Option{ + WithKeyId(kmsKey), + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + } + + workerAuth, err := newWorkerAuth(ctx, "worker-key-identifier", worker.PublicId, opt...) + require.NoError(t, err) + err = rw.Create(ctx, workerAuth) + require.NoError(t, err) + + return workerAuth.WorkerAuth +} + +func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *store.Worker { + t.Helper() + rw := db.New(conn) + kms := kms.TestKms(t, conn, wrapper) + serversRepo, err := NewRepository(rw, rw, kms) + require.NoError(t, err) + + id, err := uuid.GenerateUUID() + require.NoError(t, err) + id = "test-session-worker-" + id + + name := "test-worker-" + id + worker := &store.Worker{ + PublicId: id, + Name: name, + Address: "127.0.0.1", + } + _, _, err = serversRepo.UpsertWorker(context.Background(), worker) + require.NoError(t, err) + return worker +} diff --git a/internal/servers/worker_auth.go b/internal/servers/worker_auth.go new file mode 100644 index 000000000..d3bd84907 --- /dev/null +++ b/internal/servers/worker_auth.go @@ -0,0 +1,183 @@ +package servers + +import ( + "context" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/servers/store" + "google.golang.org/protobuf/proto" +) + +// WorkerAuth contains all fields related to an authorized Worker resource +// This includes worker public keys, the controller encryption key, +// and certificate bundles issued by the Boundary CA +type WorkerAuth struct { + *store.WorkerAuth + tableName string `gorm:"-"` +} + +// WorkerKeys contain the signing and encryption keys for a WorkerAuth resource +type WorkerKeys struct { + workerSigningPubKey []byte + workerEncryptionPubKey []byte +} + +func newWorkerAuth(ctx context.Context, workerKeyIdentifier, workerId string, opt ...Option) (*WorkerAuth, error) { + const op = "servers.newWorkerAuth" + opts := getOpts(opt...) + + if workerKeyIdentifier == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no workerKeyIdentifier") + } + if workerId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no workerId") + } + + l := &WorkerAuth{ + WorkerAuth: &store.WorkerAuth{ + WorkerKeyIdentifier: workerKeyIdentifier, + WorkerId: workerId, + }, + } + + if &opts.withWorkerKeys != nil { + l.WorkerSigningPubKey = opts.withWorkerKeys.workerSigningPubKey + l.WorkerEncryptionPubKey = opts.withWorkerKeys.workerEncryptionPubKey + } + if &opts.withControllerEncryptionPrivateKey != nil { + l.ControllerEncryptionPrivKey = opts.withControllerEncryptionPrivateKey + } + if opts.withKeyId != "" { + l.KeyId = opts.withKeyId + } + if opts.withNonce != nil { + l.Nonce = opts.withNonce + } + return l, nil +} + +func AllocWorkerAuth() *WorkerAuth { + return &WorkerAuth{ + WorkerAuth: &store.WorkerAuth{}, + } +} + +func (w *WorkerAuth) clone() *WorkerAuth { + cp := proto.Clone(w.WorkerAuth) + return &WorkerAuth{ + WorkerAuth: cp.(*store.WorkerAuth), + } +} + +// Validate is called before storing a WorkerAuth in the db +func (w *WorkerAuth) ValidateNewWorkerAuth(ctx context.Context) error { + const op = "servers.(WorkerAuth).validateNewWorkerAuth" + if w.WorkerKeyIdentifier == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing WorkerKeyIdentifier") + } + if w.WorkerId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing WorkerId") + } + if w.WorkerSigningPubKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing WorkerSigningPubKey") + } + if w.WorkerEncryptionPubKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing WorkerEncryptionPubKey") + } + if w.ControllerEncryptionPrivKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing ControllerEncryptionPrivKey") + } + if w.KeyId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing KeyId") + } + if w.Nonce == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing Nonce") + } + + return nil +} + +// TableName returns the table name. +func (w *WorkerAuth) TableName() string { + if w.tableName != "" { + return w.tableName + } + return "worker_auth_authorized" +} + +// SetTableName sets the table name. +func (w *WorkerAuth) SetTableName(n string) { + w.tableName = n +} + +// WorkerCertBundle contains all fields related to a WorkerCertBundle resource +// This includes the serial number of the issuing CA, the worker id, and the certificate bundles issued by the CA +type WorkerCertBundle struct { + *store.WorkerCertBundle + tableName string `gorm:"-"` +} + +func newWorkerCertBundle(ctx context.Context, certificatePublicKey []byte, workerKeyIdentifier string, certBundle []byte) (*WorkerCertBundle, error) { + const op = "servers.newWorkerCertBundle" + + if certificatePublicKey == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no certificatePublicKey") + } + if workerKeyIdentifier == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no workerKeyIdentifier") + } + if certBundle == nil || len(certBundle) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty certBundle") + } + + l := &WorkerCertBundle{ + WorkerCertBundle: &store.WorkerCertBundle{ + CertificatePublicKey: certificatePublicKey, + WorkerKeyIdentifier: workerKeyIdentifier, + CertBundle: certBundle, + }, + } + return l, nil +} + +func AllocWorkerCertBundle() *WorkerCertBundle { + return &WorkerCertBundle{ + WorkerCertBundle: &store.WorkerCertBundle{}, + } +} + +func (w *WorkerCertBundle) clone() *WorkerCertBundle { + cp := proto.Clone(w.WorkerCertBundle) + return &WorkerCertBundle{ + WorkerCertBundle: cp.(*store.WorkerCertBundle), + } +} + +// Validate is called before storing a WorkerCertBundle in the db +func (w *WorkerCertBundle) ValidateNewWorkerCertBundle(ctx context.Context) error { + const op = "servers.(WorkerAuth).validateNewWorkerCertBundle" + if w.CertificatePublicKey == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing CertificatePublicKey") + } + if w.WorkerKeyIdentifier == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing WorkerKeyIdentifier") + } + if w.CertBundle == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing certificate bundle") + } + + return nil +} + +// TableName returns the table name. +func (w *WorkerCertBundle) TableName() string { + if w.tableName != "" { + return w.tableName + } + return "worker_auth_certificate_bundle" +} + +// SetTableName sets the table name. +func (w *WorkerCertBundle) SetTableName(n string) { + w.tableName = n +} diff --git a/internal/servers/workerauth_store_test.go b/internal/servers/workerauth_store_test.go new file mode 100644 index 000000000..d362ce78e --- /dev/null +++ b/internal/servers/workerauth_store_test.go @@ -0,0 +1,524 @@ +package servers + +import ( + "context" + "math/rand" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "google.golang.org/protobuf/testing/protocmp" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestRootCertStore(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + kmsCache := kms.TestKms(t, conn, wrapper) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + testKey, err := databaseWrapper.KeyId(ctx) + require.NoError(t, err) + + beforeTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(-1 * time.Hour))} + afterTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(1 * time.Hour))} + publicKey := populateBytes(defaultLength) + privateKey := populateBytes(defaultLength) + certificate := populateBytes(defaultLength) + + type args struct { + serialNumber uint64 + certificate []byte + rootCertKeys RootCertificateKeys + notValidBefore *timestamp.Timestamp + notValidAfter *timestamp.Timestamp + keyId string + state CertificateState + } + + tests := []struct { + name string + args args + expectedRootCert *store.RootCertificate + wantErr bool + wantCreateErr bool + }{ + { + name: "rootcert-store-next", + args: args{ + serialNumber: 1234567890, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: NextState, + }, + expectedRootCert: &store.RootCertificate{ + SerialNumber: 1234567890, + PublicKey: publicKey, + PrivateKey: privateKey, + Certificate: certificate, + NotValidBefore: beforeTimestamp, + NotValidAfter: afterTimestamp, + KeyId: testKey, + State: "next", + }, + wantErr: false, + }, + { + name: "rootcert-store-current", + args: args{ + serialNumber: 9876543210, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: CurrentState, + }, + expectedRootCert: &store.RootCertificate{ + SerialNumber: 9876543210, + PublicKey: publicKey, + PrivateKey: privateKey, + Certificate: certificate, + NotValidBefore: beforeTimestamp, + NotValidAfter: afterTimestamp, + KeyId: testKey, + State: "current", + }, + wantErr: false, + }, + { + name: "rootcert-store-empty-key", + args: args{ + serialNumber: 9876543210, + rootCertKeys: RootCertificateKeys{publicKey: populateBytes(0), privateKey: privateKey}, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: NextState, + }, + wantErr: true, + }, + { + name: "rootcert-no-keys", + args: args{ + serialNumber: 1011121314, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: NextState, + }, + wantErr: true, + }, + { + name: "rootcert-no-cert", + args: args{ + serialNumber: 15161718, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: NextState, + }, + wantErr: true, + }, + { + name: "rootcert-invalid-timestamps", + args: args{ + serialNumber: 1920212223, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: afterTimestamp, + notValidAfter: beforeTimestamp, + keyId: testKey, + state: NextState, + }, + wantCreateErr: true, + }, + { + name: "rootcert-invalid-timestamps-2", + args: args{ + serialNumber: 2425262728, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: afterTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: NextState, + }, + wantCreateErr: true, + }, + { + name: "rootcert-invalid-state", + args: args{ + serialNumber: 3031323334, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + keyId: testKey, + state: UnknownState, + }, + wantErr: true, + }, + { + name: "rootcert-no-key-id", + args: args{ + serialNumber: 4041424344, + rootCertKeys: RootCertificateKeys{publicKey: publicKey, privateKey: privateKey}, + certificate: certificate, + notValidBefore: beforeTimestamp, + notValidAfter: afterTimestamp, + state: NextState, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + cert, err := newRootCertificate(ctx, tt.args.serialNumber, tt.args.certificate, tt.args.notValidBefore, tt.args.notValidAfter, + tt.args.rootCertKeys, tt.args.keyId, tt.args.state) + + if tt.wantErr { + assert.Error(err) + return + } + require.NoError(err) + require.NotNil(cert) + + err = rw.Create(ctx, cert) + if tt.wantCreateErr { + assert.Error(err) + return + } else { + assert.NoError(err) + assert.Equal(tt.expectedRootCert, cert.RootCertificate) + assert.Empty(cmp.Diff(tt.expectedRootCert, cert, protocmp.Transform())) + } + + deleteWhere := `state = ?` + deleted, err := rw.Delete(ctx, &RootCertificate{}, db.WithWhere(deleteWhere, cert.State)) + assert.NoError(err) + assert.Equal(1, deleted) + }) + } +} + +func TestDuplicateRootCert(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + kmsCache := kms.TestKms(t, conn, wrapper) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + testKey, err := databaseWrapper.KeyId(ctx) + require.NoError(t, err) + + beforeTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(-1 * time.Hour))} + afterTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(1 * time.Hour))} + + // Make two current and next root CAs + current1, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, + testKey, CurrentState) + require.NoError(t, err) + current2, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, + testKey, CurrentState) + next1, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, + testKey, NextState) + require.NoError(t, err) + next2, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, + testKey, NextState) + require.NoError(t, err) + + // Insert first set of certs + err = rw.Create(ctx, current1) + require.NoError(t, err) + err = rw.Create(ctx, next1) + require.NoError(t, err) + + // Insert second current cert and expect an error + err = rw.Create(ctx, current2) + require.Error(t, err) + err = rw.Create(ctx, next2) + require.Error(t, err) +} + +func TestWorkerAuthStore(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + kmsCache := kms.TestKms(t, conn, wrapper) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase) + require.NoError(t, err) + testKey, err := databaseWrapper.KeyId(ctx) + require.NoError(t, err) + + worker := TestWorker(t, conn, wrapper) + + wSignPubKey := populateBytes(defaultLength) + wEncPubKey := populateBytes(defaultLength) + workerKeys := WorkerKeys{workerSigningPubKey: wSignPubKey, workerEncryptionPubKey: wEncPubKey} + controllerKey := populateBytes(defaultLength) + nonce := populateBytes(defaultLength) + + type args struct { + workerKeyIdentifier string + workerId string + } + tests := []struct { + name string + args args + expectedWorkerAuth *store.WorkerAuth + opt []Option + wantErr bool + wantCreateErr bool + }{ + { + name: "workerauth-store", + args: args{ + workerKeyIdentifier: "worker-auth-id-123", + workerId: worker.PublicId, + }, + opt: []Option{ + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + WithKeyId(testKey), + }, + expectedWorkerAuth: &store.WorkerAuth{ + WorkerKeyIdentifier: "worker-auth-id-123", + WorkerId: worker.PublicId, + WorkerSigningPubKey: wSignPubKey, + WorkerEncryptionPubKey: wEncPubKey, + ControllerEncryptionPrivKey: controllerKey, + KeyId: testKey, + Nonce: nonce, + }, + wantErr: false, + }, + { + name: "workerauth-store-invalid-key-id", + args: args{ + workerKeyIdentifier: "worker-auth-id-456", + workerId: worker.PublicId, + }, + opt: []Option{ + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + WithKeyId("fakeyityfake"), + }, + wantCreateErr: true, + }, + { + name: "workerauth-no-pkey", + args: args{ + workerId: worker.PublicId, + }, + opt: []Option{ + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + WithKeyId(testKey), + }, + wantErr: true, + }, + { + name: "workerauth-empty-pkey", + args: args{ + workerKeyIdentifier: "", + workerId: worker.PublicId, + }, + opt: []Option{ + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + WithKeyId(testKey), + }, + wantErr: true, + }, + { + name: "workerauth-invalid-workerid", + args: args{ + workerKeyIdentifier: "worker-auth-id-101", + workerId: "bogus-worker", + }, + opt: []Option{ + WithWorkerKeys(workerKeys), + WithControllerEncryptionPrivateKey(controllerKey), + WithNonce(nonce), + WithKeyId(testKey), + }, + wantCreateErr: true, + }, + { + name: "empty-workerauth", + args: args{}, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + wAuth, err := newWorkerAuth(ctx, tt.args.workerKeyIdentifier, tt.args.workerId, tt.opt...) + + if tt.wantErr { + assert.Error(err) + return + } + require.NoError(err) + require.NotNil(wAuth) + + err = rw.Create(ctx, wAuth) + if tt.wantCreateErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tt.expectedWorkerAuth, wAuth.WorkerAuth) + assert.Empty(cmp.Diff(tt.expectedWorkerAuth, wAuth, protocmp.Transform())) + } + }) + } +} + +func TestWorkerCertBundle(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + testKey := TestKkmsKey(ctx, t, conn, wrapper) + + worker := TestWorker(t, conn, wrapper) + workerAuth := TestWorkerAuth(ctx, t, conn, worker, testKey) + rootCA := TestRootCertificate(ctx, t, conn, testKey) + certBundle := populateBytes(defaultLength) + + type args struct { + certificatePubKey []byte + workerAuthId string + certificateBundle []byte + } + tests := []struct { + name string + args args + expected *store.WorkerCertBundle + wantErr bool + wantCreateErr bool + }{ + { + name: "workercertbundle-store", + args: args{ + certificatePubKey: rootCA.PublicKey, + workerAuthId: workerAuth.WorkerKeyIdentifier, + certificateBundle: certBundle, + }, + expected: &store.WorkerCertBundle{ + CertificatePublicKey: rootCA.PublicKey, + WorkerKeyIdentifier: workerAuth.WorkerKeyIdentifier, + CertBundle: certBundle, + }, + wantErr: false, + }, + { + name: "workercertbundle-empty-cert-bundle", + args: args{ + certificatePubKey: rootCA.PublicKey, + workerAuthId: workerAuth.WorkerKeyIdentifier, + }, + wantErr: true, + }, + { + name: "workercertbundle-no-worker-id", + args: args{ + certificatePubKey: rootCA.PublicKey, + certificateBundle: certBundle, + }, + wantErr: true, + }, + { + name: "workercertbundle-invalid-worker-id", + args: args{ + certificatePubKey: rootCA.PublicKey, + workerAuthId: "bogus-worker", + certificateBundle: certBundle, + }, + wantCreateErr: true, + }, + { + name: "workercertbundle-empty-publickey", + args: args{ + workerAuthId: workerAuth.WorkerKeyIdentifier, + certificateBundle: certBundle, + }, + wantErr: true, + }, + { + name: "workercertbundle-invalid-publickey", + args: args{ + certificatePubKey: populateBytes(defaultLength), + workerAuthId: workerAuth.WorkerKeyIdentifier, + certificateBundle: certBundle, + }, + wantCreateErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + workerCertBundle, err := newWorkerCertBundle(ctx, tt.args.certificatePubKey, tt.args.workerAuthId, tt.args.certificateBundle) + + if tt.wantErr { + assert.Error(err) + return + } + require.NoError(err) + require.NotNil(workerCertBundle) + + err = rw.Create(ctx, workerCertBundle) + if tt.wantCreateErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tt.expected, workerCertBundle.WorkerCertBundle) + assert.Empty(cmp.Diff(tt.expected, workerCertBundle, protocmp.Transform())) + } + }) + } +} From 20c314787d4ff6e58eecbf5341f25f1487d25353 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Fri, 20 May 2022 10:23:15 -0400 Subject: [PATCH 016/100] fixup! feat(pki): Add PKI schemas and protos --- ...tion.up.sql => 03_worker_authentication.up.sql} | 0 internal/servers/testing.go | 14 +++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) rename internal/db/schema/migrations/oss/postgres/99/{02_worker_authentication.up.sql => 03_worker_authentication.up.sql} (100%) diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_authentication.up.sql b/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/99/02_worker_authentication.up.sql rename to internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql diff --git a/internal/servers/testing.go b/internal/servers/testing.go index 0c12e7a62..81b050195 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -2,6 +2,7 @@ package servers import ( "context" + "github.com/hashicorp/boundary/internal/types/scope" "math/rand" "testing" "time" @@ -92,12 +93,15 @@ func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *store.Work id = "test-session-worker-" + id name := "test-worker-" + id - worker := &store.Worker{ - PublicId: id, - Name: name, - Address: "127.0.0.1", + worker := &Worker{ + Worker: &store.Worker{ + PublicId: id, + Name: name, + Address: "127.0.0.1", + ScopeId: scope.Global.String(), + }, } _, _, err = serversRepo.UpsertWorker(context.Background(), worker) require.NoError(t, err) - return worker + return worker.Worker } From f6ef708466c7f4a2015434ac2b3597c35f56e983 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 20 May 2022 10:25:54 -0400 Subject: [PATCH 017/100] Update against new nodeenrollment registration cache --- go.mod | 3 ++- go.sum | 10 ++++------ internal/cmd/commands/dev/dev.go | 9 ++------- internal/daemon/controller/handler.go | 17 ++++------------- 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index a00e1c979..efe694352 100644 --- a/go.mod +++ b/go.mod @@ -91,7 +91,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220512190651-85bdcdd684e6 - github.com/hashicorp/nodeenrollment v0.0.0-20220519180751-27ee40a67a33 + github.com/hashicorp/nodeenrollment v0.0.0-20220520141833-ad7323153acc ) require ( @@ -166,6 +166,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect github.com/opencontainers/runc v1.0.2 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect diff --git a/go.sum b/go.sum index 32481aff2..8698be076 100644 --- a/go.sum +++ b/go.sum @@ -741,12 +741,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220516125915-9a115b0fb887 h1:Ov/lPq1YFKt6CLPCriEL21NqMndrJEKNLIZW9qJYpaE= -github.com/hashicorp/nodeenrollment v0.0.0-20220516125915-9a115b0fb887/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= -github.com/hashicorp/nodeenrollment v0.0.0-20220519052605-83a44d379ae6 h1:NrZnIV4TYGDymhzcx/xNIe02obXSrJsPJrPm+OZdXug= -github.com/hashicorp/nodeenrollment v0.0.0-20220519052605-83a44d379ae6/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= -github.com/hashicorp/nodeenrollment v0.0.0-20220519180751-27ee40a67a33 h1:kVIAR0iBLc224QqU4l9QQuJBN8MuEjanc7rHzuCIUVM= -github.com/hashicorp/nodeenrollment v0.0.0-20220519180751-27ee40a67a33/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.0.0-20220520141833-ad7323153acc h1:7RE6UxjVxa3uUS0A4RZwVRFnBJqNQnm5nzNwqPJy4VM= +github.com/hashicorp/nodeenrollment v0.0.0-20220520141833-ad7323153acc/go.mod h1:3054+zYuaQzgXjoy8kyd8whmx1dXUSomyMTRnWHzVXs= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= @@ -1093,6 +1089,8 @@ github.com/ory/dockertest/v3 v3.7.0 h1:Bijzonc69Ont3OU0a3TWKJ1Rzlh3TsDXP1JrTAkSm github.com/ory/dockertest/v3 v3.7.0/go.mod h1:PvCCgnP7AfBZeVrzwiUTjZx/IUXlGLC1zQlUQrLIlUE= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 1b301907f..397dcb531 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -29,7 +29,7 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/strutil" - "github.com/hashicorp/nodeenrollment/types" + "github.com/hashicorp/nodeenrollment" "github.com/mitchellh/cli" "github.com/posener/complete" "go.uber.org/atomic" @@ -689,12 +689,7 @@ func (c *Command) Run(args []string) int { case <-c.Context.Done(): return case <-time.After(time.Second): - waitingNodes, err := c.controller.NodeeFileStorage.List(c.Context, (*types.NodeInformation)(nil)) - if err != nil { - c.UI.Error(fmt.Errorf("Error listing dev worker for authentication: %w", err).Error()) - errorEncountered.Store(true) - return - } + waitingNodes := nodeenrollment.DefaultRegistrationCache.Items() if len(waitingNodes) == 0 { continue } diff --git a/internal/daemon/controller/handler.go b/internal/daemon/controller/handler.go index bbe9e7822..abdcfbbc4 100644 --- a/internal/daemon/controller/handler.go +++ b/internal/daemon/controller/handler.go @@ -43,6 +43,7 @@ import ( "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-secure-stdlib/listenerutil" "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/types" "github.com/mr-tron/base58" "google.golang.org/grpc" @@ -573,19 +574,9 @@ func handleNodes(c *Controller) http.Handler { } var err error var currVals vals - waitingNodes, err := c.NodeeFileStorage.List(c.baseContext, (*types.NodeInformation)(nil)) - if err != nil { - _, _ = w.Write([]byte(err.Error())) - w.WriteHeader(500) - return - } - for _, keyId := range waitingNodes { - ni, err := types.LoadNodeInformation(c.baseContext, c.NodeeFileStorage, keyId) - if err != nil { - _, _ = w.Write([]byte(err.Error())) - w.WriteHeader(500) - return - } + waitingNodes := nodeenrollment.DefaultRegistrationCache.Items() + for keyId, val := range waitingNodes { + ni := val.(*types.NodeInformation) if ni.Authorized { continue } From af3f75ffaf590b5c718bff4d05440098e98458da Mon Sep 17 00:00:00 2001 From: irenarindos Date: Fri, 20 May 2022 10:30:56 -0400 Subject: [PATCH 018/100] fixup! feat(pki): Add PKI schemas and protos --- internal/servers/testing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/servers/testing.go b/internal/servers/testing.go index 81b050195..def4ca5e6 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -2,7 +2,6 @@ package servers import ( "context" - "github.com/hashicorp/boundary/internal/types/scope" "math/rand" "testing" "time" @@ -12,6 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" wrapping "github.com/hashicorp/go-kms-wrapping/v2" "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" From 3fbf901739f7e7b933bae448ccb902013c673bc7 Mon Sep 17 00:00:00 2001 From: Todd Date: Fri, 20 May 2022 11:55:54 -0700 Subject: [PATCH 019/100] Add source to worker tags table (#2087) * Remove the server_id from session table, change it to worker_id on session_connection This is step 1 in updating the worker table. The worker is becoming a public resource so it must have a public id. We drop the server_id column on the session_connection table and add the worker_id column so we can use the wt_public_key type. Since db migrations are applied while the controller is offline, it should be safe to assume all session connections are not in a running state, thus allowing us to null out all the worker ids in the session_connection table. For now we don't have very good enforcement of the wt_public_id contraints, since the id is currently defined in the worker config, but this will soon change and the worker's id will be generated by the controller. * Adding worker tag db writing test. * Move the server table back to the top. * Update comment for the worker object. * Update comment for the worker table. * Add worker tag sources ("config" or "api") * Revert renaming of UpsertWorker until later. * Remove no longer needed files. * Moving the TestWorker into the servers package. Fix the worker tags test. * Update internal/proto/controller/storage/servers/store/v1/worker.proto Co-authored-by: Irena Rindos * Place the repos back together so they can be refactored in a different PR. Co-authored-by: Irena Rindos --- .../workers/worker_service_status_test.go | 6 +-- .../99/02_worker_controller_tables.up.sql | 20 ++++++++- .../storage/servers/store/v1/worker.proto | 4 ++ internal/servers/query.go | 2 +- internal/servers/repository.go | 8 ++-- internal/servers/repository_test.go | 2 +- internal/servers/store/worker.pb.go | 23 +++++++--- internal/servers/testing.go | 31 ++++++-------- internal/servers/testing_test.go | 25 +++++++++++ internal/servers/worker.go | 8 ---- internal/servers/worker_tag.go | 26 ++++++++++++ internal/servers/worker_tags_test.go | 42 ++++++++++++++++--- .../session/repository_connection_test.go | 5 ++- .../service_authorize_connection_test.go | 3 +- .../service_worker_status_report_test.go | 14 +++---- internal/session/session_cleanup_job_test.go | 16 +++---- internal/session/testing.go | 26 ------------ internal/session/testing_test.go | 3 +- 18 files changed, 171 insertions(+), 93 deletions(-) create mode 100644 internal/servers/testing_test.go create mode 100644 internal/servers/worker_tag.go diff --git a/internal/daemon/controller/handlers/workers/worker_service_status_test.go b/internal/daemon/controller/handlers/workers/worker_service_status_test.go index 9c8854fa2..bc3842897 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_status_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_status_test.go @@ -71,7 +71,7 @@ func TestStatus(t *testing.T) { target.WithSessionConnectionLimit(-1), ) - worker1 := session.TestWorker(t, conn, wrapper) + worker1 := servers.TestWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -239,7 +239,7 @@ func TestStatusSessionClosed(t *testing.T) { target.WithSessionConnectionLimit(-1), ) - worker1 := session.TestWorker(t, conn, wrapper) + worker1 := servers.TestWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -421,7 +421,7 @@ func TestStatusDeadConnection(t *testing.T) { target.WithSessionConnectionLimit(-1), ) - worker1 := session.TestWorker(t, conn, wrapper) + worker1 := servers.TestWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index 88f97444b..aa1ed8142 100644 --- a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -59,6 +59,19 @@ create trigger worker_update_time_column before update on server_worker for each row execute procedure update_time_column(); -- Create table worker tag +create table server_worker_tag_enm ( + source text primary key + constraint only_predefined_server_worker_tag_sources_allowed + check ( + source in ('configuration', 'api') + ) +); + +insert into server_worker_tag_enm (source) +values + ('configuration'), + ('api'); + create table server_worker_tag ( worker_id wt_public_id constraint server_worker_fkey @@ -67,7 +80,12 @@ create table server_worker_tag ( on update cascade, key wt_tagpair, value wt_tagpair, - primary key(worker_id, key, value) + source text not null + constraint server_worker_tag_enm_fkey + references server_worker_tag_enm(source) + on delete restrict + on update cascade, + primary key(worker_id, key, value, source) ); -- Aaand drop server_tag diff --git a/internal/proto/controller/storage/servers/store/v1/worker.proto b/internal/proto/controller/storage/servers/store/v1/worker.proto index 3898131d2..aa743f32d 100644 --- a/internal/proto/controller/storage/servers/store/v1/worker.proto +++ b/internal/proto/controller/storage/servers/store/v1/worker.proto @@ -55,4 +55,8 @@ message WorkerTag { // value is the value // @inject_tag: `gorm:"default:null"` string value = 30; + + // source is the source of the tag. Either 'configuration' or 'api'. + // @inject_tag: `gorm:"default:not_null"` + string source = 40; } diff --git a/internal/servers/query.go b/internal/servers/query.go index 81d52677e..61e07b293 100644 --- a/internal/servers/query.go +++ b/internal/servers/query.go @@ -2,5 +2,5 @@ package servers const ( deleteWhereCreateTimeSql = `create_time < ?` - deleteTagsSql = `worker_id = ?` + deleteConfigTagsSql = `source = 'configuration' and worker_id = ?` ) diff --git a/internal/servers/repository.go b/internal/servers/repository.go index 908a6beee..f6609847e 100644 --- a/internal/servers/repository.go +++ b/internal/servers/repository.go @@ -119,7 +119,8 @@ func (r *Repository) listControllersWithReader(ctx context.Context, reader db.Re // ListTagsForWorkers returns a map from the worker's id to the list of Tags // that are for that worker. All options are ignored. -func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, opt ...Option) (map[string][]*Tag, error) { +func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, _ ...Option) (map[string][]*Tag, error) { + const op = "servers.ListTagsForWorkers" var workerTags []*store.WorkerTag if err := r.reader.SearchWhere( ctx, @@ -128,7 +129,7 @@ func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, []interface{}{workerIds}, db.WithLimit(-1), ); err != nil { - return nil, errors.Wrap(ctx, err, "servers.ListTagsForWorkers", errors.WithMsg(fmt.Sprintf("worker IDs %v", workerIds))) + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("worker IDs %v", workerIds))) } ret := make(map[string][]*Tag, len(workerIds)) @@ -185,7 +186,7 @@ func (r *Repository) UpsertWorker(ctx context.Context, worker *Worker, opt ...Op // delete all tags for the given worker, then add the new ones // we've been sent. if opts.withUpdateTags { - _, err = w.Delete(ctx, &store.WorkerTag{}, db.WithWhere(deleteTagsSql, worker.GetPublicId())) + _, err = w.Delete(ctx, &store.WorkerTag{}, db.WithWhere(deleteConfigTagsSql, worker.GetPublicId())) if err != nil { return errors.Wrap(ctx, err, op+":DeleteTags", errors.WithMsg(worker.GetPublicId())) } @@ -204,6 +205,7 @@ func (r *Repository) UpsertWorker(ctx context.Context, worker *Worker, opt ...Op WorkerId: worker.GetPublicId(), Key: v.Key, Value: v.Value, + Source: string(ConfigurationTagSource), }) } if err = w.CreateItems(ctx, tags); err != nil { diff --git a/internal/servers/repository_test.go b/internal/servers/repository_test.go index 8f46ebe7e..30519f577 100644 --- a/internal/servers/repository_test.go +++ b/internal/servers/repository_test.go @@ -194,7 +194,7 @@ func TestTagUpdatingListing(t *testing.T) { require.Equal(exp, tags) } -func TestListServersWithLiveness(t *testing.T) { +func TestListWorkersWithLiveness(t *testing.T) { t.Parallel() require := require.New(t) conn, _ := db.TestSetup(t, "postgres") diff --git a/internal/servers/store/worker.pb.go b/internal/servers/store/worker.pb.go index 3ad103fc6..1b1825ab0 100644 --- a/internal/servers/store/worker.pb.go +++ b/internal/servers/store/worker.pb.go @@ -157,6 +157,9 @@ type WorkerTag struct { // value is the value // @inject_tag: `gorm:"default:null"` Value string `protobuf:"bytes,30,opt,name=value,proto3" json:"value,omitempty" gorm:"default:null"` + // source is the source of the tag. Either 'configuration' or 'api'. + // @inject_tag: `gorm:"default:null"` + Source string `protobuf:"bytes,40,opt,name=source,proto3" json:"source,omitempty" gorm:"default:null"` } func (x *WorkerTag) Reset() { @@ -212,6 +215,13 @@ func (x *WorkerTag) GetValue() string { return "" } +func (x *WorkerTag) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + var File_controller_storage_servers_store_v1_worker_proto protoreflect.FileDescriptor var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ @@ -244,16 +254,17 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, - 0x50, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x09, + 0x68, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, - 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/servers/testing.go b/internal/servers/testing.go index def4ca5e6..79e5c42b1 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -10,10 +10,8 @@ import ( "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/types/scope" wrapping "github.com/hashicorp/go-kms-wrapping/v2" - "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -39,7 +37,7 @@ func TestKkmsKey(ctx context.Context, t *testing.T, conn *db.DB, wrapper wrappin return testKey } -func TestRootCertificate(ctx context.Context, t *testing.T, conn *db.DB, kmsKey string) *store.RootCertificate { +func TestRootCertificate(ctx context.Context, t *testing.T, conn *db.DB, kmsKey string) *RootCertificate { t.Helper() rw := db.New(conn) @@ -55,10 +53,10 @@ func TestRootCertificate(ctx context.Context, t *testing.T, conn *db.DB, kmsKey rootCertKeys, kmsKey, CurrentState) err = rw.Create(ctx, cert) require.NoError(t, err) - return cert.RootCertificate + return cert } -func TestWorkerAuth(ctx context.Context, t *testing.T, conn *db.DB, worker *store.Worker, kmsKey string) *store.WorkerAuth { +func TestWorkerAuth(ctx context.Context, t *testing.T, conn *db.DB, worker *Worker, kmsKey string) *WorkerAuth { t.Helper() rw := db.New(conn) wSignPubKey := populateBytes(defaultLength) @@ -78,30 +76,25 @@ func TestWorkerAuth(ctx context.Context, t *testing.T, conn *db.DB, worker *stor err = rw.Create(ctx, workerAuth) require.NoError(t, err) - return workerAuth.WorkerAuth + return workerAuth } -func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *store.Worker { +func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *Worker { t.Helper() rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) serversRepo, err := NewRepository(rw, rw, kms) require.NoError(t, err) - id, err := uuid.GenerateUUID() + id, err := newWorkerId(context.Background()) require.NoError(t, err) - id = "test-session-worker-" + id - name := "test-worker-" + id - worker := &Worker{ - Worker: &store.Worker{ - PublicId: id, - Name: name, - Address: "127.0.0.1", - ScopeId: scope.Global.String(), - }, - } + + worker := NewWorker(scope.Global.String(), + WithPublicId(id), + WithName(name), + WithAddress("127.0.0.1")) _, _, err = serversRepo.UpsertWorker(context.Background(), worker) require.NoError(t, err) - return worker.Worker + return worker } diff --git a/internal/servers/testing_test.go b/internal/servers/testing_test.go new file mode 100644 index 000000000..0764adf3e --- /dev/null +++ b/internal/servers/testing_test.go @@ -0,0 +1,25 @@ +package servers + +import ( + "context" + "strings" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTestWorker(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + tWorker := TestWorker(t, conn, wrapper) + assert.NotNil(t, tWorker) + assert.True(t, strings.HasPrefix(tWorker.GetPublicId(), WorkerPrefix)) + + lkpWorker := NewWorker(scope.Global.String(), WithPublicId(tWorker.GetPublicId())) + rw := db.New(conn) + require.NoError(t, rw.LookupById(context.Background(), lkpWorker)) + assert.NotNil(t, lkpWorker) +} diff --git a/internal/servers/worker.go b/internal/servers/worker.go index 2b1a68af4..eb1d9b0eb 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -13,14 +13,6 @@ type Worker struct { Tags []*Tag `gorm:"-"` } -// A Tag is a custom key/value pair which can be attached to a Worker. -// Multiple Tags may contain the same key and different values in which -// case both key/value pairs are valid. -type Tag struct { - Key string - Value string -} - // NewWorker returns a new Worker. Valid options are WithName, WithDescription // WithAddress, and WithWorkerTags. All other options are ignored. func NewWorker(scopeId string, opt ...Option) *Worker { diff --git a/internal/servers/worker_tag.go b/internal/servers/worker_tag.go new file mode 100644 index 000000000..75afc0fe6 --- /dev/null +++ b/internal/servers/worker_tag.go @@ -0,0 +1,26 @@ +package servers + +// A Tag is a custom key/value pair which can be attached to a Worker. +// Multiple Tags may contain the same key and different values in which +// case both key/value pairs are valid. Tags can be sourced from either the +// worker's configuration or the api. key/value pairs can be the same from +// different sources. +type Tag struct { + Key string + Value string +} + +type TagSource string + +const ( + ConfigurationTagSource TagSource = "configuration" + ApiTagSource TagSource = "api" +) + +func (t TagSource) isValid() bool { + return t == ConfigurationTagSource || t == ApiTagSource +} + +func (t TagSource) String() string { + return string(t) +} diff --git a/internal/servers/worker_tags_test.go b/internal/servers/worker_tags_test.go index bd7e16019..679832e6a 100644 --- a/internal/servers/worker_tags_test.go +++ b/internal/servers/worker_tags_test.go @@ -6,19 +6,16 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/servers/store" - "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestWorkerTags_Create(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) rw := db.New(conn) - workerPublicid, err := newWorkerId(context.Background()) - require.NoError(t, err) - worker := NewWorker(scope.Global.String(), WithPublicId(workerPublicid)) + worker := TestWorker(t, conn, wrapper) tests := []struct { name string @@ -26,12 +23,41 @@ func TestWorkerTags_Create(t *testing.T) { wantCreateErr bool }{ { - name: "success", + name: "success api source", + want: &store.WorkerTag{ + WorkerId: worker.GetPublicId(), + Key: "key", + Value: "value", + Source: ApiTagSource.String(), + }, + }, + { + name: "success config source", + want: &store.WorkerTag{ + WorkerId: worker.GetPublicId(), + Key: "key", + Value: "value", + Source: ConfigurationTagSource.String(), + }, + }, + { + name: "unknown source", + want: &store.WorkerTag{ + WorkerId: worker.GetPublicId(), + Key: "key", + Value: "value", + Source: "unknown", + }, + wantCreateErr: true, + }, + { + name: "no source", want: &store.WorkerTag{ WorkerId: worker.GetPublicId(), Key: "key", Value: "value", }, + wantCreateErr: true, }, { name: "bad worker id", @@ -56,6 +82,7 @@ func TestWorkerTags_Create(t *testing.T) { WorkerId: worker.GetPublicId(), Value: "value", }, + wantCreateErr: true, }, { name: "missing value", @@ -63,6 +90,7 @@ func TestWorkerTags_Create(t *testing.T) { WorkerId: worker.GetPublicId(), Key: "key", }, + wantCreateErr: true, }, } @@ -72,6 +100,8 @@ func TestWorkerTags_Create(t *testing.T) { err := rw.Create(context.Background(), tt.want) if tt.wantCreateErr { assert.Error(t, err) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/session/repository_connection_test.go b/internal/session/repository_connection_test.go index ba5d38029..200df8272 100644 --- a/internal/session/repository_connection_test.go +++ b/internal/session/repository_connection_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/servers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -358,8 +359,8 @@ func TestRepository_orphanedConnections(t *testing.T) { // Create two "workers". One will remain untouched while the other "goes // away and comes back" (worker 2). - worker1 := TestWorker(t, conn, wrapper) - worker2 := TestWorker(t, conn, wrapper) + worker1 := servers.TestWorker(t, conn, wrapper) + worker2 := servers.TestWorker(t, conn, wrapper) // Create a few sessions on each, activate, and authorize a connection var connIds []string diff --git a/internal/session/service_authorize_connection_test.go b/internal/session/service_authorize_connection_test.go index b6d88580c..6261bfb06 100644 --- a/internal/session/service_authorize_connection_test.go +++ b/internal/session/service_authorize_connection_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/servers" "google.golang.org/protobuf/types/known/timestamppb" "github.com/hashicorp/boundary/internal/db" @@ -35,7 +36,7 @@ func TestService_AuthorizeConnection(t *testing.T) { composedOf.ExpirationTime = exp } s := TestSession(t, conn, wrapper, composedOf) - srv := TestWorker(t, conn, wrapper) + srv := servers.TestWorker(t, conn, wrapper) testServer = srv.PublicId tofu := TestTofu(t) _, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, tofu) diff --git a/internal/session/service_worker_status_report_test.go b/internal/session/service_worker_status_report_test.go index ff5b2b6d7..330da5202 100644 --- a/internal/session/service_worker_status_report_test.go +++ b/internal/session/service_worker_status_report_test.go @@ -68,7 +68,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "No Sessions", caseFn: func(t *testing.T) testCase { - worker := session.TestWorker(t, conn, wrapper) + worker := servers.TestWorker(t, conn, wrapper) return testCase{ worker: worker, req: []session.StateReport{}, @@ -79,7 +79,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "No Sessions already canceled", caseFn: func(t *testing.T) testCase { - worker := session.TestWorker(t, conn, wrapper) + worker := servers.TestWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -111,7 +111,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "Still Active", caseFn: func(t *testing.T) testCase { - worker := session.TestWorker(t, conn, wrapper) + worker := servers.TestWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -145,7 +145,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "SessionClosed", caseFn: func(t *testing.T) testCase { - worker := session.TestWorker(t, conn, wrapper) + worker := servers.TestWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -185,7 +185,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "MultipleSessionsClosed", caseFn: func(t *testing.T) testCase { - worker := session.TestWorker(t, conn, wrapper) + worker := servers.TestWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -252,7 +252,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "OrphanedConnection", caseFn: func(t *testing.T) testCase { - worker := session.TestWorker(t, conn, wrapper) + worker := servers.TestWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -303,7 +303,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "MultipleSessionsAndOrphanedConnections", caseFn: func(t *testing.T) testCase { - worker := session.TestWorker(t, conn, wrapper) + worker := servers.TestWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), diff --git a/internal/session/session_cleanup_job_test.go b/internal/session/session_cleanup_job_test.go index fb8d819b3..75a75fd03 100644 --- a/internal/session/session_cleanup_job_test.go +++ b/internal/session/session_cleanup_job_test.go @@ -44,8 +44,8 @@ func TestSessionConnectionCleanupJob(t *testing.T) { // Create two "workers". One will remain untouched while the other "goes // away and comes back" (worker 2). - worker1 := TestWorker(t, conn, wrapper) - worker2 := TestWorker(t, conn, wrapper) + worker1 := servers.TestWorker(t, conn, wrapper) + worker2 := servers.TestWorker(t, conn, wrapper) // Create a few sessions on each, activate, and authorize a connection var connIds []string @@ -196,10 +196,10 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // firstly, the last worker will have no connections at all, and we will be // closing the others in stages to test multiple servers being closed at // once. - worker1 := TestWorker(t, conn, wrapper) - worker2 := TestWorker(t, conn, wrapper) - worker3 := TestWorker(t, conn, wrapper) - worker4 := TestWorker(t, conn, wrapper) + worker1 := servers.TestWorker(t, conn, wrapper) + worker2 := servers.TestWorker(t, conn, wrapper) + worker3 := servers.TestWorker(t, conn, wrapper) + worker4 := servers.TestWorker(t, conn, wrapper) // Create sessions on the first three, activate, and authorize connections var worker1ConnIds, worker2ConnIds, worker3ConnIds []string @@ -471,7 +471,7 @@ func TestCloseWorkerlessConnections(t *testing.T) { } // Setup deleted worker connections - deletedWorker := TestWorker(t, conn, wrapper) + deletedWorker := servers.TestWorker(t, conn, wrapper) dActiveConn := createConnection(deletedWorker.GetPublicId()) dClosedConn := createConnection(deletedWorker.GetPublicId()) connRepo.closeConnections(ctx, []CloseWith{{ @@ -484,7 +484,7 @@ func TestCloseWorkerlessConnections(t *testing.T) { require.NoError(err) // Non deleted worker case - activeWorker := TestWorker(t, conn, wrapper) + activeWorker := servers.TestWorker(t, conn, wrapper) activeConn := createConnection(activeWorker.GetPublicId()) closedConn := createConnection(activeWorker.GetPublicId()) connRepo.closeConnections(ctx, []CloseWith{{ diff --git a/internal/session/testing.go b/internal/session/testing.go index dfc783025..4d7b9aecf 100644 --- a/internal/session/testing.go +++ b/internal/session/testing.go @@ -6,8 +6,6 @@ import ( "testing" "time" - "github.com/hashicorp/boundary/internal/types/scope" - "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/db" @@ -16,12 +14,10 @@ import ( "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" wrapping "github.com/hashicorp/go-kms-wrapping/v2" "github.com/hashicorp/go-secure-stdlib/base62" - "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -180,28 +176,6 @@ func TestTofu(t testing.TB) []byte { return []byte(tofu) } -// TestWorker inserts a worker into the db to satisfy foreign key constraints. -func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *servers.Worker { - t.Helper() - rw := db.New(conn) - kms := kms.TestKms(t, conn, wrapper) - serversRepo, err := servers.NewRepository(rw, rw, kms) - require.NoError(t, err) - - id, err := uuid.GenerateUUID() - require.NoError(t, err) - id = "test_session_worker-" + id - - name := "test-worker-" + id - worker := servers.NewWorker(scope.Global.String(), - servers.WithPublicId(id), - servers.WithName(name), - servers.WithAddress("127.0.0.1")) - _, _, err = serversRepo.UpsertWorker(context.Background(), worker) - require.NoError(t, err) - return worker -} - // TestCert is a temporary test func that intentionally doesn't take testing.T // as a parameter. It's currently used in controller.jobTestingHandler() and // should be deprecated once that function is refactored to use sessions properly. diff --git a/internal/session/testing_test.go b/internal/session/testing_test.go index 8e18df86a..f344ae896 100644 --- a/internal/session/testing_test.go +++ b/internal/session/testing_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/servers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -73,7 +74,7 @@ func Test_TestWorker(t *testing.T) { require := require.New(t) conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) - w := TestWorker(t, conn, wrapper) + w := servers.TestWorker(t, conn, wrapper) require.NotNil(w) } From 1c575a8762c9e0549bf3d499da9f4b285cc83a2c Mon Sep 17 00:00:00 2001 From: Todd Date: Fri, 20 May 2022 11:58:40 -0700 Subject: [PATCH 020/100] make gen fix. --- internal/servers/store/worker.pb.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/servers/store/worker.pb.go b/internal/servers/store/worker.pb.go index 1b1825ab0..7dec56b04 100644 --- a/internal/servers/store/worker.pb.go +++ b/internal/servers/store/worker.pb.go @@ -158,8 +158,8 @@ type WorkerTag struct { // @inject_tag: `gorm:"default:null"` Value string `protobuf:"bytes,30,opt,name=value,proto3" json:"value,omitempty" gorm:"default:null"` // source is the source of the tag. Either 'configuration' or 'api'. - // @inject_tag: `gorm:"default:null"` - Source string `protobuf:"bytes,40,opt,name=source,proto3" json:"source,omitempty" gorm:"default:null"` + // @inject_tag: `gorm:"default:not_null"` + Source string `protobuf:"bytes,40,opt,name=source,proto3" json:"source,omitempty" gorm:"default:not_null"` } func (x *WorkerTag) Reset() { From e5f02dfc26ee47897c7829c737c24129a3a20475 Mon Sep 17 00:00:00 2001 From: Todd Date: Mon, 23 May 2022 09:07:30 -0700 Subject: [PATCH 021/100] Refactor the different repo methods into separate files (#2096) * Refactor the different repo methods into separate files * Move DefaultLiveness to the repository.go file to be shared across workers and controllers. --- internal/servers/repository.go | 273 ------------------ internal/servers/repository_controller.go | 76 +++++ internal/servers/repository_nonce.go | 74 +++++ internal/servers/repository_nonce_test.go | 87 ++++++ internal/servers/repository_worker.go | 150 ++++++++++ ...tory_test.go => repository_worker_test.go} | 74 ----- 6 files changed, 387 insertions(+), 347 deletions(-) create mode 100644 internal/servers/repository_controller.go create mode 100644 internal/servers/repository_nonce.go create mode 100644 internal/servers/repository_nonce_test.go create mode 100644 internal/servers/repository_worker.go rename internal/servers/{repository_test.go => repository_worker_test.go} (66%) diff --git a/internal/servers/repository.go b/internal/servers/repository.go index f6609847e..c8dcab618 100644 --- a/internal/servers/repository.go +++ b/internal/servers/repository.go @@ -1,15 +1,11 @@ package servers import ( - "context" - "fmt" "time" - "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/servers/store" ) const ( @@ -50,272 +46,3 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms) (*Repository, error) kms: kms, }, nil } - -// ListWorkers is a passthrough to listWorkersWithReader that uses the repo's normal reader. -func (r *Repository) ListWorkers(ctx context.Context, opt ...Option) ([]*Worker, error) { - return r.listWorkersWithReader(ctx, r.reader, opt...) -} - -// listWorkersWithReader will return a listing of resources and honor the WithLimit option or the repo -// defaultLimit. It accepts a reader, allowing it to be used within a transaction or without. -func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader, opt ...Option) ([]*Worker, error) { - opts := getOpts(opt...) - liveness := opts.withLiveness - if liveness == 0 { - liveness = DefaultLiveness - } - - var where string - if liveness > 0 { - where = fmt.Sprintf("update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) - } - - var workers []*Worker - if err := reader.SearchWhere( - ctx, - &workers, - where, - []interface{}{}, - db.WithLimit(-1), - ); err != nil { - return nil, errors.Wrap(ctx, err, "workers.listWorkersWithReader") - } - - return workers, nil -} - -func (r *Repository) ListControllers(ctx context.Context, opt ...Option) ([]*store.Controller, error) { - return r.listControllersWithReader(ctx, r.reader, opt...) -} - -// listControllersWithReader will return a listing of resources and honor the -// WithLimit option or the repo defaultLimit. It accepts a reader, allowing it -// to be used within a transaction or without. -func (r *Repository) listControllersWithReader(ctx context.Context, reader db.Reader, opt ...Option) ([]*store.Controller, error) { - opts := getOpts(opt...) - liveness := opts.withLiveness - if liveness == 0 { - liveness = DefaultLiveness - } - - var where string - if liveness > 0 { - where = fmt.Sprintf("update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) - } - - var controllers []*store.Controller - if err := reader.SearchWhere( - ctx, - &controllers, - where, - []interface{}{}, - db.WithLimit(-1), - ); err != nil { - return nil, errors.Wrap(ctx, err, "workers.listControllersWithReader") - } - - return controllers, nil -} - -// ListTagsForWorkers returns a map from the worker's id to the list of Tags -// that are for that worker. All options are ignored. -func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, _ ...Option) (map[string][]*Tag, error) { - const op = "servers.ListTagsForWorkers" - var workerTags []*store.WorkerTag - if err := r.reader.SearchWhere( - ctx, - &workerTags, - "worker_id in (?)", - []interface{}{workerIds}, - db.WithLimit(-1), - ); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("worker IDs %v", workerIds))) - } - - ret := make(map[string][]*Tag, len(workerIds)) - for _, t := range workerTags { - ret[t.WorkerId] = append(ret[t.WorkerId], &Tag{Key: t.Key, Value: t.Value}) - } - return ret, nil -} - -// UpsertWorker creates a new worker if one with the provided public id doesn't -// already exist. If it does, UpsertWorker updates the worker. The -// WithUpdateTags option is the only one used. All others are ignored. -func (r *Repository) UpsertWorker(ctx context.Context, worker *Worker, opt ...Option) ([]*store.Controller, int, error) { - const op = "servers.UpsertWorker" - - switch { - case worker == nil: - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "worker is nil") - case worker.Address == "": - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "address is empty") - case worker.ScopeId == "": - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "scope id is empty") - case worker.PublicId == "": - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "public id is empty") - } - - opts := getOpts(opt...) - - var rowsUpdated int64 - var controllers []*store.Controller - _, err := r.writer.DoTx( - ctx, - db.StdRetryCnt, - db.ExpBackoff{}, - func(read db.Reader, w db.Writer) error { - var err error - onConflict := &db.OnConflict{ - Target: db.Columns{"public_id"}, - Action: append(db.SetColumns([]string{"description", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), - } - err = w.Create(ctx, worker, db.WithOnConflict(onConflict), db.WithReturnRowsAffected(&rowsUpdated)) - if err != nil { - return errors.Wrap(ctx, err, op+":Upsert") - } - - // Fetch current controllers to feed to the workers - controllers, err = r.listControllersWithReader(ctx, read) - if err != nil { - return errors.Wrap(ctx, err, op+":ListController") - } - - // If we've been told to update tags, we need to clean out old - // ones and add new ones. Within the current transaction, simply - // delete all tags for the given worker, then add the new ones - // we've been sent. - if opts.withUpdateTags { - _, err = w.Delete(ctx, &store.WorkerTag{}, db.WithWhere(deleteConfigTagsSql, worker.GetPublicId())) - if err != nil { - return errors.Wrap(ctx, err, op+":DeleteTags", errors.WithMsg(worker.GetPublicId())) - } - - // If tags were cleared out entirely, then we'll have nothing - // to do here, e.g., it will result in deletion of all tags. - // Otherwise, go through and stage each tuple for insertion - // below. - if len(worker.Tags) > 0 { - tags := make([]interface{}, 0, len(worker.Tags)) - for _, v := range worker.Tags { - if v == nil { - return errors.New(ctx, errors.InvalidParameter, op+":RangeTags", fmt.Sprintf("found nil tag value for worker %s", worker.GetPublicId())) - } - tags = append(tags, &store.WorkerTag{ - WorkerId: worker.GetPublicId(), - Key: v.Key, - Value: v.Value, - Source: string(ConfigurationTagSource), - }) - } - if err = w.CreateItems(ctx, tags); err != nil { - return errors.Wrap(ctx, err, op+":CreateTags", errors.WithMsg(worker.GetPublicId())) - } - } - } - - return nil - }, - ) - if err != nil { - return nil, db.NoRowsAffected, err - } - - return controllers, int(rowsUpdated), nil -} - -func (r *Repository) UpsertController(ctx context.Context, controller *store.Controller) (int, error) { - const op = "servers.UpsertController" - - if controller == nil { - return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "controller is nil") - } - - var rowsUpdated int64 - _, err := r.writer.DoTx( - ctx, - db.StdRetryCnt, - db.ExpBackoff{}, - func(read db.Reader, w db.Writer) error { - var err error - onConflict := &db.OnConflict{ - Target: db.Columns{"private_id"}, - Action: append(db.SetColumns([]string{"description", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), - } - err = w.Create(ctx, controller, db.WithOnConflict(onConflict), db.WithReturnRowsAffected(&rowsUpdated)) - if err != nil { - return errors.Wrap(ctx, err, op+":Upsert") - } - - return nil - }, - ) - if err != nil { - return db.NoRowsAffected, err - } - - return int(rowsUpdated), nil -} - -type Nonce struct { - Nonce string - Purpose string -} - -// TableName returns the table name. -func (n *Nonce) TableName() string { - return "nonce" -} - -const ( - NoncePurposeRecovery = "recovery" - NoncePurposeWorkerAuth = "worker-auth" -) - -// AddNonce adds a nonce -func (r *Repository) AddNonce(ctx context.Context, nonce, purpose string, opt ...Option) error { - const op = "servers.AddNonce" - if nonce == "" { - return errors.New(ctx, errors.InvalidParameter, op, "empty nonce") - } - switch purpose { - case NoncePurposeRecovery, NoncePurposeWorkerAuth: - case "": - return errors.New(ctx, errors.InvalidParameter, op, "empty nonce purpose") - default: - return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unknown nonce purpose %q", purpose)) - } - if err := r.writer.Create(ctx, &Nonce{ - Nonce: nonce, - Purpose: purpose, - }); err != nil { - return errors.Wrap(ctx, err, op+":Insertion") - } - return nil -} - -// CleanupNonces removes nonces that no longer need to be stored -func (r *Repository) CleanupNonces(ctx context.Context, opt ...Option) (int, error) { - // Use the largest validity period out of the various nonces we're looking at - maxDuration := globals.RecoveryTokenValidityPeriod - if globals.WorkerAuthNonceValidityPeriod > maxDuration { - maxDuration = globals.WorkerAuthNonceValidityPeriod - } - // If something was inserted before 3x the actual validity period, clean it out - endTime := time.Now().Add(-3 * maxDuration) - - rows, err := r.writer.Delete(ctx, &Nonce{}, db.WithWhere(deleteWhereCreateTimeSql, endTime)) - if err != nil { - return db.NoRowsAffected, errors.Wrap(ctx, err, "servers.CleanupNonces") - } - return rows, nil -} - -// ListNonces lists nonces. Used only for tests at the moment. -func (r *Repository) ListNonces(ctx context.Context, purpose string, opt ...Option) ([]*Nonce, error) { - var nonces []*Nonce - if err := r.reader.SearchWhere(ctx, &nonces, "purpose = ?", []interface{}{purpose}, db.WithLimit(-1)); err != nil { - return nil, errors.Wrap(ctx, err, "servers.ListNonces") - } - return nonces, nil -} diff --git a/internal/servers/repository_controller.go b/internal/servers/repository_controller.go new file mode 100644 index 000000000..931071e0e --- /dev/null +++ b/internal/servers/repository_controller.go @@ -0,0 +1,76 @@ +package servers + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/servers/store" +) + +func (r *Repository) ListControllers(ctx context.Context, opt ...Option) ([]*store.Controller, error) { + return r.listControllersWithReader(ctx, r.reader, opt...) +} + +// listControllersWithReader will return a listing of resources and honor the +// WithLimit option or the repo defaultLimit. It accepts a reader, allowing it +// to be used within a transaction or without. +func (r *Repository) listControllersWithReader(ctx context.Context, reader db.Reader, opt ...Option) ([]*store.Controller, error) { + opts := getOpts(opt...) + liveness := opts.withLiveness + if liveness == 0 { + liveness = DefaultLiveness + } + + var where string + if liveness > 0 { + where = fmt.Sprintf("update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) + } + + var controllers []*store.Controller + if err := reader.SearchWhere( + ctx, + &controllers, + where, + []interface{}{}, + db.WithLimit(-1), + ); err != nil { + return nil, errors.Wrap(ctx, err, "workers.listControllersWithReader") + } + + return controllers, nil +} + +func (r *Repository) UpsertController(ctx context.Context, controller *store.Controller) (int, error) { + const op = "servers.UpsertController" + + if controller == nil { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "controller is nil") + } + + var rowsUpdated int64 + _, err := r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(read db.Reader, w db.Writer) error { + var err error + onConflict := &db.OnConflict{ + Target: db.Columns{"private_id"}, + Action: append(db.SetColumns([]string{"description", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), + } + err = w.Create(ctx, controller, db.WithOnConflict(onConflict), db.WithReturnRowsAffected(&rowsUpdated)) + if err != nil { + return errors.Wrap(ctx, err, op+":Upsert") + } + + return nil + }, + ) + if err != nil { + return db.NoRowsAffected, err + } + + return int(rowsUpdated), nil +} diff --git a/internal/servers/repository_nonce.go b/internal/servers/repository_nonce.go new file mode 100644 index 000000000..b144d78ef --- /dev/null +++ b/internal/servers/repository_nonce.go @@ -0,0 +1,74 @@ +package servers + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/boundary/globals" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" +) + +type Nonce struct { + Nonce string + Purpose string +} + +// TableName returns the table name. +func (n *Nonce) TableName() string { + return "nonce" +} + +const ( + NoncePurposeRecovery = "recovery" + NoncePurposeWorkerAuth = "worker-auth" +) + +// AddNonce adds a nonce +func (r *Repository) AddNonce(ctx context.Context, nonce, purpose string, opt ...Option) error { + const op = "servers.AddNonce" + if nonce == "" { + return errors.New(ctx, errors.InvalidParameter, op, "empty nonce") + } + switch purpose { + case NoncePurposeRecovery, NoncePurposeWorkerAuth: + case "": + return errors.New(ctx, errors.InvalidParameter, op, "empty nonce purpose") + default: + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unknown nonce purpose %q", purpose)) + } + if err := r.writer.Create(ctx, &Nonce{ + Nonce: nonce, + Purpose: purpose, + }); err != nil { + return errors.Wrap(ctx, err, op+":Insertion") + } + return nil +} + +// CleanupNonces removes nonces that no longer need to be stored +func (r *Repository) CleanupNonces(ctx context.Context, opt ...Option) (int, error) { + // Use the largest validity period out of the various nonces we're looking at + maxDuration := globals.RecoveryTokenValidityPeriod + if globals.WorkerAuthNonceValidityPeriod > maxDuration { + maxDuration = globals.WorkerAuthNonceValidityPeriod + } + // If something was inserted before 3x the actual validity period, clean it out + endTime := time.Now().Add(-3 * maxDuration) + + rows, err := r.writer.Delete(ctx, &Nonce{}, db.WithWhere(deleteWhereCreateTimeSql, endTime)) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, "servers.CleanupNonces") + } + return rows, nil +} + +// ListNonces lists nonces. Used only for tests at the moment. +func (r *Repository) ListNonces(ctx context.Context, purpose string, opt ...Option) ([]*Nonce, error) { + var nonces []*Nonce + if err := r.reader.SearchWhere(ctx, &nonces, "purpose = ?", []interface{}{purpose}, db.WithLimit(-1)); err != nil { + return nil, errors.Wrap(ctx, err, "servers.ListNonces") + } + return nonces, nil +} diff --git a/internal/servers/repository_nonce_test.go b/internal/servers/repository_nonce_test.go new file mode 100644 index 000000000..18422c6a1 --- /dev/null +++ b/internal/servers/repository_nonce_test.go @@ -0,0 +1,87 @@ +package servers_test + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/boundary/api/recovery" + "github.com/hashicorp/boundary/api/roles" + "github.com/hashicorp/boundary/globals" + "github.com/hashicorp/boundary/internal/daemon/controller" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRecoveryNonces(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // Set these low so that we can not have the test run forever + globals.RecoveryTokenValidityPeriod = 10 * time.Second + globals.WorkerAuthNonceValidityPeriod = 10 * time.Second + controller.NonceCleanupInterval = 20 * time.Second + + wrapper := db.TestWrapper(t) + tc := controller.NewTestController(t, &controller.TestControllerOpts{ + RecoveryKms: wrapper, + }) + defer tc.Shutdown() + + externalWrappers := tc.Kms().GetExternalWrappers(context.Background()) + externalRecovery := externalWrappers.Recovery() + require.Equal(externalRecovery, wrapper) + + client := tc.Client() + repo := tc.ServersRepo() + + // First, validate that we can't use the same token twice. Get two tokens, + // try to use the first twice (should succeed first time, fail second), then + // ensure second token works + token1, err := recovery.GenerateRecoveryToken(tc.Context(), wrapper) + require.NoError(err) + token2, err := recovery.GenerateRecoveryToken(tc.Context(), wrapper) + require.NoError(err) + + // Token 1, try 1 + client.SetToken(token1) + roleClient := roles.NewClient(client) + _, err = roleClient.Create(tc.Context(), scope.Global.String()) + require.NoError(err) + nonces, err := repo.ListNonces(tc.Context(), servers.NoncePurposeRecovery) + require.NoError(err) + assert.Len(nonces, 1) + + // Token 1, try 2 + _, err = roleClient.Create(tc.Context(), scope.Global.String()) + require.Error(err) + nonces, err = repo.ListNonces(tc.Context(), servers.NoncePurposeRecovery) + require.NoError(err) + assert.Len(nonces, 1) + + // Token 2 + roleClient.ApiClient().SetToken(token2) + _, err = roleClient.Create(tc.Context(), scope.Global.String()) + require.NoError(err) + nonces, err = repo.ListNonces(tc.Context(), servers.NoncePurposeRecovery) + require.NoError(err) + assert.Len(nonces, 2) + + // Make sure they get cleaned up + time.Sleep(2 * controller.NonceCleanupInterval) + nonces, err = repo.ListNonces(tc.Context(), servers.NoncePurposeRecovery) + require.NoError(err) + assert.Len(nonces, 0) + + // And finally, make sure they still can't be used + for _, token := range []string{token1, token2} { + roleClient.ApiClient().SetToken(token) + _, err = roleClient.Create(tc.Context(), scope.Global.String()) + require.Error(err) + nonces, err = repo.ListNonces(tc.Context(), servers.NoncePurposeRecovery) + require.NoError(err) + assert.Len(nonces, 0) + } +} diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go new file mode 100644 index 000000000..76fcda59d --- /dev/null +++ b/internal/servers/repository_worker.go @@ -0,0 +1,150 @@ +package servers + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/servers/store" +) + +// ListWorkers is a passthrough to listWorkersWithReader that uses the repo's normal reader. +func (r *Repository) ListWorkers(ctx context.Context, opt ...Option) ([]*Worker, error) { + return r.listWorkersWithReader(ctx, r.reader, opt...) +} + +// listWorkersWithReader will return a listing of resources and honor the WithLimit option or the repo +// defaultLimit. It accepts a reader, allowing it to be used within a transaction or without. +func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader, opt ...Option) ([]*Worker, error) { + opts := getOpts(opt...) + liveness := opts.withLiveness + if liveness == 0 { + liveness = DefaultLiveness + } + + var where string + if liveness > 0 { + where = fmt.Sprintf("update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) + } + + var workers []*Worker + if err := reader.SearchWhere( + ctx, + &workers, + where, + []interface{}{}, + db.WithLimit(-1), + ); err != nil { + return nil, errors.Wrap(ctx, err, "workers.listWorkersWithReader") + } + + return workers, nil +} + +// ListTagsForWorkers returns a map from the worker's id to the list of Tags +// that are for that worker. All options are ignored. +func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, _ ...Option) (map[string][]*Tag, error) { + const op = "servers.ListTagsForWorkers" + var workerTags []*store.WorkerTag + if err := r.reader.SearchWhere( + ctx, + &workerTags, + "worker_id in (?)", + []interface{}{workerIds}, + db.WithLimit(-1), + ); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("worker IDs %v", workerIds))) + } + + ret := make(map[string][]*Tag, len(workerIds)) + for _, t := range workerTags { + ret[t.WorkerId] = append(ret[t.WorkerId], &Tag{Key: t.Key, Value: t.Value}) + } + return ret, nil +} + +// UpsertWorker creates a new worker if one with the provided public id doesn't +// already exist. If it does, UpsertWorker updates the worker. The +// WithUpdateTags option is the only one used. All others are ignored. +func (r *Repository) UpsertWorker(ctx context.Context, worker *Worker, opt ...Option) ([]*store.Controller, int, error) { + const op = "servers.UpsertWorker" + + switch { + case worker == nil: + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "worker is nil") + case worker.Address == "": + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "address is empty") + case worker.ScopeId == "": + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "scope id is empty") + case worker.PublicId == "": + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "public id is empty") + } + + opts := getOpts(opt...) + + var rowsUpdated int64 + var controllers []*store.Controller + _, err := r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(read db.Reader, w db.Writer) error { + var err error + onConflict := &db.OnConflict{ + Target: db.Columns{"public_id"}, + Action: append(db.SetColumns([]string{"description", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), + } + err = w.Create(ctx, worker, db.WithOnConflict(onConflict), db.WithReturnRowsAffected(&rowsUpdated)) + if err != nil { + return errors.Wrap(ctx, err, op+":Upsert") + } + + // Fetch current controllers to feed to the workers + controllers, err = r.listControllersWithReader(ctx, read) + if err != nil { + return errors.Wrap(ctx, err, op+":ListController") + } + + // If we've been told to update tags, we need to clean out old + // ones and add new ones. Within the current transaction, simply + // delete all tags for the given worker, then add the new ones + // we've been sent. + if opts.withUpdateTags { + _, err = w.Delete(ctx, &store.WorkerTag{}, db.WithWhere(deleteConfigTagsSql, worker.GetPublicId())) + if err != nil { + return errors.Wrap(ctx, err, op+":DeleteTags", errors.WithMsg(worker.GetPublicId())) + } + + // If tags were cleared out entirely, then we'll have nothing + // to do here, e.g., it will result in deletion of all tags. + // Otherwise, go through and stage each tuple for insertion + // below. + if len(worker.Tags) > 0 { + tags := make([]interface{}, 0, len(worker.Tags)) + for _, v := range worker.Tags { + if v == nil { + return errors.New(ctx, errors.InvalidParameter, op+":RangeTags", fmt.Sprintf("found nil tag value for worker %s", worker.GetPublicId())) + } + tags = append(tags, &store.WorkerTag{ + WorkerId: worker.GetPublicId(), + Key: v.Key, + Value: v.Value, + Source: string(ConfigurationTagSource), + }) + } + if err = w.CreateItems(ctx, tags); err != nil { + return errors.Wrap(ctx, err, op+":CreateTags", errors.WithMsg(worker.GetPublicId())) + } + } + } + + return nil + }, + ) + if err != nil { + return nil, db.NoRowsAffected, err + } + + return controllers, int(rowsUpdated), nil +} diff --git a/internal/servers/repository_test.go b/internal/servers/repository_worker_test.go similarity index 66% rename from internal/servers/repository_test.go rename to internal/servers/repository_worker_test.go index 30519f577..1ab6410b8 100644 --- a/internal/servers/repository_test.go +++ b/internal/servers/repository_worker_test.go @@ -5,88 +5,14 @@ import ( "testing" "time" - "github.com/hashicorp/boundary/api/recovery" - "github.com/hashicorp/boundary/api/roles" - "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/daemon/controller" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/types/scope" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestRecoveryNonces(t *testing.T) { - assert, require := assert.New(t), require.New(t) - - // Set these low so that we can not have the test run forever - globals.RecoveryTokenValidityPeriod = 10 * time.Second - globals.WorkerAuthNonceValidityPeriod = 10 * time.Second - controller.NonceCleanupInterval = 20 * time.Second - - wrapper := db.TestWrapper(t) - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - RecoveryKms: wrapper, - }) - defer tc.Shutdown() - - externalWrappers := tc.Kms().GetExternalWrappers(context.Background()) - externalRecovery := externalWrappers.Recovery() - require.Equal(externalRecovery, wrapper) - - client := tc.Client() - repo := tc.ServersRepo() - - // First, validate that we can't use the same token twice. Get two tokens, - // try to use the first twice (should succeed first time, fail second), then - // ensure second token works - token1, err := recovery.GenerateRecoveryToken(tc.Context(), wrapper) - require.NoError(err) - token2, err := recovery.GenerateRecoveryToken(tc.Context(), wrapper) - require.NoError(err) - - // Token 1, try 1 - client.SetToken(token1) - roleClient := roles.NewClient(client) - _, err = roleClient.Create(tc.Context(), scope.Global.String()) - require.NoError(err) - nonces, err := repo.ListNonces(tc.Context(), servers.NoncePurposeRecovery) - require.NoError(err) - assert.Len(nonces, 1) - - // Token 1, try 2 - _, err = roleClient.Create(tc.Context(), scope.Global.String()) - require.Error(err) - nonces, err = repo.ListNonces(tc.Context(), servers.NoncePurposeRecovery) - require.NoError(err) - assert.Len(nonces, 1) - - // Token 2 - roleClient.ApiClient().SetToken(token2) - _, err = roleClient.Create(tc.Context(), scope.Global.String()) - require.NoError(err) - nonces, err = repo.ListNonces(tc.Context(), servers.NoncePurposeRecovery) - require.NoError(err) - assert.Len(nonces, 2) - - // Make sure they get cleaned up - time.Sleep(2 * controller.NonceCleanupInterval) - nonces, err = repo.ListNonces(tc.Context(), servers.NoncePurposeRecovery) - require.NoError(err) - assert.Len(nonces, 0) - - // And finally, make sure they still can't be used - for _, token := range []string{token1, token2} { - roleClient.ApiClient().SetToken(token) - _, err = roleClient.Create(tc.Context(), scope.Global.String()) - require.Error(err) - nonces, err = repo.ListNonces(tc.Context(), servers.NoncePurposeRecovery) - require.NoError(err) - assert.Len(nonces, 0) - } -} - func TestTagUpdatingListing(t *testing.T) { require := require.New(t) From a281d10206724d4109494a317835368b37f8dc11 Mon Sep 17 00:00:00 2001 From: Todd Date: Tue, 24 May 2022 15:08:02 -0700 Subject: [PATCH 022/100] Add worker config table and view (#2090) * Remove the server_id from session table, change it to worker_id on session_connection This is step 1 in updating the worker table. The worker is becoming a public resource so it must have a public id. We drop the server_id column on the session_connection table and add the worker_id column so we can use the wt_public_key type. Since db migrations are applied while the controller is offline, it should be safe to assume all session connections are not in a running state, thus allowing us to null out all the worker ids in the session_connection table. For now we don't have very good enforcement of the wt_public_id constraints, since the id is currently defined in the worker config, but this will soon change and the worker's id will be generated by the controller. --- .../handlers/targets/target_service.go | 121 +++++----- .../handlers/targets/target_service_test.go | 74 ++++++ .../handlers/workers/worker_service.go | 8 +- .../workers/worker_service_status_test.go | 27 +-- .../99/02_worker_controller_tables.up.sql | 66 ++++++ .../storage/servers/store/v1/worker.proto | 32 ++- internal/servers/query.go | 8 +- internal/servers/repository.go | 12 + internal/servers/repository_worker.go | 143 +++++++----- internal/servers/repository_worker_test.go | 187 +++++++++++++--- internal/servers/store/worker.pb.go | 166 ++++++++++++-- internal/servers/testing.go | 14 +- internal/servers/worker.go | 158 ++++++++++++- internal/servers/worker_status.go | 50 +++++ internal/servers/worker_test.go | 210 ++++++++++++++++++ internal/session/query.go | 17 +- .../service_worker_status_report_test.go | 8 +- internal/session/session_cleanup_job_test.go | 67 +++++- ...on_list_benchmarks_dump_generation_test.go | 8 +- 19 files changed, 1156 insertions(+), 220 deletions(-) create mode 100644 internal/daemon/controller/handlers/targets/target_service_test.go create mode 100644 internal/servers/worker_status.go diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index 5427a0691..251962b32 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -27,6 +27,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/perms" "github.com/hashicorp/boundary/internal/requests" + "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/types/action" @@ -894,79 +895,27 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession // worker IDs below is used to contain their IDs in the same order. This is // used to fetch tags for filtering. But we avoid allocation unless we // actually need it. - var workers []*pb.WorkerInfo - var workerIds []string - hasWorkerFilter := len(t.GetWorkerFilter()) > 0 - servers, err := serversRepo.ListWorkers(ctx) + selectedWorkers, err := serversRepo.ListWorkers(ctx) if err != nil { return nil, err } - for _, v := range servers { - if hasWorkerFilter { - workerIds = append(workerIds, v.GetPublicId()) - } - workers = append(workers, &pb.WorkerInfo{Address: v.Address}) - } - if hasWorkerFilter && len(workerIds) > 0 { - finalWorkers := make([]*pb.WorkerInfo, 0, len(workers)) - // Fetch the tags for the given worker IDs - workerTags, err := serversRepo.ListTagsForWorkers(ctx, workerIds) - if err != nil { - return nil, err - } - // Build the map for filtering. This is similar to the filter map we - // built from the worker config, but with one extra level: a map of the - // worker's ID to its filter map. - tagMap := make(map[string]map[string][]string) - for wId, tags := range workerTags { - currWorkerMap := tagMap[wId] - if currWorkerMap == nil { - currWorkerMap = make(map[string][]string) - tagMap[wId] = currWorkerMap - } - for _, tag := range tags { - currWorkerMap[tag.Key] = append(currWorkerMap[tag.Key], tag.Value) - } - // We don't need to reinsert after the fact because maps are - // reference types, so we don't need to re-insert into tagMap - } - - // Create the evaluator + if len(t.GetWorkerFilter()) > 0 && len(selectedWorkers) > 0 { eval, err := bexpr.CreateEvaluator(t.GetWorkerFilter()) if err != nil { return nil, err } - - // Iterate through the known worker IDs, and evaluate. If evaluation - // returns true, add to the final worker slice, which is assigned back - // to workers after this. - for i, worker := range workerIds { - filterInput := map[string]interface{}{ - "name": worker, - "tags": tagMap[worker], - } - ok, err := eval.Evaluate(filterInput) - if err != nil && !stderrors.Is(err, pointerstructure.ErrNotFound) { - return nil, handlers.ApiErrorWithCodeAndMessage( - codes.FailedPrecondition, - fmt.Sprintf("Worker filter expression evaluation resulted in error: %s", err)) - } - if ok { - finalWorkers = append(finalWorkers, workers[i]) - } + selectedWorkers, err = workerList(selectedWorkers).filtered(eval) + if err != nil { + return nil, err } - workers = finalWorkers } - if len(workers) == 0 { + + if len(selectedWorkers) == 0 { return nil, handlers.ApiErrorWithCodeAndMessage( codes.FailedPrecondition, "No workers are available to handle this session, or all have been filtered.") } - workerAddresses := make([]string, 0, len(workers)) - for _, worker := range workers { - workerAddresses = append(workerAddresses, worker.GetAddress()) - } requestedId := req.GetHostId() staticHostRepo, err := s.staticHostRepoFn() @@ -1074,7 +1023,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession if err != nil { return nil, err } - sess, privKey, err := sessionRepo.CreateSession(ctx, wrapper, sess, workerAddresses) + sess, privKey, err := sessionRepo.CreateSession(ctx, wrapper, sess, workerList(selectedWorkers).addresses()) if err != nil { return nil, err } @@ -1202,7 +1151,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession PrivateKey: privKey, HostId: chosenEndpoint.HostId, Endpoint: endpointUrl.String(), - WorkerInfo: workers, + WorkerInfo: workerList(selectedWorkers).workerInfos(), ConnectionLimit: t.GetSessionConnectionLimit(), } marshaledSad, err := proto.Marshal(sad) @@ -2172,3 +2121,53 @@ func credentialToProto(ctx context.Context, cred credential.Credential) (*server return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c)) } } + +// workerList is a helper type to make the selection of workers clearer and more declarative. +type workerList []*servers.Worker + +// addresses converts the slice of workers to a slice of their addresses +func (w workerList) addresses() []string { + ret := make([]string, 0, len(w)) + for _, worker := range w { + ret = append(ret, worker.CanonicalAddress()) + } + return ret +} + +// workerInfos converts the slice of workers to a slice of their workerInfo protos +func (w workerList) workerInfos() []*pb.WorkerInfo { + ret := make([]*pb.WorkerInfo, 0, len(w)) + for _, worker := range w { + ret = append(ret, &pb.WorkerInfo{Address: worker.CanonicalAddress()}) + } + return ret +} + +// filtered returns a new workerList where all elements contained in it are the +// ones which from the original workerList that pass the evaluator's evaluation. +func (w workerList) filtered(eval *bexpr.Evaluator) (workerList, error) { + var ret []*servers.Worker + for _, worker := range w { + + // TODO: Decide how we are actually going to filter by name now that + // we are separating reported status name and the resource name. + name := worker.GetName() + if name == "" { + name = worker.ReportedStatus.GetName() + } + filterInput := map[string]interface{}{ + "name": name, + "tags": worker.CanonicalTags(), + } + ok, err := eval.Evaluate(filterInput) + if err != nil && !stderrors.Is(err, pointerstructure.ErrNotFound) { + return nil, handlers.ApiErrorWithCodeAndMessage( + codes.FailedPrecondition, + fmt.Sprintf("Worker filter expression evaluation resulted in error: %s", err)) + } + if ok { + ret = append(ret, worker) + } + } + return ret, nil +} diff --git a/internal/daemon/controller/handlers/targets/target_service_test.go b/internal/daemon/controller/handlers/targets/target_service_test.go new file mode 100644 index 000000000..2644b4d78 --- /dev/null +++ b/internal/daemon/controller/handlers/targets/target_service_test.go @@ -0,0 +1,74 @@ +package targets + +import ( + "fmt" + "testing" + + "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/types/scope" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" + "github.com/hashicorp/go-bexpr" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestWorkerList_Addresses(t *testing.T) { + addresses := []string{ + "test1", + "test2", + "test3", + "test4", + } + var workerInfos []*pb.WorkerInfo + var tested workerList + for _, a := range addresses { + workerInfos = append(workerInfos, &pb.WorkerInfo{Address: a}) + tested = append(tested, servers.NewWorker(scope.Global.String(), + servers.WithName(a), + servers.WithAddress(a))) + } + assert.Equal(t, addresses, tested.addresses()) + assert.Equal(t, workerInfos, tested.workerInfos()) +} + +func TestWorkerList_Filter(t *testing.T) { + var workers []*servers.Worker + for i := 0; i < 5; i++ { + w := servers.NewWorker(scope.Global.String(), + servers.WithName(fmt.Sprintf("test_worker_%d", i)), + servers.WithWorkerTags(&servers.Tag{ + Key: fmt.Sprintf("key%d", i), + Value: fmt.Sprintf("value%d", i), + })) + w.ReportedStatus = servers.NewWorkerStatus("", + servers.WithName(fmt.Sprintf("config%d", i)), + servers.WithWorkerTags(&servers.Tag{ + Key: "key", + // Note that the value is either 0 or 1 + Value: fmt.Sprintf("configvalue%d", i%2), + })) + workers = append(workers, w) + } + + { + ev, err := bexpr.CreateEvaluator(`"/name" matches "test_worker_[13]" and "configvalue2" in "/tags/key"`) + require.NoError(t, err) + got, err := workerList(workers).filtered(ev) + require.NoError(t, err) + assert.Len(t, got, 0) + } + { + ev, err := bexpr.CreateEvaluator(`"/name" matches "test_worker_[12]" and "configvalue0" in "/tags/key"`) + require.NoError(t, err) + got, err := workerList(workers).filtered(ev) + require.NoError(t, err) + assert.Len(t, got, 1) + } + { + ev, err := bexpr.CreateEvaluator(`"configvalue0" in "/tags/key"`) + require.NoError(t, err) + got, err := workerList(workers).filtered(ev) + require.NoError(t, err) + assert.Len(t, got, 3) + } +} diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index 20f5235ec..cedc1b213 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -14,7 +14,6 @@ import ( "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/session" - "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" "github.com/hashicorp/go-bexpr" "google.golang.org/grpc/codes" @@ -83,11 +82,12 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques } } - worker := servers.NewWorker(scope.Global.String(), - servers.WithPublicId(reqServer.PrivateId), + wConf := servers.NewWorkerStatus(reqServer.PrivateId, + // TODO: Change the name to its own field to make this explicit. + servers.WithName(reqServer.PrivateId), servers.WithAddress(reqServer.Address), servers.WithWorkerTags(workerTags...)) - controllers, _, err := serverRepo.UpsertWorker(ctx, worker, servers.WithUpdateTags(req.GetUpdateTags())) + controllers, _, err := serverRepo.UpsertWorkerStatus(ctx, wConf, servers.WithUpdateTags(req.GetUpdateTags())) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error storing worker status")) return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error storing worker status: %v", err) diff --git a/internal/daemon/controller/handlers/workers/worker_service_status_test.go b/internal/daemon/controller/handlers/workers/worker_service_status_test.go index bc3842897..8e1da4997 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_status_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_status_test.go @@ -5,9 +5,6 @@ import ( "sync" "testing" - "github.com/hashicorp/boundary/internal/servers/store" - "github.com/hashicorp/boundary/internal/types/scope" - "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/boundary/internal/authtoken" @@ -18,6 +15,7 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" @@ -38,9 +36,8 @@ func TestStatus(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorker(ctx, servers.NewWorker( - scope.Global.String(), - servers.WithPublicId("test_worker1"), + serverRepo.UpsertWorkerStatus(ctx, servers.NewWorkerStatus( + "test_worker1", servers.WithAddress("127.0.0.1"))) serversRepoFn := func() (*servers.Repository, error) { @@ -107,7 +104,7 @@ func TestStatus(t *testing.T) { req: &pbs.StatusRequest{ Worker: &servers.Server{ PrivateId: worker1.PublicId, - Address: worker1.Address, + Address: worker1.CanonicalAddress(), CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, }, @@ -127,7 +124,7 @@ func TestStatus(t *testing.T) { req: &pbs.StatusRequest{ Worker: &servers.Server{ PrivateId: worker1.PublicId, - Address: worker1.Address, + Address: worker1.CanonicalAddress(), CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, }, @@ -206,9 +203,8 @@ func TestStatusSessionClosed(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorker(ctx, servers.NewWorker( - scope.Global.String(), - servers.WithPublicId("test_worker1"), + serverRepo.UpsertWorkerStatus(ctx, servers.NewWorkerStatus( + "test_worker1", servers.WithAddress("127.0.0.1"))) serversRepoFn := func() (*servers.Repository, error) { @@ -292,7 +288,7 @@ func TestStatusSessionClosed(t *testing.T) { req: &pbs.StatusRequest{ Worker: &servers.Server{ PrivateId: worker1.PublicId, - Address: worker1.Address, + Address: worker1.CanonicalAddress(), CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, }, @@ -388,9 +384,8 @@ func TestStatusDeadConnection(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorker(ctx, servers.NewWorker( - scope.Global.String(), - servers.WithPublicId("test_worker1"), + serverRepo.UpsertWorkerStatus(ctx, servers.NewWorkerStatus( + "test_worker1", servers.WithAddress("127.0.0.1"))) serversRepoFn := func() (*servers.Repository, error) { @@ -462,7 +457,7 @@ func TestStatusDeadConnection(t *testing.T) { req := &pbs.StatusRequest{ Worker: &servers.Server{ PrivateId: worker1.PublicId, - Address: worker1.Address, + Address: worker1.CanonicalAddress(), CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, }, diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index aa1ed8142..ef48224ea 100644 --- a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -58,6 +58,33 @@ create trigger worker_insert_time_column before insert on server_worker create trigger worker_update_time_column before update on server_worker for each row execute procedure update_time_column(); +create table server_worker_status ( + worker_id wt_public_id primary key + constraint server_worker_fkey + references server_worker(public_id) + on delete cascade + on update cascade, + create_time wt_timestamp, + update_time wt_timestamp, + -- This is the calculated address that the worker reports it is reachable on. + address wt_network_address not null, + name wt_name +); +comment on table server_worker_status is + 'server_worker_status is a table where each row represents values that a Boundary worker reports to a controller.'; + +create trigger immutable_columns before update on server_worker_status + for each row execute procedure immutable_columns('worker_id', 'create_time'); + +create trigger default_create_time_column before insert on server_worker_status + for each row execute procedure default_create_time(); + +create trigger worker_insert_time_column before insert on server_worker_status + for each row execute procedure update_time_column(); + +create trigger worker_update_time_column before update on server_worker_status + for each row execute procedure update_time_column(); + -- Create table worker tag create table server_worker_tag_enm ( source text primary key @@ -88,6 +115,45 @@ create table server_worker_tag ( primary key(worker_id, key, value, source) ); +-- worker_aggregate view allows the worker and configuration to be read at the +-- same time. +create view server_worker_aggregate as + with worker_config_tags(worker_id, source, tags) as ( + select + ct.worker_id, + ct.source, + -- keys and tags can be any lowercase printable character so use uppercase characters as delimitors. + string_agg(distinct concat_ws('Y', ct.key, ct.value), 'Z') as tags + from server_worker_tag ct + group by ct.worker_id, ct.source + ) +select + w.public_id, + w.scope_id, + w.description, + w.name, + w.address, + w.create_time, + w.update_time, + w.version, + ws.name as worker_status_name, + ws.address as worker_status_address, + ws.update_time as worker_status_update_time, + ws.create_time as worker_status_create_time, + -- keys and tags can be any lowercase printable character so use uppercase characters as delimitors. + wt.tags as api_tags, + ct.tags as worker_config_tags +from server_worker w + left join server_worker_status ws on + w.public_id = ws.worker_id + left join worker_config_tags wt on + w.public_id = wt.worker_id and wt.source = 'api' + left join worker_config_tags ct on + w.public_id = ct.worker_id and ct.source = 'configuration'; +comment on view server_worker_aggregate is +'server_worker_aggregate contains the worker resource with its worker provided config values and its configuration and api provided tags.'; + + -- Aaand drop server_tag drop table server_tag; diff --git a/internal/proto/controller/storage/servers/store/v1/worker.proto b/internal/proto/controller/storage/servers/store/v1/worker.proto index aa743f32d..281c9497a 100644 --- a/internal/proto/controller/storage/servers/store/v1/worker.proto +++ b/internal/proto/controller/storage/servers/store/v1/worker.proto @@ -20,7 +20,7 @@ message Worker { // @inject_tag: `gorm:"default:null"` string description = 30; - // Address for the worker. This must be set + // Address for the worker. This is optional. // @inject_tag: `gorm:"default:null"` string address = 40; @@ -45,18 +45,42 @@ message Worker { // worker_id, key, value, and source. message WorkerTag { // worker_id is the public key that key of the worker this tag is for. - // @inject_tag: `gorm:"default:null"` + // @inject_tag: `gorm:"primary_key"` string worker_id = 10; // key is the key of the tag. This must be set. - // @inject_tag: `gorm:"default:null"` + // @inject_tag: `gorm:"primary_key"` string key = 20; // value is the value - // @inject_tag: `gorm:"default:null"` + // @inject_tag: `gorm:"primary_key"` string value = 30; // source is the source of the tag. Either 'configuration' or 'api'. // @inject_tag: `gorm:"default:not_null"` string source = 40; } + +// WorkerStatus contains the fields that the worker reports to the controller +// in its status updates. +message WorkerStatus { + // worker_id is the public key that key of the worker this tag is for. + // @inject_tag: `gorm:"primary_key"` + string worker_id = 10; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 20; + + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 30; + + // Name of the resource (optional) + // @inject_tag: `gorm:"default:null"` + string name = 40; + + // Address for the worker. This must be set. + // @inject_tag: `gorm:"default:null"` + string address = 50; +} diff --git a/internal/servers/query.go b/internal/servers/query.go index 61e07b293..3d51150ee 100644 --- a/internal/servers/query.go +++ b/internal/servers/query.go @@ -2,5 +2,11 @@ package servers const ( deleteWhereCreateTimeSql = `create_time < ?` - deleteConfigTagsSql = `source = 'configuration' and worker_id = ?` + deleteTagsByWorkerIdSql = ` + delete + from server_worker_tag + where + source = ? + and + worker_id = ?` ) diff --git a/internal/servers/repository.go b/internal/servers/repository.go index c8dcab618..d482364d4 100644 --- a/internal/servers/repository.go +++ b/internal/servers/repository.go @@ -1,6 +1,7 @@ package servers import ( + "reflect" "time" "github.com/hashicorp/boundary/internal/db" @@ -46,3 +47,14 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms) (*Repository, error) kms: kms, }, nil } + +func isNil(i interface{}) bool { + if i == nil { + return true + } + switch reflect.TypeOf(i).Kind() { + case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: + return reflect.ValueOf(i).IsNil() + } + return false +} diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index 76fcda59d..e2e828362 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" ) // ListWorkers is a passthrough to listWorkersWithReader that uses the repo's normal reader. @@ -14,9 +15,16 @@ func (r *Repository) ListWorkers(ctx context.Context, opt ...Option) ([]*Worker, return r.listWorkersWithReader(ctx, r.reader, opt...) } -// listWorkersWithReader will return a listing of resources and honor the WithLimit option or the repo -// defaultLimit. It accepts a reader, allowing it to be used within a transaction or without. +// listWorkersWithReader will return a listing of resources and honor the +// WithLimit option. If WithLiveness is zero the default liveness value is used, +// if it is negative then the last status update time is ignored. This method +// accepts a reader, allowing it to be used within a transaction or without. func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader, opt ...Option) ([]*Worker, error) { + const op = "workers.listWorkersWithReader" + switch { + case isNil(reader): + return nil, errors.New(ctx, errors.InvalidParameter, op, "reader is nil") + } opts := getOpts(opt...) liveness := opts.withLiveness if liveness == 0 { @@ -25,20 +33,28 @@ func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader var where string if liveness > 0 { - where = fmt.Sprintf("update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) + where = fmt.Sprintf("worker_status_update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) } - var workers []*Worker + var wAggs []*workerAggregate if err := reader.SearchWhere( ctx, - &workers, + &wAggs, where, []interface{}{}, - db.WithLimit(-1), + db.WithLimit(opts.withLimit), ); err != nil { - return nil, errors.Wrap(ctx, err, "workers.listWorkersWithReader") + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error searching for workers")) } + workers := make([]*Worker, 0, len(wAggs)) + for _, a := range wAggs { + w, err := a.toWorker(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error converting workerAggregate to Worker")) + } + workers = append(workers, w) + } return workers, nil } @@ -64,21 +80,20 @@ func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, return ret, nil } -// UpsertWorker creates a new worker if one with the provided public id doesn't -// already exist. If it does, UpsertWorker updates the worker. The +// UpsertWorkerStatus creates a new worker if one with the provided public id doesn't +// already exist. If it does, UpsertWorkerStatus updates the worker. The // WithUpdateTags option is the only one used. All others are ignored. -func (r *Repository) UpsertWorker(ctx context.Context, worker *Worker, opt ...Option) ([]*store.Controller, int, error) { - const op = "servers.UpsertWorker" +// Workers are intentionally not oplogged. +func (r *Repository) UpsertWorkerStatus(ctx context.Context, wStatus *WorkerStatus, opt ...Option) ([]*store.Controller, int, error) { + const op = "servers.UpsertWorkerStatus" switch { - case worker == nil: - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "worker is nil") - case worker.Address == "": + case wStatus == nil: + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "wStatus is nil") + case wStatus.Address == "": return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "address is empty") - case worker.ScopeId == "": - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "scope id is empty") - case worker.PublicId == "": - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "public id is empty") + case wStatus.WorkerId == "": + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "worker id is empty") } opts := getOpts(opt...) @@ -89,19 +104,28 @@ func (r *Repository) UpsertWorker(ctx context.Context, worker *Worker, opt ...Op ctx, db.StdRetryCnt, db.ExpBackoff{}, - func(read db.Reader, w db.Writer) error { - var err error - onConflict := &db.OnConflict{ + func(reader db.Reader, w db.Writer) error { + worker := NewWorker(scope.Global.String(), WithPublicId(wStatus.GetWorkerId())) + workerCreateConflict := &db.OnConflict{ Target: db.Columns{"public_id"}, - Action: append(db.SetColumns([]string{"description", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), + Action: db.DoNothing(true), } - err = w.Create(ctx, worker, db.WithOnConflict(onConflict), db.WithReturnRowsAffected(&rowsUpdated)) + if err := w.Create(ctx, worker, db.WithOnConflict(workerCreateConflict)); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("error creating a worker")) + } + + var err error + onConfigConflict := &db.OnConflict{ + Target: db.Columns{"worker_id"}, + Action: append(db.SetColumns([]string{"name", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), + } + err = w.Create(ctx, wStatus, db.WithOnConflict(onConfigConflict), db.WithReturnRowsAffected(&rowsUpdated)) if err != nil { return errors.Wrap(ctx, err, op+":Upsert") } // Fetch current controllers to feed to the workers - controllers, err = r.listControllersWithReader(ctx, read) + controllers, err = r.listControllersWithReader(ctx, reader) if err != nil { return errors.Wrap(ctx, err, op+":ListController") } @@ -111,32 +135,7 @@ func (r *Repository) UpsertWorker(ctx context.Context, worker *Worker, opt ...Op // delete all tags for the given worker, then add the new ones // we've been sent. if opts.withUpdateTags { - _, err = w.Delete(ctx, &store.WorkerTag{}, db.WithWhere(deleteConfigTagsSql, worker.GetPublicId())) - if err != nil { - return errors.Wrap(ctx, err, op+":DeleteTags", errors.WithMsg(worker.GetPublicId())) - } - - // If tags were cleared out entirely, then we'll have nothing - // to do here, e.g., it will result in deletion of all tags. - // Otherwise, go through and stage each tuple for insertion - // below. - if len(worker.Tags) > 0 { - tags := make([]interface{}, 0, len(worker.Tags)) - for _, v := range worker.Tags { - if v == nil { - return errors.New(ctx, errors.InvalidParameter, op+":RangeTags", fmt.Sprintf("found nil tag value for worker %s", worker.GetPublicId())) - } - tags = append(tags, &store.WorkerTag{ - WorkerId: worker.GetPublicId(), - Key: v.Key, - Value: v.Value, - Source: string(ConfigurationTagSource), - }) - } - if err = w.CreateItems(ctx, tags); err != nil { - return errors.Wrap(ctx, err, op+":CreateTags", errors.WithMsg(worker.GetPublicId())) - } - } + setWorkerTags(ctx, w, wStatus.GetWorkerId(), ConfigurationTagSource, wStatus.Tags) } return nil @@ -148,3 +147,47 @@ func (r *Repository) UpsertWorker(ctx context.Context, worker *Worker, opt ...Op return controllers, int(rowsUpdated), nil } + +// setWorkerTags removes all existing tags from the same source and worker id +// and creates new ones based on the ones provided. This function should be +// called from inside a db transaction. +// Workers/worker tags are intentionally not oplogged. +func setWorkerTags(ctx context.Context, w db.Writer, id string, ts TagSource, tags []*Tag) error { + const op = "servers.setWorkerTags" + switch { + case !ts.isValid(): + return errors.New(ctx, errors.InvalidParameter, op, "invalid tag source provided") + case id == "": + return errors.New(ctx, errors.InvalidParameter, op, "worker id is empty") + case isNil(w): + return errors.New(ctx, errors.InvalidParameter, op, "db.Writer is nil") + } + _, err := w.Exec(ctx, deleteTagsByWorkerIdSql, []interface{}{ts.String(), id}) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("couldn't delete exist tags for worker %q", id))) + } + + // If tags were cleared out entirely, then we'll have nothing + // to do here, e.g., it will result in deletion of all tags. + // Otherwise, go through and stage each tuple for insertion + // below. + if len(tags) > 0 { + uTags := make([]interface{}, 0, len(tags)) + for _, v := range tags { + if v == nil { + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("found nil tag value for worker %s", id)) + } + uTags = append(uTags, &store.WorkerTag{ + WorkerId: id, + Key: v.Key, + Value: v.Value, + Source: ts.String(), + }) + } + if err = w.CreateItems(ctx, uTags); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("error creating tags for worker %q", id))) + } + } + + return nil +} diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 1ab6410b8..a69d474af 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -2,29 +2,153 @@ package servers_test import ( "context" + "fmt" "testing" "time" - "github.com/hashicorp/boundary/internal/daemon/controller" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" - "github.com/hashicorp/boundary/internal/types/scope" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestUpsertWorkerStatus(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + ctx := context.Background() + + wStatus1 := servers.NewWorkerStatus("test_worker_1", + servers.WithAddress("address"), + servers.WithName("config_name1")) + _, _, err = repo.UpsertWorkerStatus(ctx, wStatus1) + require.NoError(t, err) + { + workers, err := repo.ListWorkers(ctx) + require.NoError(t, err) + assert.Len(t, workers, 1) + worker := workers[0] + assert.Equal(t, wStatus1.GetWorkerId(), worker.GetPublicId()) + assert.Equal(t, wStatus1.Address, worker.CanonicalAddress()) + assert.Empty(t, worker.Name) + assert.Equal(t, worker.ReportedStatus.CreateTime, worker.CreateTime) + assert.Equal(t, worker.LastConnectionUpdate(), worker.UpdateTime) + assert.Equal(t, uint32(1), worker.Version) + assert.Empty(t, worker.Address) + assert.Empty(t, worker.Description) + } + + // Remove the status name, verify that it doesn't remove the resource's name. + updatedStatus1 := servers.NewWorkerStatus(wStatus1.WorkerId, + servers.WithAddress(wStatus1.Address)) + _, _, err = repo.UpsertWorkerStatus(ctx, updatedStatus1) + require.NoError(t, err) + { + workers, err := repo.ListWorkers(ctx) + require.NoError(t, err) + assert.Len(t, workers, 1) + worker := workers[0] + // The resource's name remains untouched + assert.Empty(t, worker.Name) + // The ReportedStatus's name is correctly cleared + assert.Empty(t, worker.ReportedStatus.Name) + // The last connection update time was increased + assert.Greater(t, worker.LastConnectionUpdate().AsTime().UnixMilli(), worker.UpdateTime.AsTime().UnixMilli()) + assert.Equal(t, worker.UpdateTime, worker.CreateTime) + + assert.Equal(t, updatedStatus1.GetWorkerId(), worker.GetPublicId()) + assert.Equal(t, updatedStatus1.Address, worker.CanonicalAddress()) + assert.Equal(t, worker.ReportedStatus.CreateTime, worker.CreateTime) + assert.Equal(t, uint32(1), worker.Version) + assert.Empty(t, worker.Address) + assert.Empty(t, worker.Description) + } + + failureCases := []struct { + name string + status *servers.WorkerStatus + errAssert func(*testing.T, error) + }{ + { + name: "no address", + status: servers.NewWorkerStatus("worker_with_no_address", + servers.WithName("worker_with_no_address")), + errAssert: func(t *testing.T, err error) { + t.Helper() + assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) + }, + }, + { + name: "invalid id", + status: servers.NewWorkerStatus("short", + servers.WithAddress("someaddress"), + servers.WithName("worker_with_short_id")), + errAssert: func(t *testing.T, err error) { + t.Helper() + assert.True(t, errors.IsCheckConstraintError(err)) + }, + }, + { + name: "no id", + status: servers.NewWorkerStatus("", + servers.WithAddress("anotheraddress"), + servers.WithName("no_address")), + errAssert: func(t *testing.T, err error) { + t.Helper() + assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) + }, + }, + { + name: "no status", + status: nil, + errAssert: func(t *testing.T, err error) { + t.Helper() + assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) + }, + }, + } + for _, tc := range failureCases { + t.Run(fmt.Sprintf("Failures %s", tc.name), func(t *testing.T) { + _, _, err = repo.UpsertWorkerStatus(ctx, tc.status) + assert.Error(t, err) + tc.errAssert(t, err) + + // Still only the original worker exists. + workers, err := repo.ListWorkers(ctx) + require.NoError(t, err) + assert.Len(t, workers, 1) + }) + } + + { + anotherStatus := servers.NewWorkerStatus("another_test_worker", + servers.WithAddress("address")) + _, _, err = repo.UpsertWorkerStatus(ctx, anotherStatus) + require.NoError(t, err) + { + workers, err := repo.ListWorkers(ctx) + require.NoError(t, err) + assert.Len(t, workers, 2) + } + } +} + func TestTagUpdatingListing(t *testing.T) { require := require.New(t) - + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) wrapper := db.TestWrapper(t) - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - RecoveryKms: wrapper, - }) - defer tc.Shutdown() + kms := kms.TestKms(t, conn, wrapper) + repo, err := servers.NewRepository(rw, rw, kms) + require.NoError(err) + ctx := context.Background() - repo := tc.ServersRepo() - srv := servers.NewWorker(scope.Global.String(), - servers.WithPublicId("test_worker_1"), + wStatus := servers.NewWorkerStatus("test_worker_1", servers.WithAddress("127.0.0.1"), servers.WithWorkerTags( &servers.Tag{ @@ -36,11 +160,10 @@ func TestTagUpdatingListing(t *testing.T) { Value: "value2", })) - _, _, err := repo.UpsertWorker(tc.Context(), srv, servers.WithUpdateTags(true)) + _, _, err = repo.UpsertWorkerStatus(ctx, wStatus, servers.WithUpdateTags(true)) require.NoError(err) - srv = servers.NewWorker(scope.Global.String(), - servers.WithPublicId("test_worker_2"), + wStatus = servers.NewWorkerStatus("test_worker_2", servers.WithAddress("127.0.0.1"), servers.WithWorkerTags( &servers.Tag{ @@ -51,10 +174,10 @@ func TestTagUpdatingListing(t *testing.T) { Key: "tag2", Value: "value2", })) - _, _, err = repo.UpsertWorker(tc.Context(), srv, servers.WithUpdateTags(true)) + _, _, err = repo.UpsertWorkerStatus(ctx, wStatus, servers.WithUpdateTags(true)) require.NoError(err) - tags, err := repo.ListTagsForWorkers(tc.Context(), []string{"test_worker_1", "test_worker_2"}) + tags, err := repo.ListTagsForWorkers(ctx, []string{"test_worker_1", "test_worker_2"}) require.NoError(err) // Base case @@ -82,8 +205,7 @@ func TestTagUpdatingListing(t *testing.T) { require.Equal(exp, tags) // Update without saying to update tags - srv = servers.NewWorker(scope.Global.String(), - servers.WithPublicId("test_worker_2"), + wStatus = servers.NewWorkerStatus("test_worker_2", servers.WithAddress("192.168.1.1"), servers.WithWorkerTags( &servers.Tag{ @@ -94,16 +216,16 @@ func TestTagUpdatingListing(t *testing.T) { Key: "tag22", Value: "value22", })) - _, _, err = repo.UpsertWorker(tc.Context(), srv) + _, _, err = repo.UpsertWorkerStatus(ctx, wStatus) require.NoError(err) - tags, err = repo.ListTagsForWorkers(tc.Context(), []string{"test_worker_1", "test_worker_2"}) + tags, err = repo.ListTagsForWorkers(ctx, []string{"test_worker_1", "test_worker_2"}) require.NoError(err) require.Equal(exp, tags) // Update tags and test again - _, _, err = repo.UpsertWorker(tc.Context(), srv, servers.WithUpdateTags(true)) + _, _, err = repo.UpsertWorkerStatus(ctx, wStatus, servers.WithUpdateTags(true)) require.NoError(err) - tags, err = repo.ListTagsForWorkers(tc.Context(), []string{"test_worker_1", "test_worker_2"}) + tags, err = repo.ListTagsForWorkers(ctx, []string{"test_worker_1", "test_worker_2"}) require.NoError(err) require.NotEqual(exp, tags) // Update and try again @@ -131,27 +253,26 @@ func TestListWorkersWithLiveness(t *testing.T) { require.NoError(err) ctx := context.Background() - newWorker := func(publicId string) *servers.Worker { - result := servers.NewWorker(scope.Global.String(), - servers.WithPublicId(publicId), + newWorkerStatus := func(publicId string) *servers.WorkerStatus { + result := servers.NewWorkerStatus(publicId, servers.WithAddress("127.0.0.1")) - _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, result) + _, rowsUpdated, err := serversRepo.UpsertWorkerStatus(ctx, result) require.NoError(err) require.Equal(1, rowsUpdated) return result } - server1 := newWorker("test_worker_1") - server2 := newWorker("test_worker_2") - server3 := newWorker("test_worker_3") + workConf1 := newWorkerStatus("test_worker_1") + workConf2 := newWorkerStatus("test_worker_2") + workConf3 := newWorkerStatus("test_worker_3") // Sleep the default liveness time (15sec currently) +1s time.Sleep(time.Second * 16) // Push an upsert to the first worker so that its status has been // updated. - _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, server1) + _, rowsUpdated, err := serversRepo.UpsertWorkerStatus(ctx, workConf1) require.NoError(err) require.Equal(1, rowsUpdated) @@ -174,10 +295,10 @@ func TestListWorkersWithLiveness(t *testing.T) { result, err := serversRepo.ListWorkers(ctx) require.NoError(err) require.Len(result, 1) - requireIds([]string{server1.PublicId}, result) + requireIds([]string{workConf1.WorkerId}, result) // Upsert second server. - _, rowsUpdated, err = serversRepo.UpsertWorker(ctx, server2) + _, rowsUpdated, err = serversRepo.UpsertWorkerStatus(ctx, workConf2) require.NoError(err) require.Equal(1, rowsUpdated) @@ -186,11 +307,11 @@ func TestListWorkersWithLiveness(t *testing.T) { result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(time.Second*5)) require.NoError(err) require.Len(result, 2) - requireIds([]string{server1.PublicId, server2.PublicId}, result) + requireIds([]string{workConf1.WorkerId, workConf2.WorkerId}, result) // Liveness disabled, should get all three workers. result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(-1)) require.NoError(err) require.Len(result, 3) - requireIds([]string{server1.PublicId, server2.PublicId, server3.PublicId}, result) + requireIds([]string{workConf1.WorkerId, workConf2.WorkerId, workConf3.WorkerId}, result) } diff --git a/internal/servers/store/worker.pb.go b/internal/servers/store/worker.pb.go index 7dec56b04..fc7a0b554 100644 --- a/internal/servers/store/worker.pb.go +++ b/internal/servers/store/worker.pb.go @@ -36,7 +36,7 @@ type Worker struct { // Description of the resource (optional) // @inject_tag: `gorm:"default:null"` Description string `protobuf:"bytes,30,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` - // Address for the worker. This must be set + // Address for the worker. This is optional. // @inject_tag: `gorm:"default:null"` Address string `protobuf:"bytes,40,opt,name=address,proto3" json:"address,omitempty" gorm:"default:null"` // The create_time is set by the database. @@ -149,14 +149,14 @@ type WorkerTag struct { unknownFields protoimpl.UnknownFields // worker_id is the public key that key of the worker this tag is for. - // @inject_tag: `gorm:"default:null"` - WorkerId string `protobuf:"bytes,10,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty" gorm:"default:null"` + // @inject_tag: `gorm:"primary_key"` + WorkerId string `protobuf:"bytes,10,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty" gorm:"primary_key"` // key is the key of the tag. This must be set. - // @inject_tag: `gorm:"default:null"` - Key string `protobuf:"bytes,20,opt,name=key,proto3" json:"key,omitempty" gorm:"default:null"` + // @inject_tag: `gorm:"primary_key"` + Key string `protobuf:"bytes,20,opt,name=key,proto3" json:"key,omitempty" gorm:"primary_key"` // value is the value - // @inject_tag: `gorm:"default:null"` - Value string `protobuf:"bytes,30,opt,name=value,proto3" json:"value,omitempty" gorm:"default:null"` + // @inject_tag: `gorm:"primary_key"` + Value string `protobuf:"bytes,30,opt,name=value,proto3" json:"value,omitempty" gorm:"primary_key"` // source is the source of the tag. Either 'configuration' or 'api'. // @inject_tag: `gorm:"default:not_null"` Source string `protobuf:"bytes,40,opt,name=source,proto3" json:"source,omitempty" gorm:"default:not_null"` @@ -222,6 +222,97 @@ func (x *WorkerTag) GetSource() string { return "" } +// WorkerStatus contains the fields that the worker reports to the controller +// in its status updates. +type WorkerStatus struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // worker_id is the public key that key of the worker this tag is for. + // @inject_tag: `gorm:"primary_key"` + WorkerId string `protobuf:"bytes,10,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty" gorm:"primary_key"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,20,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,30,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // Name of the resource (optional) + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // Address for the worker. This must be set. + // @inject_tag: `gorm:"default:null"` + Address string `protobuf:"bytes,50,opt,name=address,proto3" json:"address,omitempty" gorm:"default:null"` +} + +func (x *WorkerStatus) Reset() { + *x = WorkerStatus{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_worker_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WorkerStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkerStatus) ProtoMessage() {} + +func (x *WorkerStatus) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_worker_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkerStatus.ProtoReflect.Descriptor instead. +func (*WorkerStatus) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_worker_proto_rawDescGZIP(), []int{2} +} + +func (x *WorkerStatus) GetWorkerId() string { + if x != nil { + return x.WorkerId + } + return "" +} + +func (x *WorkerStatus) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *WorkerStatus) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *WorkerStatus) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *WorkerStatus) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + var File_controller_storage_servers_store_v1_worker_proto protoreflect.FileDescriptor var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ @@ -260,11 +351,27 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xf3, 0x01, 0x0a, 0x0c, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, + 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, + 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -279,20 +386,23 @@ func file_controller_storage_servers_store_v1_worker_proto_rawDescGZIP() []byte return file_controller_storage_servers_store_v1_worker_proto_rawDescData } -var file_controller_storage_servers_store_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_controller_storage_servers_store_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_controller_storage_servers_store_v1_worker_proto_goTypes = []interface{}{ (*Worker)(nil), // 0: controller.storage.servers.store.v1.Worker (*WorkerTag)(nil), // 1: controller.storage.servers.store.v1.WorkerTag - (*timestamp.Timestamp)(nil), // 2: controller.storage.timestamp.v1.Timestamp + (*WorkerStatus)(nil), // 2: controller.storage.servers.store.v1.WorkerStatus + (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_servers_store_v1_worker_proto_depIdxs = []int32{ - 2, // 0: controller.storage.servers.store.v1.Worker.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 2, // 1: controller.storage.servers.store.v1.Worker.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 3, // 0: controller.storage.servers.store.v1.Worker.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 1: controller.storage.servers.store.v1.Worker.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 2: controller.storage.servers.store.v1.WorkerStatus.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 3: controller.storage.servers.store.v1.WorkerStatus.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_controller_storage_servers_store_v1_worker_proto_init() } @@ -325,6 +435,18 @@ func file_controller_storage_servers_store_v1_worker_proto_init() { return nil } } + file_controller_storage_servers_store_v1_worker_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkerStatus); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -332,7 +454,7 @@ func file_controller_storage_servers_store_v1_worker_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_servers_store_v1_worker_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/servers/testing.go b/internal/servers/testing.go index 79e5c42b1..3a4ec7d28 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -79,6 +79,7 @@ func TestWorkerAuth(ctx context.Context, t *testing.T, conn *db.DB, worker *Work return workerAuth } +// TestWorker inserts a worker into the db to satisfy foreign key constraints. func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *Worker { t.Helper() rw := db.New(conn) @@ -89,12 +90,15 @@ func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *Worker { id, err := newWorkerId(context.Background()) require.NoError(t, err) name := "test-worker-" + id - - worker := NewWorker(scope.Global.String(), - WithPublicId(id), + wStatus := NewWorkerStatus(id, WithName(name), WithAddress("127.0.0.1")) - _, _, err = serversRepo.UpsertWorker(context.Background(), worker) + _, _, err = serversRepo.UpsertWorkerStatus(context.Background(), wStatus) require.NoError(t, err) - return worker + + wrk := NewWorker(scope.Global.String(), WithPublicId(id)) + require.NoError(t, rw.LookupById(context.Background(), wrk)) + wrk.ReportedStatus = wStatus + + return wrk } diff --git a/internal/servers/worker.go b/internal/servers/worker.go index eb1d9b0eb..b232e07a7 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -1,6 +1,11 @@ package servers import ( + "context" + "strings" + + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/servers/store" "google.golang.org/protobuf/proto" ) @@ -10,7 +15,9 @@ import ( // authorizing and establishing a session. It is owned by a scope. type Worker struct { *store.Worker - Tags []*Tag `gorm:"-"` + + ReportedStatus *WorkerStatus `gorm:"-"` + Tags []*Tag `gorm:"-"` } // NewWorker returns a new Worker. Valid options are WithName, WithDescription @@ -30,18 +37,163 @@ func NewWorker(scopeId string, opt ...Option) *Worker { } func (w *Worker) clone() *Worker { + if w == nil { + return nil + } tags := make([]*Tag, 0, len(w.Tags)) for _, t := range w.Tags { tags = append(tags, &Tag{Key: t.Key, Value: t.Value}) } cw := proto.Clone(w.Worker) + crs := w.ReportedStatus.clone() return &Worker{ - Worker: cw.(*store.Worker), - Tags: tags, + Worker: cw.(*store.Worker), + Tags: tags, + ReportedStatus: crs, } } +// CanonicalAddress returns the actual address boundary believes should be used +// to communicate with this worker. This will be the worker resource's address +// unless it is not set in which case it will use address the worker provides +// in its connection status updates. If neither is available, an empty string +// is returned. +func (w *Worker) CanonicalAddress() string { + switch { + case w.Address != "": + return w.GetAddress() + case w.ReportedStatus != nil: + return w.ReportedStatus.GetAddress() + default: + return "" + } +} + +// 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. +func (w *Worker) CanonicalTags() map[string][]string { + dedupedTags := make(map[Tag]struct{}) + for _, t := range w.Tags { + dedupedTags[*t] = struct{}{} + } + if w.ReportedStatus != nil { + for _, t := range w.ReportedStatus.Tags { + dedupedTags[*t] = struct{}{} + } + } + tags := make(map[string][]string) + for t := range dedupedTags { + tags[t.Key] = append(tags[t.Key], t.Value) + } + return tags +} + +// LastConnectionUpdate 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) LastConnectionUpdate() *timestamp.Timestamp { + if w.ReportedStatus == nil { + return nil + } + return w.ReportedStatus.GetUpdateTime() +} + // TableName overrides the table name used by Worker to `server_worker` func (Worker) TableName() string { return "server_worker" } + +// workerAggregate contains an aggregated view of the values associated with +// a single worker. +type workerAggregate struct { + PublicId string `gorm:"primary_key"` + ScopeId string + Name string + Description string + CreateTime *timestamp.Timestamp + UpdateTime *timestamp.Timestamp + Address string + Version uint32 + ApiTags string + WorkerStatusName string + WorkerStatusAddress string + WorkerStatusCreateTime *timestamp.Timestamp + WorkerStatusUpdateTime *timestamp.Timestamp + WorkerConfigTags string +} + +func (a *workerAggregate) toWorker(ctx context.Context) (*Worker, error) { + const op = "servers.(workerAggregate).toWorker" + workerOptions := []Option{ + WithPublicId(a.PublicId), + WithName(a.Name), + WithDescription(a.Description), + WithAddress(a.Address), + } + tags, err := tagsFromAggregatedTagString(ctx, a.ApiTags) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error parsing config tag string")) + } + if len(tags) > 0 { + workerOptions = append(workerOptions, WithWorkerTags(tags...)) + } + worker := NewWorker(a.ScopeId, workerOptions...) + worker.CreateTime = a.CreateTime + worker.UpdateTime = a.UpdateTime + worker.Version = a.Version + + if a.WorkerStatusCreateTime == nil { + return worker, nil + } + statusOptions := []Option{ + WithName(a.WorkerStatusName), + WithAddress(a.WorkerStatusAddress), + } + tags, err = tagsFromAggregatedTagString(ctx, a.WorkerConfigTags) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error parsing config tag string")) + } + if len(tags) > 0 { + statusOptions = append(statusOptions, WithWorkerTags(tags...)) + } + cfg := NewWorkerStatus(a.PublicId, statusOptions...) + cfg.CreateTime = a.WorkerStatusCreateTime + cfg.UpdateTime = a.WorkerStatusUpdateTime + + worker.ReportedStatus = cfg + return worker, nil +} + +// tagsForAggregatedTagString parses a deliminated string in the format returned +// by the database for the server_worker_aggregate view and returns []*Tag. +// The string is in the format of key1Yvalue1Zkey2Yvalue2Zkey3Yvalue3. Y and Z +// ares chosen for deliminators since tag keys and values are restricted from +// having capitalized letters in them. +func tagsFromAggregatedTagString(ctx context.Context, s string) ([]*Tag, error) { + if s == "" { + return nil, nil + } + const op = "servers.tagsFromAggregatedTagString" + const aggregateDelimiter = "Z" + const pairDelimiter = "Y" + var tags []*Tag + for _, kv := range strings.Split(s, aggregateDelimiter) { + res := strings.SplitN(kv, pairDelimiter, 3) + if len(res) != 2 { + return nil, errors.New(ctx, errors.Internal, op, "invalid aggregated tag pairs") + } + tags = append(tags, &Tag{ + Key: res[0], + Value: res[1], + }) + } + return tags, nil +} + +func (a *workerAggregate) GetPublicId() string { + return a.PublicId +} + +func (workerAggregate) TableName() string { + return "server_worker_aggregate" +} diff --git a/internal/servers/worker_status.go b/internal/servers/worker_status.go new file mode 100644 index 000000000..c8918618e --- /dev/null +++ b/internal/servers/worker_status.go @@ -0,0 +1,50 @@ +package servers + +import ( + "github.com/hashicorp/boundary/internal/servers/store" + "google.golang.org/protobuf/proto" +) + +// A WorkerStatus is a container for the fields that a worker reports to the +// controller about itself in its status updates. While this may include a +// Name, this is different from the Worker's name in that the Worker's name is +// the name of the Worker resource in boundary where the name in the +// WorkerStatus is the name the worker has reported about itself. +type WorkerStatus struct { + *store.WorkerStatus + Tags []*Tag `gorm:"-"` +} + +// NewWorkerStatus returns a new WorkerStatus. Valid options are WithName, +// WithAddress, and WithWorkerTags. All other options are ignored. +func NewWorkerStatus(workerId string, opt ...Option) *WorkerStatus { + opts := getOpts(opt...) + return &WorkerStatus{ + WorkerStatus: &store.WorkerStatus{ + WorkerId: workerId, + Name: opts.withName, + Address: opts.withAddress, + }, + Tags: opts.withWorkerTags, + } +} + +func (w *WorkerStatus) clone() *WorkerStatus { + if w == nil { + return nil + } + tags := make([]*Tag, 0, len(w.Tags)) + for _, t := range w.Tags { + tags = append(tags, &Tag{Key: t.Key, Value: t.Value}) + } + cw := proto.Clone(w.WorkerStatus) + return &WorkerStatus{ + WorkerStatus: cw.(*store.WorkerStatus), + Tags: tags, + } +} + +// TableName overrides the table name used by WorkerStatus to `server_worker_status` +func (WorkerStatus) TableName() string { + return "server_worker_status" +} diff --git a/internal/servers/worker_test.go b/internal/servers/worker_test.go index 4b4bf2acd..d01eeeca2 100644 --- a/internal/servers/worker_test.go +++ b/internal/servers/worker_test.go @@ -10,9 +10,219 @@ import ( "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/testing/protocmp" ) +func TestWorkerCanonicalAddress(t *testing.T) { + worker := NewWorker(scope.Global.String()) + worker.ReportedStatus = NewWorkerStatus("foo", WithAddress("status")) + assert.Equal(t, "status", worker.CanonicalAddress()) + worker.Address = "worker" + assert.Equal(t, "worker", worker.CanonicalAddress()) +} + +func TestWorkerCanonicalTags(t *testing.T) { + w := NewWorker(scope.Global.String(), + WithWorkerTags( + &Tag{Key: "key", Value: "apis unique"}, + &Tag{Key: "key", Value: "shared"}, + &Tag{Key: "key2", Value: "apis key2 unique"}, + )) + w.ReportedStatus = NewWorkerStatus("", + WithWorkerTags( + &Tag{Key: "key", Value: "configs unique"}, + &Tag{Key: "key", Value: "shared"}, + &Tag{Key: "key3", Value: "configs key3 unique"}, + )) + + got := w.CanonicalTags() + assert.Len(t, got, 3, "2 keys expected, 'key' and 'key2'") + assert.ElementsMatch(t, got["key"], []string{"shared", "apis unique", "configs unique"}) + assert.ElementsMatch(t, got["key2"], []string{"apis key2 unique"}) + assert.ElementsMatch(t, got["key3"], []string{"configs key3 unique"}) +} + +func TestWorkerAggregate(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + ctx := context.Background() + + getAggWorker := func(id string) *Worker { + agg := &workerAggregate{PublicId: id} + require.NoError(t, rw.LookupById(ctx, agg)) + got, err := agg.toWorker(ctx) + assert.NoError(t, err) + return got + } + + // Worker without a status + { + id, err := newWorkerId(ctx) + require.NoError(t, err) + require.NoError(t, rw.Create(ctx, + NewWorker(scope.Global.String(), + WithPublicId(id), + WithName(id), + WithAddress("address")))) + + got := getAggWorker(id) + assert.Equal(t, id, got.GetPublicId()) + assert.Equal(t, scope.Global.String(), got.GetScopeId()) + assert.Equal(t, id, got.GetName()) + assert.Equal(t, "address", got.GetAddress()) + assert.Equal(t, uint32(1), got.GetVersion()) + assert.Nil(t, got.ReportedStatus) + assert.Empty(t, got.CanonicalTags()) + } + + // Worker with status + { + id, err := newWorkerId(ctx) + require.NoError(t, err) + require.NoError(t, rw.Create(ctx, + NewWorker(scope.Global.String(), WithPublicId(id)))) + require.NoError(t, rw.Create(ctx, + NewWorkerStatus(id, + WithAddress("address"), + WithName(id)))) + + got := getAggWorker(id) + assert.Equal(t, id, got.GetPublicId()) + assert.Equal(t, uint32(1), got.GetVersion()) + assert.NotNil(t, got.ReportedStatus) + assert.Equal(t, id, got.ReportedStatus.GetWorkerId()) + assert.Equal(t, id, got.ReportedStatus.GetName()) + assert.NotNil(t, got.ReportedStatus.CreateTime) + assert.NotNil(t, got.ReportedStatus.UpdateTime) + assert.Equal(t, "address", got.ReportedStatus.GetAddress()) + } + + // Worker with a config tag + { + id, err := newWorkerId(ctx) + require.NoError(t, err) + require.NoError(t, rw.Create(ctx, + NewWorker(scope.Global.String(), WithPublicId(id)))) + require.NoError(t, rw.Create(ctx, + NewWorkerStatus(id, WithAddress("address")))) + require.NoError(t, rw.Create(ctx, + &store.WorkerTag{ + WorkerId: id, + Key: "key", + Value: "val", + Source: ConfigurationTagSource.String(), + })) + + got := getAggWorker(id) + assert.Equal(t, id, got.GetPublicId()) + assert.Equal(t, uint32(1), got.GetVersion()) + assert.NotNil(t, got.ReportedStatus) + assert.Empty(t, got.Tags) + assert.Equal(t, got.ReportedStatus.Tags, []*Tag{{Key: "key", Value: "val"}}) + } + + // Worker with many config tag + { + id, err := newWorkerId(ctx) + require.NoError(t, err) + require.NoError(t, rw.Create(ctx, + NewWorker(scope.Global.String(), WithPublicId(id)))) + require.NoError(t, rw.Create(ctx, + NewWorkerStatus(id, WithAddress("address")))) + require.NoError(t, rw.Create(ctx, &store.WorkerTag{ + WorkerId: id, + Key: "key", + Value: "val", + Source: ConfigurationTagSource.String(), + })) + require.NoError(t, rw.Create(ctx, &store.WorkerTag{ + WorkerId: id, + Key: "key", + Value: "val2", + Source: ConfigurationTagSource.String(), + })) + require.NoError(t, rw.Create(ctx, &store.WorkerTag{ + WorkerId: id, + Key: "key2", + Value: "val2", + Source: ConfigurationTagSource.String(), + })) + + got := getAggWorker(id) + require.NotNil(t, got.ReportedStatus) + assert.Empty(t, got.Tags) + assert.ElementsMatch(t, got.ReportedStatus.Tags, []*Tag{ + {Key: "key", Value: "val"}, + {Key: "key", Value: "val2"}, + {Key: "key2", Value: "val2"}, + }) + } + + // Worker with an api tag + { + id, err := newWorkerId(ctx) + require.NoError(t, err) + require.NoError(t, rw.Create(ctx, + NewWorker(scope.Global.String(), WithPublicId(id)))) + require.NoError(t, rw.Create(ctx, + NewWorkerStatus(id, WithAddress("address")))) + require.NoError(t, rw.Create(ctx, + &store.WorkerTag{ + WorkerId: id, + Key: "key", + Value: "val", + Source: ApiTagSource.String(), + })) + + got := getAggWorker(id) + assert.Equal(t, id, got.GetPublicId()) + assert.Equal(t, uint32(1), got.GetVersion()) + assert.NotNil(t, got.ReportedStatus) + assert.Empty(t, got.ReportedStatus.Tags) + assert.Equal(t, got.Tags, []*Tag{{Key: "key", Value: "val"}}) + } + + // Worker with mix of tag sources + { + id, err := newWorkerId(ctx) + require.NoError(t, err) + require.NoError(t, rw.Create(ctx, + NewWorker(scope.Global.String(), WithPublicId(id)))) + require.NoError(t, rw.Create(ctx, + NewWorkerStatus(id, WithAddress("address")))) + require.NoError(t, rw.Create(ctx, &store.WorkerTag{ + WorkerId: id, + Key: "key", + Value: "val", + Source: ConfigurationTagSource.String(), + })) + require.NoError(t, rw.Create(ctx, &store.WorkerTag{ + WorkerId: id, + Key: "key", + Value: "val2", + Source: ApiTagSource.String(), + })) + require.NoError(t, rw.Create(ctx, &store.WorkerTag{ + WorkerId: id, + Key: "key2", + Value: "val2", + Source: ApiTagSource.String(), + })) + + got := getAggWorker(id) + require.NotNil(t, got.ReportedStatus) + assert.ElementsMatch(t, got.Tags, []*Tag{ + {Key: "key", Value: "val2"}, + {Key: "key2", Value: "val2"}, + }) + assert.ElementsMatch(t, got.ReportedStatus.Tags, []*Tag{ + {Key: "key", Value: "val"}, + }) + } +} + func TestWorker_New(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") diff --git a/internal/session/query.go b/internal/session/query.go index 8ede00ad8..ae68dcece 100644 --- a/internal/session/query.go +++ b/internal/session/query.go @@ -315,18 +315,25 @@ where // * not closed // * belong to servers that have not reported in within an acceptable // threshold of time + // * belong to servers where we do not know when they last reported. // // and marks them as closed. // // The query returns the set of servers that have had connections closed // along with their last update time and the number of connections closed on - // each. + // each. If we do not know the last update time, we use the current time. closeConnectionsForDeadServersCte = ` with dead_workers (worker_id, last_update_time) as ( - select public_id, update_time - from server_worker - where update_time < wt_sub_seconds_from_now(@grace_period_seconds) + select + w.public_id as worker_id, + ws.update_time as last_update_time + from server_worker w + left join server_worker_status ws + on w.public_id = ws.worker_id + where + ws.update_time is null + or ws.update_time < wt_sub_seconds_from_now(@grace_period_seconds) ), closed_connections (connection_id, worker_id) as ( update session_connection @@ -336,7 +343,7 @@ where returning public_id, worker_id ) select closed_connections.worker_id, - dead_workers.last_update_time, + coalesce(dead_workers.last_update_time, current_timestamp) as last_update_time, count(closed_connections.connection_id) as number_connections_closed from closed_connections join dead_workers diff --git a/internal/session/service_worker_status_report_test.go b/internal/session/service_worker_status_report_test.go index 330da5202..e524a7458 100644 --- a/internal/session/service_worker_status_report_test.go +++ b/internal/session/service_worker_status_report_test.go @@ -4,15 +4,13 @@ import ( "context" "testing" - "github.com/hashicorp/boundary/internal/servers/store" - "github.com/hashicorp/boundary/internal/types/scope" - "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" @@ -33,8 +31,8 @@ func TestWorkerStatusReport(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorker(ctx, servers.NewWorker(scope.Global.String(), - servers.WithPublicId("test_worker1"), + serverRepo.UpsertWorkerStatus(ctx, servers.NewWorkerStatus( + "test_worker1", servers.WithAddress("127.0.0.1"))) repo, err := session.NewRepository(rw, rw, kms) diff --git a/internal/session/session_cleanup_job_test.go b/internal/session/session_cleanup_job_test.go index 75a75fd03..d7bb5887f 100644 --- a/internal/session/session_cleanup_job_test.go +++ b/internal/session/session_cleanup_job_test.go @@ -108,7 +108,7 @@ func TestSessionConnectionCleanupJob(t *testing.T) { // Push an upsert to the first worker so that its status has been // updated. - _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, worker1) + _, rowsUpdated, err := serversRepo.UpsertWorkerStatus(ctx, worker1.ReportedStatus) require.NoError(err) require.Equal(1, rowsUpdated) @@ -121,7 +121,7 @@ func TestSessionConnectionCleanupJob(t *testing.T) { require.True(ok) require.Len(connIds, 6) for _, connId := range connIds { - _, states, err := connectionRepo.LookupConnection(ctx, connId, nil) + _, states, err := connectionRepo.LookupConnection(ctx, connId) require.NoError(err) var foundClosed bool for _, state := range states { @@ -168,6 +168,61 @@ func TestSessionConnectionCleanupJobNewJobErr(t *testing.T) { require.Nil(job) } +func TestCloseConnections_ManuallyDeletedConfig(t *testing.T) { + t.Parallel() + ctx := context.Background() + require := require.New(t) + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, wrapper) + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(rw, rw, kms) + require.NoError(err) + connRepo, err := NewConnectionRepository(ctx, rw, rw, kms) + require.NoError(err) + serversRepo, err := servers.NewRepository(rw, rw, kms) + require.NoError(err) + + gracePeriod := 1 * time.Second + job, err := newSessionConnectionCleanupJob(rw, deadWorkerConnCloseMinGrace) + require.NoError(err) + + worker := servers.TestWorker(t, conn, wrapper) + sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithDbOpts(db.WithSkipVetForWrite(true))) + sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, []byte("foo")) + require.NoError(err) + c, cs, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), worker.GetPublicId()) + require.NoError(err) + require.Len(cs, 1) + require.Equal(StatusAuthorized, cs[0].Status) + + _, cs, err = connRepo.ConnectConnection(ctx, ConnectWith{ + ConnectionId: c.GetPublicId(), + ClientTcpAddress: "127.0.0.1", + ClientTcpPort: 22, + EndpointTcpAddress: "127.0.0.1", + EndpointTcpPort: 22, + UserClientIp: "127.0.0.1", + }) + + // The single worker's connections aren't cleaned up + _, _, err = serversRepo.UpsertWorkerStatus(ctx, worker.ReportedStatus) + assert.NoError(t, err) + result, err := job.closeConnectionsForDeadWorkers(ctx, gracePeriod) + require.NoError(err) + require.Empty(result) + + // the manually deleted status causes the connections to be cleaned + _, _, err = serversRepo.UpsertWorkerStatus(ctx, worker.ReportedStatus) + assert.NoError(t, err) + _, err = rw.Delete(ctx, worker.ReportedStatus) + assert.NoError(t, err) + result, err = job.closeConnectionsForDeadWorkers(ctx, gracePeriod) + require.NoError(err) + require.Len(result, 1) +} + func TestCloseConnectionsForDeadWorkers(t *testing.T) { t.Parallel() ctx := context.Background() @@ -290,7 +345,7 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // the most up-to-date fields. updateServer := func(t *testing.T, w *servers.Worker) *servers.Worker { t.Helper() - _, rowsUpdated, err := serversRepo.UpsertWorker(ctx, w) + _, rowsUpdated, err := serversRepo.UpsertWorkerStatus(ctx, w.ReportedStatus) require.NoError(err) require.Equal(1, rowsUpdated) servers, err := serversRepo.ListWorkers(ctx) @@ -399,7 +454,7 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { require.Equal([]closeConnectionsForDeadWorkersResult{ { WorkerId: worker1.PublicId, - LastUpdateTime: timestampPbAsUTC(t, worker1.UpdateTime.AsTime()), + LastUpdateTime: timestampPbAsUTC(t, worker1.LastConnectionUpdate().AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, }, result) @@ -423,12 +478,12 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { require.ElementsMatch([]closeConnectionsForDeadWorkersResult{ { WorkerId: worker2.PublicId, - LastUpdateTime: timestampPbAsUTC(t, worker2.UpdateTime.AsTime()), + LastUpdateTime: timestampPbAsUTC(t, worker2.LastConnectionUpdate().AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, { WorkerId: worker3.PublicId, - LastUpdateTime: timestampPbAsUTC(t, worker3.UpdateTime.AsTime()), + LastUpdateTime: timestampPbAsUTC(t, worker3.LastConnectionUpdate().AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, }, result) diff --git a/testing/dbtest/session_list_benchmarks_dump_generation_test.go b/testing/dbtest/session_list_benchmarks_dump_generation_test.go index 5adf48a79..d09986b9e 100644 --- a/testing/dbtest/session_list_benchmarks_dump_generation_test.go +++ b/testing/dbtest/session_list_benchmarks_dump_generation_test.go @@ -22,7 +22,6 @@ import ( "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" - "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/testing/dbtest" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" @@ -132,10 +131,9 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) { require.NoError(err) serversRepo, err := servers.NewRepository(rw, rw, kms) require.NoError(err) - worker := servers.NewWorker(scope.Global.String(), - servers.WithPublicId("test_worker_1"), - servers.WithAddress("127.0.0.1")) - _, _, err = serversRepo.UpsertWorker(ctx, worker) + _, _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerStatus( + "test_worker_1", + servers.WithAddress("127.0.0.1"))) require.NoError(err) usersStart := time.Now() From 70c5da1048408a37eb55bae60b52ff4903713417 Mon Sep 17 00:00:00 2001 From: Jim Date: Thu, 26 May 2022 13:32:21 -0400 Subject: [PATCH 023/100] feature (workers): add repository CreateWorker(...) (#2105) --- .dockerignore | 4 + .github/workflows/build.yml | 26 ++- CHANGELOG.md | 4 + Makefile | 10 ++ go.mod | 14 +- go.sum | 18 +-- internal/cmd/commands/database/funcs.go | 3 +- internal/cmd/commands/database/funcs_test.go | 2 +- internal/cmd/commands/database/init.go | 4 +- internal/cmd/commands/database/migrate.go | 2 +- .../credential/vault/client_certificate.go | 4 +- .../vault/client_certificate_test.go | 25 +-- internal/credential/vault/vault_token.go | 2 +- internal/db/testing.go | 19 +++ internal/servers/options.go | 14 +- internal/servers/options_test.go | 41 ++++- internal/servers/repository_worker.go | 48 ++++++ internal/servers/repository_worker_test.go | 153 ++++++++++++++++++ internal/tests/api/targets/target_test.go | 2 +- internal/ui/VERSION | 2 +- 20 files changed, 343 insertions(+), 54 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..0b18ed53d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +** +!bin/** +!dist/** +!.release/docker/** diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24fdc6633..1d1e7bf32 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,10 +60,28 @@ jobs: project="$(go list -m)" sha="$(git rev-parse HEAD)" echo "::set-output name=ldflags::"-s -w -X \'$project/version.GitCommit=$sha\'"" + + get-product-edition: + runs-on: ubuntu-latest + outputs: + product-edition: ${{ steps.get-product-edition.outputs.product-edition}} + steps: + - uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: "1.18.1" + - name: Get product edition + id: get-product-edition + run: | + make edition + echo "::set-output name=product-edition::$(make edition)" + build-other: needs: - get-product-version + - get-product-edition - set-ld-flags runs-on: ubuntu-latest strategy: @@ -102,7 +120,7 @@ jobs: workflow: build-admin-ui.yaml commit: ${{ steps.set-sha.outputs.sha }} repo: "hashicorp/boundary-ui" - name: admin-ui + name: admin-ui-${{ needs.get-product-edition.outputs.product-edition }} path: internal/ui/.tmp/boundary-ui/ui/admin/dist - name: Go Build env: @@ -124,6 +142,7 @@ jobs: build-linux: needs: - get-product-version + - get-product-edition - set-ld-flags runs-on: ubuntu-latest strategy: @@ -157,7 +176,7 @@ jobs: workflow: build-admin-ui.yaml commit: ${{ steps.set-sha.outputs.sha }} repo: "hashicorp/boundary-ui" - name: admin-ui + name: admin-ui-${{ needs.get-product-edition.outputs.product-edition }} path: internal/ui/.tmp/boundary-ui/ui/admin/dist - name: Go Build env: @@ -209,6 +228,7 @@ jobs: build-darwin: needs: - get-product-version + - get-product-edition - set-ld-flags runs-on: macos-latest strategy: @@ -239,7 +259,7 @@ jobs: workflow: build-admin-ui.yaml commit: ${{ steps.set-sha.outputs.sha }} repo: "hashicorp/boundary-ui" - name: admin-ui + name: admin-ui-${{ needs.get-product-edition.outputs.product-edition }} path: internal/ui/.tmp/boundary-ui/ui/admin/dist - name: Go Build env: diff --git a/CHANGELOG.md b/CHANGELOG.md index 48341934b..24a59ef26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. ### Bug Fixes +## 0.8.1 (2022/05/13) + +### Bug Fixes + * controller: Do not shut down cluster listener when it receives an invalid packet ([Issue](https://github.com/hashicorp/boundary/issues/2072), [PR](https://github.com/hashicorp/boundary/pull/2073)) diff --git a/Makefile b/Makefile index bdd427988..750ea7f46 100644 --- a/Makefile +++ b/Makefile @@ -327,3 +327,13 @@ ci-verify: # This is used for release builds by .github/workflows/build.yml version: @go run ./cmd/boundary version | awk '/Version Number:/ { print $$3 }' + +EDITION?= +.PHONY: edition +# This is used for release builds by .github/workflows/build.yml +edition: + @if [ -z "$(EDITION)" ]; then \ + go run ./cmd/boundary version -format=json | jq -r '.version_metadata // "oss"'; \ + else \ + echo $(EDITION); \ + fi; \ diff --git a/go.mod b/go.mod index efe694352..49f7063a5 100644 --- a/go.mod +++ b/go.mod @@ -90,7 +90,8 @@ require ( require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this is a branch and should be updated before merging require ( - github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220512190651-85bdcdd684e6 + github.com/DATA-DOG/go-sqlmock v1.5.0 + github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b github.com/hashicorp/nodeenrollment v0.0.0-20220520141833-ad7323153acc ) @@ -98,7 +99,6 @@ require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/AlecAivazis/survey/v2 v2.2.9 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver/v3 v3.1.1 // indirect @@ -172,7 +172,6 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/sethvargo/go-diceware v0.3.0 // indirect @@ -195,14 +194,5 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gorm.io/driver/sqlite v1.2.4 // indirect - modernc.org/cc/v3 v3.32.4 // indirect - modernc.org/ccgo/v3 v3.9.2 // indirect - modernc.org/libc v1.9.5 // indirect - modernc.org/mathutil v1.2.2 // indirect - modernc.org/memory v1.0.4 // indirect - modernc.org/opt v0.1.1 // indirect - modernc.org/sqlite v1.10.6 // indirect - modernc.org/strutil v1.1.0 // indirect - modernc.org/token v1.0.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 8698be076..7678a413e 100644 --- a/go.sum +++ b/go.sum @@ -377,7 +377,6 @@ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNE github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= @@ -678,8 +677,8 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= -github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220512190651-85bdcdd684e6 h1:3BknAN8P8326GPQac8e3iM132lwtwkO/3OilBU67S1I= -github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220512190651-85bdcdd684e6/go.mod h1:TzFKcCsMKyMwFiMfH2MfO8SXqsFQLxzR7Z8JZiGzpK4= +github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b h1:NmqGrxmUGR1XRhmYA3wsD2BktB9PfbCDIhXd4FM0hbk= +github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b/go.mod h1:puo5dXNWmXx4xoKCzbKSevcFQ4w+z72k3ZF3EVo6zI4= github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.2 h1:it114Kjpk79JVh6AfxFMP5oi1gb+RPzcSLiG3zF/J0w= github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.2/go.mod h1:b9mq0bG5xJ10ZcTEQJLZ22O9y71hIt2MGTLqROA/SAM= github.com/hashicorp/go-kms-wrapping/v2 v2.0.6-0.20220512190651-85bdcdd684e6 h1:isD0/0pGWppbxkakRvnX4M4k65+ZSRW0yELW+twLPDk= @@ -1159,7 +1158,6 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -1967,41 +1965,29 @@ k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAG k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= -modernc.org/cc/v3 v3.32.4 h1:1ScT6MCQRWwvwVdERhGPsPq0f55J1/pFEOCiqM7zc78= modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= -modernc.org/ccgo/v3 v3.9.2 h1:mOLFgduk60HFuPmxSix3AluTEh7zhozkby+e1VDo/ro= modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo= modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.5 h1:zv111ldxmP7DJ5mOIqzRbza7ZDl3kh4ncKfASB2jIYY= modernc.org/libc v1.9.5/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= -modernc.org/sqlite v1.10.6 h1:iNDTQbULcm0IJAqrzCm2JcCqxaKRS94rJ5/clBMRmc8= modernc.org/sqlite v1.10.6/go.mod h1:Z9FEjUtZP4qFEg6/SiADg9XCER7aYy9a/j7Pg9P7CPs= -modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc= modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/tcl v1.5.2 h1:sYNjGr4zK6cDH74USl8wVJRrvDX6UOLpG0j4lFvR0W0= modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo= -modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc= modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= diff --git a/internal/cmd/commands/database/funcs.go b/internal/cmd/commands/database/funcs.go index 105c5ea1e..fb9c69f83 100644 --- a/internal/cmd/commands/database/funcs.go +++ b/internal/cmd/commands/database/funcs.go @@ -16,7 +16,7 @@ import ( // We expect the database already to be initialized iff initialized is set to true. // Returns a cleanup function which must be called even if an error is returned and // an error code where a non-zero value indicates an error happened. -func migrateDatabase(ctx context.Context, ui cli.Ui, dialect, u string, initialized bool) (func(), int) { +func migrateDatabase(ctx context.Context, ui cli.Ui, dialect, u string, initialized bool, maxOpenConns int) (func(), int) { noop := func() {} // This database is used to keep an exclusive lock on the database for the // remainder of the command @@ -25,6 +25,7 @@ func migrateDatabase(ctx context.Context, ui cli.Ui, dialect, u string, initiali ui.Error(fmt.Errorf("Error establishing db connection: %w", err).Error()) return noop, 2 } + dBase.SetMaxOpenConns(maxOpenConns) if err := dBase.PingContext(ctx); err != nil { ui.Error(fmt.Sprintf("Unable to connect to the database at %q", u)) return noop, 2 diff --git a/internal/cmd/commands/database/funcs_test.go b/internal/cmd/commands/database/funcs_test.go index fbecd15cc..5f115abaf 100644 --- a/internal/cmd/commands/database/funcs_test.go +++ b/internal/cmd/commands/database/funcs_test.go @@ -193,7 +193,7 @@ func TestMigrateDatabase(t *testing.T) { t.Run(tc.name, func(t *testing.T) { u := tc.urlProvider() ui := cli.NewMockUi() - clean, errCode := migrateDatabase(ctx, ui, dialect, u, tc.initialized) + clean, errCode := migrateDatabase(ctx, ui, dialect, u, tc.initialized, 10) clean() assert.EqualValues(t, tc.expectedCode, errCode) assert.Equal(t, tc.expectedOutput, ui.OutputWriter.String()) diff --git a/internal/cmd/commands/database/init.go b/internal/cmd/commands/database/init.go index 19bef5ba8..409748aaa 100644 --- a/internal/cmd/commands/database/init.go +++ b/internal/cmd/commands/database/init.go @@ -239,6 +239,8 @@ func (c *InitCommand) Run(args []string) (retCode int) { return base.CommandUserError } + c.DatabaseMaxOpenConnections = c.Config.Controller.Database.MaxOpenConnections + var migrationUrlToParse string if c.Config.Controller.Database.MigrationUrl != "" { migrationUrlToParse = c.Config.Controller.Database.MigrationUrl @@ -262,7 +264,7 @@ func (c *InitCommand) Run(args []string) (retCode int) { return base.CommandUserError } - clean, errCode := migrateDatabase(c.Context, c.UI, dialect, migrationUrl, false) + clean, errCode := migrateDatabase(c.Context, c.UI, dialect, migrationUrl, false, c.DatabaseMaxOpenConnections) defer clean() switch errCode { case 0: diff --git a/internal/cmd/commands/database/migrate.go b/internal/cmd/commands/database/migrate.go index 19d47210c..63d74de9b 100644 --- a/internal/cmd/commands/database/migrate.go +++ b/internal/cmd/commands/database/migrate.go @@ -259,7 +259,7 @@ plugins { return base.CommandUserError } - clean, errCode := migrateDatabase(c.Context, c.UI, dialect, migrationUrl, true) + clean, errCode := migrateDatabase(c.Context, c.UI, dialect, migrationUrl, true, c.Config.Controller.Database.MaxOpenConnections) defer clean() if errCode != 0 { return errCode diff --git a/internal/credential/vault/client_certificate.go b/internal/credential/vault/client_certificate.go index 057fa29f9..0813f6813 100644 --- a/internal/credential/vault/client_certificate.go +++ b/internal/credential/vault/client_certificate.go @@ -75,7 +75,7 @@ func (c *ClientCertificate) SetTableName(n string) { func (c *ClientCertificate) encrypt(ctx context.Context, cipher wrapping.Wrapper) error { const op = "vault.(ClientCertificate).encrypt" if len(c.CertificateKey) == 0 { - errors.New(ctx, errors.InvalidParameter, op, "no certificate key defined") + return errors.New(ctx, errors.InvalidParameter, op, "no certificate key defined") } if err := structwrapping.WrapStruct(ctx, cipher, c.ClientCertificate, nil); err != nil { return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt)) @@ -86,7 +86,7 @@ func (c *ClientCertificate) encrypt(ctx context.Context, cipher wrapping.Wrapper } c.KeyId = keyId if err := c.hmacCertificateKey(ctx, cipher); err != nil { - errors.Wrap(ctx, err, op) + return errors.Wrap(ctx, err, op) } return nil } diff --git a/internal/credential/vault/client_certificate_test.go b/internal/credential/vault/client_certificate_test.go index 21af5dd26..4e2842379 100644 --- a/internal/credential/vault/client_certificate_test.go +++ b/internal/credential/vault/client_certificate_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/boundary/internal/credential/vault/store" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/stretchr/testify/assert" @@ -49,11 +50,12 @@ func TestClientCertificate_New(t *testing.T) { } tests := []struct { - name string - args args - want *ClientCertificate - wantErr bool - wantEncryptErr bool + name string + args args + want *ClientCertificate + wantErr bool + wantEncryptErr bool + wantEncryptErrCode errors.Code }{ { name: "missing-certificate", @@ -64,7 +66,7 @@ func TestClientCertificate_New(t *testing.T) { wantErr: true, }, { - name: "valid-missing-key", + name: "missing-key", args: args{ certificate: []byte(certPem), }, @@ -73,7 +75,8 @@ func TestClientCertificate_New(t *testing.T) { Certificate: []byte(certPem), }, }, - wantEncryptErr: true, + wantEncryptErr: true, + wantEncryptErrCode: errors.InvalidParameter, }, { name: "valid", @@ -116,10 +119,12 @@ func TestClientCertificate_New(t *testing.T) { err = got.encrypt(ctx, databaseWrapper) if tt.wantEncryptErr { require.Error(err) - } else { - require.NoError(err) - require.NoError(got.decrypt(ctx, databaseWrapper)) + assert.Truef(errors.Match(errors.T(tt.wantEncryptErrCode), err), "%v", err) + return } + + require.NoError(err) + assert.NoError(got.decrypt(ctx, databaseWrapper)) }) } } diff --git a/internal/credential/vault/vault_token.go b/internal/credential/vault/vault_token.go index a0ac6efa5..872981698 100644 --- a/internal/credential/vault/vault_token.go +++ b/internal/credential/vault/vault_token.go @@ -122,7 +122,7 @@ func (t *Token) encrypt(ctx context.Context, cipher wrapping.Wrapper) error { } keyId, err := cipher.KeyId(ctx) if err != nil { - errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("error fetching wrapper key id")) + return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("error fetching wrapper key id")) } t.KeyId = keyId return nil diff --git a/internal/db/testing.go b/internal/db/testing.go index 34fe68e7f..b97c58ff3 100644 --- a/internal/db/testing.go +++ b/internal/db/testing.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/DATA-DOG/go-sqlmock" _ "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/source/file" "github.com/hashicorp/boundary/internal/db/schema" @@ -20,6 +21,7 @@ import ( aead "github.com/hashicorp/go-kms-wrapping/v2/aead" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + pgDriver "gorm.io/driver/postgres" ) // setup the tests (initialize the database one-time and intialized @@ -73,6 +75,23 @@ func TestSetup(t testing.TB, dialect string, opt ...TestOption) (*DB, string) { return db, url } +// TestSetupWithMock will return a test DB and an associated Sqlmock which can +// be used to mock out the db responses. +func TestSetupWithMock(t *testing.T) (*DB, sqlmock.Sqlmock) { + t.Helper() + require := require.New(t) + db, mock, err := sqlmock.New() + require.NoError(err) + require.NoError(err) + dbw, err := dbw.OpenWith(pgDriver.New(pgDriver.Config{ + Conn: db, + })) + require.NoError(err) + return &DB{ + wrapped: dbw, + }, mock +} + // TestWrapper initializes an AEAD wrapping.Wrapper for testing the oplog func TestWrapper(t testing.TB) wrapping.Wrapper { t.Helper() diff --git a/internal/servers/options.go b/internal/servers/options.go index 6d1e274f3..cee422b4b 100644 --- a/internal/servers/options.go +++ b/internal/servers/options.go @@ -1,6 +1,7 @@ package servers import ( + "context" "time" ) @@ -31,10 +32,13 @@ type options struct { withControllerEncryptionPrivateKey []byte withKeyId string withNonce []byte + withNewIdFunc func(context.Context) (string, error) } func getDefaultOptions() options { - return options{} + return options{ + withNewIdFunc: newWorkerId, + } } // WithDescription provides an optional description. @@ -128,3 +132,11 @@ func WithNonce(nonce []byte) Option { o.withNonce = nonce } } + +// WithNewIdFunc allows an optional factory function for new worker IDs to be +// specified (this option is likely only useful for tests). +func WithNewIdFunc(fn func(context.Context) (string, error)) Option { + return func(o *options) { + o.withNewIdFunc = fn + } +} diff --git a/internal/servers/options_test.go b/internal/servers/options_test.go index 2e4e21381..08d05c3d7 100644 --- a/internal/servers/options_test.go +++ b/internal/servers/options_test.go @@ -1,6 +1,9 @@ package servers import ( + "context" + "reflect" + "runtime" "testing" "time" @@ -10,49 +13,60 @@ import ( // Test_GetOpts provides unit tests for GetOpts and all the options func Test_GetOpts(t *testing.T) { t.Parallel() - opts := getOpts() - assert.Equal(t, options{}, opts) - t.Run("WithName", func(t *testing.T) { opts := getOpts(WithName("test")) testOpts := getDefaultOptions() testOpts.withName = "test" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithDescription", func(t *testing.T) { opts := getOpts(WithDescription("test desc")) testOpts := getDefaultOptions() testOpts.withDescription = "test desc" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithLimit", func(t *testing.T) { opts := getOpts(WithLimit(5)) testOpts := getDefaultOptions() testOpts.withLimit = 5 + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithAddress", func(t *testing.T) { opts := getOpts(WithAddress("test")) testOpts := getDefaultOptions() testOpts.withAddress = "test" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithPublicId", func(t *testing.T) { opts := getOpts(WithPublicId("test")) testOpts := getDefaultOptions() testOpts.withPublicId = "test" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithLiveness", func(t *testing.T) { opts := getOpts(WithLiveness(time.Hour)) testOpts := getDefaultOptions() testOpts.withLiveness = time.Hour + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithUpdateTags", func(t *testing.T) { opts := getOpts(WithUpdateTags(true)) testOpts := getDefaultOptions() testOpts.withUpdateTags = true + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithWorkerTags", func(t *testing.T) { @@ -63,6 +77,8 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithWorkerTags(tags...)) testOpts := getDefaultOptions() testOpts.withWorkerTags = tags + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithWorkerKeys", func(t *testing.T) { @@ -77,6 +93,8 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithWorkerKeys(keys)) testOpts = getDefaultOptions() testOpts.withWorkerKeys = keys + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(opts, testOpts) }) t.Run("WithControllerEncryptionPrivateKey", func(t *testing.T) { @@ -88,6 +106,8 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithControllerEncryptionPrivateKey(key)) testOpts = getDefaultOptions() testOpts.withControllerEncryptionPrivateKey = key + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(opts, testOpts) }) t.Run("WithKeyId", func(t *testing.T) { @@ -97,6 +117,8 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithKeyId("hi i'm another key id")) testOpts = getDefaultOptions() testOpts.withKeyId = "hi i'm another key id" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(opts, testOpts) }) t.Run("WithNonce", func(t *testing.T) { @@ -108,6 +130,19 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithNonce(nonce)) testOpts = getDefaultOptions() testOpts.withNonce = nonce + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(opts, testOpts) }) + t.Run("WithNewIdFunc", func(t *testing.T) { + assert := assert.New(t) + testFn := func(context.Context) (string, error) { return "", nil } + opts := getOpts(WithNewIdFunc(testFn)) + testOpts := getDefaultOptions() + testOpts.withNewIdFunc = testFn + assert.Equal( + runtime.FuncForPC(reflect.ValueOf(opts.withNewIdFunc).Pointer()).Name(), + runtime.FuncForPC(reflect.ValueOf(testOpts.withNewIdFunc).Pointer()).Name(), + ) + }) } diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index e2e828362..965f2a020 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -191,3 +191,51 @@ func setWorkerTags(ctx context.Context, w db.Writer, id string, ts TagSource, ta return nil } + +// CreateWorker will create a worker in the repository and return the written +// worker. Creating a worker is not intentionally oplogged. A worker's +// ReportedStatus and Tags are intentionally ignored when creating a worker (not +// included). Currently, a worker can only be created in the global scope +// +// Options supported: WithNewIdFunc (this option is likely only useful for tests) +func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Option) (*Worker, error) { + const op = "servers.CreateWorker" + switch { + case worker == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing worker") + case worker.PublicId != "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "public id is not empty") + case worker.ScopeId == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") + case worker.ScopeId != scope.Global.String(): + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("scope id must be %q", scope.Global.String())) + } + + opts := getOpts(opt...) + var err error + if worker.PublicId, err = opts.withNewIdFunc(ctx); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to generate worker id")) + } + + var returnedWorker *Worker + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + returnedWorker = worker.clone() + err := w.Create( + ctx, + returnedWorker, + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + return nil + }, + ) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create worker")) + } + return returnedWorker, nil +} diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index a69d474af..2f47b4666 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -8,8 +8,11 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -315,3 +318,153 @@ func TestListWorkersWithLiveness(t *testing.T) { require.Len(result, 3) requireIds([]string{workConf1.WorkerId, workConf2.WorkerId, workConf3.WorkerId}, result) } + +func TestRepository_CreateWorker(t *testing.T) { + t.Parallel() + testCtx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + testRepo, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + + iamRepo := iam.TestRepo(t, conn, wrapper) + org, _ := iam.TestScopes(t, iamRepo) + + testNewIdFn := func(context.Context) (string, error) { + return "", errors.New(testCtx, errors.Internal, "test", "testNewIdFn-error") + } + + tests := []struct { + name string + setup func() *servers.Worker + repo *servers.Repository + opt []servers.Option + wantErr bool + wantErrIs errors.Code + wantErrContains string + }{ + { + name: "missing-worker", + setup: func() *servers.Worker { + return nil + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "missing worker", + }, + { + name: "public-id-not-empty", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + var err error + w.PublicId, err = db.NewPublicId(servers.WorkerPrefix) + require.NoError(t, err) + return w + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "public id is not empty", + }, + { + name: "empty-scope-id", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + w.ScopeId = "" + return w + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "missing scope id", + }, + { + name: "org-scope-id", + setup: func() *servers.Worker { + w := servers.NewWorker(org.PublicId) + return w + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "scope id must be \"global\"", + }, + { + name: "new-id-error", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + return w + }, + repo: testRepo, + opt: []servers.Option{servers.WithNewIdFunc(testNewIdFn)}, + wantErr: true, + wantErrIs: errors.Internal, + wantErrContains: "testNewIdFn-error", + }, + { + name: "create-error", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + return w + }, + repo: func() *servers.Repository { + conn, mock := db.TestSetupWithMock(t) + writer := db.New(conn) + mock.ExpectBegin() + mock.ExpectQuery(`INSERT`).WillReturnError(errors.New(testCtx, errors.Internal, "test", "create-error")) + mock.ExpectRollback() + r, err := servers.NewRepository(rw, writer, kms) + require.NoError(t, err) + return r + }(), + wantErr: true, + wantErrContains: "unable to create worker", + }, + { + name: "success", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + return w + }, + repo: testRepo, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + testWorker := tc.setup() + got, err := tc.repo.CreateWorker(testCtx, testWorker, tc.opt...) + if tc.wantErr { + require.Error(err) + assert.Nil(got) + if tc.wantErrIs != errors.Unknown { + assert.True(errors.Match(errors.T(tc.wantErrIs), err)) + } + if tc.wantErrContains != "" { + assert.Contains(err.Error(), tc.wantErrContains) + } + return + } + require.NoError(err) + assert.NotNil(got) + assert.NotEmpty(got.PublicId) + assert.NotEmpty(got.UpdateTime) + assert.NotEmpty(got.CreateTime) + + found := &servers.Worker{ + Worker: &store.Worker{ + PublicId: got.PublicId, + }, + } + err = rw.LookupByPublicId(testCtx, found) + require.NoError(err) + if found.Tags == nil { + found.Tags = []*servers.Tag{} + } + assert.Equal(got, found) + }) + } +} diff --git a/internal/tests/api/targets/target_test.go b/internal/tests/api/targets/target_test.go index c7966c319..835b00f71 100644 --- a/internal/tests/api/targets/target_test.go +++ b/internal/tests/api/targets/target_test.go @@ -179,11 +179,11 @@ func TestList(t *testing.T) { require.NoError(err) expected[i] = tcr.Item } + filterItem := expected[3] ul, err = tarClient.List(tc.Context(), proj.GetPublicId()) require.NoError(err) assert.ElementsMatch(comparableSlice(expected), comparableSlice(ul.Items)) - filterItem := ul.Items[3] ul, err = tarClient.List(tc.Context(), proj.GetPublicId(), targets.WithFilter(fmt.Sprintf(`"/item/id"==%q`, filterItem.Id))) require.NoError(err) diff --git a/internal/ui/VERSION b/internal/ui/VERSION index e1c104f63..c5193b209 100644 --- a/internal/ui/VERSION +++ b/internal/ui/VERSION @@ -1,4 +1,4 @@ -8545697b430a432bd50c1bde6a080759f803f157 Add toolbar regions to align refresh button to the right (#1098) +698160d423e8fee640d3286c3a3f17434f56a12b # This file determines the version of the UI to embed in the boundary binary. # Update this file by running 'make update-ui-version' from the root of this repo. # Set UI_COMMITISH when running the above target to update to a specific version. From 74fce3776bb0c1d914d7d6c6f567845e427f8b02 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Thu, 26 May 2022 12:53:58 -0400 Subject: [PATCH 024/100] feature(workers): add repository DeleteWorker --- internal/servers/repository_worker.go | 40 +++++++++++ internal/servers/repository_worker_test.go | 77 ++++++++++++++++++++++ internal/servers/worker.go | 5 ++ 3 files changed, 122 insertions(+) diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index 965f2a020..58a254d17 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -10,6 +10,46 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" ) +// DeleteWorker will delete a worker from the repository. +func (r *Repository) DeleteWorker(ctx context.Context, publicId string, _ ...Option) (int, error) { + const op = "servers.(Repository).DeleteWorker" + if publicId == "" { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing public id") + } + worker := allocWorker() + worker.Worker.PublicId = publicId + + var rowsDeleted int + _, err := r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + deleteWorker := worker.clone() + var err error + rowsDeleted, err = w.Delete( + ctx, + deleteWorker, + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rowsDeleted > 1 { + // return err, which will result in a rollback of the delete + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been deleted") + } + return nil + }, + ) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId))) + } + if rowsDeleted == 0 { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("delete failed for worker with workerId: %s", publicId))) + } + return rowsDeleted, nil +} + // ListWorkers is a passthrough to listWorkersWithReader that uses the repo's normal reader. func (r *Repository) ListWorkers(ctx context.Context, opt ...Option) ([]*Worker, error) { return r.listWorkersWithReader(ctx, r.reader, opt...) diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 2f47b4666..bffd4ba9c 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -13,10 +13,87 @@ import ( "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/go-dbw" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestDeleteWorker(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + ctx := context.Background() + + type args struct { + worker *servers.Worker + } + tests := []struct { + name string + args args + wantRowsDeleted int + wantErr bool + wantErrMsg string + }{ + { + name: "valid", + args: args{ + worker: servers.TestWorker(t, conn, wrapper), + }, + wantRowsDeleted: 1, + wantErr: false, + }, + { + name: "no-public-id", + args: args{ + worker: func() *servers.Worker { + w := servers.Worker{Worker: &store.Worker{}} + return &w + }(), + }, + wantRowsDeleted: 0, + wantErr: true, + wantErrMsg: "servers.(Repository).DeleteWorker: missing public id: parameter violation: error #100", + }, + { + name: "not-found", + args: args{ + worker: func() *servers.Worker { + w := servers.Worker{Worker: &store.Worker{}} + id, err := db.NewPublicId("w") + require.NoError(t, err) + w.PublicId = id + return &w + }(), + }, + wantRowsDeleted: 0, + wantErr: true, + wantErrMsg: "servers.(Repository).DeleteWorker: Delete failed for worker with workerId:", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + deletedRows, err := repo.DeleteWorker(ctx, tt.args.worker.Worker.PublicId) + if tt.wantErr { + assert.Error(err) + assert.Equal(0, deletedRows) + assert.Contains(err.Error(), tt.wantErrMsg) + + return + } + assert.NoError(err) + assert.Equal(tt.wantRowsDeleted, deletedRows) + + // Validate that the worker no longer exists + err = rw.LookupByPublicId(ctx, tt.args.worker) + assert.ErrorIs(err, dbw.ErrRecordNotFound) + }) + } +} + func TestUpsertWorkerStatus(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) diff --git a/internal/servers/worker.go b/internal/servers/worker.go index b232e07a7..bfa218d4c 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -36,6 +36,11 @@ func NewWorker(scopeId string, opt ...Option) *Worker { } } +// allocWorker will allocate a Worker +func allocWorker() Worker { + return Worker{Worker: &store.Worker{}} +} + func (w *Worker) clone() *Worker { if w == nil { return nil From c811dff89b8d416485dda043a97830fe9d2c9b37 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Tue, 24 May 2022 14:10:13 -0400 Subject: [PATCH 025/100] refact(api): move worker handlers --- .../workers => cluster/handlers}/multihop_service.go | 2 +- .../workers => cluster/handlers}/worker_service.go | 2 +- .../handlers}/worker_service_status_test.go | 10 +++++----- .../handlers}/worker_service_test.go | 6 +++--- .../handlers/targets/tcp/target_service_test.go | 8 ++++---- internal/daemon/controller/listeners.go | 6 +++--- internal/daemon/worker/listeners.go | 6 +++--- .../workers => worker}/worker_proxy_service.go | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) rename internal/daemon/{controller/handlers/workers => cluster/handlers}/multihop_service.go (98%) rename internal/daemon/{controller/handlers/workers => cluster/handlers}/worker_service.go (99%) rename internal/daemon/{controller/handlers/workers => cluster/handlers}/worker_service_status_test.go (97%) rename internal/daemon/{controller/handlers/workers => cluster/handlers}/worker_service_test.go (96%) rename internal/daemon/{controller/handlers/workers => worker}/worker_proxy_service.go (99%) diff --git a/internal/daemon/controller/handlers/workers/multihop_service.go b/internal/daemon/cluster/handlers/multihop_service.go similarity index 98% rename from internal/daemon/controller/handlers/workers/multihop_service.go rename to internal/daemon/cluster/handlers/multihop_service.go index 9a84ea9f9..ca1bde762 100644 --- a/internal/daemon/controller/handlers/workers/multihop_service.go +++ b/internal/daemon/cluster/handlers/multihop_service.go @@ -1,4 +1,4 @@ -package workers +package handlers import ( "context" diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/cluster/handlers/worker_service.go similarity index 99% rename from internal/daemon/controller/handlers/workers/worker_service.go rename to internal/daemon/cluster/handlers/worker_service.go index cedc1b213..5940f74c6 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/cluster/handlers/worker_service.go @@ -1,4 +1,4 @@ -package workers +package handlers import ( "context" diff --git a/internal/daemon/controller/handlers/workers/worker_service_status_test.go b/internal/daemon/cluster/handlers/worker_service_status_test.go similarity index 97% rename from internal/daemon/controller/handlers/workers/worker_service_status_test.go rename to internal/daemon/cluster/handlers/worker_service_status_test.go index 8e1da4997..5d3fefb72 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_status_test.go +++ b/internal/daemon/cluster/handlers/worker_service_status_test.go @@ -1,4 +1,4 @@ -package workers_test +package handlers_test import ( "context" @@ -8,7 +8,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/boundary/internal/authtoken" - "github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers" + "github.com/hashicorp/boundary/internal/daemon/cluster/handlers" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/host/static" @@ -85,7 +85,7 @@ func TestStatus(t *testing.T) { require.NoError(t, err) require.NoError(t, err) - s := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) + s := handlers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) require.NotNil(t, s) connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PublicId) @@ -264,7 +264,7 @@ func TestStatusSessionClosed(t *testing.T) { sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, tofu2) require.NoError(t, err) - s := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) + s := handlers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) require.NotNil(t, s) connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PublicId) @@ -445,7 +445,7 @@ func TestStatusDeadConnection(t *testing.T) { sess2, _, err = repo.ActivateSession(ctx, sess2.PublicId, sess2.Version, tofu2) require.NoError(t, err) - s := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) + s := handlers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) require.NotNil(t, s) connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PublicId) diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/cluster/handlers/worker_service_test.go similarity index 96% rename from internal/daemon/controller/handlers/workers/worker_service_test.go rename to internal/daemon/cluster/handlers/worker_service_test.go index 8bd637002..c5357b716 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/cluster/handlers/worker_service_test.go @@ -1,4 +1,4 @@ -package workers_test +package handlers_test import ( "context" @@ -8,7 +8,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/boundary/internal/authtoken" - "github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers" + "github.com/hashicorp/boundary/internal/daemon/cluster/handlers" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/host/static" @@ -100,7 +100,7 @@ func TestLookupSession(t *testing.T) { err = repo.AddSessionCredentials(ctx, egressSess.ScopeId, egressSess.GetPublicId(), workerCreds) require.NoError(t, err) - s := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, new(sync.Map), kms) + s := handlers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, new(sync.Map), kms) require.NotNil(t, s) cases := []struct { diff --git a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go index 1a526d127..26120d855 100644 --- a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go @@ -16,11 +16,11 @@ import ( "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/credential" "github.com/hashicorp/boundary/internal/credential/vault" + cluster "github.com/hashicorp/boundary/internal/daemon/cluster/handlers" "github.com/hashicorp/boundary/internal/daemon/controller/auth" "github.com/hashicorp/boundary/internal/daemon/controller/handlers" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets" - "github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" @@ -2787,7 +2787,7 @@ func TestAuthorizeSession(t *testing.T) { require.NoError(t, err) // Tell our DB that there is a worker ready to serve the data - workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) + workerService := cluster.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) _, err = workerService.Status(ctx, &spbs.StatusRequest{ Worker: &spb.Server{ PrivateId: "w_1234567890", @@ -3075,7 +3075,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { require.NoError(t, err) // Tell our DB that there is a worker ready to serve the data - workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) + workerService := cluster.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) _, err = workerService.Status(ctx, &spbs.StatusRequest{ Worker: &spb.Server{ PrivateId: "w_1234567890", @@ -3206,7 +3206,7 @@ func TestAuthorizeSession_Errors(t *testing.T) { store := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor) workerExists := func(tar target.Target) (version uint32) { - workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) + workerService := cluster.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) _, err := workerService.Status(context.Background(), &spbs.StatusRequest{ Worker: &spb.Server{ PrivateId: "w_1234567890", diff --git a/internal/daemon/controller/listeners.go b/internal/daemon/controller/listeners.go index 09365a7f7..ddd53ed35 100644 --- a/internal/daemon/controller/listeners.go +++ b/internal/daemon/controller/listeners.go @@ -13,7 +13,7 @@ import ( grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" "github.com/hashicorp/boundary/internal/cmd/base" - "github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers" + "github.com/hashicorp/boundary/internal/daemon/cluster/handlers" "github.com/hashicorp/boundary/internal/daemon/controller/internal/metric" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/go-multierror" @@ -140,12 +140,12 @@ func (c *Controller) configureForCluster(ln *base.ServerListener) (func(), error ), ) - workerService := workers.NewWorkerServiceServer(c.ServersRepoFn, c.SessionRepoFn, c.ConnectionRepoFn, + workerService := handlers.NewWorkerServiceServer(c.ServersRepoFn, c.SessionRepoFn, c.ConnectionRepoFn, c.workerStatusUpdateTimes, c.kms) pbs.RegisterServerCoordinationServiceServer(workerServer, workerService) pbs.RegisterSessionServiceServer(workerServer, workerService) - multihopService := workers.NewMultihopServiceServer( + multihopService := handlers.NewMultihopServiceServer( nodeauth.MakeCurrentParametersFactory(c.baseContext, nodee.NopTransactionStorage(c.NodeeFileStorage)), ) multihop.RegisterMultihopServiceServer(workerServer, multihopService) diff --git a/internal/daemon/worker/listeners.go b/internal/daemon/worker/listeners.go index 3f83115b1..0da0f3a62 100644 --- a/internal/daemon/worker/listeners.go +++ b/internal/daemon/worker/listeners.go @@ -13,7 +13,7 @@ import ( "time" "github.com/hashicorp/boundary/internal/cmd/base" - "github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers" + "github.com/hashicorp/boundary/internal/daemon/cluster/handlers" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/go-multierror" @@ -136,11 +136,11 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) grpc.MaxRecvMsgSize(math.MaxInt32), grpc.MaxSendMsgSize(math.MaxInt32), ) - multihopService := workers.NewMultihopServiceServer( + multihopService := handlers.NewMultihopServiceServer( nodeauth.MakeCurrentParametersFactory(w.baseContext, nodee.NopTransactionStorage(w.NodeeFileStorage)), ) multihop.RegisterMultihopServiceServer(downstreamServer, multihopService) - statusSessionService := workers.NewWorkerProxyServiceServer(w.controllerStatusConn, w.controllerSessionConn) + statusSessionService := NewWorkerProxyServiceServer(w.controllerStatusConn, w.controllerSessionConn) pbs.RegisterServerCoordinationServiceServer(downstreamServer, statusSessionService) pbs.RegisterSessionServiceServer(downstreamServer, statusSessionService) diff --git a/internal/daemon/controller/handlers/workers/worker_proxy_service.go b/internal/daemon/worker/worker_proxy_service.go similarity index 99% rename from internal/daemon/controller/handlers/workers/worker_proxy_service.go rename to internal/daemon/worker/worker_proxy_service.go index 198030dc2..54e657ed2 100644 --- a/internal/daemon/controller/handlers/workers/worker_proxy_service.go +++ b/internal/daemon/worker/worker_proxy_service.go @@ -1,4 +1,4 @@ -package workers +package worker import ( "context" From fa2efe98783d9b494461362e341afec1df6ac68a Mon Sep 17 00:00:00 2001 From: Todd Date: Fri, 27 May 2022 08:29:23 -0700 Subject: [PATCH 026/100] Create the Worker API resource and the CRUDL operation definitions for a worker. (#2114) --- internal/gen/controller.swagger.json | 310 +++++++ .../api/services/worker_service.pb.go | 856 ++++++++++++++++++ .../api/services/worker_service.pb.gw.go | 632 +++++++++++++ .../api/services/worker_service_grpc.pb.go | 291 ++++++ .../api/resources/workers/v1/worker.proto | 91 ++ .../api/services/v1/worker_service.proto | 128 +++ .../api/resources/workers/worker.pb.go | 482 ++++++++++ 7 files changed, 2790 insertions(+) create mode 100644 internal/gen/controller/api/services/worker_service.pb.go create mode 100644 internal/gen/controller/api/services/worker_service.pb.gw.go create mode 100644 internal/gen/controller/api/services/worker_service_grpc.pb.go create mode 100644 internal/proto/controller/api/resources/workers/v1/worker.proto create mode 100644 internal/proto/controller/api/services/v1/worker_service.proto create mode 100644 sdk/pbs/controller/api/resources/workers/worker.pb.go diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 5edeba6aa..76cb6f60d 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -49,6 +49,9 @@ }, { "name": "controller.api.services.v1.UserService" + }, + { + "name": "controller.api.services.v1.WorkerService" } ], "schemes": [ @@ -3649,6 +3652,153 @@ "controller.api.services.v1.UserService" ] } + }, + "/v1/workers": { + "get": { + "summary": "Lists all Workers.", + "operationId": "WorkerService_ListWorkers", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/controller.api.services.v1.ListWorkersResponse" + } + } + }, + "parameters": [ + { + "name": "scope_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "recursive", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "filter", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.WorkerService" + ] + }, + "post": { + "summary": "Creates a single Worker.", + "operationId": "WorkerService_CreateWorker", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + } + }, + "parameters": [ + { + "name": "item", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + } + ], + "tags": [ + "controller.api.services.v1.WorkerService" + ] + } + }, + "/v1/workers/{id}": { + "get": { + "summary": "Gets a single Worker.", + "operationId": "WorkerService_GetWorker", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.WorkerService" + ] + }, + "delete": { + "summary": "Deletes a Worker.", + "operationId": "WorkerService_DeleteWorker", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/controller.api.services.v1.DeleteWorkerResponse" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.WorkerService" + ] + }, + "patch": { + "summary": "Updates a Worker.", + "operationId": "WorkerService_UpdateWorker", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "item", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + }, + { + "name": "update_mask", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.WorkerService" + ] + } } }, "definitions": { @@ -5346,6 +5496,125 @@ }, "title": "User contains all fields related to a User resource" }, + "controller.api.resources.workers.v1.Worker": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Output only. The ID of the User.", + "readOnly": true + }, + "scope_id": { + "type": "string", + "description": "The ID of the Scope this resource is in." + }, + "scope": { + "$ref": "#/definitions/controller.api.resources.scopes.v1.ScopeInfo", + "description": "Output only. Scope information for this resource.", + "readOnly": true + }, + "name": { + "type": "string", + "description": "Optional name for identification purposes." + }, + "description": { + "type": "string", + "description": "Optional user-set description for identification purposes." + }, + "created_time": { + "type": "string", + "format": "date-time", + "description": "Output only. The time this resource was created.", + "readOnly": true + }, + "updated_time": { + "type": "string", + "format": "date-time", + "description": "Output only. The time this resource was last updated.", + "readOnly": true + }, + "version": { + "type": "integer", + "format": "int64", + "description": "Version is used in mutation requests, after the initial creation, to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version." + }, + "address": { + "type": "string", + "description": "The address that this worker is reachable at." + }, + "canonical_address": { + "type": "string", + "description": "Output only. This is the address used by authorized session. The value\nis the same as the address field if set. Otherwise it uses the address the\nworker reports.", + "readOnly": true + }, + "tags": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "object" + } + }, + "description": "Output only. The tags attached to this worker.", + "readOnly": true + }, + "canonical_tags": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "object" + } + }, + "description": "Output only. The deduplicated union of the tags reported by the worker and\nthe tags added through the user facing api. This is used when applying\nworker filters.", + "readOnly": true + }, + "last_status_time": { + "type": "string", + "format": "date-time", + "description": "Output only. The time this worker daemon last reported its status.", + "readOnly": true + }, + "worker_config": { + "$ref": "#/definitions/controller.api.resources.workers.v1.WorkerConfig", + "description": "Output only. The set of information the worker daemon has reported about\nitself.", + "readOnly": true + } + }, + "title": "Worker contains all fields related to a Worker resource" + }, + "controller.api.resources.workers.v1.WorkerConfig": { + "type": "object", + "properties": { + "address": { + "type": "string", + "description": "Output only. The address the worker daemon reports itself as reachable at.", + "readOnly": true + }, + "name": { + "type": "string", + "description": "Output only. The name the worker daemon reports itself having.", + "readOnly": true + }, + "version": { + "type": "string", + "description": "Output only. The boundary version the worker has reported itself as running.", + "readOnly": true + }, + "tags": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "object" + } + }, + "description": "Output only. The tags reporteed by the worker daemon.", + "readOnly": true + } + }, + "description": "WorkerConfig contains configuration values reported by the worker daemon." + }, "controller.api.services.v1.AddGroupMembersResponse": { "type": "object", "properties": { @@ -5610,6 +5879,17 @@ } } }, + "controller.api.services.v1.CreateWorkerResponse": { + "type": "object", + "properties": { + "uri": { + "type": "string" + }, + "item": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + } + }, "controller.api.services.v1.DeleteAccountResponse": { "type": "object" }, @@ -5652,6 +5932,9 @@ "controller.api.services.v1.DeleteUserResponse": { "type": "object" }, + "controller.api.services.v1.DeleteWorkerResponse": { + "type": "object" + }, "controller.api.services.v1.GetAccountResponse": { "type": "object", "properties": { @@ -5772,6 +6055,14 @@ } } }, + "controller.api.services.v1.GetWorkerResponse": { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + } + }, "controller.api.services.v1.ListAccountsResponse": { "type": "object", "properties": { @@ -5937,6 +6228,17 @@ } } }, + "controller.api.services.v1.ListWorkersResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + } + } + }, "controller.api.services.v1.RemoveGroupMembersResponse": { "type": "object", "properties": { @@ -6193,6 +6495,14 @@ } } }, + "controller.api.services.v1.UpdateWorkerResponse": { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + } + }, "google.protobuf.NullValue": { "type": "string", "enum": [ diff --git a/internal/gen/controller/api/services/worker_service.pb.go b/internal/gen/controller/api/services/worker_service.pb.go new file mode 100644 index 000000000..bff4323ee --- /dev/null +++ b/internal/gen/controller/api/services/worker_service.pb.go @@ -0,0 +1,856 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/api/services/v1/worker_service.proto + +package services + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + workers "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetWorkerRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: `class:"public"` +} + +func (x *GetWorkerRequest) Reset() { + *x = GetWorkerRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetWorkerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWorkerRequest) ProtoMessage() {} + +func (x *GetWorkerRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWorkerRequest.ProtoReflect.Descriptor instead. +func (*GetWorkerRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{0} +} + +func (x *GetWorkerRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetWorkerResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *workers.Worker `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *GetWorkerResponse) Reset() { + *x = GetWorkerResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetWorkerResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWorkerResponse) ProtoMessage() {} + +func (x *GetWorkerResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWorkerResponse.ProtoReflect.Descriptor instead. +func (*GetWorkerResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{1} +} + +func (x *GetWorkerResponse) GetItem() *workers.Worker { + if x != nil { + return x.Item + } + return nil +} + +type ListWorkersRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ScopeId string `protobuf:"bytes,1,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty"` // @gotags: `class:"public"` + Recursive bool `protobuf:"varint,20,opt,name=recursive,proto3" json:"recursive,omitempty"` // @gotags: `class:"public"` + Filter string `protobuf:"bytes,30,opt,name=filter,proto3" json:"filter,omitempty"` // @gotags: `class:"sensitive"` +} + +func (x *ListWorkersRequest) Reset() { + *x = ListWorkersRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListWorkersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListWorkersRequest) ProtoMessage() {} + +func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListWorkersRequest.ProtoReflect.Descriptor instead. +func (*ListWorkersRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{2} +} + +func (x *ListWorkersRequest) GetScopeId() string { + if x != nil { + return x.ScopeId + } + return "" +} + +func (x *ListWorkersRequest) GetRecursive() bool { + if x != nil { + return x.Recursive + } + return false +} + +func (x *ListWorkersRequest) GetFilter() string { + if x != nil { + return x.Filter + } + return "" +} + +type ListWorkersResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Items []*workers.Worker `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` +} + +func (x *ListWorkersResponse) Reset() { + *x = ListWorkersResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListWorkersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListWorkersResponse) ProtoMessage() {} + +func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListWorkersResponse.ProtoReflect.Descriptor instead. +func (*ListWorkersResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{3} +} + +func (x *ListWorkersResponse) GetItems() []*workers.Worker { + if x != nil { + return x.Items + } + return nil +} + +type CreateWorkerRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *workers.Worker `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *CreateWorkerRequest) Reset() { + *x = CreateWorkerRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateWorkerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateWorkerRequest) ProtoMessage() {} + +func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateWorkerRequest.ProtoReflect.Descriptor instead. +func (*CreateWorkerRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateWorkerRequest) GetItem() *workers.Worker { + if x != nil { + return x.Item + } + return nil +} + +type CreateWorkerResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"` // @gotags: `class:"public"` + Item *workers.Worker `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *CreateWorkerResponse) Reset() { + *x = CreateWorkerResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateWorkerResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateWorkerResponse) ProtoMessage() {} + +func (x *CreateWorkerResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateWorkerResponse.ProtoReflect.Descriptor instead. +func (*CreateWorkerResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateWorkerResponse) GetUri() string { + if x != nil { + return x.Uri + } + return "" +} + +func (x *CreateWorkerResponse) GetItem() *workers.Worker { + if x != nil { + return x.Item + } + return nil +} + +type UpdateWorkerRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: `class:"public"` + Item *workers.Worker `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` + UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=update_mask,proto3" json:"update_mask,omitempty"` +} + +func (x *UpdateWorkerRequest) Reset() { + *x = UpdateWorkerRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateWorkerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateWorkerRequest) ProtoMessage() {} + +func (x *UpdateWorkerRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateWorkerRequest.ProtoReflect.Descriptor instead. +func (*UpdateWorkerRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{6} +} + +func (x *UpdateWorkerRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateWorkerRequest) GetItem() *workers.Worker { + if x != nil { + return x.Item + } + return nil +} + +func (x *UpdateWorkerRequest) GetUpdateMask() *fieldmaskpb.FieldMask { + if x != nil { + return x.UpdateMask + } + return nil +} + +type UpdateWorkerResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *workers.Worker `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *UpdateWorkerResponse) Reset() { + *x = UpdateWorkerResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateWorkerResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateWorkerResponse) ProtoMessage() {} + +func (x *UpdateWorkerResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateWorkerResponse.ProtoReflect.Descriptor instead. +func (*UpdateWorkerResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{7} +} + +func (x *UpdateWorkerResponse) GetItem() *workers.Worker { + if x != nil { + return x.Item + } + return nil +} + +type DeleteWorkerRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: `class:"public"` +} + +func (x *DeleteWorkerRequest) Reset() { + *x = DeleteWorkerRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteWorkerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteWorkerRequest) ProtoMessage() {} + +func (x *DeleteWorkerRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteWorkerRequest.ProtoReflect.Descriptor instead. +func (*DeleteWorkerRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{8} +} + +func (x *DeleteWorkerRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DeleteWorkerResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteWorkerResponse) Reset() { + *x = DeleteWorkerResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteWorkerResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteWorkerResponse) ProtoMessage() {} + +func (x *DeleteWorkerResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteWorkerResponse.ProtoReflect.Descriptor instead. +func (*DeleteWorkerResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{9} +} + +var File_controller_api_services_v1_worker_service_proto protoreflect.FileDescriptor + +var file_controller_api_services_v1_worker_service_proto_rawDesc = []byte{ + 0x0a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x1a, 0x30, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, + 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0x22, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x22, 0x54, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x65, 0x0a, 0x12, 0x4c, 0x69, 0x73, + 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, + 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, + 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x22, 0x58, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x56, 0x0a, 0x13, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x22, 0x69, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, + 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x3f, 0x0a, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xa4, 0x01, + 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x3c, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, + 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, + 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x57, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x25, 0x0a, + 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xd7, 0x06, 0x0a, + 0x0d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xa2, + 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x2c, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x92, 0x41, 0x17, 0x12, 0x15, + 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x10, 0x2f, 0x76, 0x31, + 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x12, 0x9a, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x73, 0x12, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x92, 0x41, 0x14, 0x12, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x73, + 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, + 0x12, 0xaf, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, 0x92, 0x41, 0x1a, 0x12, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x57, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x77, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x12, 0xad, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x92, 0x41, 0x13, 0x12, 0x11, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1e, 0x32, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x12, 0xa1, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x92, 0x41, 0x13, 0x12, 0x11, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x12, 0x2a, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_api_services_v1_worker_service_proto_rawDescOnce sync.Once + file_controller_api_services_v1_worker_service_proto_rawDescData = file_controller_api_services_v1_worker_service_proto_rawDesc +) + +func file_controller_api_services_v1_worker_service_proto_rawDescGZIP() []byte { + file_controller_api_services_v1_worker_service_proto_rawDescOnce.Do(func() { + file_controller_api_services_v1_worker_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_services_v1_worker_service_proto_rawDescData) + }) + return file_controller_api_services_v1_worker_service_proto_rawDescData +} + +var file_controller_api_services_v1_worker_service_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_controller_api_services_v1_worker_service_proto_goTypes = []interface{}{ + (*GetWorkerRequest)(nil), // 0: controller.api.services.v1.GetWorkerRequest + (*GetWorkerResponse)(nil), // 1: controller.api.services.v1.GetWorkerResponse + (*ListWorkersRequest)(nil), // 2: controller.api.services.v1.ListWorkersRequest + (*ListWorkersResponse)(nil), // 3: controller.api.services.v1.ListWorkersResponse + (*CreateWorkerRequest)(nil), // 4: controller.api.services.v1.CreateWorkerRequest + (*CreateWorkerResponse)(nil), // 5: controller.api.services.v1.CreateWorkerResponse + (*UpdateWorkerRequest)(nil), // 6: controller.api.services.v1.UpdateWorkerRequest + (*UpdateWorkerResponse)(nil), // 7: controller.api.services.v1.UpdateWorkerResponse + (*DeleteWorkerRequest)(nil), // 8: controller.api.services.v1.DeleteWorkerRequest + (*DeleteWorkerResponse)(nil), // 9: controller.api.services.v1.DeleteWorkerResponse + (*workers.Worker)(nil), // 10: controller.api.resources.workers.v1.Worker + (*fieldmaskpb.FieldMask)(nil), // 11: google.protobuf.FieldMask +} +var file_controller_api_services_v1_worker_service_proto_depIdxs = []int32{ + 10, // 0: controller.api.services.v1.GetWorkerResponse.item:type_name -> controller.api.resources.workers.v1.Worker + 10, // 1: controller.api.services.v1.ListWorkersResponse.items:type_name -> controller.api.resources.workers.v1.Worker + 10, // 2: controller.api.services.v1.CreateWorkerRequest.item:type_name -> controller.api.resources.workers.v1.Worker + 10, // 3: controller.api.services.v1.CreateWorkerResponse.item:type_name -> controller.api.resources.workers.v1.Worker + 10, // 4: controller.api.services.v1.UpdateWorkerRequest.item:type_name -> controller.api.resources.workers.v1.Worker + 11, // 5: controller.api.services.v1.UpdateWorkerRequest.update_mask:type_name -> google.protobuf.FieldMask + 10, // 6: controller.api.services.v1.UpdateWorkerResponse.item:type_name -> controller.api.resources.workers.v1.Worker + 0, // 7: controller.api.services.v1.WorkerService.GetWorker:input_type -> controller.api.services.v1.GetWorkerRequest + 2, // 8: controller.api.services.v1.WorkerService.ListWorkers:input_type -> controller.api.services.v1.ListWorkersRequest + 4, // 9: controller.api.services.v1.WorkerService.CreateWorker:input_type -> controller.api.services.v1.CreateWorkerRequest + 6, // 10: controller.api.services.v1.WorkerService.UpdateWorker:input_type -> controller.api.services.v1.UpdateWorkerRequest + 8, // 11: controller.api.services.v1.WorkerService.DeleteWorker:input_type -> controller.api.services.v1.DeleteWorkerRequest + 1, // 12: controller.api.services.v1.WorkerService.GetWorker:output_type -> controller.api.services.v1.GetWorkerResponse + 3, // 13: controller.api.services.v1.WorkerService.ListWorkers:output_type -> controller.api.services.v1.ListWorkersResponse + 5, // 14: controller.api.services.v1.WorkerService.CreateWorker:output_type -> controller.api.services.v1.CreateWorkerResponse + 7, // 15: controller.api.services.v1.WorkerService.UpdateWorker:output_type -> controller.api.services.v1.UpdateWorkerResponse + 9, // 16: controller.api.services.v1.WorkerService.DeleteWorker:output_type -> controller.api.services.v1.DeleteWorkerResponse + 12, // [12:17] is the sub-list for method output_type + 7, // [7:12] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_controller_api_services_v1_worker_service_proto_init() } +func file_controller_api_services_v1_worker_service_proto_init() { + if File_controller_api_services_v1_worker_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_api_services_v1_worker_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_worker_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkerResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_worker_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListWorkersRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_worker_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListWorkersResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_worker_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateWorkerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_worker_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateWorkerResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_worker_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateWorkerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_worker_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateWorkerResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_worker_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteWorkerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_worker_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteWorkerResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_api_services_v1_worker_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_controller_api_services_v1_worker_service_proto_goTypes, + DependencyIndexes: file_controller_api_services_v1_worker_service_proto_depIdxs, + MessageInfos: file_controller_api_services_v1_worker_service_proto_msgTypes, + }.Build() + File_controller_api_services_v1_worker_service_proto = out.File + file_controller_api_services_v1_worker_service_proto_rawDesc = nil + file_controller_api_services_v1_worker_service_proto_goTypes = nil + file_controller_api_services_v1_worker_service_proto_depIdxs = nil +} diff --git a/internal/gen/controller/api/services/worker_service.pb.gw.go b/internal/gen/controller/api/services/worker_service.pb.gw.go new file mode 100644 index 000000000..7a5e1897a --- /dev/null +++ b/internal/gen/controller/api/services/worker_service.pb.gw.go @@ -0,0 +1,632 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: controller/api/services/v1/worker_service.proto + +/* +Package services is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package services + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_WorkerService_GetWorker_0(ctx context.Context, marshaler runtime.Marshaler, client WorkerServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetWorkerRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := client.GetWorker(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WorkerService_GetWorker_0(ctx context.Context, marshaler runtime.Marshaler, server WorkerServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetWorkerRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := server.GetWorker(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_WorkerService_ListWorkers_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_WorkerService_ListWorkers_0(ctx context.Context, marshaler runtime.Marshaler, client WorkerServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListWorkersRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WorkerService_ListWorkers_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListWorkers(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WorkerService_ListWorkers_0(ctx context.Context, marshaler runtime.Marshaler, server WorkerServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListWorkersRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WorkerService_ListWorkers_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListWorkers(ctx, &protoReq) + return msg, metadata, err + +} + +func request_WorkerService_CreateWorker_0(ctx context.Context, marshaler runtime.Marshaler, client WorkerServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateWorkerRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.CreateWorker(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WorkerService_CreateWorker_0(ctx context.Context, marshaler runtime.Marshaler, server WorkerServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateWorkerRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.CreateWorker(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_WorkerService_UpdateWorker_0 = &utilities.DoubleArray{Encoding: map[string]int{"item": 0, "id": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + +func request_WorkerService_UpdateWorker_0(ctx context.Context, marshaler runtime.Marshaler, client WorkerServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateWorkerRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 { + if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Item); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } else { + protoReq.UpdateMask = fieldMask + } + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WorkerService_UpdateWorker_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.UpdateWorker(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WorkerService_UpdateWorker_0(ctx context.Context, marshaler runtime.Marshaler, server WorkerServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateWorkerRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 { + if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Item); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } else { + protoReq.UpdateMask = fieldMask + } + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WorkerService_UpdateWorker_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.UpdateWorker(ctx, &protoReq) + return msg, metadata, err + +} + +func request_WorkerService_DeleteWorker_0(ctx context.Context, marshaler runtime.Marshaler, client WorkerServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteWorkerRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := client.DeleteWorker(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WorkerService_DeleteWorker_0(ctx context.Context, marshaler runtime.Marshaler, server WorkerServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteWorkerRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := server.DeleteWorker(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterWorkerServiceHandlerServer registers the http handlers for service WorkerService to "mux". +// UnaryRPC :call WorkerServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterWorkerServiceHandlerFromEndpoint instead. +func RegisterWorkerServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server WorkerServiceServer) error { + + mux.Handle("GET", pattern_WorkerService_GetWorker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/GetWorker", runtime.WithHTTPPathPattern("/v1/workers/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WorkerService_GetWorker_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WorkerService_GetWorker_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_GetWorker_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WorkerService_ListWorkers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/ListWorkers", runtime.WithHTTPPathPattern("/v1/workers")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WorkerService_ListWorkers_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WorkerService_ListWorkers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_WorkerService_CreateWorker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/CreateWorker", runtime.WithHTTPPathPattern("/v1/workers")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WorkerService_CreateWorker_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WorkerService_CreateWorker_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_CreateWorker_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PATCH", pattern_WorkerService_UpdateWorker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/UpdateWorker", runtime.WithHTTPPathPattern("/v1/workers/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WorkerService_UpdateWorker_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WorkerService_UpdateWorker_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_UpdateWorker_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_WorkerService_DeleteWorker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/DeleteWorker", runtime.WithHTTPPathPattern("/v1/workers/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WorkerService_DeleteWorker_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WorkerService_DeleteWorker_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterWorkerServiceHandlerFromEndpoint is same as RegisterWorkerServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterWorkerServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterWorkerServiceHandler(ctx, mux, conn) +} + +// RegisterWorkerServiceHandler registers the http handlers for service WorkerService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterWorkerServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterWorkerServiceHandlerClient(ctx, mux, NewWorkerServiceClient(conn)) +} + +// RegisterWorkerServiceHandlerClient registers the http handlers for service WorkerService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "WorkerServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "WorkerServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "WorkerServiceClient" to call the correct interceptors. +func RegisterWorkerServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client WorkerServiceClient) error { + + mux.Handle("GET", pattern_WorkerService_GetWorker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/GetWorker", runtime.WithHTTPPathPattern("/v1/workers/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WorkerService_GetWorker_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WorkerService_GetWorker_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_GetWorker_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WorkerService_ListWorkers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/ListWorkers", runtime.WithHTTPPathPattern("/v1/workers")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WorkerService_ListWorkers_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WorkerService_ListWorkers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_WorkerService_CreateWorker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/CreateWorker", runtime.WithHTTPPathPattern("/v1/workers")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WorkerService_CreateWorker_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WorkerService_CreateWorker_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_CreateWorker_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PATCH", pattern_WorkerService_UpdateWorker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/UpdateWorker", runtime.WithHTTPPathPattern("/v1/workers/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WorkerService_UpdateWorker_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WorkerService_UpdateWorker_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_UpdateWorker_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_WorkerService_DeleteWorker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/DeleteWorker", runtime.WithHTTPPathPattern("/v1/workers/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WorkerService_DeleteWorker_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WorkerService_DeleteWorker_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +type response_WorkerService_GetWorker_0 struct { + proto.Message +} + +func (m response_WorkerService_GetWorker_0) XXX_ResponseBody() interface{} { + response := m.Message.(*GetWorkerResponse) + return response.Item +} + +type response_WorkerService_CreateWorker_0 struct { + proto.Message +} + +func (m response_WorkerService_CreateWorker_0) XXX_ResponseBody() interface{} { + response := m.Message.(*CreateWorkerResponse) + return response.Item +} + +type response_WorkerService_UpdateWorker_0 struct { + proto.Message +} + +func (m response_WorkerService_UpdateWorker_0) XXX_ResponseBody() interface{} { + response := m.Message.(*UpdateWorkerResponse) + return response.Item +} + +var ( + pattern_WorkerService_GetWorker_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "workers", "id"}, "")) + + pattern_WorkerService_ListWorkers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "workers"}, "")) + + pattern_WorkerService_CreateWorker_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "workers"}, "")) + + pattern_WorkerService_UpdateWorker_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "workers", "id"}, "")) + + pattern_WorkerService_DeleteWorker_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "workers", "id"}, "")) +) + +var ( + forward_WorkerService_GetWorker_0 = runtime.ForwardResponseMessage + + forward_WorkerService_ListWorkers_0 = runtime.ForwardResponseMessage + + forward_WorkerService_CreateWorker_0 = runtime.ForwardResponseMessage + + forward_WorkerService_UpdateWorker_0 = runtime.ForwardResponseMessage + + forward_WorkerService_DeleteWorker_0 = runtime.ForwardResponseMessage +) diff --git a/internal/gen/controller/api/services/worker_service_grpc.pb.go b/internal/gen/controller/api/services/worker_service_grpc.pb.go new file mode 100644 index 000000000..6002ef699 --- /dev/null +++ b/internal/gen/controller/api/services/worker_service_grpc.pb.go @@ -0,0 +1,291 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package services + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// WorkerServiceClient is the client API for WorkerService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type WorkerServiceClient interface { + // GetWorker returns a stored Worker if present. The provided request + // must include the Worker ID for the Worker being retrieved. If + // that ID is missing, malformed or reference a non existing + // resource an error is returned. + GetWorker(ctx context.Context, in *GetWorkerRequest, opts ...grpc.CallOption) (*GetWorkerResponse, error) + // ListWorkers returns a list of stored Workers which exist inside the provided + // scope. The request must include the scope ID for the Workers being listed. + // If the scope ID is missing, malformed, or reference a non existing scope, + // an error is returned. + ListWorkers(ctx context.Context, in *ListWorkersRequest, opts ...grpc.CallOption) (*ListWorkersResponse, error) + // CreateWorker creates and stores a Worker in boundary. The provided + // request must include the Scope id in which the Worker will be created. + // If the Scope id is missing, malformed or references a non existing + // resource, an error is returned. If a name is provided that is in + // use in another Worker in the same scope, an error is returned. + CreateWorker(ctx context.Context, in *CreateWorkerRequest, opts ...grpc.CallOption) (*CreateWorkerResponse, error) + // UpdateWorker updates an existing Worker in boundary. The provided + // Worker must not have any read only fields set. The update mask must be + // included in the request and contain at least 1 mutable field. To unset + // a field's value, include the field in the update mask and don't set it + // in the provided Worker. An error is returned if either the Worker id is + // missing or reference a non existing resource. An error is also returned + // if the request attempts to update the name to one that is already in use + // in this Scope. + UpdateWorker(ctx context.Context, in *UpdateWorkerRequest, opts ...grpc.CallOption) (*UpdateWorkerResponse, error) + // DeleteWorker removes a Worker from Boundary. If the provided Worker ID + // is malformed or not provided an error is returned. + DeleteWorker(ctx context.Context, in *DeleteWorkerRequest, opts ...grpc.CallOption) (*DeleteWorkerResponse, error) +} + +type workerServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewWorkerServiceClient(cc grpc.ClientConnInterface) WorkerServiceClient { + return &workerServiceClient{cc} +} + +func (c *workerServiceClient) GetWorker(ctx context.Context, in *GetWorkerRequest, opts ...grpc.CallOption) (*GetWorkerResponse, error) { + out := new(GetWorkerResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.WorkerService/GetWorker", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *workerServiceClient) ListWorkers(ctx context.Context, in *ListWorkersRequest, opts ...grpc.CallOption) (*ListWorkersResponse, error) { + out := new(ListWorkersResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.WorkerService/ListWorkers", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *workerServiceClient) CreateWorker(ctx context.Context, in *CreateWorkerRequest, opts ...grpc.CallOption) (*CreateWorkerResponse, error) { + out := new(CreateWorkerResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.WorkerService/CreateWorker", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *workerServiceClient) UpdateWorker(ctx context.Context, in *UpdateWorkerRequest, opts ...grpc.CallOption) (*UpdateWorkerResponse, error) { + out := new(UpdateWorkerResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.WorkerService/UpdateWorker", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *workerServiceClient) DeleteWorker(ctx context.Context, in *DeleteWorkerRequest, opts ...grpc.CallOption) (*DeleteWorkerResponse, error) { + out := new(DeleteWorkerResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.WorkerService/DeleteWorker", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// WorkerServiceServer is the server API for WorkerService service. +// All implementations must embed UnimplementedWorkerServiceServer +// for forward compatibility +type WorkerServiceServer interface { + // GetWorker returns a stored Worker if present. The provided request + // must include the Worker ID for the Worker being retrieved. If + // that ID is missing, malformed or reference a non existing + // resource an error is returned. + GetWorker(context.Context, *GetWorkerRequest) (*GetWorkerResponse, error) + // ListWorkers returns a list of stored Workers which exist inside the provided + // scope. The request must include the scope ID for the Workers being listed. + // If the scope ID is missing, malformed, or reference a non existing scope, + // an error is returned. + ListWorkers(context.Context, *ListWorkersRequest) (*ListWorkersResponse, error) + // CreateWorker creates and stores a Worker in boundary. The provided + // request must include the Scope id in which the Worker will be created. + // If the Scope id is missing, malformed or references a non existing + // resource, an error is returned. If a name is provided that is in + // use in another Worker in the same scope, an error is returned. + CreateWorker(context.Context, *CreateWorkerRequest) (*CreateWorkerResponse, error) + // UpdateWorker updates an existing Worker in boundary. The provided + // Worker must not have any read only fields set. The update mask must be + // included in the request and contain at least 1 mutable field. To unset + // a field's value, include the field in the update mask and don't set it + // in the provided Worker. An error is returned if either the Worker id is + // missing or reference a non existing resource. An error is also returned + // if the request attempts to update the name to one that is already in use + // in this Scope. + UpdateWorker(context.Context, *UpdateWorkerRequest) (*UpdateWorkerResponse, error) + // DeleteWorker removes a Worker from Boundary. If the provided Worker ID + // is malformed or not provided an error is returned. + DeleteWorker(context.Context, *DeleteWorkerRequest) (*DeleteWorkerResponse, error) + mustEmbedUnimplementedWorkerServiceServer() +} + +// UnimplementedWorkerServiceServer must be embedded to have forward compatible implementations. +type UnimplementedWorkerServiceServer struct { +} + +func (UnimplementedWorkerServiceServer) GetWorker(context.Context, *GetWorkerRequest) (*GetWorkerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetWorker not implemented") +} +func (UnimplementedWorkerServiceServer) ListWorkers(context.Context, *ListWorkersRequest) (*ListWorkersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListWorkers not implemented") +} +func (UnimplementedWorkerServiceServer) CreateWorker(context.Context, *CreateWorkerRequest) (*CreateWorkerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateWorker not implemented") +} +func (UnimplementedWorkerServiceServer) UpdateWorker(context.Context, *UpdateWorkerRequest) (*UpdateWorkerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateWorker not implemented") +} +func (UnimplementedWorkerServiceServer) DeleteWorker(context.Context, *DeleteWorkerRequest) (*DeleteWorkerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteWorker not implemented") +} +func (UnimplementedWorkerServiceServer) mustEmbedUnimplementedWorkerServiceServer() {} + +// UnsafeWorkerServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to WorkerServiceServer will +// result in compilation errors. +type UnsafeWorkerServiceServer interface { + mustEmbedUnimplementedWorkerServiceServer() +} + +func RegisterWorkerServiceServer(s grpc.ServiceRegistrar, srv WorkerServiceServer) { + s.RegisterService(&WorkerService_ServiceDesc, srv) +} + +func _WorkerService_GetWorker_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetWorkerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkerServiceServer).GetWorker(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.WorkerService/GetWorker", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkerServiceServer).GetWorker(ctx, req.(*GetWorkerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WorkerService_ListWorkers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListWorkersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkerServiceServer).ListWorkers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.WorkerService/ListWorkers", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkerServiceServer).ListWorkers(ctx, req.(*ListWorkersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WorkerService_CreateWorker_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateWorkerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkerServiceServer).CreateWorker(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.WorkerService/CreateWorker", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkerServiceServer).CreateWorker(ctx, req.(*CreateWorkerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WorkerService_UpdateWorker_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateWorkerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkerServiceServer).UpdateWorker(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.WorkerService/UpdateWorker", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkerServiceServer).UpdateWorker(ctx, req.(*UpdateWorkerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WorkerService_DeleteWorker_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteWorkerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkerServiceServer).DeleteWorker(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.WorkerService/DeleteWorker", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkerServiceServer).DeleteWorker(ctx, req.(*DeleteWorkerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// WorkerService_ServiceDesc is the grpc.ServiceDesc for WorkerService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var WorkerService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "controller.api.services.v1.WorkerService", + HandlerType: (*WorkerServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetWorker", + Handler: _WorkerService_GetWorker_Handler, + }, + { + MethodName: "ListWorkers", + Handler: _WorkerService_ListWorkers_Handler, + }, + { + MethodName: "CreateWorker", + Handler: _WorkerService_CreateWorker_Handler, + }, + { + MethodName: "UpdateWorker", + Handler: _WorkerService_UpdateWorker_Handler, + }, + { + MethodName: "DeleteWorker", + Handler: _WorkerService_DeleteWorker_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "controller/api/services/v1/worker_service.proto", +} diff --git a/internal/proto/controller/api/resources/workers/v1/worker.proto b/internal/proto/controller/api/resources/workers/v1/worker.proto new file mode 100644 index 000000000..1f9112cb0 --- /dev/null +++ b/internal/proto/controller/api/resources/workers/v1/worker.proto @@ -0,0 +1,91 @@ +syntax = "proto3"; + +package controller.api.resources.workers.v1; + +import "controller/api/resources/scopes/v1/scope.proto"; +import "controller/custom_options/v1/options.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; + +option go_package = "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers;workers"; + +// WorkerConfig contains configuration values reported by the worker daemon. +message WorkerConfig { + // Output only. The address the worker daemon reports itself as reachable at. + string address = 10; + + // Output only. The name the worker daemon reports itself having. + string name = 20; + + // Output only. The boundary version the worker has reported itself as running. + string version = 30; + + // Output only. The tags reporteed by the worker daemon. + map tags = 40; +} + +// Worker contains all fields related to a Worker resource +message Worker { + // Output only. The ID of the User. + string id = 10; // @gotags: `class:"public"` + + // The ID of the Scope this resource is in. + string scope_id = 20 [json_name = "scope_id"]; // @gotags: `class:"public"` + + // Output only. Scope information for this resource. + resources.scopes.v1.ScopeInfo scope = 30; + + // Optional name for identification purposes. + google.protobuf.StringValue name = 40 [ + (custom_options.v1.generate_sdk_option) = true, + (custom_options.v1.mask_mapping) = { + this: "name" + that: "name" + } + ]; // @gotags: `class:"sensitive"` + + // Optional user-set description for identification purposes. + google.protobuf.StringValue description = 50 [ + (custom_options.v1.generate_sdk_option) = true, + (custom_options.v1.mask_mapping) = { + this: "description" + that: "description" + } + ]; // @gotags: `class:"sensitive"` + + // Output only. The time this resource was created. + google.protobuf.Timestamp created_time = 60 [json_name = "created_time"]; // @gotags: `class:"public"` + + // Output only. The time this resource was last updated. + google.protobuf.Timestamp updated_time = 70 [json_name = "updated_time"]; // @gotags: `class:"public"` + + // Version is used in mutation requests, after the initial creation, to ensure this resource has not changed. + // The mutation will fail if the version does not match the latest known good version. + uint32 version = 80; // @gotags: `class:"public"` + + // The address that this worker is reachable at. + google.protobuf.StringValue address = 90; // @gotags: `class:"public"` + + // Output only. This is the address used by authorized session. The value + // is the same as the address field if set. Otherwise it uses the address the + // worker reports. + string canonical_address = 100; // @gotags: `class:"public"` + + // Output only. The tags attached to this worker. + map tags = 110; // @gotags: `class:"public"` + + // Output only. The deduplicated union of the tags reported by the worker and + // the tags added through the user facing api. This is used when applying + // worker filters. + map canonical_tags = 120; // @gotags: `class:"public"` + + // Output only. The time this worker daemon last reported its status. + google.protobuf.Timestamp last_status_time = 130 [json_name = "last_status_time"]; // @gotags: `class:"public"` + + // Output only. The set of information the worker daemon has reported about + // itself. + WorkerConfig worker_config = 140 [json_name = "worker_config"]; + +// TODO: Add worker auth related fields. +} diff --git a/internal/proto/controller/api/services/v1/worker_service.proto b/internal/proto/controller/api/services/v1/worker_service.proto new file mode 100644 index 000000000..fea2c3d9a --- /dev/null +++ b/internal/proto/controller/api/services/v1/worker_service.proto @@ -0,0 +1,128 @@ +syntax = "proto3"; + +package controller.api.services.v1; + +import "controller/api/resources/workers/v1/worker.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/field_mask.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; + +option go_package = "github.com/hashicorp/boundary/internal/gen/controller/api/services;services"; + +service WorkerService { + // GetWorker returns a stored Worker if present. The provided request + // must include the Worker ID for the Worker being retrieved. If + // that ID is missing, malformed or reference a non existing + // resource an error is returned. + rpc GetWorker(GetWorkerRequest) returns (GetWorkerResponse) { + option (google.api.http) = { + get: "/v1/workers/{id}" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Gets a single Worker." + }; + } + + // ListWorkers returns a list of stored Workers which exist inside the provided + // scope. The request must include the scope ID for the Workers being listed. + // If the scope ID is missing, malformed, or reference a non existing scope, + // an error is returned. + rpc ListWorkers(ListWorkersRequest) returns (ListWorkersResponse) { + option (google.api.http) = { + get: "/v1/workers" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Lists all Workers." + }; + } + + // CreateWorker creates and stores a Worker in boundary. The provided + // request must include the Scope id in which the Worker will be created. + // If the Scope id is missing, malformed or references a non existing + // resource, an error is returned. If a name is provided that is in + // use in another Worker in the same scope, an error is returned. + rpc CreateWorker(CreateWorkerRequest) returns (CreateWorkerResponse) { + option (google.api.http) = { + post: "/v1/workers" + body: "item" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Creates a single Worker." + }; + } + + // UpdateWorker updates an existing Worker in boundary. The provided + // Worker must not have any read only fields set. The update mask must be + // included in the request and contain at least 1 mutable field. To unset + // a field's value, include the field in the update mask and don't set it + // in the provided Worker. An error is returned if either the Worker id is + // missing or reference a non existing resource. An error is also returned + // if the request attempts to update the name to one that is already in use + // in this Scope. + rpc UpdateWorker(UpdateWorkerRequest) returns (UpdateWorkerResponse) { + option (google.api.http) = { + patch: "/v1/workers/{id}" + body: "item" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Updates a Worker." + }; + } + + // DeleteWorker removes a Worker from Boundary. If the provided Worker ID + // is malformed or not provided an error is returned. + rpc DeleteWorker(DeleteWorkerRequest) returns (DeleteWorkerResponse) { + option (google.api.http) = { + delete: "/v1/workers/{id}" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Deletes a Worker." + }; + } +} + +message GetWorkerRequest { + string id = 1; // @gotags: `class:"public"` +} + +message GetWorkerResponse { + resources.workers.v1.Worker item = 1; +} + +message ListWorkersRequest { + string scope_id = 1; // @gotags: `class:"public"` + bool recursive = 20 [json_name = "recursive"]; // @gotags: `class:"public"` + string filter = 30 [json_name = "filter"]; // @gotags: `class:"sensitive"` +} + +message ListWorkersResponse { + repeated resources.workers.v1.Worker items = 1; +} + +message CreateWorkerRequest { + resources.workers.v1.Worker item = 1; +} + +message CreateWorkerResponse { + string uri = 1; // @gotags: `class:"public"` + resources.workers.v1.Worker item = 2; +} + +message UpdateWorkerRequest { + string id = 1; // @gotags: `class:"public"` + resources.workers.v1.Worker item = 2; + google.protobuf.FieldMask update_mask = 3 [json_name = "update_mask"]; +} + +message UpdateWorkerResponse { + resources.workers.v1.Worker item = 1; +} + +message DeleteWorkerRequest { + string id = 1; // @gotags: `class:"public"` +} + +message DeleteWorkerResponse {} diff --git a/sdk/pbs/controller/api/resources/workers/worker.pb.go b/sdk/pbs/controller/api/resources/workers/worker.pb.go new file mode 100644 index 000000000..a35883402 --- /dev/null +++ b/sdk/pbs/controller/api/resources/workers/worker.pb.go @@ -0,0 +1,482 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/api/resources/workers/v1/worker.proto + +package workers + +import ( + scopes "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" + _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// WorkerConfig contains configuration values reported by the worker daemon. +type WorkerConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Output only. The address the worker daemon reports itself as reachable at. + Address string `protobuf:"bytes,10,opt,name=address,proto3" json:"address,omitempty"` + // Output only. The name the worker daemon reports itself having. + Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty"` + // Output only. The boundary version the worker has reported itself as running. + Version string `protobuf:"bytes,30,opt,name=version,proto3" json:"version,omitempty"` + // Output only. The tags reporteed by the worker daemon. + Tags map[string]*structpb.ListValue `protobuf:"bytes,40,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *WorkerConfig) Reset() { + *x = WorkerConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WorkerConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkerConfig) ProtoMessage() {} + +func (x *WorkerConfig) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkerConfig.ProtoReflect.Descriptor instead. +func (*WorkerConfig) Descriptor() ([]byte, []int) { + return file_controller_api_resources_workers_v1_worker_proto_rawDescGZIP(), []int{0} +} + +func (x *WorkerConfig) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *WorkerConfig) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *WorkerConfig) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *WorkerConfig) GetTags() map[string]*structpb.ListValue { + if x != nil { + return x.Tags + } + return nil +} + +// Worker contains all fields related to a Worker resource +type Worker struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Output only. The ID of the User. + Id string `protobuf:"bytes,10,opt,name=id,proto3" json:"id,omitempty"` // @gotags: `class:"public"` + // The ID of the Scope this resource is in. + ScopeId string `protobuf:"bytes,20,opt,name=scope_id,proto3" json:"scope_id,omitempty"` // @gotags: `class:"public"` + // Output only. Scope information for this resource. + Scope *scopes.ScopeInfo `protobuf:"bytes,30,opt,name=scope,proto3" json:"scope,omitempty"` + // Optional name for identification purposes. + Name *wrapperspb.StringValue `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty"` // @gotags: `class:"sensitive"` + // Optional user-set description for identification purposes. + Description *wrapperspb.StringValue `protobuf:"bytes,50,opt,name=description,proto3" json:"description,omitempty"` // @gotags: `class:"sensitive"` + // Output only. The time this resource was created. + CreatedTime *timestamppb.Timestamp `protobuf:"bytes,60,opt,name=created_time,proto3" json:"created_time,omitempty"` // @gotags: `class:"public"` + // Output only. The time this resource was last updated. + UpdatedTime *timestamppb.Timestamp `protobuf:"bytes,70,opt,name=updated_time,proto3" json:"updated_time,omitempty"` // @gotags: `class:"public"` + // Version is used in mutation requests, after the initial creation, to ensure this resource has not changed. + // The mutation will fail if the version does not match the latest known good version. + Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty"` // @gotags: `class:"public"` + // The address that this worker is reachable at. + Address *wrapperspb.StringValue `protobuf:"bytes,90,opt,name=address,proto3" json:"address,omitempty"` // @gotags: `class:"public"` + // Output only. This is the address used by authorized session. The value + // is the same as the address field if set. Otherwise it uses the address the + // worker reports. + CanonicalAddress string `protobuf:"bytes,100,opt,name=canonical_address,json=canonicalAddress,proto3" json:"canonical_address,omitempty"` // @gotags: `class:"public"` + // Output only. The tags attached to this worker. + Tags map[string]*structpb.ListValue `protobuf:"bytes,110,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // @gotags: `class:"public"` + // Output only. The deduplicated union of the tags reported by the worker and + // the tags added through the user facing api. This is used when applying + // worker filters. + CanonicalTags map[string]*structpb.ListValue `protobuf:"bytes,120,rep,name=canonical_tags,json=canonicalTags,proto3" json:"canonical_tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // @gotags: `class:"public"` + // Output only. The time this worker daemon last reported its status. + LastStatusTime *timestamppb.Timestamp `protobuf:"bytes,130,opt,name=last_status_time,proto3" json:"last_status_time,omitempty"` // @gotags: `class:"public"` + // Output only. The set of information the worker daemon has reported about + // itself. + WorkerConfig *WorkerConfig `protobuf:"bytes,140,opt,name=worker_config,proto3" json:"worker_config,omitempty"` +} + +func (x *Worker) Reset() { + *x = Worker{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Worker) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Worker) ProtoMessage() {} + +func (x *Worker) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Worker.ProtoReflect.Descriptor instead. +func (*Worker) Descriptor() ([]byte, []int) { + return file_controller_api_resources_workers_v1_worker_proto_rawDescGZIP(), []int{1} +} + +func (x *Worker) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Worker) GetScopeId() string { + if x != nil { + return x.ScopeId + } + return "" +} + +func (x *Worker) GetScope() *scopes.ScopeInfo { + if x != nil { + return x.Scope + } + return nil +} + +func (x *Worker) GetName() *wrapperspb.StringValue { + if x != nil { + return x.Name + } + return nil +} + +func (x *Worker) GetDescription() *wrapperspb.StringValue { + if x != nil { + return x.Description + } + return nil +} + +func (x *Worker) GetCreatedTime() *timestamppb.Timestamp { + if x != nil { + return x.CreatedTime + } + return nil +} + +func (x *Worker) GetUpdatedTime() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedTime + } + return nil +} + +func (x *Worker) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *Worker) GetAddress() *wrapperspb.StringValue { + if x != nil { + return x.Address + } + return nil +} + +func (x *Worker) GetCanonicalAddress() string { + if x != nil { + return x.CanonicalAddress + } + return "" +} + +func (x *Worker) GetTags() map[string]*structpb.ListValue { + if x != nil { + return x.Tags + } + return nil +} + +func (x *Worker) GetCanonicalTags() map[string]*structpb.ListValue { + if x != nil { + return x.CanonicalTags + } + return nil +} + +func (x *Worker) GetLastStatusTime() *timestamppb.Timestamp { + if x != nil { + return x.LastStatusTime + } + return nil +} + +func (x *Worker) GetWorkerConfig() *WorkerConfig { + if x != nil { + return x.WorkerConfig + } + return nil +} + +var File_controller_api_resources_workers_v1_worker_proto protoreflect.FileDescriptor + +var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ + 0x0a, 0x30, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xfc, 0x01, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x04, 0x74, + 0x61, 0x67, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x61, 0x67, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x53, 0x0a, 0x09, + 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0xac, 0x08, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, + 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x46, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, + 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, + 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x5a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, + 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, + 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, + 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, + 0x61, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, + 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, + 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x61, 0x6e, + 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x10, 0x6c, 0x61, + 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x82, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, + 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x53, 0x0a, + 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, + 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, + 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_api_resources_workers_v1_worker_proto_rawDescOnce sync.Once + file_controller_api_resources_workers_v1_worker_proto_rawDescData = file_controller_api_resources_workers_v1_worker_proto_rawDesc +) + +func file_controller_api_resources_workers_v1_worker_proto_rawDescGZIP() []byte { + file_controller_api_resources_workers_v1_worker_proto_rawDescOnce.Do(func() { + file_controller_api_resources_workers_v1_worker_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_resources_workers_v1_worker_proto_rawDescData) + }) + return file_controller_api_resources_workers_v1_worker_proto_rawDescData +} + +var file_controller_api_resources_workers_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_controller_api_resources_workers_v1_worker_proto_goTypes = []interface{}{ + (*WorkerConfig)(nil), // 0: controller.api.resources.workers.v1.WorkerConfig + (*Worker)(nil), // 1: controller.api.resources.workers.v1.Worker + nil, // 2: controller.api.resources.workers.v1.WorkerConfig.TagsEntry + nil, // 3: controller.api.resources.workers.v1.Worker.TagsEntry + nil, // 4: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry + (*scopes.ScopeInfo)(nil), // 5: controller.api.resources.scopes.v1.ScopeInfo + (*wrapperspb.StringValue)(nil), // 6: google.protobuf.StringValue + (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp + (*structpb.ListValue)(nil), // 8: google.protobuf.ListValue +} +var file_controller_api_resources_workers_v1_worker_proto_depIdxs = []int32{ + 2, // 0: controller.api.resources.workers.v1.WorkerConfig.tags:type_name -> controller.api.resources.workers.v1.WorkerConfig.TagsEntry + 5, // 1: controller.api.resources.workers.v1.Worker.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 6, // 2: controller.api.resources.workers.v1.Worker.name:type_name -> google.protobuf.StringValue + 6, // 3: controller.api.resources.workers.v1.Worker.description:type_name -> google.protobuf.StringValue + 7, // 4: controller.api.resources.workers.v1.Worker.created_time:type_name -> google.protobuf.Timestamp + 7, // 5: controller.api.resources.workers.v1.Worker.updated_time:type_name -> google.protobuf.Timestamp + 6, // 6: controller.api.resources.workers.v1.Worker.address:type_name -> google.protobuf.StringValue + 3, // 7: controller.api.resources.workers.v1.Worker.tags:type_name -> controller.api.resources.workers.v1.Worker.TagsEntry + 4, // 8: controller.api.resources.workers.v1.Worker.canonical_tags:type_name -> controller.api.resources.workers.v1.Worker.CanonicalTagsEntry + 7, // 9: controller.api.resources.workers.v1.Worker.last_status_time:type_name -> google.protobuf.Timestamp + 0, // 10: controller.api.resources.workers.v1.Worker.worker_config:type_name -> controller.api.resources.workers.v1.WorkerConfig + 8, // 11: controller.api.resources.workers.v1.WorkerConfig.TagsEntry.value:type_name -> google.protobuf.ListValue + 8, // 12: controller.api.resources.workers.v1.Worker.TagsEntry.value:type_name -> google.protobuf.ListValue + 8, // 13: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry.value:type_name -> google.protobuf.ListValue + 14, // [14:14] is the sub-list for method output_type + 14, // [14:14] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name +} + +func init() { file_controller_api_resources_workers_v1_worker_proto_init() } +func file_controller_api_resources_workers_v1_worker_proto_init() { + if File_controller_api_resources_workers_v1_worker_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_api_resources_workers_v1_worker_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_resources_workers_v1_worker_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Worker); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_api_resources_workers_v1_worker_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_api_resources_workers_v1_worker_proto_goTypes, + DependencyIndexes: file_controller_api_resources_workers_v1_worker_proto_depIdxs, + MessageInfos: file_controller_api_resources_workers_v1_worker_proto_msgTypes, + }.Build() + File_controller_api_resources_workers_v1_worker_proto = out.File + file_controller_api_resources_workers_v1_worker_proto_rawDesc = nil + file_controller_api_resources_workers_v1_worker_proto_goTypes = nil + file_controller_api_resources_workers_v1_worker_proto_depIdxs = nil +} From eece174e5eb9c4217698320e7ad7530a98dbd57a Mon Sep 17 00:00:00 2001 From: Michael Gaffney Date: Tue, 31 May 2022 13:57:50 -0400 Subject: [PATCH 027/100] test(worker): Fix TestDeleteWorker --- internal/servers/repository_worker_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index bffd4ba9c..0d55c72e3 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -70,7 +70,7 @@ func TestDeleteWorker(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantErrMsg: "servers.(Repository).DeleteWorker: Delete failed for worker with workerId:", + wantErrMsg: "servers.(Repository).DeleteWorker: delete failed for worker with workerId:", }, } for _, tt := range tests { From 7bfe35da0dc5e9230567d8dd3c690f1de568c5c7 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 1 Jun 2022 01:09:56 -0400 Subject: [PATCH 028/100] Update controller address handling (#2089) * Don't assume it's non-empty or that this is an issue * Ensure it's empty if the response is coming back through the multihop service so that it simply uses what is in the config file, at least for now --- internal/daemon/worker/status.go | 16 ++++++---------- internal/daemon/worker/worker_proxy_service.go | 10 +++++++++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index 1a29642b3..4ddad3862 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -189,16 +189,12 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { } } else { w.updateTags.Store(false) - addrs := make([]resolver.Address, 0, len(result.Controllers)) - strAddrs := make([]string, 0, len(result.Controllers)) - for _, v := range result.Controllers { - addrs = append(addrs, resolver.Address{Addr: v.Address}) - strAddrs = append(strAddrs, v.Address) - } - switch len(strAddrs) { - case 0: - event.WriteError(statusCtx, op, errors.New("got no controller addresses from controller; possibly prior to first status save, not persisting")) - default: + // This may be nil if we are in a multiple hop scenario + if len(result.Controllers) > 0 { + addrs := make([]resolver.Address, 0, len(result.Controllers)) + for _, v := range result.Controllers { + addrs = append(addrs, resolver.Address{Addr: v.Address}) + } w.Resolver().UpdateState(resolver.State{Addresses: addrs}) } w.lastStatusSuccess.Store(&LastStatusInformation{StatusResponse: result, StatusTime: time.Now()}) diff --git a/internal/daemon/worker/worker_proxy_service.go b/internal/daemon/worker/worker_proxy_service.go index 54e657ed2..e70b4451a 100644 --- a/internal/daemon/worker/worker_proxy_service.go +++ b/internal/daemon/worker/worker_proxy_service.go @@ -31,7 +31,15 @@ var ( ) func (ws *workerProxyServiceServer) Status(ctx context.Context, req *pbs.StatusRequest) (*pbs.StatusResponse, error) { - return ws.scsClient.Load().(pbs.ServerCoordinationServiceClient).Status(ctx, req) + resp, err := ws.scsClient.Load().(pbs.ServerCoordinationServiceClient).Status(ctx, req) + + if resp != nil { + // We don't currently support distributing new addreses to workers + // multiple hops away so ensure they're stripped out + resp.Controllers = nil + } + + return resp, err } func (ws *workerProxyServiceServer) LookupSession(ctx context.Context, req *pbs.LookupSessionRequest) (*pbs.LookupSessionResponse, error) { From bf3b0340e5b28e54593b8e219c50ab5eecbfd145 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Fri, 27 May 2022 13:17:29 -0400 Subject: [PATCH 029/100] feat(workerauth): add storage for RootCertificates aggregate --- .../99/03_worker_authentication.up.sql | 58 ++++++- .../servers/store/v1/root_certificate.proto | 14 ++ internal/servers/root_certificate.go | 37 ++++- internal/servers/store/root_certificate.pb.go | 155 ++++++++++++++---- internal/servers/workerauth_store_test.go | 9 +- 5 files changed, 237 insertions(+), 36 deletions(-) diff --git a/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql b/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql index d64967532..b2aca4b53 100644 --- a/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql @@ -16,6 +16,39 @@ values ('next'), ('current'); +create table worker_auth_ca( + private_id text primary key + constraint only_roots_id_allowed + check (private_id in ('roots')), + version wt_version, + create_time wt_timestamp, + update_time wt_timestamp +); + +create trigger + immutable_columns + before + update on worker_auth_ca + for each row execute procedure immutable_columns('private_id','create_time'); + +create trigger worker_auth_ca_default_create_time_column + before + insert on worker_auth_ca + for each row execute procedure default_create_time(); + +create trigger worker_auth_ca_update_time_column + before + update on worker_auth_ca + for each row execute procedure update_time_column(); + +insert into worker_auth_ca(private_id) values('roots'); + +create trigger update_version_column after update on worker_auth_ca + for each row execute procedure update_version_column(); + +comment on table worker_auth_ca is + 'worker_auth_ca is a one-row versioned table used for locking for certificate rotation on the worker_auth_ca_certificate table.'; + create table worker_auth_ca_certificate( serial_number numeric, certificate bytea not null @@ -40,9 +73,19 @@ create table worker_auth_ca_certificate( constraint worker_auth_operational_indicator_enm_fkey references worker_auth_operational_indicator_enm(state) on delete restrict - on update cascade + on update cascade, + issuing_ca text not null + constraint worker_auth_ca_fkey + references worker_auth_ca(private_id) ); +create trigger + immutable_columns + before + update on worker_auth_ca_certificate + for each row execute procedure immutable_columns('serial_number', 'certificate', 'not_valid_before', 'not_valid_after', + 'public_key', 'private_key', 'key_id', 'state', 'issuing_ca'); + comment on table worker_auth_ca_certificate is 'worker_auth_ca_certificate is a table where each row represents a root certificate for used for worker authentication.'; @@ -70,6 +113,13 @@ create table worker_auth_authorized( nonce bytea ); +create trigger + immutable_columns + before + update on worker_auth_authorized + for each row execute procedure immutable_columns('worker_key_identifier', 'worker_id', 'worker_signing_pub_key', + 'worker_encryption_pub_key', 'controller_encryption_priv_key', 'key_id', 'nonce'); + comment on table worker_auth_authorized is 'worker_auth_authorized is a table where each row represents key and cert data associated with an authorized worker.'; @@ -93,4 +143,10 @@ create table worker_auth_certificate_bundle( comment on table worker_auth_certificate_bundle is 'worker_auth_certificate_bundle is a table where each row represents a cert bundle issued by a ca certificate for a worker.'; +create trigger + immutable_columns + before + update on worker_auth_certificate_bundle + for each row execute procedure immutable_columns('certificate_public_key', 'worker_key_identifier', 'cert_bundle'); + commit; \ No newline at end of file diff --git a/internal/proto/controller/storage/servers/store/v1/root_certificate.proto b/internal/proto/controller/storage/servers/store/v1/root_certificate.proto index 16a4d2907..1094e0bde 100644 --- a/internal/proto/controller/storage/servers/store/v1/root_certificate.proto +++ b/internal/proto/controller/storage/servers/store/v1/root_certificate.proto @@ -7,6 +7,16 @@ import "controller/storage/timestamp/v1/timestamp.proto"; option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; +// CertificateAuthority is a versioned entity used to lock the database when rotation RootCertificates +message CertificateAuthority { + // @inject_tag: `gorm:"primary_key"` + string private_id = 10; + + // version allows optimistic locking of the resource. + // @inject_tag: `gorm:"default:null"` + uint32 version = 20; +} + // RootCertificate contains all fields related to a RootCertificate resource message RootCertificate { // The serial number of the root certificate @@ -39,4 +49,8 @@ message RootCertificate { // State is an enum value indicating if this is the next or current root cert // @inject_tag: `gorm:"not_null"` string state = 80; + + // A reference to the CertificateAuthority + // @inject_tag: `gorm:"not_null"` + string issuing_ca = 90; } diff --git a/internal/servers/root_certificate.go b/internal/servers/root_certificate.go index 4e935fa95..f944c7e68 100644 --- a/internal/servers/root_certificate.go +++ b/internal/servers/root_certificate.go @@ -11,6 +11,38 @@ import ( "google.golang.org/protobuf/proto" ) +// The CertificateAuthority id will always be set to "roots". +// The const ca_id contains this value +const ca_id = "roots" + +// CertificateAuthority is a versioned entity used to lock the database when rotation RootCertificates +type CertificateAuthority struct { + *store.CertificateAuthority + tableName string `gorm:"-"` +} + +func newCertificateAuthority() *CertificateAuthority { + ca := &CertificateAuthority{ + CertificateAuthority: &store.CertificateAuthority{ + PrivateId: ca_id, + }, + } + return ca +} + +// TableName returns the table name. +func (r *CertificateAuthority) TableName() string { + if r.tableName != "" { + return r.tableName + } + return "worker_auth_ca" +} + +// SetTableName sets the table name. +func (r *CertificateAuthority) SetTableName(n string) { + r.tableName = n +} + // RootCertificate contains fields related to a RootCertificate resource // This includes public/ private keys, the PEM encoded certificate, and the certificate validity period type RootCertificate struct { @@ -18,6 +50,7 @@ type RootCertificate struct { tableName string `gorm:"-"` } +// RootCertificateKeys contains the public and private keys for use in constructing a RootCertificate type RootCertificateKeys struct { publicKey []byte privateKey []byte @@ -63,12 +96,13 @@ func newRootCertificate(ctx context.Context, serialNumber uint64, certificate [] PrivateKey: rootCertificateKeys.privateKey, KeyId: keyId, State: string(state), + IssuingCa: ca_id, }, } return l, nil } -func AllocRootCertificate() *RootCertificate { +func allocRootCertificate() *RootCertificate { return &RootCertificate{ RootCertificate: &store.RootCertificate{}, } @@ -81,6 +115,7 @@ func (r *RootCertificate) clone() *RootCertificate { } } +// Validate the RootCertificate. On success, return nil func (r *RootCertificate) ValidateNewRootCertificate(ctx context.Context) error { const op = "servers.(RootCertificate).ValidateNewRootCertificate" if &r.SerialNumber == nil { diff --git a/internal/servers/store/root_certificate.pb.go b/internal/servers/store/root_certificate.pb.go index 7e8feee6a..f3a32e013 100644 --- a/internal/servers/store/root_certificate.pb.go +++ b/internal/servers/store/root_certificate.pb.go @@ -23,6 +23,65 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// CertificateAuthority is a versioned entity used to lock the database when rotation RootCertificates +type CertificateAuthority struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // @inject_tag: `gorm:"primary_key"` + PrivateId string `protobuf:"bytes,10,opt,name=private_id,json=privateId,proto3" json:"private_id,omitempty" gorm:"primary_key"` + // version allows optimistic locking of the resource. + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,20,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` +} + +func (x *CertificateAuthority) Reset() { + *x = CertificateAuthority{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CertificateAuthority) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CertificateAuthority) ProtoMessage() {} + +func (x *CertificateAuthority) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CertificateAuthority.ProtoReflect.Descriptor instead. +func (*CertificateAuthority) Descriptor() ([]byte, []int) { + return file_controller_storage_servers_store_v1_root_certificate_proto_rawDescGZIP(), []int{0} +} + +func (x *CertificateAuthority) GetPrivateId() string { + if x != nil { + return x.PrivateId + } + return "" +} + +func (x *CertificateAuthority) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + // RootCertificate contains all fields related to a RootCertificate resource type RootCertificate struct { state protoimpl.MessageState @@ -52,12 +111,15 @@ type RootCertificate struct { // State is an enum value indicating if this is the next or current root cert // @inject_tag: `gorm:"not_null"` State string `protobuf:"bytes,80,opt,name=state,proto3" json:"state,omitempty" gorm:"not_null"` + // A reference to the CertificateAuthority + // @inject_tag: `gorm:"not_null"` + IssuingCa string `protobuf:"bytes,90,opt,name=issuing_ca,json=issuingCa,proto3" json:"issuing_ca,omitempty" gorm:"not_null"` } func (x *RootCertificate) Reset() { *x = RootCertificate{} if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0] + mi := &file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -70,7 +132,7 @@ func (x *RootCertificate) String() string { func (*RootCertificate) ProtoMessage() {} func (x *RootCertificate) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0] + mi := &file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -83,7 +145,7 @@ func (x *RootCertificate) ProtoReflect() protoreflect.Message { // Deprecated: Use RootCertificate.ProtoReflect.Descriptor instead. func (*RootCertificate) Descriptor() ([]byte, []int) { - return file_controller_storage_servers_store_v1_root_certificate_proto_rawDescGZIP(), []int{0} + return file_controller_storage_servers_store_v1_root_certificate_proto_rawDescGZIP(), []int{1} } func (x *RootCertificate) GetSerialNumber() uint64 { @@ -142,6 +204,13 @@ func (x *RootCertificate) GetState() string { return "" } +func (x *RootCertificate) GetIssuingCa() string { + if x != nil { + return x.IssuingCa + } + return "" +} + var File_controller_storage_servers_store_v1_root_certificate_proto protoreflect.FileDescriptor var file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc = []byte{ @@ -154,30 +223,37 @@ var file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc = [] 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xef, 0x02, 0x0a, 0x0f, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, - 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x73, - 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x63, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x54, 0x0a, - 0x10, 0x6e, 0x6f, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, - 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6e, 0x6f, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x42, 0x65, 0x66, - 0x6f, 0x72, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, - 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x14, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x74, 0x6f, 0x22, 0x4f, 0x0a, 0x14, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x22, 0x8e, 0x03, 0x0a, 0x0f, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, 0x61, + 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, + 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x54, + 0x0a, 0x10, 0x6e, 0x6f, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x62, 0x65, 0x66, 0x6f, + 0x72, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6e, 0x6f, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x42, 0x65, + 0x66, 0x6f, 0x72, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, + 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, + 0x5f, 0x63, 0x61, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x73, 0x73, 0x75, 0x69, + 0x6e, 0x67, 0x43, 0x61, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, @@ -196,14 +272,15 @@ func file_controller_storage_servers_store_v1_root_certificate_proto_rawDescGZIP return file_controller_storage_servers_store_v1_root_certificate_proto_rawDescData } -var file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_controller_storage_servers_store_v1_root_certificate_proto_goTypes = []interface{}{ - (*RootCertificate)(nil), // 0: controller.storage.servers.store.v1.RootCertificate - (*timestamp.Timestamp)(nil), // 1: controller.storage.timestamp.v1.Timestamp + (*CertificateAuthority)(nil), // 0: controller.storage.servers.store.v1.CertificateAuthority + (*RootCertificate)(nil), // 1: controller.storage.servers.store.v1.RootCertificate + (*timestamp.Timestamp)(nil), // 2: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_servers_store_v1_root_certificate_proto_depIdxs = []int32{ - 1, // 0: controller.storage.servers.store.v1.RootCertificate.not_valid_before:type_name -> controller.storage.timestamp.v1.Timestamp - 1, // 1: controller.storage.servers.store.v1.RootCertificate.not_valid_after:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // 0: controller.storage.servers.store.v1.RootCertificate.not_valid_before:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // 1: controller.storage.servers.store.v1.RootCertificate.not_valid_after:type_name -> controller.storage.timestamp.v1.Timestamp 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name @@ -218,6 +295,18 @@ func file_controller_storage_servers_store_v1_root_certificate_proto_init() { } if !protoimpl.UnsafeEnabled { file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CertificateAuthority); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_servers_store_v1_root_certificate_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RootCertificate); i { case 0: return &v.state @@ -236,7 +325,7 @@ func file_controller_storage_servers_store_v1_root_certificate_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_servers_store_v1_root_certificate_proto_rawDesc, NumEnums: 0, - NumMessages: 1, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/servers/workerauth_store_test.go b/internal/servers/workerauth_store_test.go index d362ce78e..488f5fe6a 100644 --- a/internal/servers/workerauth_store_test.go +++ b/internal/servers/workerauth_store_test.go @@ -75,6 +75,7 @@ func TestRootCertStore(t *testing.T) { NotValidAfter: afterTimestamp, KeyId: testKey, State: "next", + IssuingCa: ca_id, }, wantErr: false, }, @@ -98,6 +99,7 @@ func TestRootCertStore(t *testing.T) { NotValidAfter: afterTimestamp, KeyId: testKey, State: "current", + IssuingCa: ca_id, }, wantErr: false, }, @@ -240,7 +242,12 @@ func TestDuplicateRootCert(t *testing.T) { beforeTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(-1 * time.Hour))} afterTimestamp := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(1 * time.Hour))} - // Make two current and next root CAs + // Attempt to create a duplicate CA and expect a failure + certAuthority := newCertificateAuthority() + err = rw.Create(ctx, certAuthority) + require.Error(t, err) + + // Make two current and next root certs current1, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, testKey, CurrentState) From 46e97a2b91f1d94cfbf95c0e2a116529505e0fd8 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 1 Jun 2022 13:35:46 -0400 Subject: [PATCH 030/100] Update to new nodee lib bits (#2120) --- go.mod | 3 +- go.sum | 6 +- internal/cmd/commands/dev/dev.go | 20 +++-- internal/cmd/commands/server/server.go | 11 ++- .../cluster/handlers/multihop_service.go | 76 ++++++++++++++----- internal/daemon/controller/controller.go | 18 ++++- internal/daemon/controller/handler.go | 34 +-------- .../controller/intercepting_listener.go | 3 +- internal/daemon/controller/listeners.go | 34 +++++---- internal/daemon/controller/listeners_test.go | 4 + .../daemon/worker/controller_connection.go | 9 +-- internal/daemon/worker/listeners.go | 68 ++++++++++------- internal/daemon/worker/worker.go | 28 +++++-- 13 files changed, 179 insertions(+), 135 deletions(-) diff --git a/go.mod b/go.mod index 49f7063a5..c1d4fd75b 100644 --- a/go.mod +++ b/go.mod @@ -92,7 +92,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b - github.com/hashicorp/nodeenrollment v0.0.0-20220520141833-ad7323153acc + github.com/hashicorp/nodeenrollment v0.0.0-20220601055910-5718dde51fae ) require ( @@ -166,7 +166,6 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect github.com/opencontainers/runc v1.0.2 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect diff --git a/go.sum b/go.sum index 7678a413e..a1d9084db 100644 --- a/go.sum +++ b/go.sum @@ -740,8 +740,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220520141833-ad7323153acc h1:7RE6UxjVxa3uUS0A4RZwVRFnBJqNQnm5nzNwqPJy4VM= -github.com/hashicorp/nodeenrollment v0.0.0-20220520141833-ad7323153acc/go.mod h1:3054+zYuaQzgXjoy8kyd8whmx1dXUSomyMTRnWHzVXs= +github.com/hashicorp/nodeenrollment v0.0.0-20220601055910-5718dde51fae h1:A8JdiMkZuic8905EgiTDw9cKOb6v1BSrfDMNlS9oejo= +github.com/hashicorp/nodeenrollment v0.0.0-20220601055910-5718dde51fae/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= @@ -1088,8 +1088,6 @@ github.com/ory/dockertest/v3 v3.7.0 h1:Bijzonc69Ont3OU0a3TWKJ1Rzlh3TsDXP1JrTAkSm github.com/ory/dockertest/v3 v3.7.0/go.mod h1:PvCCgnP7AfBZeVrzwiUTjZx/IUXlGLC1zQlUQrLIlUE= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 397dcb531..0b70e3246 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -29,7 +29,6 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/strutil" - "github.com/hashicorp/nodeenrollment" "github.com/mitchellh/cli" "github.com/posener/complete" "go.uber.org/atomic" @@ -602,7 +601,7 @@ func (c *Command) Run(args []string) int { opts = append(opts, base.WithContainerImage(c.flagContainerImage)) } if err := c.CreateDevDatabase(c.Context, opts...); err != nil { - c.UI.Error(fmt.Errorf("Error creating dev database container %w", err).Error()) + c.UI.Error(fmt.Errorf("Error creating dev database container: %w", err).Error()) return base.CommandCliError } @@ -677,23 +676,22 @@ func (c *Command) Run(args []string) int { } if !c.flagUseEphemeralKmsWorkerAuthMethod { - if c.worker.NodeeKeyId == "" { - c.UI.Error("No worker key ID found at worker registration time") + req := c.worker.NodeeRegistrationRequest + if req == "" { + c.UI.Error("No worker registration request found at worker start time") return base.CommandCliError } - c.InfoKeys = append(c.InfoKeys, "worker key identifier") - c.Info["worker key identifier"] = c.worker.NodeeKeyId + c.InfoKeys = append(c.InfoKeys, "worker registration request") + c.Info["worker registration request"] = req + c.InfoKeys = append(c.InfoKeys, "worker current key id") + c.Info["worker current key id"] = c.worker.NodeeCurrentKeyId go func() { for { select { case <-c.Context.Done(): return case <-time.After(time.Second): - waitingNodes := nodeenrollment.DefaultRegistrationCache.Items() - if len(waitingNodes) == 0 { - continue - } - if err := c.controller.AuthorizeNodeeWorker(c.worker.NodeeKeyId); err != nil { + if err := c.controller.AuthorizeNodeeWorker(req); err != nil { c.UI.Error(fmt.Errorf("Error authorizing node: %w", err).Error()) errorEncountered.Store(true) return diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index ca2f18281..ca17036f4 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -464,8 +464,9 @@ func (c *Command) Run(args []string) int { return base.CommandCliError } if c.WorkerAuthKms == nil { - if c.worker.NodeeKeyId == "" { - retErr := fmt.Errorf("No worker key ID found at worker registration time") + req := c.worker.NodeeRegistrationRequest + if req == "" { + retErr := fmt.Errorf("No worker registration request found at worker startup time") if err := c.worker.Shutdown(); err != nil { c.UI.Error(retErr.Error()) retErr = fmt.Errorf("Error shutting down worker: %w", err) @@ -476,8 +477,10 @@ func (c *Command) Run(args []string) int { } return base.CommandCliError } - c.InfoKeys = append(c.InfoKeys, "worker key identifier") - c.Info["worker key identifier"] = c.worker.NodeeKeyId + c.InfoKeys = append(c.InfoKeys, "worker registration request") + c.Info["worker registration request"] = req + c.InfoKeys = append(c.InfoKeys, "worker current key id") + c.Info["worker current key id"] = c.worker.NodeeCurrentKeyId } } diff --git a/internal/daemon/cluster/handlers/multihop_service.go b/internal/daemon/cluster/handlers/multihop_service.go index ca1bde762..2fe742f18 100644 --- a/internal/daemon/cluster/handlers/multihop_service.go +++ b/internal/daemon/cluster/handlers/multihop_service.go @@ -2,45 +2,81 @@ package handlers import ( "context" + "errors" "fmt" + "sync/atomic" - pbs "github.com/hashicorp/nodeenrollment/multihop" - "github.com/hashicorp/nodeenrollment/nodeauth" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/multihop" "github.com/hashicorp/nodeenrollment/registration" "github.com/hashicorp/nodeenrollment/tls" "github.com/hashicorp/nodeenrollment/types" + "github.com/hashicorp/nodeenrollment/util/temperror" ) type multihopServiceServer struct { - pbs.UnimplementedMultihopServiceServer + multihop.UnimplementedMultihopServiceServer - currentParams nodeauth.CurrentParameterFactory + storage nodeenrollment.Storage + direct bool + client *atomic.Value + options []nodeenrollment.Option } -func NewMultihopServiceServer( - currentParams nodeauth.CurrentParameterFactory, -) *multihopServiceServer { - return &multihopServiceServer{ - currentParams: currentParams, +func NewMultihopServiceServer(storage nodeenrollment.Storage, direct bool, client *atomic.Value, opt ...nodeenrollment.Option) (*multihopServiceServer, error) { + const op = "cluster.handlers.NewMultihopServiceServer" + + switch { + case direct && nodeenrollment.IsNil(storage): + return nil, fmt.Errorf("%s: running on controller and nil storage provided", op) + case !direct && client == nil: + return nil, fmt.Errorf("%s: running on worker and nil client provided", op) } + + return &multihopServiceServer{ + storage: storage, + direct: direct, + client: client, + options: opt, + }, nil } -var _ pbs.MultihopServiceServer = (*multihopServiceServer)(nil) +var _ multihop.MultihopServiceServer = (*multihopServiceServer)(nil) func (m *multihopServiceServer) FetchNodeCredentials(ctx context.Context, req *types.FetchNodeCredentialsRequest) (*types.FetchNodeCredentialsResponse, error) { - const op = "workers.(multihopServiceServer).FetchNodeCredentials" - _, storage, opt, err := m.currentParams() - if err != nil { - return nil, fmt.Errorf("%s: error getting current parameters: %w", op, err) + const op = "cluster.handlers.(multihopServiceServer).FetchNodeCredentials" + switch m.direct { + case true: + return registration.FetchNodeCredentials(ctx, m.storage, req, m.options...) + + default: + client := m.client.Load() + if client == nil { + return nil, fmt.Errorf("%s: error fetching multihop connection, client is nil", op) + } + multihopClient, ok := client.(multihop.MultihopServiceClient) + if !ok { + return nil, temperror.New(errors.New("client could not be understood as a multihop service client")) + } + return multihopClient.FetchNodeCredentials(ctx, req) } - return registration.FetchNodeCredentials(ctx, storage, req, opt...) } func (m *multihopServiceServer) GenerateServerCertificates(ctx context.Context, req *types.GenerateServerCertificatesRequest) (*types.GenerateServerCertificatesResponse, error) { - const op = "workers.(multihopServiceServer).GenerateServerCertificates" - _, storage, opt, err := m.currentParams() - if err != nil { - return nil, fmt.Errorf("%s: error getting current parameters: %w", op, err) + const op = "cluster.handlers.(multihopServiceServer).GenerateServerCertificates" + switch m.direct { + case true: + return tls.GenerateServerCertificates(ctx, m.storage, req, m.options...) + + default: + client := m.client.Load() + if client == nil { + return nil, fmt.Errorf("%s: error fetching multihop connection, client is nil", op) + } + multihopClient, ok := client.(multihop.MultihopServiceClient) + if !ok { + return nil, temperror.New(errors.New("client could not be understood as a multihop service client")) + } + return multihopClient.GenerateServerCertificates(ctx, req) } - return tls.GenerateServerCertificates(ctx, storage, req, opt...) } diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index a1da547a9..ae2955aed 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -39,8 +39,11 @@ import ( "github.com/hashicorp/nodeenrollment/registration" "github.com/hashicorp/nodeenrollment/rotation" nodeefile "github.com/hashicorp/nodeenrollment/storage/file" + "github.com/hashicorp/nodeenrollment/types" + "github.com/mr-tron/base58" ua "go.uber.org/atomic" "google.golang.org/grpc" + "google.golang.org/protobuf/proto" ) type Controller struct { @@ -311,6 +314,7 @@ func (c *Controller) Start() error { event.WriteSysEvent(context.TODO(), op, "already started, skipping") return nil } + c.baseContext, c.baseCancel = context.WithCancel(context.Background()) var err error @@ -406,6 +410,16 @@ func (c *Controller) WorkerStatusUpdateTimes() *sync.Map { return c.workerStatusUpdateTimes } -func (c *Controller) AuthorizeNodeeWorker(keyId string) error { - return registration.AuthorizeNode(c.baseContext, c.NodeeFileStorage, keyId) +func (c *Controller) AuthorizeNodeeWorker(request string) error { + const op = "controller.(Controller).AuthorizeWorker" + reqBytes, err := base58.FastBase58Decoding(request) + if err != nil { + return fmt.Errorf("(%s) error base64-decoding fetch node creds next proto value", op) + } + // Decode the proto into the request + req := new(types.FetchNodeCredentialsRequest) + if err := proto.Unmarshal(reqBytes, req); err != nil { + return fmt.Errorf("(%s) error unmarshaling common name value: %w", op, err) + } + return registration.AuthorizeNode(c.baseContext, c.NodeeFileStorage, req) } diff --git a/internal/daemon/controller/handler.go b/internal/daemon/controller/handler.go index abdcfbbc4..d8ed85e48 100644 --- a/internal/daemon/controller/handler.go +++ b/internal/daemon/controller/handler.go @@ -43,8 +43,6 @@ import ( "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-secure-stdlib/listenerutil" "github.com/hashicorp/go-secure-stdlib/strutil" - "github.com/hashicorp/nodeenrollment" - "github.com/hashicorp/nodeenrollment/types" "github.com/mr-tron/base58" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -568,34 +566,6 @@ func wrapHandlerWithCallbackInterceptor(h http.Handler, c *Controller) http.Hand func handleNodes(c *Controller) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { switch req.Method { - case http.MethodGet: - type vals struct { - WaitingNodes []string `json:"waiting_nodes"` - } - var err error - var currVals vals - waitingNodes := nodeenrollment.DefaultRegistrationCache.Items() - for keyId, val := range waitingNodes { - ni := val.(*types.NodeInformation) - if ni.Authorized { - continue - } - currVals.WaitingNodes = append(currVals.WaitingNodes, keyId) - } - ret, err := json.Marshal(currVals) - if err != nil { - _, _ = w.Write([]byte(err.Error())) - w.WriteHeader(500) - return - } - _, err = w.Write(ret) - if err != nil { - _, _ = w.Write([]byte(err.Error())) - w.WriteHeader(500) - return - } - w.WriteHeader(200) - return case http.MethodPost: body, err := io.ReadAll(req.Body) @@ -606,7 +576,7 @@ func handleNodes(c *Controller) http.Handler { return } type val struct { - KeyId string `json:"key_id"` + Request string `json:"request"` } var currVal val if err := json.Unmarshal(body, &currVal); err != nil { @@ -614,7 +584,7 @@ func handleNodes(c *Controller) http.Handler { w.WriteHeader(500) return } - if err := c.AuthorizeNodeeWorker(currVal.KeyId); err != nil { + if err := c.AuthorizeNodeeWorker(currVal.Request); err != nil { _, _ = w.Write([]byte(err.Error())) w.WriteHeader(500) return diff --git a/internal/daemon/controller/intercepting_listener.go b/internal/daemon/controller/intercepting_listener.go index cf97474d0..8d0d08c54 100644 --- a/internal/daemon/controller/intercepting_listener.go +++ b/internal/daemon/controller/intercepting_listener.go @@ -9,7 +9,6 @@ import ( "github.com/hashicorp/boundary/internal/observability/event" nodee "github.com/hashicorp/nodeenrollment" - "github.com/hashicorp/nodeenrollment/nodeauth" ) // tempError is an error that satisfies the temporary error interface that is @@ -79,7 +78,7 @@ func (m *interceptingListener) Accept() (net.Conn, error) { tlsConn := conn.(*tls.Conn) switch { - case nodeauth.ContainsNodeAuthAlpnProto(tlsConn.ConnectionState().NegotiatedProtocol): + case nodee.ContainsKnownAlpnProto(tlsConn.ConnectionState().NegotiatedProtocol): keyId, err := nodee.KeyIdFromPkix(tlsConn.ConnectionState().PeerCertificates[0].SubjectKeyId) if err != nil { if err := conn.Close(); err != nil { diff --git a/internal/daemon/controller/listeners.go b/internal/daemon/controller/listeners.go index ddd53ed35..b4bd1046a 100644 --- a/internal/daemon/controller/listeners.go +++ b/internal/daemon/controller/listeners.go @@ -17,9 +17,8 @@ import ( "github.com/hashicorp/boundary/internal/daemon/controller/internal/metric" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/go-multierror" - nodee "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/multihop" - "github.com/hashicorp/nodeenrollment/nodeauth" + "github.com/hashicorp/nodeenrollment/protocol" "google.golang.org/grpc" ) @@ -106,18 +105,16 @@ func (c *Controller) configureForApi(ln *base.ServerListener) ([]func(), error) } func (c *Controller) configureForCluster(ln *base.ServerListener) (func(), error) { - l, err := nodeauth.NewInterceptingListener( - ln.ClusterListener, - &tls.Config{ - GetConfigForClient: c.validateWorkerTls, - }, - nodeauth.MakeCurrentParametersFactory( - c.baseContext, - nodee.NopTransactionStorage(c.NodeeFileStorage), - ), - nil, - nil, - ) + const op = "controller.configureForCluster" + l, err := protocol.NewInterceptingListener( + &protocol.InterceptingListenerConfiguration{ + Context: c.baseContext, + Storage: c.NodeeFileStorage, + BaseListener: ln.ClusterListener, + BaseTlsConfiguration: &tls.Config{ + GetConfigForClient: c.validateWorkerTls, + }, + }) if err != nil { return nil, fmt.Errorf("error instantiating node auth listener: %w", err) } @@ -145,9 +142,14 @@ func (c *Controller) configureForCluster(ln *base.ServerListener) (func(), error pbs.RegisterServerCoordinationServiceServer(workerServer, workerService) pbs.RegisterSessionServiceServer(workerServer, workerService) - multihopService := handlers.NewMultihopServiceServer( - nodeauth.MakeCurrentParametersFactory(c.baseContext, nodee.NopTransactionStorage(c.NodeeFileStorage)), + multihopService, err := handlers.NewMultihopServiceServer( + c.NodeeFileStorage, + true, + nil, ) + if err != nil { + return nil, fmt.Errorf("%s: error creating multihop service handler: %w", op, err) + } multihop.RegisterMultihopServiceServer(workerServer, multihopService) metric.InitializeClusterCollectors(c.conf.PrometheusRegisterer, workerServer) diff --git a/internal/daemon/controller/listeners_test.go b/internal/daemon/controller/listeners_test.go index 30a3b769c..db1119291 100644 --- a/internal/daemon/controller/listeners_test.go +++ b/internal/daemon/controller/listeners_test.go @@ -23,6 +23,7 @@ import ( "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-secure-stdlib/configutil/v2" "github.com/hashicorp/go-secure-stdlib/listenerutil" + nodeefile "github.com/hashicorp/nodeenrollment/storage/file" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/protobuf/proto" @@ -250,6 +251,9 @@ func TestStartListeners(t *testing.T) { c.baseContext = ctx c.baseCancel = cancel + c.NodeeFileStorage, err = nodeefile.NewFileStorage(c.baseContext) + require.NoError(t, err) + err = c.startListeners() require.NoError(t, err) diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index f235c82cc..28dec4f0c 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -23,9 +23,8 @@ import ( pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/go-secure-stdlib/base62" - nodee "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/multihop" - "github.com/hashicorp/nodeenrollment/nodeauth" + "github.com/hashicorp/nodeenrollment/protocol" "google.golang.org/grpc" "google.golang.org/grpc/backoff" "google.golang.org/grpc/credentials/insecure" @@ -74,11 +73,7 @@ func (w *Worker) controllerDialerFunc() func(context.Context, string) (net.Conn, case w.conf.WorkerAuthKms != nil: conn, err = w.v1KmsAuthDialFn(ctx, addr) default: - conn, err = nodeauth.AuthDialFn( - nodeauth.MakeCurrentParametersFactory( - ctx, - nodee.NopTransactionStorage(w.NodeeFileStorage), - ))(ctx, addr) + conn, err = protocol.Dial(ctx, w.NodeeFileStorage, addr) } if !w.everAuthenticated.Load() && err == nil && conn != nil { diff --git a/internal/daemon/worker/listeners.go b/internal/daemon/worker/listeners.go index 0da0f3a62..ed98ac843 100644 --- a/internal/daemon/worker/listeners.go +++ b/internal/daemon/worker/listeners.go @@ -19,8 +19,10 @@ import ( "github.com/hashicorp/go-multierror" nodee "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/multihop" - "github.com/hashicorp/nodeenrollment/nodeauth" + "github.com/hashicorp/nodeenrollment/protocol" "github.com/hashicorp/nodeenrollment/types" + "github.com/hashicorp/nodeenrollment/util/splitlistener" + "github.com/hashicorp/nodeenrollment/util/temperror" "google.golang.org/grpc" ) @@ -50,6 +52,8 @@ func (w *Worker) startListeners() error { } func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) (func(), error) { + const op = "worker.configureForWorker" + handler, err := w.handler(HandlerProperties{ListenerConfig: ln.Config}) if err != nil { return nil, err @@ -88,12 +92,13 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) ) (*types.FetchNodeCredentialsResponse, error) { client := w.controllerMultihopConn.Load() if client == nil { - return nil, nodeauth.NewTempError(errors.New("error fetching controller connection, client is nil")) + return nil, temperror.New(errors.New("error fetching controller connection, client is nil")) } multihopClient, ok := client.(multihop.MultihopServiceClient) if !ok { - return nil, nodeauth.NewTempError(errors.New("client could not be understood as a multihop service client")) + return nil, temperror.New(errors.New("client could not be understood as a multihop service client")) } + // log.Println("proxying fetch node credentials request") return multihopClient.FetchNodeCredentials(ctx, req) } @@ -105,40 +110,45 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) ) (*types.GenerateServerCertificatesResponse, error) { client := w.controllerMultihopConn.Load() if client == nil { - return nil, nodeauth.NewTempError(errors.New("error fetching controller connection, client is nil")) + return nil, temperror.New(errors.New("error fetching controller connection, client is nil")) } multihopClient, ok := client.(multihop.MultihopServiceClient) if !ok { - return nil, nodeauth.NewTempError(errors.New("client could not be understood as a multihop service client")) + return nil, temperror.New(errors.New("client could not be understood as a multihop service client")) } + // log.Println("proxying generate server cert request") return multihopClient.GenerateServerCertificates(ctx, req) } - interceptingListener, err := nodeauth.NewInterceptingListener( - ln.ProxyListener, - &tls.Config{ - GetConfigForClient: w.getSessionTls, - }, - nodeauth.MakeCurrentParametersFactory( - w.baseContext, - nodee.NopTransactionStorage(w.NodeeFileStorage), - ), - fetchCredsFn, - generateServerCertificatesFn, - ) + interceptingListener, err := protocol.NewInterceptingListener( + &protocol.InterceptingListenerConfiguration{ + Context: w.baseContext, + Storage: w.NodeeFileStorage, + BaseListener: ln.ProxyListener, + BaseTlsConfiguration: &tls.Config{ + GetConfigForClient: w.getSessionTls, + }, + FetchCredsFunc: fetchCredsFn, + GenerateServerCertificatesFunc: generateServerCertificatesFn, + }) if err != nil { return nil, fmt.Errorf("error instantiating node auth listener: %w", err) } - w.nodeeTeeListener = nodeauth.NewTeeListener(interceptingListener) + w.nodeeSplitListener = splitlistener.New(interceptingListener) downstreamServer := grpc.NewServer( grpc.MaxRecvMsgSize(math.MaxInt32), grpc.MaxSendMsgSize(math.MaxInt32), ) - multihopService := handlers.NewMultihopServiceServer( - nodeauth.MakeCurrentParametersFactory(w.baseContext, nodee.NopTransactionStorage(w.NodeeFileStorage)), + multihopService, err := handlers.NewMultihopServiceServer( + w.NodeeFileStorage, + false, + w.controllerMultihopConn, ) + if err != nil { + return nil, fmt.Errorf("%s: error creating multihop service handler: %w", op, err) + } multihop.RegisterMultihopServiceServer(downstreamServer, multihopService) statusSessionService := NewWorkerProxyServiceServer(w.controllerStatusConn, w.controllerSessionConn) pbs.RegisterServerCoordinationServiceServer(downstreamServer, statusSessionService) @@ -147,9 +157,9 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) ln.GrpcServer = downstreamServer return func() { - go w.nodeeTeeListener.Start() - go httpServer.Serve(w.nodeeTeeListener.OtherListener()) - go ln.GrpcServer.Serve(w.nodeeTeeListener.NodeeListener()) + go w.nodeeSplitListener.Start() + go httpServer.Serve(w.nodeeSplitListener.OtherListener()) + go ln.GrpcServer.Serve(w.nodeeSplitListener.NodeEnrollmentListener()) }, nil } @@ -162,21 +172,21 @@ func (w *Worker) stopServersAndListeners() error { // really likes to hang on closing. Maybe because it's never served a // connection? This is a workaround to force it until I can dig in. var cancel context.CancelFunc - if w.nodeeTeeListener != nil { + if w.nodeeSplitListener != nil { var ctx context.Context ctx, cancel = context.WithTimeout(w.baseContext, 2*time.Second) go func() { <-ctx.Done() - w.nodeeTeeListener.Stop() + w.nodeeSplitListener.Stop() cancel() }() } stopErrors := mg.Wait() - if w.nodeeTeeListener != nil { + if w.nodeeSplitListener != nil { cancel() - err := w.nodeeTeeListener.Stop() + err := w.nodeeSplitListener.Stop() if err != nil { stopErrors = multierror.Append(stopErrors, err) } @@ -227,8 +237,8 @@ func (w *Worker) stopAnyListeners() error { } var closeErrors *multierror.Error var err error - if w.nodeeTeeListener != nil { - err = w.nodeeTeeListener.Stop() + if w.nodeeSplitListener != nil { + err = w.nodeeSplitListener.Stop() } else if w.proxyListener.ProxyListener != nil { err = w.proxyListener.ProxyListener.Close() } diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 182c7832b..7da2e46cb 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -15,6 +15,8 @@ import ( "time" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/nodeenrollment" + "github.com/mr-tron/base58" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/cmd/base" @@ -26,13 +28,13 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-secure-stdlib/mlock" - nodee "github.com/hashicorp/nodeenrollment" - "github.com/hashicorp/nodeenrollment/nodeauth" nodeefile "github.com/hashicorp/nodeenrollment/storage/file" "github.com/hashicorp/nodeenrollment/types" + "github.com/hashicorp/nodeenrollment/util/splitlistener" ua "go.uber.org/atomic" "google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver/manual" + "google.golang.org/protobuf/proto" ) type randFn func(length int) (string, error) @@ -73,9 +75,10 @@ type Worker struct { updateTags *ua.Bool // PoC: Testing bits for BYOW - NodeeFileStorage *nodeefile.FileStorage - NodeeKeyId string - nodeeTeeListener *nodeauth.TeeListener + NodeeFileStorage *nodeefile.FileStorage + NodeeCurrentKeyId string + NodeeRegistrationRequest string + nodeeSplitListener *splitlistener.SplitListener // Test-specific options TestOverrideX509VerifyDnsName string @@ -190,7 +193,20 @@ func (w *Worker) Start() error { if err != nil { return fmt.Errorf("error generating new node creds: %w", err) } - w.NodeeKeyId, err = nodee.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) + + req, err := nodeCreds.CreateFetchNodeCredentialsRequest(w.baseContext) + if err != nil { + return fmt.Errorf("error creating fetch credentials request: %w", err) + } + reqBytes, err := proto.Marshal(req) + if err != nil { + return fmt.Errorf("error marshaling fetch credentials request: %w", err) + } + w.NodeeRegistrationRequest = base58.FastBase58Encoding(reqBytes) + if err != nil { + return fmt.Errorf("error encoding registration request: %w", err) + } + w.NodeeCurrentKeyId, err = nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) if err != nil { return fmt.Errorf("error deriving key id: %w", err) } From 731a45eba6c99267ef01374ea5b02afce762cdaa Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 1 Jun 2022 13:25:29 -0700 Subject: [PATCH 031/100] Merge the worker_status table with the worker table (#2111) --- .../server/worker_shutdown_reload_test.go | 1 - .../server/worker_tags_reload_test.go | 19 +- internal/cmd/config/config.go | 1 + .../daemon/cluster/handlers/worker_service.go | 67 ++- .../handlers/worker_service_status_test.go | 27 +- .../handlers/targets/target_service.go | 2 +- .../handlers/targets/target_service_test.go | 36 +- internal/daemon/controller/testing.go | 10 +- internal/daemon/worker/testing.go | 2 + .../99/02_worker_controller_tables.up.sql | 90 ++- internal/db/timestamp/timestamp.go | 2 +- .../storage/servers/store/v1/worker.proto | 37 +- internal/servers/options.go | 8 - internal/servers/options_test.go | 8 - internal/servers/public_ids.go | 12 + internal/servers/repository_worker.go | 172 ++++-- internal/servers/repository_worker_test.go | 349 ++++++----- internal/servers/store/worker.pb.go | 200 ++----- internal/servers/testing.go | 12 +- internal/servers/testing_test.go | 3 +- internal/servers/worker.go | 159 ++--- internal/servers/worker_status.go | 50 -- internal/servers/worker_test.go | 562 +++++++++++++++--- internal/session/query.go | 9 +- .../service_worker_status_report_test.go | 4 +- internal/session/session_cleanup_job_test.go | 87 +-- ...on_list_benchmarks_dump_generation_test.go | 4 +- 27 files changed, 1169 insertions(+), 764 deletions(-) delete mode 100644 internal/servers/worker_status.go diff --git a/internal/cmd/commands/server/worker_shutdown_reload_test.go b/internal/cmd/commands/server/worker_shutdown_reload_test.go index 0afc6efb4..bf77ad451 100644 --- a/internal/cmd/commands/server/worker_shutdown_reload_test.go +++ b/internal/cmd/commands/server/worker_shutdown_reload_test.go @@ -77,7 +77,6 @@ func TestServer_ShutdownWorker(t *testing.T) { tcl := targets.NewClient(client) tgtL, err := tcl.List(ctx, scope.Global.String(), targets.WithRecursive(true)) - // tgt, err := tcl.Read(ctx, "ttcp_1234567890") require.NoError(err) require.Len(tgtL.Items, 1) tgt := tgtL.Items[0] diff --git a/internal/cmd/commands/server/worker_tags_reload_test.go b/internal/cmd/commands/server/worker_tags_reload_test.go index 5229e8cd7..5dce67d62 100644 --- a/internal/cmd/commands/server/worker_tags_reload_test.go +++ b/internal/cmd/commands/server/worker_tags_reload_test.go @@ -44,7 +44,7 @@ listener "tcp" { const tag1Config = ` worker { - name = "w_1234567890" + name = "test" description = "A default worker created in dev mode" controllers = ["%s"] tags { @@ -55,7 +55,7 @@ worker { const tag2Config = ` worker { - name = "w_1234567890" + name = "test" description = "A default worker created in dev mode" controllers = ["%s"] tags { @@ -98,18 +98,17 @@ func TestServer_ReloadWorkerTags(t *testing.T) { t.Helper() serversRepo, err := testController.Controller().ServersRepoFn() require.NoError(err) - tags, err := serversRepo.ListTagsForWorkers(testController.Context(), []string{name}) + w, err := serversRepo.LookupWorkerByWorkerReportedName(testController.Context(), name) require.NoError(err) - require.Len(tags[name], 2) - require.Equal(key, tags[name][0].Key) - require.Equal(values[0], tags[name][0].Value) - require.Equal(key, tags[name][1].Key) - require.Equal(values[1], tags[name][1].Value) + require.NotNil(w) + v, ok := w.CanonicalTags()[key] + require.True(ok) + require.ElementsMatch(values, v) } // Give time to populate up to the controller time.Sleep(10 * time.Second) - fetchWorkerTags("w_1234567890", "type", []string{"dev", "local"}) + fetchWorkerTags("test", "type", []string{"dev", "local"}) cmd.presetConfig.Store(fmt.Sprintf(workerBaseConfig+tag2Config, key, testController.ClusterAddrs()[0])) @@ -121,7 +120,7 @@ func TestServer_ReloadWorkerTags(t *testing.T) { } time.Sleep(10 * time.Second) - fetchWorkerTags("w_1234567890", "foo", []string{"bar", "baz"}) + fetchWorkerTags("test", "foo", []string{"bar", "baz"}) close(cmd.ShutdownCh) diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index 0c753f5f9..6d1b85fba 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -208,6 +208,7 @@ func initNameIfEmpty(name *string) error { if *name, err = db.NewPublicId("w"); err != nil { return err } + *name = strings.ToLower(*name) } return nil } diff --git a/internal/daemon/cluster/handlers/worker_service.go b/internal/daemon/cluster/handlers/worker_service.go index 5940f74c6..1ddda90c5 100644 --- a/internal/daemon/cluster/handlers/worker_service.go +++ b/internal/daemon/cluster/handlers/worker_service.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/session" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" "github.com/hashicorp/go-bexpr" "google.golang.org/grpc/codes" @@ -63,12 +64,6 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques event.WriteError(ctx, op, err, event.WithInfoMsg("error getting servers repo")) return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error acquiring repo to store worker status: %v", err) } - sessRepo, err := ws.sessionRepoFn() - connectionRepo, err := ws.connectionRepoFn() - if err != nil { - event.WriteError(ctx, op, err, event.WithInfoMsg("error getting sessions repo")) - return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error acquiring repo to query session status: %v", err) - } reqServer := req.GetWorker() // Convert API tags to storage tags @@ -82,16 +77,20 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques } } - wConf := servers.NewWorkerStatus(reqServer.PrivateId, - // TODO: Change the name to its own field to make this explicit. + wConf := servers.NewWorkerForStatus(scope.Global.String(), servers.WithName(reqServer.PrivateId), servers.WithAddress(reqServer.Address), servers.WithWorkerTags(workerTags...)) - controllers, _, err := serverRepo.UpsertWorkerStatus(ctx, wConf, servers.WithUpdateTags(req.GetUpdateTags())) + wrk, err := serverRepo.UpsertWorkerStatus(ctx, wConf, servers.WithUpdateTags(req.GetUpdateTags())) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error storing worker status")) return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error storing worker status: %v", err) } + controllers, err := serverRepo.ListControllers(ctx) + if err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error getting current controllers")) + return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error getting current controllers: %v", err) + } responseControllers := []*servers.Server{} for _, c := range controllers { @@ -139,9 +138,22 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques } } - notActive, err := session.WorkerStatusReport(ctx, sessRepo, connectionRepo, req.Worker.PrivateId, stateReport) + sessRepo, err := ws.sessionRepoFn() if err != nil { - return nil, status.Errorf(codes.Internal, "Error comparing state of sessions for worker: %s: %v", req.Worker.PrivateId, err) + event.WriteError(ctx, op, err, event.WithInfoMsg("error getting sessions repo")) + return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error acquiring repo to query session status: %v", err) + } + connectionRepo, err := ws.connectionRepoFn() + if err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error getting connection repo")) + return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error acquiring repo to query session status: %v", err) + } + + notActive, err := session.WorkerStatusReport(ctx, sessRepo, connectionRepo, wrk.GetPublicId(), stateReport) + if err != nil { + return nil, status.Errorf(codes.Internal, + "Error comparing state of sessions for worker %q with public id %q: %v", + req.Worker.PrivateId, wrk.GetPublicId(), err) } for _, na := range notActive { var connChanges []*pbs.Connection @@ -198,20 +210,13 @@ func (ws *workerServiceServer) LookupSession(ctx context.Context, req *pbs.Looku event.WriteError(ctx, op, err, event.WithInfoMsg("error getting servers repo")) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error acquiring server repo when looking up session: %v", err) } - workerTags, err := serversRepo.ListTagsForWorkers(ctx, []string{req.ServerId}) + w, err := serversRepo.LookupWorkerByWorkerReportedName(ctx, req.GetServerId()) if err != nil { - event.WriteError(ctx, op, err, event.WithInfoMsg("error looking up tags for server", "server_id", req.ServerId)) + event.WriteError(ctx, op, err, event.WithInfoMsg("error looking up worker by name", "name", req.ServerId)) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error looking up tags for server: %v", err) } // Build the map for filtering. - tagMap := make(map[string][]string) - for _, tags := range workerTags { - for _, tag := range tags { - tagMap[tag.Key] = append(tagMap[tag.Key], tag.Value) - // We don't need to reinsert after the fact because maps are - // reference types, so we don't need to re-insert into tagMap - } - } + tagMap := w.CanonicalTags() // Create the evaluator eval, err := bexpr.CreateEvaluator(sessionInfo.WorkerFilter) @@ -219,8 +224,12 @@ func (ws *workerServiceServer) LookupSession(ctx context.Context, req *pbs.Looku event.WriteError(ctx, op, err, event.WithInfoMsg("error creating worker filter evaluator", "server_id", req.ServerId)) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error creating worker filter evaluator: %v", err) } + name := w.GetName() + if name == "" { + name = w.GetWorkerReportedName() + } filterInput := map[string]interface{}{ - "name": req.ServerId, + "name": name, "tags": tagMap, } ok, err := eval.Evaluate(filterInput) @@ -347,7 +356,19 @@ func (ws *workerServiceServer) AuthorizeConnection(ctx context.Context, req *pbs return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err) } - connectionInfo, connStates, authzSummary, err := session.AuthorizeConnection(ctx, sessionRepo, connectionRepo, req.GetSessionId(), req.GetWorkerId()) + serversRepo, err := ws.serversRepoFn() + if err != nil { + return nil, status.Errorf(codes.Internal, "error getting servers repo: %v", err) + } + w, err := serversRepo.LookupWorkerByWorkerReportedName(ctx, req.GetWorkerId()) + if err != nil { + return nil, status.Errorf(codes.Internal, "error looking up worker: %v", err) + } + if w == nil { + return nil, status.Errorf(codes.NotFound, "worker not found with name %q", req.GetWorkerId()) + } + + connectionInfo, connStates, authzSummary, err := session.AuthorizeConnection(ctx, sessionRepo, connectionRepo, req.GetSessionId(), w.GetPublicId()) if err != nil { return nil, err } diff --git a/internal/daemon/cluster/handlers/worker_service_status_test.go b/internal/daemon/cluster/handlers/worker_service_status_test.go index 5d3fefb72..67112c63d 100644 --- a/internal/daemon/cluster/handlers/worker_service_status_test.go +++ b/internal/daemon/cluster/handlers/worker_service_status_test.go @@ -36,10 +36,6 @@ func TestStatus(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorkerStatus(ctx, servers.NewWorkerStatus( - "test_worker1", - servers.WithAddress("127.0.0.1"))) - serversRepoFn := func() (*servers.Repository, error) { return serverRepo, nil } @@ -103,7 +99,7 @@ func TestStatus(t *testing.T) { wantErr: false, req: &pbs.StatusRequest{ Worker: &servers.Server{ - PrivateId: worker1.PublicId, + PrivateId: worker1.GetWorkerReportedName(), Address: worker1.CanonicalAddress(), CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, @@ -123,7 +119,7 @@ func TestStatus(t *testing.T) { wantErr: false, req: &pbs.StatusRequest{ Worker: &servers.Server{ - PrivateId: worker1.PublicId, + PrivateId: worker1.GetWorkerReportedName(), Address: worker1.CanonicalAddress(), CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, @@ -203,10 +199,6 @@ func TestStatusSessionClosed(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorkerStatus(ctx, servers.NewWorkerStatus( - "test_worker1", - servers.WithAddress("127.0.0.1"))) - serversRepoFn := func() (*servers.Repository, error) { return serverRepo, nil } @@ -287,7 +279,7 @@ func TestStatusSessionClosed(t *testing.T) { }, req: &pbs.StatusRequest{ Worker: &servers.Server{ - PrivateId: worker1.PublicId, + PrivateId: worker1.GetWorkerReportedName(), Address: worker1.CanonicalAddress(), CreateTime: worker1.CreateTime, UpdateTime: worker1.UpdateTime, @@ -384,9 +376,8 @@ func TestStatusDeadConnection(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorkerStatus(ctx, servers.NewWorkerStatus( - "test_worker1", - servers.WithAddress("127.0.0.1"))) + + worker1 := servers.TestWorker(t, conn, wrapper) serversRepoFn := func() (*servers.Repository, error) { return serverRepo, nil @@ -416,8 +407,6 @@ func TestStatusDeadConnection(t *testing.T) { target.WithSessionConnectionLimit(-1), ) - worker1 := servers.TestWorker(t, conn, wrapper) - sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -456,10 +445,8 @@ func TestStatusDeadConnection(t *testing.T) { req := &pbs.StatusRequest{ Worker: &servers.Server{ - PrivateId: worker1.PublicId, - Address: worker1.CanonicalAddress(), - CreateTime: worker1.CreateTime, - UpdateTime: worker1.UpdateTime, + PrivateId: worker1.GetWorkerReportedName(), + Address: worker1.GetWorkerReportedAddress(), }, Jobs: []*pbs.JobStatus{ { diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index 251962b32..25dfea85a 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -2153,7 +2153,7 @@ func (w workerList) filtered(eval *bexpr.Evaluator) (workerList, error) { // we are separating reported status name and the resource name. name := worker.GetName() if name == "" { - name = worker.ReportedStatus.GetName() + name = worker.GetWorkerReportedName() } filterInput := map[string]interface{}{ "name": name, diff --git a/internal/daemon/controller/handlers/targets/target_service_test.go b/internal/daemon/controller/handlers/targets/target_service_test.go index 2644b4d78..675946ac4 100644 --- a/internal/daemon/controller/handlers/targets/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/target_service_test.go @@ -34,20 +34,22 @@ func TestWorkerList_Addresses(t *testing.T) { func TestWorkerList_Filter(t *testing.T) { var workers []*servers.Worker for i := 0; i < 5; i++ { - w := servers.NewWorker(scope.Global.String(), - servers.WithName(fmt.Sprintf("test_worker_%d", i)), - servers.WithWorkerTags(&servers.Tag{ - Key: fmt.Sprintf("key%d", i), - Value: fmt.Sprintf("value%d", i), - })) - w.ReportedStatus = servers.NewWorkerStatus("", - servers.WithName(fmt.Sprintf("config%d", i)), - servers.WithWorkerTags(&servers.Tag{ - Key: "key", - // Note that the value is either 0 or 1 - Value: fmt.Sprintf("configvalue%d", i%2), - })) - workers = append(workers, w) + switch { + case i%2 == 0: + workers = append(workers, servers.NewWorker(scope.Global.String(), + servers.WithName(fmt.Sprintf("test_worker_%d", i)), + servers.WithWorkerTags(&servers.Tag{ + Key: fmt.Sprintf("key%d", i), + Value: fmt.Sprintf("value%d", i), + }))) + default: + workers = append(workers, servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName(fmt.Sprintf("test_worker_%d", i)), + servers.WithWorkerTags(&servers.Tag{ + Key: "key", + Value: "configvalue", + }))) + } } { @@ -58,17 +60,17 @@ func TestWorkerList_Filter(t *testing.T) { assert.Len(t, got, 0) } { - ev, err := bexpr.CreateEvaluator(`"/name" matches "test_worker_[12]" and "configvalue0" in "/tags/key"`) + ev, err := bexpr.CreateEvaluator(`"/name" matches "test_worker_[12]" and "configvalue" in "/tags/key"`) require.NoError(t, err) got, err := workerList(workers).filtered(ev) require.NoError(t, err) assert.Len(t, got, 1) } { - ev, err := bexpr.CreateEvaluator(`"configvalue0" in "/tags/key"`) + ev, err := bexpr.CreateEvaluator(`"configvalue" in "/tags/key"`) require.NoError(t, err) got, err := workerList(workers).filtered(ev) require.NoError(t, err) - assert.Len(t, got, 3) + assert.Len(t, got, 2) } } diff --git a/internal/daemon/controller/testing.go b/internal/daemon/controller/testing.go index 39a3663fd..ecbf36466 100644 --- a/internal/daemon/controller/testing.go +++ b/internal/daemon/controller/testing.go @@ -705,10 +705,10 @@ func (tc *TestController) AddClusterControllerMember(t testing.TB, opts *TestCon // WaitForNextWorkerStatusUpdate waits for the next status check from a worker to // come in. If it does not come in within the default status grace // period, this function returns an error. -func (tc *TestController) WaitForNextWorkerStatusUpdate(workerId string) error { +func (tc *TestController) WaitForNextWorkerStatusUpdate(workerStatusName string) error { const op = "controller.(TestController).WaitForNextWorkerStatusUpdate" ctx := context.TODO() - event.WriteSysEvent(ctx, op, "waiting for next status report from worker", "worker", workerId) + event.WriteSysEvent(ctx, op, "waiting for next status report from worker", "worker", workerStatusName) waitStatusStart := time.Now() ctx, cancel := context.WithTimeout(tc.ctx, tc.b.StatusGracePeriodDuration) defer cancel() @@ -747,7 +747,7 @@ func (tc *TestController) WaitForNextWorkerStatusUpdate(workerId string) error { return false } - if workerStatusUpdateId == workerId { + if workerStatusUpdateId == workerStatusName { waitStatusCurrent = workerStatusUpdateTime return false } @@ -765,10 +765,10 @@ func (tc *TestController) WaitForNextWorkerStatusUpdate(workerId string) error { } if err != nil { - event.WriteError(ctx, op, err, event.WithInfoMsg("error waiting for next status report from worker", "worker", workerId)) + event.WriteError(ctx, op, err, event.WithInfoMsg("error waiting for next status report from worker", "worker", workerStatusName)) return err } - event.WriteSysEvent(ctx, op, "waiting for next status report from worker received successfully", "worker", workerId) + event.WriteSysEvent(ctx, op, "waiting for next status report from worker received successfully", "worker", workerStatusName) return nil } diff --git a/internal/daemon/worker/testing.go b/internal/daemon/worker/testing.go index a394e72d1..d73145cc9 100644 --- a/internal/daemon/worker/testing.go +++ b/internal/daemon/worker/testing.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "strings" "testing" "time" @@ -320,6 +321,7 @@ func (tw *TestWorker) AddClusterWorkerMember(t testing.TB, opts *TestWorkerOpts) if err != nil { t.Fatal(err) } + nextOpts.Name = strings.ToLower(nextOpts.Name) event.WriteSysEvent(context.TODO(), op, "worker name generated", "name", nextOpts.Name) } return NewTestWorker(t, nextOpts) diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index ef48224ea..a9a70096d 100644 --- a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -40,8 +40,37 @@ create table server_worker ( create_time wt_timestamp, update_time wt_timestamp, version wt_version, + last_status_time timestamp with time zone, + constraint last_status_time_not_before_create_time + check (last_status_time >= create_time), + -- This is the calculated address that the worker reports it is reachable on. + -- This must be set if the worker has ever received a status update. + worker_reported_address wt_network_address + constraint worker_reported_address_must_be_set_by_status + check ( + (last_status_time is null and worker_reported_address is null) + or + (last_status_time is not null and worker_reported_address is not null) + ), + -- This is the name that the worker reports in it's status updates. + worker_reported_name wt_name + -- TODO: When we are recording the worker node's key id allow that as an + -- alternative value that must be set besides worker_reported_name if + -- last_status_time is set. + constraint worker_reported_name_must_be_set_by_status + check ( + (last_status_time is null and worker_reported_name is null) + or + (last_status_time is not null and worker_reported_name is not null) + ) + constraint worker_reported_name_must_be_lowercase + check (lower(trim(worker_reported_name)) = worker_reported_name) + constraint worker_reported_name_only_has_printable_characters + check (worker_reported_name !~ '[^[:print:]]'), constraint server_worker_scope_id_name_uq - unique(scope_id, name) + unique(scope_id, name), + constraint server_worker_scope_id_worker_reported_name_uq + unique(scope_id, worker_reported_name) ); comment on table server_worker is 'server_worker is a table where each row represents a Boundary worker.'; @@ -58,32 +87,38 @@ create trigger worker_insert_time_column before insert on server_worker create trigger worker_update_time_column before update on server_worker for each row execute procedure update_time_column(); -create table server_worker_status ( - worker_id wt_public_id primary key - constraint server_worker_fkey - references server_worker(public_id) - on delete cascade - on update cascade, - create_time wt_timestamp, - update_time wt_timestamp, - -- This is the calculated address that the worker reports it is reachable on. - address wt_network_address not null, - name wt_name -); -comment on table server_worker_status is - 'server_worker_status is a table where each row represents values that a Boundary worker reports to a controller.'; +create trigger update_version_column after update of version, description, name, address on server_worker + for each row execute procedure update_version_column(); -create trigger immutable_columns before update on server_worker_status - for each row execute procedure immutable_columns('worker_id', 'create_time'); +create function update_server_worker_update_last_status_time_column() + returns trigger +as $$ +begin + new.last_status_time = now(); + return new; +end; +$$ language plpgsql; +comment on function update_server_worker_update_last_status_time_column is + 'function used to update the last_status_time column in server_worker to now'; -create trigger default_create_time_column before insert on server_worker_status - for each row execute procedure default_create_time(); +create trigger update_server_worker_last_status_time_column before update of worker_reported_address, worker_reported_name on server_worker + for each row execute procedure update_server_worker_update_last_status_time_column(); -create trigger worker_insert_time_column before insert on server_worker_status - for each row execute procedure update_time_column(); +create function insert_server_worker_update_last_status_time_column() + returns trigger +as $$ +begin + if new.worker_reported_address is not null or new.worker_reported_name is not null then + new.last_status_time = now(); + end if; + return new; +end; +$$ language plpgsql; +comment on function insert_server_worker_update_last_status_time_column is + 'function used to update the last_status_time column in server_worker to now'; -create trigger worker_update_time_column before update on server_worker_status - for each row execute procedure update_time_column(); +create trigger insert_server_worker_last_update_time_column before insert on server_worker + for each row execute procedure insert_server_worker_update_last_status_time_column(); -- Create table worker tag create table server_worker_tag_enm ( @@ -136,16 +171,13 @@ select w.create_time, w.update_time, w.version, - ws.name as worker_status_name, - ws.address as worker_status_address, - ws.update_time as worker_status_update_time, - ws.create_time as worker_status_create_time, + w.worker_reported_name, + w.worker_reported_address, + w.last_status_time, -- keys and tags can be any lowercase printable character so use uppercase characters as delimitors. wt.tags as api_tags, ct.tags as worker_config_tags from server_worker w - left join server_worker_status ws on - w.public_id = ws.worker_id left join worker_config_tags wt on w.public_id = wt.worker_id and wt.source = 'api' left join worker_config_tags ct on diff --git a/internal/db/timestamp/timestamp.go b/internal/db/timestamp/timestamp.go index 4910d738b..774a79076 100644 --- a/internal/db/timestamp/timestamp.go +++ b/internal/db/timestamp/timestamp.go @@ -22,5 +22,5 @@ func Now() *Timestamp { // AsTime converts x to a time.Time. func (x *Timestamp) AsTime() time.Time { - return x.Timestamp.AsTime() + return x.GetTimestamp().AsTime() } diff --git a/internal/proto/controller/storage/servers/store/v1/worker.proto b/internal/proto/controller/storage/servers/store/v1/worker.proto index 281c9497a..e6c5dff3f 100644 --- a/internal/proto/controller/storage/servers/store/v1/worker.proto +++ b/internal/proto/controller/storage/servers/store/v1/worker.proto @@ -39,6 +39,19 @@ message Worker { // version allows optimistic locking of the resource. // @inject_tag: `gorm:"default:null"` uint32 version = 80; + + // Name of the resource (optional) + // @inject_tag: `gorm:"default:null"` + string worker_reported_name = 90; + + // Address for the worker. This must be set when the worker has received + // at least 1 status update fromt he worker daemon. + // @inject_tag: `gorm:"default:null"` + string worker_reported_address = 100; + + // The last_status_time is the last time a worker daemon sent a status update. + // @inject_tag: `gorm:"default:null"` + timestamp.v1.Timestamp last_status_time = 110; } // WorkerTag is a tag for a worker. The primary key is comprised of the @@ -60,27 +73,3 @@ message WorkerTag { // @inject_tag: `gorm:"default:not_null"` string source = 40; } - -// WorkerStatus contains the fields that the worker reports to the controller -// in its status updates. -message WorkerStatus { - // worker_id is the public key that key of the worker this tag is for. - // @inject_tag: `gorm:"primary_key"` - string worker_id = 10; - - // The create_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - timestamp.v1.Timestamp create_time = 20; - - // The update_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - timestamp.v1.Timestamp update_time = 30; - - // Name of the resource (optional) - // @inject_tag: `gorm:"default:null"` - string name = 40; - - // Address for the worker. This must be set. - // @inject_tag: `gorm:"default:null"` - string address = 50; -} diff --git a/internal/servers/options.go b/internal/servers/options.go index cee422b4b..9fb7f6f2e 100644 --- a/internal/servers/options.go +++ b/internal/servers/options.go @@ -22,7 +22,6 @@ type options struct { withName string withDescription string withAddress string - withPublicId string withLimit int withLiveness time.Duration withUpdateTags bool @@ -62,13 +61,6 @@ func WithAddress(address string) Option { } } -// WithPublicId provides an optional public id -func WithPublicId(id string) Option { - return func(o *options) { - o.withPublicId = id - } -} - // WithLimit provides an option to provide a limit. Intentionally allowing // negative integers. If WithLimit < 0, then unlimited results are returned. // If WithLimit == 0, then default limits are used for results. diff --git a/internal/servers/options_test.go b/internal/servers/options_test.go index 08d05c3d7..3a34e4a7d 100644 --- a/internal/servers/options_test.go +++ b/internal/servers/options_test.go @@ -45,14 +45,6 @@ func Test_GetOpts(t *testing.T) { testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) - t.Run("WithPublicId", func(t *testing.T) { - opts := getOpts(WithPublicId("test")) - testOpts := getDefaultOptions() - testOpts.withPublicId = "test" - opts.withNewIdFunc = nil - testOpts.withNewIdFunc = nil - assert.Equal(t, opts, testOpts) - }) t.Run("WithLiveness", func(t *testing.T) { opts := getOpts(WithLiveness(time.Hour)) testOpts := getDefaultOptions() diff --git a/internal/servers/public_ids.go b/internal/servers/public_ids.go index 16d9a7829..25974e32c 100644 --- a/internal/servers/public_ids.go +++ b/internal/servers/public_ids.go @@ -19,3 +19,15 @@ func newWorkerId(ctx context.Context) (string, error) { } return id, nil } + +func newWorkerIdFromName(ctx context.Context, name string) (string, error) { + const op = "servers.newWorkerIdFromName" + if name == "" { + return "", errors.New(ctx, errors.InvalidParameter, op, "name is empty") + } + id, err := db.NewPublicId(WorkerPrefix, db.WithPrngValues([]string{name})) + if err != nil { + return "", errors.Wrap(ctx, err, op) + } + return id, nil +} diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index 58a254d17..f32fa5a65 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -50,6 +50,68 @@ func (r *Repository) DeleteWorker(ctx context.Context, publicId string, _ ...Opt return rowsDeleted, nil } +// LookupWorkerByWorkerReportedName returns the worker which has had a status update +// where the worker has reported this name. This is different from the name +// on the resource itself which is set over the API. In the event that no +// worker is found that matches then nil, nil will be returned. +func (r *Repository) LookupWorkerByWorkerReportedName(ctx context.Context, name string) (*Worker, error) { + const op = "servers.(Repository).LookupWorkerByWorkerReportedName" + switch { + case name == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "name is empty") + } + // we derive the id instead of doing a query to be consistant with the + // UpsertWorkerStatus flow which uses this function to upsert a worker. + id, err := newWorkerIdFromName(ctx, name) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error while calculating the worker's id")) + } + w, err := lookupWorker(ctx, r.reader, id) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return w, nil +} + +// LookupWorker returns the worker for the provided publicId. This returns +// nil nil in the situation where no worker can be found with that public id. +func (r *Repository) LookupWorker(ctx context.Context, publicId string, _ ...Option) (*Worker, error) { + const op = "servers.(Repository).LookupWorker" + switch { + case publicId == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "publicId is empty") + } + w, err := lookupWorker(ctx, r.reader, publicId) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return w, nil +} + +// lookupWorker returns the worker for the provided id. This returns +// nil nil in the situation where no worker can be found with that public id. +func lookupWorker(ctx context.Context, reader db.Reader, id string) (*Worker, error) { + const op = "servers.lookupWorker" + switch { + case id == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "id is empty") + } + wAgg := &workerAggregate{} + wAgg.PublicId = id + err := reader.LookupById(ctx, wAgg) + if err != nil { + if errors.IsNotFoundError(err) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op) + } + w, err := wAgg.toWorker(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return w, nil +} + // ListWorkers is a passthrough to listWorkersWithReader that uses the repo's normal reader. func (r *Repository) ListWorkers(ctx context.Context, opt ...Option) ([]*Worker, error) { return r.listWorkersWithReader(ctx, r.reader, opt...) @@ -73,7 +135,7 @@ func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader var where string if liveness > 0 { - where = fmt.Sprintf("worker_status_update_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) + where = fmt.Sprintf("last_status_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) } var wAggs []*workerAggregate @@ -98,94 +160,84 @@ func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader return workers, nil } -// ListTagsForWorkers returns a map from the worker's id to the list of Tags -// that are for that worker. All options are ignored. -func (r *Repository) ListTagsForWorkers(ctx context.Context, workerIds []string, _ ...Option) (map[string][]*Tag, error) { - const op = "servers.ListTagsForWorkers" - var workerTags []*store.WorkerTag - if err := r.reader.SearchWhere( - ctx, - &workerTags, - "worker_id in (?)", - []interface{}{workerIds}, - db.WithLimit(-1), - ); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("worker IDs %v", workerIds))) - } - - ret := make(map[string][]*Tag, len(workerIds)) - for _, t := range workerTags { - ret[t.WorkerId] = append(ret[t.WorkerId], &Tag{Key: t.Key, Value: t.Value}) - } - return ret, nil -} - -// UpsertWorkerStatus creates a new worker if one with the provided public id doesn't -// already exist. If it does, UpsertWorkerStatus updates the worker. The -// WithUpdateTags option is the only one used. All others are ignored. +// UpsertWorkerStatus creates a new worker if one with the provided public id +// doesn't already exist. If it does, UpsertWorkerStatus updates the worker +// status. This returns the Worker object with the updated WorkerStatus applied. +// The WithUpdateTags option is the only ones used. All others are ignored. // Workers are intentionally not oplogged. -func (r *Repository) UpsertWorkerStatus(ctx context.Context, wStatus *WorkerStatus, opt ...Option) ([]*store.Controller, int, error) { +func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt ...Option) (*Worker, error) { const op = "servers.UpsertWorkerStatus" switch { - case wStatus == nil: - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "wStatus is nil") - case wStatus.Address == "": - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "address is empty") - case wStatus.WorkerId == "": - return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "worker id is empty") + case worker == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "worker is nil") + case worker.GetWorkerReportedAddress() == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "worker reported address is empty") + case worker.ScopeId == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "scope id is empty") + case worker.PublicId != "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "worker id is not empty") + case worker.GetWorkerReportedName() == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "worker reported name is empty") + case len(worker.apiTags) > 0: + return nil, errors.New(ctx, errors.InvalidParameter, op, "api tags is not empty") } + // Only retain the worker reported fields. + worker = NewWorkerForStatus(worker.GetScopeId(), + WithName(worker.GetWorkerReportedName()), + WithAddress(worker.GetWorkerReportedAddress()), + WithWorkerTags(worker.configTags...)) + opts := getOpts(opt...) - var rowsUpdated int64 - var controllers []*store.Controller - _, err := r.writer.DoTx( + var err error + worker.PublicId, err = newWorkerIdFromName(ctx, worker.GetWorkerReportedName()) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error generating worker id")) + } + + var ret *Worker + _, err = r.writer.DoTx( ctx, db.StdRetryCnt, db.ExpBackoff{}, func(reader db.Reader, w db.Writer) error { - worker := NewWorker(scope.Global.String(), WithPublicId(wStatus.GetWorkerId())) + worker := worker.clone() workerCreateConflict := &db.OnConflict{ Target: db.Columns{"public_id"}, - Action: db.DoNothing(true), + Action: append(db.SetColumns([]string{"worker_reported_name", "worker_reported_address"}), + db.SetColumnValues(map[string]interface{}{"last_status_time": "now()"})...), } if err := w.Create(ctx, worker, db.WithOnConflict(workerCreateConflict)); err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("error creating a worker")) } - var err error - onConfigConflict := &db.OnConflict{ - Target: db.Columns{"worker_id"}, - Action: append(db.SetColumns([]string{"name", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), - } - err = w.Create(ctx, wStatus, db.WithOnConflict(onConfigConflict), db.WithReturnRowsAffected(&rowsUpdated)) - if err != nil { - return errors.Wrap(ctx, err, op+":Upsert") - } - - // Fetch current controllers to feed to the workers - controllers, err = r.listControllersWithReader(ctx, reader) - if err != nil { - return errors.Wrap(ctx, err, op+":ListController") - } - // If we've been told to update tags, we need to clean out old // ones and add new ones. Within the current transaction, simply // delete all tags for the given worker, then add the new ones // we've been sent. if opts.withUpdateTags { - setWorkerTags(ctx, w, wStatus.GetWorkerId(), ConfigurationTagSource, wStatus.Tags) + setWorkerTags(ctx, w, worker.GetPublicId(), ConfigurationTagSource, worker.configTags) + } + + wAgg := &workerAggregate{PublicId: worker.GetPublicId()} + if err := reader.LookupById(ctx, wAgg); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("error looking up worker aggregate")) + } + ret, err = wAgg.toWorker(ctx) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("error converting worker aggregate to worker")) } return nil }, ) if err != nil { - return nil, db.NoRowsAffected, err + return nil, err } - return controllers, int(rowsUpdated), nil + return ret, nil } // setWorkerTags removes all existing tags from the same source and worker id @@ -249,6 +301,12 @@ func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Op return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") case worker.ScopeId != scope.Global.String(): return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("scope id must be %q", scope.Global.String())) + case worker.WorkerReportedAddress != "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "worker reported address is not empty") + case worker.WorkerReportedName != "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "worker reported name is not empty") + case worker.LastStatusTime != nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "last status time is not nil") } opts := getOpts(opt...) diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 0d55c72e3..04e8f4f3e 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -3,10 +3,13 @@ package servers_test import ( "context" "fmt" + "strings" "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" @@ -16,6 +19,7 @@ import ( "github.com/hashicorp/go-dbw" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" ) func TestDeleteWorker(t *testing.T) { @@ -94,6 +98,72 @@ func TestDeleteWorker(t *testing.T) { } } +func TestLookupWorkerByWorkerReportedName(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + ctx := context.Background() + + w := servers.TestWorker(t, conn, wrapper) + t.Run("success", func(t *testing.T) { + got, err := repo.LookupWorkerByWorkerReportedName(ctx, w.GetWorkerReportedName()) + require.NoError(t, err) + assert.Empty(t, cmp.Diff(w.Worker, got.Worker, protocmp.Transform())) + }) + t.Run("not found", func(t *testing.T) { + got, err := repo.LookupWorkerByWorkerReportedName(ctx, "unknown_name") + require.NoError(t, err) + assert.Nil(t, got) + }) + t.Run("db error", func(t *testing.T) { + conn, mock := db.TestSetupWithMock(t) + rw := db.New(conn) + mock.ExpectQuery(`SELECT`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "lookup-error")) + r, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + got, err := r.LookupWorkerByWorkerReportedName(ctx, w.GetWorkerReportedName()) + assert.NoError(t, mock.ExpectationsWereMet()) + assert.Truef(t, errors.Match(errors.T(errors.Op("servers.(Repository).LookupWorkerByWorkerReportedName")), err), "got error %v", err) + assert.Nil(t, got) + }) +} + +func TestLookupWorker(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + repo, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + ctx := context.Background() + + w := servers.TestWorker(t, conn, wrapper) + t.Run("success", func(t *testing.T) { + got, err := repo.LookupWorker(ctx, w.GetPublicId()) + require.NoError(t, err) + assert.Empty(t, cmp.Diff(w.Worker, got.Worker, protocmp.Transform())) + }) + t.Run("not found", func(t *testing.T) { + got, err := repo.LookupWorker(ctx, "w_unknownid") + require.NoError(t, err) + assert.Nil(t, got) + }) + t.Run("db error", func(t *testing.T) { + conn, mock := db.TestSetupWithMock(t) + rw := db.New(conn) + mock.ExpectQuery(`SELECT`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "lookup-error")) + r, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + got, err := r.LookupWorker(ctx, w.GetPublicId()) + assert.NoError(t, mock.ExpectationsWereMet()) + assert.Truef(t, errors.Match(errors.T(errors.Op("servers.(Repository).LookupWorker")), err), "got error %v", err) + assert.Nil(t, got) + }) +} + func TestUpsertWorkerStatus(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) @@ -103,47 +173,15 @@ func TestUpsertWorkerStatus(t *testing.T) { require.NoError(t, err) ctx := context.Background() - wStatus1 := servers.NewWorkerStatus("test_worker_1", - servers.WithAddress("address"), - servers.WithName("config_name1")) - _, _, err = repo.UpsertWorkerStatus(ctx, wStatus1) + wStatus1 := servers.NewWorkerForStatus(scope.Global.String(), + servers.WithAddress("address"), servers.WithName("config_name1")) + worker, err := repo.UpsertWorkerStatus(ctx, wStatus1) require.NoError(t, err) { - workers, err := repo.ListWorkers(ctx) - require.NoError(t, err) - assert.Len(t, workers, 1) - worker := workers[0] - assert.Equal(t, wStatus1.GetWorkerId(), worker.GetPublicId()) - assert.Equal(t, wStatus1.Address, worker.CanonicalAddress()) + assert.True(t, strings.HasPrefix(worker.GetPublicId(), "w_")) + assert.Equal(t, wStatus1.GetWorkerReportedAddress(), worker.CanonicalAddress()) assert.Empty(t, worker.Name) - assert.Equal(t, worker.ReportedStatus.CreateTime, worker.CreateTime) - assert.Equal(t, worker.LastConnectionUpdate(), worker.UpdateTime) - assert.Equal(t, uint32(1), worker.Version) - assert.Empty(t, worker.Address) - assert.Empty(t, worker.Description) - } - - // Remove the status name, verify that it doesn't remove the resource's name. - updatedStatus1 := servers.NewWorkerStatus(wStatus1.WorkerId, - servers.WithAddress(wStatus1.Address)) - _, _, err = repo.UpsertWorkerStatus(ctx, updatedStatus1) - require.NoError(t, err) - { - workers, err := repo.ListWorkers(ctx) - require.NoError(t, err) - assert.Len(t, workers, 1) - worker := workers[0] - // The resource's name remains untouched - assert.Empty(t, worker.Name) - // The ReportedStatus's name is correctly cleared - assert.Empty(t, worker.ReportedStatus.Name) - // The last connection update time was increased - assert.Greater(t, worker.LastConnectionUpdate().AsTime().UnixMilli(), worker.UpdateTime.AsTime().UnixMilli()) - assert.Equal(t, worker.UpdateTime, worker.CreateTime) - - assert.Equal(t, updatedStatus1.GetWorkerId(), worker.GetPublicId()) - assert.Equal(t, updatedStatus1.Address, worker.CanonicalAddress()) - assert.Equal(t, worker.ReportedStatus.CreateTime, worker.CreateTime) + assert.Equal(t, worker.GetLastStatusTime(), worker.UpdateTime) assert.Equal(t, uint32(1), worker.Version) assert.Empty(t, worker.Address) assert.Empty(t, worker.Description) @@ -151,12 +189,14 @@ func TestUpsertWorkerStatus(t *testing.T) { failureCases := []struct { name string - status *servers.WorkerStatus + repo *servers.Repository + status *servers.Worker errAssert func(*testing.T, error) }{ { name: "no address", - status: servers.NewWorkerStatus("worker_with_no_address", + repo: repo, + status: servers.NewWorkerForStatus(scope.Global.String(), servers.WithName("worker_with_no_address")), errAssert: func(t *testing.T, err error) { t.Helper() @@ -164,20 +204,25 @@ func TestUpsertWorkerStatus(t *testing.T) { }, }, { - name: "invalid id", - status: servers.NewWorkerStatus("short", - servers.WithAddress("someaddress"), - servers.WithName("worker_with_short_id")), + name: "cant specifying public id", + repo: repo, + status: func() *servers.Worker { + w := servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName("worker_with_no_address"), + servers.WithAddress("workeraddress")) + w.PublicId = "w_specified" + return w + }(), errAssert: func(t *testing.T, err error) { t.Helper() - assert.True(t, errors.IsCheckConstraintError(err)) + assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) }, }, { - name: "no id", - status: servers.NewWorkerStatus("", - servers.WithAddress("anotheraddress"), - servers.WithName("no_address")), + name: "no name", + repo: repo, + status: servers.NewWorkerForStatus(scope.Global.String(), + servers.WithAddress("no_name_address")), errAssert: func(t *testing.T, err error) { t.Helper() assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) @@ -185,16 +230,63 @@ func TestUpsertWorkerStatus(t *testing.T) { }, { name: "no status", + repo: repo, status: nil, errAssert: func(t *testing.T, err error) { t.Helper() assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) }, }, + { + name: "empty scope", + repo: repo, + status: servers.NewWorkerForStatus("", + servers.WithAddress("address"), + servers.WithName("config_name1")), + errAssert: func(t *testing.T, err error) { + t.Helper() + assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) + }, + }, + { + name: "providing api tags", + repo: repo, + status: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String(), + servers.WithWorkerTags(&servers.Tag{Key: "test", Value: "test"})) + w.WorkerReportedAddress = "some_address" + w.WorkerReportedName = "providing api tags" + return w + }(), + errAssert: func(t *testing.T, err error) { + t.Helper() + assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) + }, + }, + { + name: "database failure", + repo: func() *servers.Repository { + conn, mock := db.TestSetupWithMock(t) + rw := db.New(conn) + mock.ExpectBegin() + mock.ExpectQuery(`INSERT`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "create-error")) + mock.ExpectRollback() + r, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + return r + }(), + status: servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName("database failure"), + servers.WithAddress("address")), + errAssert: func(t *testing.T, err error) { + t.Helper() + assert.Error(t, err) + }, + }, } for _, tc := range failureCases { t.Run(fmt.Sprintf("Failures %s", tc.name), func(t *testing.T) { - _, _, err = repo.UpsertWorkerStatus(ctx, tc.status) + _, err = tc.repo.UpsertWorkerStatus(ctx, tc.status) assert.Error(t, err) tc.errAssert(t, err) @@ -206,9 +298,10 @@ func TestUpsertWorkerStatus(t *testing.T) { } { - anotherStatus := servers.NewWorkerStatus("another_test_worker", + anotherStatus := servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName("another_test_worker"), servers.WithAddress("address")) - _, _, err = repo.UpsertWorkerStatus(ctx, anotherStatus) + _, err = repo.UpsertWorkerStatus(ctx, anotherStatus) require.NoError(t, err) { workers, err := repo.ListWorkers(ctx) @@ -228,7 +321,9 @@ func TestTagUpdatingListing(t *testing.T) { require.NoError(err) ctx := context.Background() - wStatus := servers.NewWorkerStatus("test_worker_1", + worker1 := servers.TestWorker(t, conn, wrapper) + wStatus := servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName(worker1.GetWorkerReportedName()), servers.WithAddress("127.0.0.1"), servers.WithWorkerTags( &servers.Tag{ @@ -240,53 +335,16 @@ func TestTagUpdatingListing(t *testing.T) { Value: "value2", })) - _, _, err = repo.UpsertWorkerStatus(ctx, wStatus, servers.WithUpdateTags(true)) + worker1, err = repo.UpsertWorkerStatus(ctx, wStatus, + servers.WithUpdateTags(true)) require.NoError(err) - - wStatus = servers.NewWorkerStatus("test_worker_2", - servers.WithAddress("127.0.0.1"), - servers.WithWorkerTags( - &servers.Tag{ - Key: "tag2", - Value: "value1", - }, - &servers.Tag{ - Key: "tag2", - Value: "value2", - })) - _, _, err = repo.UpsertWorkerStatus(ctx, wStatus, servers.WithUpdateTags(true)) - require.NoError(err) - - tags, err := repo.ListTagsForWorkers(ctx, []string{"test_worker_1", "test_worker_2"}) - require.NoError(err) - - // Base case - exp := map[string][]*servers.Tag{ - "test_worker_1": { - { - Key: "tag1", - Value: "value1", - }, { - Key: "tag1", - Value: "value2", - }, - }, - "test_worker_2": { - { - Key: "tag2", - Value: "value1", - }, - { - Key: "tag2", - Value: "value2", - }, - }, - } - require.Equal(exp, tags) + assert.Len(t, worker1.CanonicalTags(), 1) + assert.ElementsMatch(t, []string{"value1", "value2"}, worker1.CanonicalTags()["tag1"]) // Update without saying to update tags - wStatus = servers.NewWorkerStatus("test_worker_2", - servers.WithAddress("192.168.1.1"), + wStatus = servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName(worker1.GetWorkerReportedName()), + servers.WithAddress(worker1.GetWorkerReportedAddress()), servers.WithWorkerTags( &servers.Tag{ Key: "tag22", @@ -296,30 +354,16 @@ func TestTagUpdatingListing(t *testing.T) { Key: "tag22", Value: "value22", })) - _, _, err = repo.UpsertWorkerStatus(ctx, wStatus) + worker1, err = repo.UpsertWorkerStatus(ctx, wStatus) require.NoError(err) - tags, err = repo.ListTagsForWorkers(ctx, []string{"test_worker_1", "test_worker_2"}) - require.NoError(err) - require.Equal(exp, tags) + assert.Len(t, worker1.CanonicalTags(), 1) + assert.ElementsMatch(t, []string{"value1", "value2"}, worker1.CanonicalTags()["tag1"]) // Update tags and test again - _, _, err = repo.UpsertWorkerStatus(ctx, wStatus, servers.WithUpdateTags(true)) + worker1, err = repo.UpsertWorkerStatus(ctx, wStatus, servers.WithUpdateTags(true)) require.NoError(err) - tags, err = repo.ListTagsForWorkers(ctx, []string{"test_worker_1", "test_worker_2"}) - require.NoError(err) - require.NotEqual(exp, tags) - // Update and try again - exp["test_worker_2"] = []*servers.Tag{ - { - Key: "tag22", - Value: "value21", - }, - { - Key: "tag22", - Value: "value22", - }, - } - require.Equal(exp, tags) + assert.Len(t, worker1.CanonicalTags(), 1) + assert.ElementsMatch(t, []string{"value21", "value22"}, worker1.CanonicalTags()["tag22"]) } func TestListWorkersWithLiveness(t *testing.T) { @@ -333,28 +377,19 @@ func TestListWorkersWithLiveness(t *testing.T) { require.NoError(err) ctx := context.Background() - newWorkerStatus := func(publicId string) *servers.WorkerStatus { - result := servers.NewWorkerStatus(publicId, - servers.WithAddress("127.0.0.1")) - _, rowsUpdated, err := serversRepo.UpsertWorkerStatus(ctx, result) - require.NoError(err) - require.Equal(1, rowsUpdated) - - return result - } - - workConf1 := newWorkerStatus("test_worker_1") - workConf2 := newWorkerStatus("test_worker_2") - workConf3 := newWorkerStatus("test_worker_3") + worker1 := servers.TestWorker(t, conn, wrapper) + worker2 := servers.TestWorker(t, conn, wrapper) + worker3 := servers.TestWorker(t, conn, wrapper) // Sleep the default liveness time (15sec currently) +1s time.Sleep(time.Second * 16) // Push an upsert to the first worker so that its status has been // updated. - _, rowsUpdated, err := serversRepo.UpsertWorkerStatus(ctx, workConf1) + _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName(worker1.GetWorkerReportedName()), + servers.WithAddress(worker1.GetWorkerReportedAddress()))) require.NoError(err) - require.Equal(1, rowsUpdated) requireIds := func(expected []string, actual []*servers.Worker) { require.Len(expected, len(actual)) @@ -375,25 +410,26 @@ func TestListWorkersWithLiveness(t *testing.T) { result, err := serversRepo.ListWorkers(ctx) require.NoError(err) require.Len(result, 1) - requireIds([]string{workConf1.WorkerId}, result) + requireIds([]string{worker1.GetPublicId()}, result) // Upsert second server. - _, rowsUpdated, err = serversRepo.UpsertWorkerStatus(ctx, workConf2) + _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName(worker2.GetWorkerReportedName()), + servers.WithAddress(worker1.GetWorkerReportedAddress()))) require.NoError(err) - require.Equal(1, rowsUpdated) // Static liveness. Should get two, so long as this did not take // more than 5s to execute. result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(time.Second*5)) require.NoError(err) require.Len(result, 2) - requireIds([]string{workConf1.WorkerId, workConf2.WorkerId}, result) + requireIds([]string{worker1.GetPublicId(), worker2.GetPublicId()}, result) // Liveness disabled, should get all three workers. result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(-1)) require.NoError(err) require.Len(result, 3) - requireIds([]string{workConf1.WorkerId, workConf2.WorkerId, workConf3.WorkerId}, result) + requireIds([]string{worker1.GetPublicId(), worker2.GetPublicId(), worker3.GetPublicId()}, result) } func TestRepository_CreateWorker(t *testing.T) { @@ -500,6 +536,42 @@ func TestRepository_CreateWorker(t *testing.T) { wantErr: true, wantErrContains: "unable to create worker", }, + { + name: "no worker reported address", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + w.WorkerReportedAddress = "foo" + return w + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "worker reported address is not empty", + }, + { + name: "no worker reported name", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + w.WorkerReportedName = "foo" + return w + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "worker reported name is not empty", + }, + { + name: "no last status update", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + w.LastStatusTime = timestamp.Now() + return w + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "last status time is not nil", + }, { name: "success", setup: func() *servers.Worker { @@ -538,9 +610,6 @@ func TestRepository_CreateWorker(t *testing.T) { } err = rw.LookupByPublicId(testCtx, found) require.NoError(err) - if found.Tags == nil { - found.Tags = []*servers.Tag{} - } assert.Equal(got, found) }) } diff --git a/internal/servers/store/worker.pb.go b/internal/servers/store/worker.pb.go index fc7a0b554..c92cc11fc 100644 --- a/internal/servers/store/worker.pb.go +++ b/internal/servers/store/worker.pb.go @@ -51,6 +51,16 @@ type Worker struct { // version allows optimistic locking of the resource. // @inject_tag: `gorm:"default:null"` Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` + // Name of the resource (optional) + // @inject_tag: `gorm:"default:null"` + WorkerReportedName string `protobuf:"bytes,90,opt,name=worker_reported_name,json=workerReportedName,proto3" json:"worker_reported_name,omitempty" gorm:"default:null"` + // Address for the worker. This must be set when the worker has received + // at least 1 status update fromt he worker daemon. + // @inject_tag: `gorm:"default:null"` + WorkerReportedAddress string `protobuf:"bytes,100,opt,name=worker_reported_address,json=workerReportedAddress,proto3" json:"worker_reported_address,omitempty" gorm:"default:null"` + // The last_status_time is the last time a worker daemon sent a status update. + // @inject_tag: `gorm:"default:null"` + LastStatusTime *timestamp.Timestamp `protobuf:"bytes,110,opt,name=last_status_time,json=lastStatusTime,proto3" json:"last_status_time,omitempty" gorm:"default:null"` } func (x *Worker) Reset() { @@ -141,6 +151,27 @@ func (x *Worker) GetVersion() uint32 { return 0 } +func (x *Worker) GetWorkerReportedName() string { + if x != nil { + return x.WorkerReportedName + } + return "" +} + +func (x *Worker) GetWorkerReportedAddress() string { + if x != nil { + return x.WorkerReportedAddress + } + return "" +} + +func (x *Worker) GetLastStatusTime() *timestamp.Timestamp { + if x != nil { + return x.LastStatusTime + } + return nil +} + // WorkerTag is a tag for a worker. The primary key is comprised of the // worker_id, key, value, and source. type WorkerTag struct { @@ -222,97 +253,6 @@ func (x *WorkerTag) GetSource() string { return "" } -// WorkerStatus contains the fields that the worker reports to the controller -// in its status updates. -type WorkerStatus struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // worker_id is the public key that key of the worker this tag is for. - // @inject_tag: `gorm:"primary_key"` - WorkerId string `protobuf:"bytes,10,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty" gorm:"primary_key"` - // The create_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - CreateTime *timestamp.Timestamp `protobuf:"bytes,20,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` - // The update_time is set by the database. - // @inject_tag: `gorm:"default:current_timestamp"` - UpdateTime *timestamp.Timestamp `protobuf:"bytes,30,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` - // Name of the resource (optional) - // @inject_tag: `gorm:"default:null"` - Name string `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` - // Address for the worker. This must be set. - // @inject_tag: `gorm:"default:null"` - Address string `protobuf:"bytes,50,opt,name=address,proto3" json:"address,omitempty" gorm:"default:null"` -} - -func (x *WorkerStatus) Reset() { - *x = WorkerStatus{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_servers_store_v1_worker_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WorkerStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WorkerStatus) ProtoMessage() {} - -func (x *WorkerStatus) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_servers_store_v1_worker_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WorkerStatus.ProtoReflect.Descriptor instead. -func (*WorkerStatus) Descriptor() ([]byte, []int) { - return file_controller_storage_servers_store_v1_worker_proto_rawDescGZIP(), []int{2} -} - -func (x *WorkerStatus) GetWorkerId() string { - if x != nil { - return x.WorkerId - } - return "" -} - -func (x *WorkerStatus) GetCreateTime() *timestamp.Timestamp { - if x != nil { - return x.CreateTime - } - return nil -} - -func (x *WorkerStatus) GetUpdateTime() *timestamp.Timestamp { - if x != nil { - return x.UpdateTime - } - return nil -} - -func (x *WorkerStatus) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *WorkerStatus) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - var File_controller_storage_servers_store_v1_worker_proto protoreflect.FileDescriptor var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ @@ -324,7 +264,7 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc4, 0x02, 0x0a, 0x06, 0x57, 0x6f, 0x72, + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x84, 0x04, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, @@ -344,34 +284,30 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, + 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x30, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x36, 0x0a, 0x17, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x64, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x54, 0x0a, 0x10, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x6e, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xf3, 0x01, 0x0a, 0x0c, 0x57, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, - 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, - 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -386,23 +322,21 @@ func file_controller_storage_servers_store_v1_worker_proto_rawDescGZIP() []byte return file_controller_storage_servers_store_v1_worker_proto_rawDescData } -var file_controller_storage_servers_store_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_controller_storage_servers_store_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_controller_storage_servers_store_v1_worker_proto_goTypes = []interface{}{ (*Worker)(nil), // 0: controller.storage.servers.store.v1.Worker (*WorkerTag)(nil), // 1: controller.storage.servers.store.v1.WorkerTag - (*WorkerStatus)(nil), // 2: controller.storage.servers.store.v1.WorkerStatus - (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp + (*timestamp.Timestamp)(nil), // 2: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_servers_store_v1_worker_proto_depIdxs = []int32{ - 3, // 0: controller.storage.servers.store.v1.Worker.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 1: controller.storage.servers.store.v1.Worker.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 2: controller.storage.servers.store.v1.WorkerStatus.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 3: controller.storage.servers.store.v1.WorkerStatus.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 2, // 0: controller.storage.servers.store.v1.Worker.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // 1: controller.storage.servers.store.v1.Worker.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // 2: controller.storage.servers.store.v1.Worker.last_status_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_controller_storage_servers_store_v1_worker_proto_init() } @@ -435,18 +369,6 @@ func file_controller_storage_servers_store_v1_worker_proto_init() { return nil } } - file_controller_storage_servers_store_v1_worker_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkerStatus); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } type x struct{} out := protoimpl.TypeBuilder{ @@ -454,7 +376,7 @@ func file_controller_storage_servers_store_v1_worker_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_servers_store_v1_worker_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/servers/testing.go b/internal/servers/testing.go index 3a4ec7d28..b027d7876 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -3,6 +3,7 @@ package servers import ( "context" "math/rand" + "strings" "testing" "time" @@ -89,16 +90,13 @@ func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *Worker { id, err := newWorkerId(context.Background()) require.NoError(t, err) - name := "test-worker-" + id - wStatus := NewWorkerStatus(id, + name := "test-worker-" + strings.ToLower(id) + wrk := NewWorkerForStatus(scope.Global.String(), WithName(name), WithAddress("127.0.0.1")) - _, _, err = serversRepo.UpsertWorkerStatus(context.Background(), wStatus) + wrk, err = serversRepo.UpsertWorkerStatus(context.Background(), wrk) require.NoError(t, err) - - wrk := NewWorker(scope.Global.String(), WithPublicId(id)) - require.NoError(t, rw.LookupById(context.Background(), wrk)) - wrk.ReportedStatus = wStatus + require.NotNil(t, wrk) return wrk } diff --git a/internal/servers/testing_test.go b/internal/servers/testing_test.go index 0764adf3e..4525dc678 100644 --- a/internal/servers/testing_test.go +++ b/internal/servers/testing_test.go @@ -18,7 +18,8 @@ func TestTestWorker(t *testing.T) { assert.NotNil(t, tWorker) assert.True(t, strings.HasPrefix(tWorker.GetPublicId(), WorkerPrefix)) - lkpWorker := NewWorker(scope.Global.String(), WithPublicId(tWorker.GetPublicId())) + lkpWorker := NewWorker(scope.Global.String()) + lkpWorker.PublicId = tWorker.GetPublicId() rw := db.New(conn) require.NoError(t, rw.LookupById(context.Background(), lkpWorker)) assert.NotNil(t, lkpWorker) diff --git a/internal/servers/worker.go b/internal/servers/worker.go index bfa218d4c..f7978fb0a 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -16,23 +16,39 @@ import ( type Worker struct { *store.Worker - ReportedStatus *WorkerStatus `gorm:"-"` - Tags []*Tag `gorm:"-"` + apiTags []*Tag `gorm:"-"` + configTags []*Tag `gorm:"-"` } // NewWorker returns a new Worker. Valid options are WithName, WithDescription -// WithAddress, and WithWorkerTags. All other options are ignored. +// 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...) return &Worker{ Worker: &store.Worker{ ScopeId: scopeId, - PublicId: opts.withPublicId, Name: opts.withName, Description: opts.withDescription, Address: opts.withAddress, }, - Tags: opts.withWorkerTags, + apiTags: opts.withWorkerTags, + } +} + +// NewWorkerForStatus returns a new Worker usable for status updates. +// Valid options are WithName, WithAddress, and WithWorkerTags, all of which +// are assigned to the worker reported variations of these fields. +// All other options are ignored. +func NewWorkerForStatus(scopeId string, opt ...Option) *Worker { + opts := getOpts(opt...) + return &Worker{ + Worker: &store.Worker{ + ScopeId: scopeId, + WorkerReportedName: opts.withName, + WorkerReportedAddress: opts.withAddress, + }, + configTags: opts.withWorkerTags, } } @@ -45,17 +61,23 @@ func (w *Worker) clone() *Worker { if w == nil { return nil } - tags := make([]*Tag, 0, len(w.Tags)) - for _, t := range w.Tags { - tags = append(tags, &Tag{Key: t.Key, Value: t.Value}) - } cw := proto.Clone(w.Worker) - crs := w.ReportedStatus.clone() - return &Worker{ - Worker: cw.(*store.Worker), - Tags: tags, - ReportedStatus: crs, + cWorker := &Worker{ + Worker: cw.(*store.Worker), } + if w.apiTags != nil { + cWorker.apiTags = make([]*Tag, 0, len(w.apiTags)) + for _, t := range w.apiTags { + cWorker.apiTags = append(cWorker.apiTags, &Tag{Key: t.Key, Value: t.Value}) + } + } + if w.configTags != nil { + cWorker.configTags = make([]*Tag, 0, len(w.configTags)) + for _, t := range w.configTags { + cWorker.configTags = append(cWorker.configTags, &Tag{Key: t.Key, Value: t.Value}) + } + } + return cWorker } // CanonicalAddress returns the actual address boundary believes should be used @@ -64,27 +86,21 @@ func (w *Worker) clone() *Worker { // in its connection status updates. If neither is available, an empty string // is returned. func (w *Worker) CanonicalAddress() string { - switch { - case w.Address != "": + if w.GetAddress() != "" { return w.GetAddress() - case w.ReportedStatus != nil: - return w.ReportedStatus.GetAddress() - default: - return "" } + return w.GetWorkerReportedAddress() } // 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. func (w *Worker) CanonicalTags() map[string][]string { dedupedTags := make(map[Tag]struct{}) - for _, t := range w.Tags { + for _, t := range w.apiTags { dedupedTags[*t] = struct{}{} } - if w.ReportedStatus != nil { - for _, t := range w.ReportedStatus.Tags { - dedupedTags[*t] = struct{}{} - } + for _, t := range w.configTags { + dedupedTags[*t] = struct{}{} } tags := make(map[string][]string) for t := range dedupedTags { @@ -93,14 +109,30 @@ func (w *Worker) CanonicalTags() map[string][]string { return tags } -// LastConnectionUpdate contains the last time the worker has reported to the +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 +} + +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 +} + +// 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) LastConnectionUpdate() *timestamp.Timestamp { - if w.ReportedStatus == nil { +func (w *Worker) GetLastStatusTime() *timestamp.Timestamp { + if w == nil || w.Worker == nil || w.Worker.GetLastStatusTime().AsTime() == timestamp.NegativeInfinityTS { return nil } - return w.ReportedStatus.GetUpdateTime() + return w.Worker.GetLastStatusTime() } // TableName overrides the table name used by Worker to `server_worker` @@ -111,61 +143,50 @@ func (Worker) TableName() string { // workerAggregate contains an aggregated view of the values associated with // a single worker. type workerAggregate struct { - PublicId string `gorm:"primary_key"` - ScopeId string - Name string - Description string - CreateTime *timestamp.Timestamp - UpdateTime *timestamp.Timestamp - Address string - Version uint32 - ApiTags string - WorkerStatusName string - WorkerStatusAddress string - WorkerStatusCreateTime *timestamp.Timestamp - WorkerStatusUpdateTime *timestamp.Timestamp - WorkerConfigTags string + PublicId string `gorm:"primary_key"` + ScopeId string + Name string + Description string + CreateTime *timestamp.Timestamp + UpdateTime *timestamp.Timestamp + Address string + Version uint32 + ApiTags string + WorkerReportedName string + WorkerReportedAddress string + LastStatusTime *timestamp.Timestamp + WorkerConfigTags string } func (a *workerAggregate) toWorker(ctx context.Context) (*Worker, error) { const op = "servers.(workerAggregate).toWorker" - workerOptions := []Option{ - WithPublicId(a.PublicId), - WithName(a.Name), - WithDescription(a.Description), - WithAddress(a.Address), + worker := &Worker{ + Worker: &store.Worker{ + PublicId: a.PublicId, + Name: a.Name, + Description: a.Description, + Address: a.Address, + CreateTime: a.CreateTime, + UpdateTime: a.UpdateTime, + ScopeId: a.ScopeId, + Version: a.Version, + WorkerReportedAddress: a.WorkerReportedAddress, + WorkerReportedName: a.WorkerReportedName, + LastStatusTime: a.LastStatusTime, + }, } tags, err := tagsFromAggregatedTagString(ctx, a.ApiTags) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error parsing config tag string")) } - if len(tags) > 0 { - workerOptions = append(workerOptions, WithWorkerTags(tags...)) - } - worker := NewWorker(a.ScopeId, workerOptions...) - worker.CreateTime = a.CreateTime - worker.UpdateTime = a.UpdateTime - worker.Version = a.Version + worker.apiTags = tags - if a.WorkerStatusCreateTime == nil { - return worker, nil - } - statusOptions := []Option{ - WithName(a.WorkerStatusName), - WithAddress(a.WorkerStatusAddress), - } tags, err = tagsFromAggregatedTagString(ctx, a.WorkerConfigTags) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error parsing config tag string")) } - if len(tags) > 0 { - statusOptions = append(statusOptions, WithWorkerTags(tags...)) - } - cfg := NewWorkerStatus(a.PublicId, statusOptions...) - cfg.CreateTime = a.WorkerStatusCreateTime - cfg.UpdateTime = a.WorkerStatusUpdateTime + worker.configTags = tags - worker.ReportedStatus = cfg return worker, nil } diff --git a/internal/servers/worker_status.go b/internal/servers/worker_status.go deleted file mode 100644 index c8918618e..000000000 --- a/internal/servers/worker_status.go +++ /dev/null @@ -1,50 +0,0 @@ -package servers - -import ( - "github.com/hashicorp/boundary/internal/servers/store" - "google.golang.org/protobuf/proto" -) - -// A WorkerStatus is a container for the fields that a worker reports to the -// controller about itself in its status updates. While this may include a -// Name, this is different from the Worker's name in that the Worker's name is -// the name of the Worker resource in boundary where the name in the -// WorkerStatus is the name the worker has reported about itself. -type WorkerStatus struct { - *store.WorkerStatus - Tags []*Tag `gorm:"-"` -} - -// NewWorkerStatus returns a new WorkerStatus. Valid options are WithName, -// WithAddress, and WithWorkerTags. All other options are ignored. -func NewWorkerStatus(workerId string, opt ...Option) *WorkerStatus { - opts := getOpts(opt...) - return &WorkerStatus{ - WorkerStatus: &store.WorkerStatus{ - WorkerId: workerId, - Name: opts.withName, - Address: opts.withAddress, - }, - Tags: opts.withWorkerTags, - } -} - -func (w *WorkerStatus) clone() *WorkerStatus { - if w == nil { - return nil - } - tags := make([]*Tag, 0, len(w.Tags)) - for _, t := range w.Tags { - tags = append(tags, &Tag{Key: t.Key, Value: t.Value}) - } - cw := proto.Clone(w.WorkerStatus) - return &WorkerStatus{ - WorkerStatus: cw.(*store.WorkerStatus), - Tags: tags, - } -} - -// TableName overrides the table name used by WorkerStatus to `server_worker_status` -func (WorkerStatus) TableName() string { - return "server_worker_status" -} diff --git a/internal/servers/worker_test.go b/internal/servers/worker_test.go index d01eeeca2..2830aba62 100644 --- a/internal/servers/worker_test.go +++ b/internal/servers/worker_test.go @@ -2,6 +2,7 @@ package servers import ( "context" + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -15,8 +16,7 @@ import ( ) func TestWorkerCanonicalAddress(t *testing.T) { - worker := NewWorker(scope.Global.String()) - worker.ReportedStatus = NewWorkerStatus("foo", WithAddress("status")) + worker := NewWorkerForStatus(scope.Global.String(), WithAddress("status")) assert.Equal(t, "status", worker.CanonicalAddress()) worker.Address = "worker" assert.Equal(t, "worker", worker.CanonicalAddress()) @@ -29,12 +29,11 @@ func TestWorkerCanonicalTags(t *testing.T) { &Tag{Key: "key", Value: "shared"}, &Tag{Key: "key2", Value: "apis key2 unique"}, )) - w.ReportedStatus = NewWorkerStatus("", - WithWorkerTags( - &Tag{Key: "key", Value: "configs unique"}, - &Tag{Key: "key", Value: "shared"}, - &Tag{Key: "key3", Value: "configs key3 unique"}, - )) + w.configTags = []*Tag{ + {Key: "key", Value: "configs unique"}, + {Key: "key", Value: "shared"}, + {Key: "key3", Value: "configs key3 unique"}, + } got := w.CanonicalTags() assert.Len(t, got, 3, "2 keys expected, 'key' and 'key2'") @@ -61,11 +60,11 @@ func TestWorkerAggregate(t *testing.T) { { id, err := newWorkerId(ctx) require.NoError(t, err) - require.NoError(t, rw.Create(ctx, - NewWorker(scope.Global.String(), - WithPublicId(id), - WithName(id), - WithAddress("address")))) + w := NewWorker(scope.Global.String(), + WithName(id), + WithAddress("address")) + w.PublicId = id + require.NoError(t, rw.Create(ctx, w)) got := getAggWorker(id) assert.Equal(t, id, got.GetPublicId()) @@ -73,7 +72,7 @@ func TestWorkerAggregate(t *testing.T) { assert.Equal(t, id, got.GetName()) assert.Equal(t, "address", got.GetAddress()) assert.Equal(t, uint32(1), got.GetVersion()) - assert.Nil(t, got.ReportedStatus) + assert.Nil(t, got.GetLastStatusTime()) assert.Empty(t, got.CanonicalTags()) } @@ -81,32 +80,29 @@ func TestWorkerAggregate(t *testing.T) { { id, err := newWorkerId(ctx) require.NoError(t, err) - require.NoError(t, rw.Create(ctx, - NewWorker(scope.Global.String(), WithPublicId(id)))) - require.NoError(t, rw.Create(ctx, - NewWorkerStatus(id, - WithAddress("address"), - WithName(id)))) + w := NewWorkerForStatus(scope.Global.String(), + WithAddress("address"), + WithName(strings.ToLower(id))) + w.PublicId = id + require.NoError(t, rw.Create(ctx, w)) got := getAggWorker(id) assert.Equal(t, id, got.GetPublicId()) assert.Equal(t, uint32(1), got.GetVersion()) - assert.NotNil(t, got.ReportedStatus) - assert.Equal(t, id, got.ReportedStatus.GetWorkerId()) - assert.Equal(t, id, got.ReportedStatus.GetName()) - assert.NotNil(t, got.ReportedStatus.CreateTime) - assert.NotNil(t, got.ReportedStatus.UpdateTime) - assert.Equal(t, "address", got.ReportedStatus.GetAddress()) + assert.NotNil(t, got.GetLastStatusTime()) + assert.Equal(t, strings.ToLower(id), got.GetWorkerReportedName()) + assert.Equal(t, "address", got.GetWorkerReportedAddress()) } // Worker with a config tag { id, err := newWorkerId(ctx) require.NoError(t, err) - require.NoError(t, rw.Create(ctx, - NewWorker(scope.Global.String(), WithPublicId(id)))) - require.NoError(t, rw.Create(ctx, - NewWorkerStatus(id, WithAddress("address")))) + ws := NewWorkerForStatus(scope.Global.String(), + WithAddress("address"), + WithName(strings.ToLower(id))) + ws.PublicId = id + require.NoError(t, rw.Create(ctx, ws)) require.NoError(t, rw.Create(ctx, &store.WorkerTag{ WorkerId: id, @@ -118,19 +114,20 @@ func TestWorkerAggregate(t *testing.T) { got := getAggWorker(id) assert.Equal(t, id, got.GetPublicId()) assert.Equal(t, uint32(1), got.GetVersion()) - assert.NotNil(t, got.ReportedStatus) - assert.Empty(t, got.Tags) - assert.Equal(t, got.ReportedStatus.Tags, []*Tag{{Key: "key", Value: "val"}}) + assert.NotNil(t, got.GetLastStatusTime()) + assert.Empty(t, got.apiTags) + assert.Equal(t, got.configTags, []*Tag{{Key: "key", Value: "val"}}) } // Worker with many config tag { id, err := newWorkerId(ctx) require.NoError(t, err) - require.NoError(t, rw.Create(ctx, - NewWorker(scope.Global.String(), WithPublicId(id)))) - require.NoError(t, rw.Create(ctx, - NewWorkerStatus(id, WithAddress("address")))) + ws := NewWorkerForStatus(scope.Global.String(), + WithAddress("address"), + WithName(strings.ToLower(id))) + ws.PublicId = id + require.NoError(t, rw.Create(ctx, ws)) require.NoError(t, rw.Create(ctx, &store.WorkerTag{ WorkerId: id, Key: "key", @@ -151,9 +148,9 @@ func TestWorkerAggregate(t *testing.T) { })) got := getAggWorker(id) - require.NotNil(t, got.ReportedStatus) - assert.Empty(t, got.Tags) - assert.ElementsMatch(t, got.ReportedStatus.Tags, []*Tag{ + require.NotNil(t, got.GetLastStatusTime()) + assert.Empty(t, got.apiTags) + assert.ElementsMatch(t, got.configTags, []*Tag{ {Key: "key", Value: "val"}, {Key: "key", Value: "val2"}, {Key: "key2", Value: "val2"}, @@ -164,10 +161,11 @@ func TestWorkerAggregate(t *testing.T) { { id, err := newWorkerId(ctx) require.NoError(t, err) - require.NoError(t, rw.Create(ctx, - NewWorker(scope.Global.String(), WithPublicId(id)))) - require.NoError(t, rw.Create(ctx, - NewWorkerStatus(id, WithAddress("address")))) + ws := NewWorkerForStatus(scope.Global.String(), + WithAddress("address"), + WithName(strings.ToLower(id))) + ws.PublicId = id + require.NoError(t, rw.Create(ctx, ws)) require.NoError(t, rw.Create(ctx, &store.WorkerTag{ WorkerId: id, @@ -179,19 +177,20 @@ func TestWorkerAggregate(t *testing.T) { got := getAggWorker(id) assert.Equal(t, id, got.GetPublicId()) assert.Equal(t, uint32(1), got.GetVersion()) - assert.NotNil(t, got.ReportedStatus) - assert.Empty(t, got.ReportedStatus.Tags) - assert.Equal(t, got.Tags, []*Tag{{Key: "key", Value: "val"}}) + assert.NotNil(t, got.GetLastStatusTime()) + assert.Empty(t, got.GetConfigTags()) + assert.Equal(t, got.apiTags, []*Tag{{Key: "key", Value: "val"}}) } // Worker with mix of tag sources { id, err := newWorkerId(ctx) require.NoError(t, err) - require.NoError(t, rw.Create(ctx, - NewWorker(scope.Global.String(), WithPublicId(id)))) - require.NoError(t, rw.Create(ctx, - NewWorkerStatus(id, WithAddress("address")))) + ws := NewWorkerForStatus(scope.Global.String(), + WithAddress("address"), + WithName(strings.ToLower(id))) + ws.PublicId = id + require.NoError(t, rw.Create(ctx, ws)) require.NoError(t, rw.Create(ctx, &store.WorkerTag{ WorkerId: id, Key: "key", @@ -212,17 +211,455 @@ func TestWorkerAggregate(t *testing.T) { })) got := getAggWorker(id) - require.NotNil(t, got.ReportedStatus) - assert.ElementsMatch(t, got.Tags, []*Tag{ + require.NotNil(t, got.GetLastStatusTime()) + assert.ElementsMatch(t, got.apiTags, []*Tag{ {Key: "key", Value: "val2"}, {Key: "key2", Value: "val2"}, }) - assert.ElementsMatch(t, got.ReportedStatus.Tags, []*Tag{ + assert.ElementsMatch(t, got.configTags, []*Tag{ {Key: "key", Value: "val"}, }) } } +func TestWorker_Update(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + ctx := context.Background() + + newId := func() string { + id, err := newWorkerId(ctx) + require.NoError(t, err) + return id + } + + cases := []struct { + name string + initial Worker + update Worker + mask []string + nullMask []string + assert func(t *testing.T, init, up *Worker) + wantUpdateError bool + }{ + { + name: "base update with base", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Description: "base update with base", + }, + }, + update: Worker{ + Worker: &store.Worker{ + Description: "base update with base updated", + }, + }, + mask: []string{"Description"}, + assert: func(t *testing.T, init, up *Worker) { + t.Helper() + assert.Equal(t, "base update with base updated", up.Description) + assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) + assert.Equal(t, uint32(2), up.Version) + assert.Nil(t, up.GetLastStatusTime()) + }, + }, + { + name: "base update with worker reported address and name", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Description: "base update with status", + }, + }, + update: Worker{ + Worker: &store.Worker{ + WorkerReportedName: "base update with worker reported address and name", + WorkerReportedAddress: "base update with worker reported address and name", + }, + }, + mask: []string{"WorkerReportedAddress", "WorkerReportedName"}, + assert: func(t *testing.T, init, up *Worker) { + t.Helper() + assert.Equal(t, "base update with worker reported address and name", up.WorkerReportedAddress) + assert.Equal(t, "base update with worker reported address and name", up.WorkerReportedName) + assert.Equal(t, uint32(1), up.Version) + assert.NotNil(t, up.GetLastStatusTime()) + assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) + assert.Equal(t, up.GetLastStatusTime().AsTime(), up.GetUpdateTime().AsTime()) + }, + }, + { + name: "base update with worker reported address", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Description: "base update with status", + }, + }, + update: Worker{ + Worker: &store.Worker{ + WorkerReportedAddress: "base update with worker reported address", + }, + }, + mask: []string{"WorkerReportedAddress"}, + wantUpdateError: true, + }, + { + // If any status fields are set then worker reported address must + // be set. + name: "base update with worker reported name", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Description: "base update with status", + }, + }, + update: Worker{ + Worker: &store.Worker{ + WorkerReportedName: "base update with worker reported name", + }, + }, + mask: []string{"WorkerReportedName"}, + wantUpdateError: true, + }, + { + name: "base update with worker reported fields", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Description: "base update with status", + }, + }, + update: Worker{ + Worker: &store.Worker{ + WorkerReportedName: "base update with worker reported fields", + WorkerReportedAddress: "base update with worker reported fields", + }, + }, + mask: []string{"WorkerReportedName", "WorkerReportedAddress"}, + assert: func(t *testing.T, init, up *Worker) { + t.Helper() + assert.Equal(t, "base update with worker reported fields", up.WorkerReportedAddress) + assert.Equal(t, "base update with worker reported fields", up.WorkerReportedName) + assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) + assert.Equal(t, uint32(1), up.Version) + assert.NotNil(t, up.GetLastStatusTime()) + assert.Equal(t, up.GetLastStatusTime().AsTime(), up.GetUpdateTime().AsTime()) + }, + }, + { + name: "worker reported update with base", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + WorkerReportedAddress: "worker reported update with base", + WorkerReportedName: "worker reported update with base", + }, + }, + update: Worker{ + Worker: &store.Worker{ + Description: "worker reported update with base", + }, + }, + mask: []string{"Description"}, + assert: func(t *testing.T, init, up *Worker) { + t.Helper() + assert.Equal(t, "worker reported update with base", up.Description) + assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) + assert.Equal(t, uint32(2), up.Version) + assert.NotNil(t, up.GetLastStatusTime()) + assert.Equal(t, init.GetLastStatusTime(), up.GetLastStatusTime()) + }, + }, + { + name: "worker reported update name to unprintable", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + WorkerReportedAddress: "worker reported update name to unprintable", + WorkerReportedName: "worker reported update name to unprintable", + }, + }, + update: Worker{ + Worker: &store.Worker{ + WorkerReportedName: "unprintable \u0008 name", + }, + }, + mask: []string{"WorkerReportedName"}, + wantUpdateError: true, + }, + { + name: "worker reported clearing address", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + WorkerReportedAddress: "worker reported clearing address", + WorkerReportedName: "worker reported clearing address", + }, + }, + update: Worker{ + Worker: &store.Worker{}, + }, + nullMask: []string{"WorkerReportedAddress"}, + wantUpdateError: true, + }, + { + name: "worker reported updating worker reported", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + WorkerReportedAddress: "start", + WorkerReportedName: "start", + }, + }, + update: Worker{ + Worker: &store.Worker{ + WorkerReportedAddress: "worker reported updating worker reported", + }, + }, + mask: []string{"WorkerReportedAddress"}, + assert: func(t *testing.T, init, up *Worker) { + t.Helper() + assert.Equal(t, "worker reported updating worker reported", up.GetWorkerReportedAddress()) + assert.NotNil(t, up.GetLastStatusTime()) + assert.Greater(t, up.GetLastStatusTime().AsTime(), init.GetLastStatusTime().AsTime()) + // We don't modify the worker version while operating only on the worker fields + assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) + assert.Equal(t, uint32(1), up.Version) + }, + }, + { + name: "worker reported clearing worker reported name", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + WorkerReportedAddress: "worker reported clearing worker reported name", + WorkerReportedName: "worker reported clearing worker reported name", + }, + }, + update: Worker{ + Worker: &store.Worker{}, + }, + nullMask: []string{"WorkerReportedName"}, + wantUpdateError: true, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + init := tc.initial.clone() + require.NoError(t, rw.Create(ctx, init)) + up := tc.update.clone() + up.PublicId = init.PublicId + _, err := rw.Update(ctx, up, tc.mask, tc.nullMask) + if tc.wantUpdateError { + assert.Error(t, err) + return + } else { + require.NoError(t, err) + } + tc.assert(t, init.clone(), up.clone()) + }) + } +} + +func TestWorker_Create(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + ctx := context.Background() + + newId := func() string { + id, err := newWorkerId(ctx) + require.NoError(t, err) + return id + } + + type wanted struct { + lastStatusUpdated bool + createError bool + } + + cases := []struct { + name string + in Worker + want wanted + }{ + { + name: "base", + in: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + }, + }, + want: wanted{}, + }, + { + name: "with non status fields", + in: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "with non status fields", + Description: "with non status fields", + Address: "address", + }, + }, + want: wanted{}, + }, + { + name: "with upper case worker reported name", + in: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + WorkerReportedName: "With Upper Case Worker Reported Name", + WorkerReportedAddress: "address", + }, + }, + want: wanted{createError: true}, + }, + { + name: "with unprintable worker reported name", + in: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + WorkerReportedName: "unprintable \u0008 worker reported name", + WorkerReportedAddress: "address", + }, + }, + want: wanted{createError: true}, + }, + { + name: "with status fields", + in: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + WorkerReportedName: "with non status fields", + WorkerReportedAddress: "with non status fields", + }, + }, + want: wanted{ + lastStatusUpdated: true, + }, + }, + { + name: "with worker reported address", + in: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + WorkerReportedAddress: "with worker reported address", + }, + }, + want: wanted{ + createError: true, + }, + }, + { + // The worker reported address is a required field if any of the + // worker reported fields are set. + name: "with worker reported name", + in: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + WorkerReportedName: "with worker reported name", + }, + }, + want: wanted{ + createError: true, + }, + }, + { + name: "non status fields with worker reported fields", + in: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "non status fields with worker reported fields", + Description: "non status fields with worker reported fields", + Address: "address", + WorkerReportedAddress: "non status fields with worker reported fields", + WorkerReportedName: "non status fields with worker reported fields", + }, + }, + want: wanted{ + lastStatusUpdated: true, + }, + }, + { + // The worker reported address is a required field if any of the + // worker reported fields are set. + name: "non status fields with worker reported name", + in: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "non status fields with worker reported name", + Description: "non status fields with worker reported name", + Address: "address", + WorkerReportedName: "non status fields with worker reported name", + }, + }, + want: wanted{ + createError: true, + }, + }, + { + name: "non status fields with worker reported address", + in: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "non status fields with worker reported address", + Description: "non status fields with worker reported address", + Address: "address", + WorkerReportedAddress: "non status fields with worker reported address", + }, + }, + want: wanted{ + createError: true, + }, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + w := tc.in.clone() + err := rw.Create(ctx, w) + if tc.want.createError { + assert.Error(t, err) + return + } else { + assert.NoError(t, err) + } + + if tc.want.lastStatusUpdated { + assert.NotNil(t, w.GetLastStatusTime()) + } else { + assert.Nil(t, w.GetLastStatusTime()) + } + + assert.NotNil(t, w.GetCreateTime()) + assert.Equal(t, w.GetCreateTime(), w.GetUpdateTime()) + }) + } +} + func TestWorker_New(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") @@ -285,21 +722,6 @@ func TestWorker_New(t *testing.T) { }, wantCreateErr: true, }, - { - name: "with public id", - args: args{ - scopeId: scope.Global.String(), - opts: []Option{ - WithPublicId("w_test_public_id"), - }, - }, - want: &Worker{ - Worker: &store.Worker{ - PublicId: "w_test_public_id", - ScopeId: scope.Global.String(), - }, - }, - }, { name: "with name", args: args{ @@ -360,7 +782,7 @@ func TestWorker_New(t *testing.T) { Worker: &store.Worker{ ScopeId: scope.Global.String(), }, - Tags: []*Tag{ + apiTags: []*Tag{ { Key: "key", Value: "val", diff --git a/internal/session/query.go b/internal/session/query.go index ae68dcece..3e7a3e3b1 100644 --- a/internal/session/query.go +++ b/internal/session/query.go @@ -327,13 +327,10 @@ where dead_workers (worker_id, last_update_time) as ( select w.public_id as worker_id, - ws.update_time as last_update_time + w.last_status_time as last_update_time from server_worker w - left join server_worker_status ws - on w.public_id = ws.worker_id where - ws.update_time is null - or ws.update_time < wt_sub_seconds_from_now(@grace_period_seconds) + w.last_status_time < wt_sub_seconds_from_now(@grace_period_seconds) ), closed_connections (connection_id, worker_id) as ( update session_connection @@ -343,7 +340,7 @@ where returning public_id, worker_id ) select closed_connections.worker_id, - coalesce(dead_workers.last_update_time, current_timestamp) as last_update_time, + dead_workers.last_update_time as last_update_time, count(closed_connections.connection_id) as number_connections_closed from closed_connections join dead_workers diff --git a/internal/session/service_worker_status_report_test.go b/internal/session/service_worker_status_report_test.go index e524a7458..706c2db56 100644 --- a/internal/session/service_worker_status_report_test.go +++ b/internal/session/service_worker_status_report_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -31,8 +32,7 @@ func TestWorkerStatusReport(t *testing.T) { PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorkerStatus(ctx, servers.NewWorkerStatus( - "test_worker1", + serverRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), servers.WithAddress("127.0.0.1"))) repo, err := session.NewRepository(rw, rw, kms) diff --git a/internal/session/session_cleanup_job_test.go b/internal/session/session_cleanup_job_test.go index d7bb5887f..b261506dc 100644 --- a/internal/session/session_cleanup_job_test.go +++ b/internal/session/session_cleanup_job_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -108,9 +109,10 @@ func TestSessionConnectionCleanupJob(t *testing.T) { // Push an upsert to the first worker so that its status has been // updated. - _, rowsUpdated, err := serversRepo.UpsertWorkerStatus(ctx, worker1.ReportedStatus) + _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName(worker1.GetWorkerReportedName()), + servers.WithAddress(worker1.GetWorkerReportedAddress()))) require.NoError(err) - require.Equal(1, rowsUpdated) // Run the job. require.NoError(job.Run(ctx)) @@ -168,61 +170,6 @@ func TestSessionConnectionCleanupJobNewJobErr(t *testing.T) { require.Nil(job) } -func TestCloseConnections_ManuallyDeletedConfig(t *testing.T) { - t.Parallel() - ctx := context.Background() - require := require.New(t) - conn, _ := db.TestSetup(t, "postgres") - rw := db.New(conn) - wrapper := db.TestWrapper(t) - iamRepo := iam.TestRepo(t, conn, wrapper) - kms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, kms) - require.NoError(err) - connRepo, err := NewConnectionRepository(ctx, rw, rw, kms) - require.NoError(err) - serversRepo, err := servers.NewRepository(rw, rw, kms) - require.NoError(err) - - gracePeriod := 1 * time.Second - job, err := newSessionConnectionCleanupJob(rw, deadWorkerConnCloseMinGrace) - require.NoError(err) - - worker := servers.TestWorker(t, conn, wrapper) - sess := TestDefaultSession(t, conn, wrapper, iamRepo, WithDbOpts(db.WithSkipVetForWrite(true))) - sess, _, err = repo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, []byte("foo")) - require.NoError(err) - c, cs, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), worker.GetPublicId()) - require.NoError(err) - require.Len(cs, 1) - require.Equal(StatusAuthorized, cs[0].Status) - - _, cs, err = connRepo.ConnectConnection(ctx, ConnectWith{ - ConnectionId: c.GetPublicId(), - ClientTcpAddress: "127.0.0.1", - ClientTcpPort: 22, - EndpointTcpAddress: "127.0.0.1", - EndpointTcpPort: 22, - UserClientIp: "127.0.0.1", - }) - - // The single worker's connections aren't cleaned up - _, _, err = serversRepo.UpsertWorkerStatus(ctx, worker.ReportedStatus) - assert.NoError(t, err) - result, err := job.closeConnectionsForDeadWorkers(ctx, gracePeriod) - require.NoError(err) - require.Empty(result) - - // the manually deleted status causes the connections to be cleaned - _, _, err = serversRepo.UpsertWorkerStatus(ctx, worker.ReportedStatus) - assert.NoError(t, err) - _, err = rw.Delete(ctx, worker.ReportedStatus) - assert.NoError(t, err) - result, err = job.closeConnectionsForDeadWorkers(ctx, gracePeriod) - require.NoError(err) - require.Len(result, 1) -} - func TestCloseConnectionsForDeadWorkers(t *testing.T) { t.Parallel() ctx := context.Background() @@ -345,21 +292,13 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // the most up-to-date fields. updateServer := func(t *testing.T, w *servers.Worker) *servers.Worker { t.Helper() - _, rowsUpdated, err := serversRepo.UpsertWorkerStatus(ctx, w.ReportedStatus) + // The worker provided name will derive the WorkerId every time + // UpsertWorkerStatus is called. Setting the Worker Id on the caller + // side is an error. + w.PublicId = "" + wkr, err := serversRepo.UpsertWorkerStatus(ctx, w) require.NoError(err) - require.Equal(1, rowsUpdated) - servers, err := serversRepo.ListWorkers(ctx) - require.NoError(err) - for _, server := range servers { - if server.PublicId == w.PublicId { - return server - } - } - - require.FailNowf("server %q not found after updating", w.PublicId) - // Looks weird but needed to build, as we fail in testify instead - // of returning an error - return nil + return wkr } // requireConnectionStatus is a helper expecting all connections on a worker @@ -454,7 +393,7 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { require.Equal([]closeConnectionsForDeadWorkersResult{ { WorkerId: worker1.PublicId, - LastUpdateTime: timestampPbAsUTC(t, worker1.LastConnectionUpdate().AsTime()), + LastUpdateTime: timestampPbAsUTC(t, worker1.GetLastStatusTime().AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, }, result) @@ -478,12 +417,12 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { require.ElementsMatch([]closeConnectionsForDeadWorkersResult{ { WorkerId: worker2.PublicId, - LastUpdateTime: timestampPbAsUTC(t, worker2.LastConnectionUpdate().AsTime()), + LastUpdateTime: timestampPbAsUTC(t, worker2.GetLastStatusTime().AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, { WorkerId: worker3.PublicId, - LastUpdateTime: timestampPbAsUTC(t, worker3.LastConnectionUpdate().AsTime()), + LastUpdateTime: timestampPbAsUTC(t, worker3.GetLastStatusTime().AsTime()), NumberConnectionsClosed: 12, // 18 per server, with 6 closed already }, }, result) diff --git a/testing/dbtest/session_list_benchmarks_dump_generation_test.go b/testing/dbtest/session_list_benchmarks_dump_generation_test.go index d09986b9e..daccd5945 100644 --- a/testing/dbtest/session_list_benchmarks_dump_generation_test.go +++ b/testing/dbtest/session_list_benchmarks_dump_generation_test.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/testing/dbtest" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" @@ -131,8 +132,7 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) { require.NoError(err) serversRepo, err := servers.NewRepository(rw, rw, kms) require.NoError(err) - _, _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerStatus( - "test_worker_1", + _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), servers.WithAddress("127.0.0.1"))) require.NoError(err) From 587c6372c25a4129e51decda33fa7e0d817888ad Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 1 Jun 2022 14:00:24 -0700 Subject: [PATCH 032/100] Add worker update to server repo (#2117) --- internal/servers/repository_worker.go | 86 +++++++ internal/servers/repository_worker_test.go | 265 +++++++++++++++++++++ 2 files changed, 351 insertions(+) diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index f32fa5a65..18b70c94a 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -3,8 +3,10 @@ package servers import ( "context" "fmt" + "strings" "github.com/hashicorp/boundary/internal/db" + dbcommon "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/types/scope" @@ -284,6 +286,90 @@ func setWorkerTags(ctx context.Context, w db.Writer, id string, ts TagSource, ta return nil } +// UpdateWorker will update a worker in the repository and return the resulting +// worker. fieldMaskPaths provides field_mask.proto paths for fields that should +// be updated. Fields will be set to NULL if the field is a zero value and +// included in fieldMask. Name, Description, and Address are the only updatable +// fields, if no updatable fields are included in the fieldMaskPaths, then an +// error is returned. If any paths besides those listed above are included in +// the path then an error is returned. +func (r *Repository) UpdateWorker(ctx context.Context, worker *Worker, version uint32, fieldMaskPaths []string, opt ...Option) (*Worker, int, error) { + const ( + nameField = "name" + descField = "description" + addressField = "address" + ) + const op = "servers.(Repository).UpdateWorker" + switch { + case worker == nil: + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "worker is nil") + case worker.PublicId == "": + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing public id") + case version == 0: + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "version is zero") + } + + for _, f := range fieldMaskPaths { + switch { + case strings.EqualFold(nameField, f): + case strings.EqualFold(descField, f): + case strings.EqualFold(addressField, f): + default: + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) + } + } + + var dbMask, nullFields []string + dbMask, nullFields = dbcommon.BuildUpdatePaths( + map[string]interface{}{ + nameField: worker.Name, + descField: worker.Description, + addressField: worker.Address, + }, + fieldMaskPaths, + nil, + ) + if len(dbMask) == 0 && len(nullFields) == 0 { + return nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "no fields to update") + } + + var rowsUpdated int + var ret *Worker + var err error + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(reader db.Reader, w db.Writer) error { + worker := worker.clone() + rowsUpdated, err = w.Update(ctx, worker, dbMask, nullFields, db.WithVersion(&version)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rowsUpdated > 1 { + // return err, which will result in a rollback of the update + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated") + } + + wAgg := &workerAggregate{PublicId: worker.GetPublicId()} + if err := reader.LookupById(ctx, wAgg); err != nil { + return errors.Wrap(ctx, err, op) + } + if ret, err = wAgg.toWorker(ctx); err != nil { + return err + } + return nil + }, + ) + if err != nil { + if errors.IsUniqueError(err) { + return nil, db.NoRowsAffected, errors.New(ctx, errors.NotUnique, op, fmt.Sprintf("worker with name %q already exists", worker.Name)) + } + return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for %s", worker.GetPublicId()))) + } + return ret, rowsUpdated, nil +} + // CreateWorker will create a worker in the repository and return the written // worker. Creating a worker is not intentionally oplogged. A worker's // ReportedStatus and Tags are intentionally ignored when creating a worker (not diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 04e8f4f3e..351f28b80 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -614,3 +614,268 @@ func TestRepository_CreateWorker(t *testing.T) { }) } } + +func TestRepository_UpdateWorker(t *testing.T) { + t.Parallel() + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + + repo, err := servers.NewRepository(rw, rw, kmsCache) + require.NoError(t, err) + + setCases := []struct { + name string + modifyWorker func(*testing.T, *servers.Worker) + path []string + assertGot func(*testing.T, *servers.Worker) + wantErr bool + }{ + { + name: "update address", + modifyWorker: func(t *testing.T, w *servers.Worker) { + t.Helper() + w.Address = "foo" + }, + path: []string{"address"}, + assertGot: func(t *testing.T, w *servers.Worker) { + t.Helper() + assert.Equal(t, "foo", w.GetAddress()) + assert.Equal(t, uint32(2), w.GetVersion()) + assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) + assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) + }, + }, + { + name: "update name", + modifyWorker: func(t *testing.T, w *servers.Worker) { + t.Helper() + w.Name = "foo" + }, + path: []string{"Name"}, + assertGot: func(t *testing.T, w *servers.Worker) { + t.Helper() + assert.Equal(t, "foo", w.GetName()) + assert.Equal(t, uint32(2), w.GetVersion()) + assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) + assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) + }, + }, + { + name: "update description", + modifyWorker: func(t *testing.T, w *servers.Worker) { + t.Helper() + w.Description = "foo" + }, + path: []string{"Description"}, + assertGot: func(t *testing.T, w *servers.Worker) { + t.Helper() + assert.Equal(t, "foo", w.GetDescription()) + assert.Equal(t, uint32(2), w.GetVersion()) + assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) + assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) + }, + }, + { + name: "update worker reported name", + modifyWorker: func(t *testing.T, w *servers.Worker) { + t.Helper() + w.WorkerReportedName = "foo" + }, + path: []string{"WorkerReportedName"}, + wantErr: true, + }, + { + name: "update worker reported name and name", + modifyWorker: func(t *testing.T, w *servers.Worker) { + t.Helper() + w.WorkerReportedName = "foo" + w.Name = "another" + }, + path: []string{"WorkerReportedName", "Name"}, + wantErr: true, + }, + { + name: "Clear worker reported name", + modifyWorker: func(t *testing.T, w *servers.Worker) {}, + path: []string{"WorkerReportedName"}, + wantErr: true, + }, + } + for _, tt := range setCases { + t.Run(tt.name, func(t *testing.T) { + wkr := servers.TestWorker(t, conn, wrapper) + tt.modifyWorker(t, wkr) + got, _, err := repo.UpdateWorker(ctx, wkr, 1, tt.path) + if tt.wantErr { + assert.Error(t, err) + return + } else { + require.NoError(t, err) + } + tt.assertGot(t, got) + }) + } + + // Clear cases + clearCases := []struct { + name string + modifyWorker func(*testing.T, *servers.Worker) + path []string + assertGot func(*testing.T, *servers.Worker) + wantErr bool + }{ + { + name: "clear address", + modifyWorker: func(t *testing.T, w *servers.Worker) { + t.Helper() + w.Address = "" + }, + path: []string{"address"}, + assertGot: func(t *testing.T, w *servers.Worker) { + t.Helper() + assert.Empty(t, w.GetAddress()) + assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) + assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) + }, + }, + { + name: "clear name", + modifyWorker: func(t *testing.T, w *servers.Worker) { + t.Helper() + w.Name = "" + }, + path: []string{"Name"}, + assertGot: func(t *testing.T, w *servers.Worker) { + t.Helper() + assert.Empty(t, w.GetName()) + assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) + assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) + }, + }, + { + name: "clear description", + modifyWorker: func(t *testing.T, w *servers.Worker) { + t.Helper() + w.Description = "" + }, + path: []string{"Description"}, + assertGot: func(t *testing.T, w *servers.Worker) { + t.Helper() + assert.Empty(t, w.GetDescription()) + assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) + assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) + }, + }, + } + for _, tt := range clearCases { + t.Run(tt.name, func(t *testing.T) { + wkr := servers.TestWorker(t, conn, wrapper) + wkr.Name = tt.name + wkr.Description = tt.name + wkr.Address = tt.name + wkr, _, err := repo.UpdateWorker(ctx, wkr, 1, []string{"name", "description", "address"}) + require.NoError(t, err) + + tt.modifyWorker(t, wkr) + got, _, err := repo.UpdateWorker(ctx, wkr, 2, tt.path) + if tt.wantErr { + assert.Error(t, err) + return + } else { + require.NoError(t, err) + } + tt.assertGot(t, got) + }) + } + + t.Run("version is wrong", func(t *testing.T) { + wkr := servers.TestWorker(t, conn, wrapper) + wkr.Address = "version is wrong" + result, numUpdated, err := repo.UpdateWorker(ctx, wkr, 2, []string{"address"}) + require.NoError(t, err) + assert.Zero(t, numUpdated) + assert.Equal(t, wkr.GetUpdateTime().AsTime(), result.GetUpdateTime().AsTime()) + }) + + errorCases := []struct { + name string + input *servers.Worker + path []string + version uint32 + wantErr *errors.Template + }{ + { + name: "nil worker", + path: []string{"name"}, + version: 1, + wantErr: errors.T(errors.InvalidParameter), + }, + { + name: "empty path", + input: servers.TestWorker(t, conn, wrapper), + version: 1, + wantErr: errors.T(errors.EmptyFieldMask), + }, + { + name: "0 version", + input: servers.TestWorker(t, conn, wrapper), + path: []string{"name"}, + version: 0, + wantErr: errors.T(errors.InvalidParameter), + }, + { + name: "no public id", + input: func() *servers.Worker { + w := servers.TestWorker(t, conn, wrapper) + w.PublicId = "" + return w + }(), + path: []string{"name"}, + version: 0, + wantErr: errors.T(errors.InvalidParameter), + }, + { + name: "unrecognized path", + input: servers.TestWorker(t, conn, wrapper), + path: []string{"UnrecognizedField"}, + version: 1, + wantErr: errors.T(errors.InvalidFieldMask), + }, + { + name: "not found worker", + input: func() *servers.Worker { + w := servers.TestWorker(t, conn, wrapper) + w.PublicId = "w_notfoundworker" + return w + }(), + path: []string{"name"}, + version: 1, + wantErr: errors.T(errors.RecordNotFound), + }, + { + name: "duplicate name", + input: func() *servers.Worker { + w1 := servers.TestWorker(t, conn, wrapper) + w1.Name = "somenamethatijustmadeup" + w1, _, err := repo.UpdateWorker(ctx, w1, w1.Version, []string{"name"}, nil) + require.NoError(t, err) + w2 := servers.TestWorker(t, conn, wrapper) + w2.Name = w1.Name + return w2 + }(), + path: []string{"name"}, + version: 1, + wantErr: errors.T("worker with name \"somenamethatijustmadeup\" already exists"), + }, + } + for _, tt := range errorCases { + t.Run(tt.name, func(t *testing.T) { + _, updated, err := repo.UpdateWorker(ctx, tt.input, tt.version, tt.path) + assert.Equal(t, 0, updated) + assert.Truef(t, errors.Match(tt.wantErr, err), "Didn't match error %v", err) + }) + } +} From 4716c1a788ee976669734d4bdd9476e8459a3037 Mon Sep 17 00:00:00 2001 From: Todd Date: Thu, 2 Jun 2022 09:28:34 -0700 Subject: [PATCH 033/100] Update List Workers Repo function to allow limits and provide scopes (#2121) * Update List Workers Repo function to allow limits and provide scopes Co-authored-by: Irena Rindos Co-authored-by: Michael Gaffney --- .../handlers/targets/target_service.go | 2 +- internal/servers/repository.go | 17 +++- internal/servers/repository_worker.go | 46 ++++++----- internal/servers/repository_worker_test.go | 82 +++++++++++++++++-- 4 files changed, 117 insertions(+), 30 deletions(-) diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index 25dfea85a..efa23f89e 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -895,7 +895,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession // worker IDs below is used to contain their IDs in the same order. This is // used to fetch tags for filtering. But we avoid allocation unless we // actually need it. - selectedWorkers, err := serversRepo.ListWorkers(ctx) + selectedWorkers, err := serversRepo.ListWorkers(ctx, []string{scope.Global.String()}) if err != nil { return nil, err } diff --git a/internal/servers/repository.go b/internal/servers/repository.go index d482364d4..0fbcf292c 100644 --- a/internal/servers/repository.go +++ b/internal/servers/repository.go @@ -26,11 +26,13 @@ type Repository struct { reader db.Reader writer db.Writer kms *kms.Kms + // defaultLimit provides a default for limiting the number of results returned from the repo + defaultLimit int } // NewRepository creates a new servers Repository. Supports the options: WithLimit // which sets a default limit on results returned by repo operations. -func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms) (*Repository, error) { +func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) { const op = "servers.NewRepository" if r == nil { return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil reader") @@ -41,10 +43,17 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms) (*Repository, error) if kms == nil { return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil kms") } + + opts := getOpts(opt...) + if opts.withLimit == 0 { + // zero signals the boundary defaults should be used. + opts.withLimit = db.DefaultLimit + } return &Repository{ - reader: r, - writer: w, - kms: kms, + reader: r, + writer: w, + kms: kms, + defaultLimit: opts.withLimit, }, nil } diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index 18b70c94a..787521afe 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -114,39 +114,47 @@ func lookupWorker(ctx context.Context, reader db.Reader, id string) (*Worker, er return w, nil } -// ListWorkers is a passthrough to listWorkersWithReader that uses the repo's normal reader. -func (r *Repository) ListWorkers(ctx context.Context, opt ...Option) ([]*Worker, error) { - return r.listWorkersWithReader(ctx, r.reader, opt...) -} - -// listWorkersWithReader will return a listing of resources and honor the -// WithLimit option. If WithLiveness is zero the default liveness value is used, -// if it is negative then the last status update time is ignored. This method -// accepts a reader, allowing it to be used within a transaction or without. -func (r *Repository) listWorkersWithReader(ctx context.Context, reader db.Reader, opt ...Option) ([]*Worker, error) { - const op = "workers.listWorkersWithReader" +// ListWorkers will return a listing of Workers and honor the WithLimit option. +// If WithLiveness is zero the default liveness value is used, if it is negative +// then the last status update time is ignored. +// If WithLimit < 0, then unlimited results are returned. If WithLimit == 0, then +// default limits are used for results. +func (r *Repository) ListWorkers(ctx context.Context, scopeIds []string, opt ...Option) ([]*Worker, error) { + const op = "servers.(Repository).ListWorkers" switch { - case isNil(reader): - return nil, errors.New(ctx, errors.InvalidParameter, op, "reader is nil") + case len(scopeIds) == 0: + return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope ids set") } + opts := getOpts(opt...) liveness := opts.withLiveness if liveness == 0 { liveness = DefaultLiveness } - var where string + var where []string + var whereArgs []interface{} if liveness > 0 { - where = fmt.Sprintf("last_status_time > now() - interval '%d seconds'", uint32(liveness.Seconds())) + where = append(where, fmt.Sprintf("last_status_time > now() - interval '%d seconds'", uint32(liveness.Seconds()))) + } + if len(scopeIds) > 0 { + where = append(where, "scope_id in (?)") + whereArgs = append(whereArgs, scopeIds) + } + + limit := r.defaultLimit + if opts.withLimit != 0 { + // non-zero signals an override of the default limit for the repo. + limit = opts.withLimit } var wAggs []*workerAggregate - if err := reader.SearchWhere( + if err := r.reader.SearchWhere( ctx, &wAggs, - where, - []interface{}{}, - db.WithLimit(opts.withLimit), + strings.Join(where, " and "), + whereArgs, + db.WithLimit(limit), ); err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error searching for workers")) } diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 351f28b80..5096f786f 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -291,7 +291,7 @@ func TestUpsertWorkerStatus(t *testing.T) { tc.errAssert(t, err) // Still only the original worker exists. - workers, err := repo.ListWorkers(ctx) + workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()}) require.NoError(t, err) assert.Len(t, workers, 1) }) @@ -304,7 +304,7 @@ func TestUpsertWorkerStatus(t *testing.T) { _, err = repo.UpsertWorkerStatus(ctx, anotherStatus) require.NoError(t, err) { - workers, err := repo.ListWorkers(ctx) + workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()}) require.NoError(t, err) assert.Len(t, workers, 2) } @@ -366,7 +366,77 @@ func TestTagUpdatingListing(t *testing.T) { assert.ElementsMatch(t, []string{"value21", "value22"}, worker1.CanonicalTags()["tag22"]) } -func TestListWorkersWithLiveness(t *testing.T) { +func TestListWorkers(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + const testLimit = 10 + repo, err := servers.NewRepository(rw, rw, kms, servers.WithLimit(testLimit)) + require.NoError(t, err) + ctx := context.Background() + + tests := []struct { + name string + createCnt int + reqScopes []string + opts []servers.Option + wantCnt int + wantErr bool + }{ + { + name: "no-limit", + createCnt: testLimit + 1, + reqScopes: []string{scope.Global.String()}, + opts: []servers.Option{servers.WithLimit(-1)}, + wantCnt: testLimit + 1, + wantErr: false, + }, + { + name: "default-limit", + createCnt: testLimit + 1, + reqScopes: []string{scope.Global.String()}, + wantCnt: testLimit, + wantErr: false, + }, + { + name: "custom-limit", + createCnt: testLimit + 1, + reqScopes: []string{scope.Global.String()}, + opts: []servers.Option{servers.WithLimit(3)}, + wantCnt: 3, + wantErr: false, + }, + { + name: "no-scope", + createCnt: 1, + reqScopes: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := servers.NewWorker(scope.Global.String()) + db.TestDeleteWhere(t, conn, w, "true") + for i := 0; i < tt.createCnt; i++ { + servers.TestWorker(t, conn, wrapper) + } + // the purpose of these tests isn't to check liveness, so disable + // liveness checking. + opts := append(tt.opts, servers.WithLiveness(-1)) + got, err := repo.ListWorkers(ctx, tt.reqScopes, opts...) + if tt.wantErr { + assert.Error(t, err) + return + } + require.NoError(t, err) + assert.Len(t, got, tt.wantCnt) + }) + } +} + +func TestListWorkers_WithLiveness(t *testing.T) { t.Parallel() require := require.New(t) conn, _ := db.TestSetup(t, "postgres") @@ -407,7 +477,7 @@ func TestListWorkersWithLiveness(t *testing.T) { } // Default liveness, should only list 1 - result, err := serversRepo.ListWorkers(ctx) + result, err := serversRepo.ListWorkers(ctx, []string{scope.Global.String()}) require.NoError(err) require.Len(result, 1) requireIds([]string{worker1.GetPublicId()}, result) @@ -420,13 +490,13 @@ func TestListWorkersWithLiveness(t *testing.T) { // Static liveness. Should get two, so long as this did not take // more than 5s to execute. - result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(time.Second*5)) + result, err = serversRepo.ListWorkers(ctx, []string{scope.Global.String()}, servers.WithLiveness(time.Second*5)) require.NoError(err) require.Len(result, 2) requireIds([]string{worker1.GetPublicId(), worker2.GetPublicId()}, result) // Liveness disabled, should get all three workers. - result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(-1)) + result, err = serversRepo.ListWorkers(ctx, []string{scope.Global.String()}, servers.WithLiveness(-1)) require.NoError(err) require.Len(result, 3) requireIds([]string{worker1.GetPublicId(), worker2.GetPublicId(), worker3.GetPublicId()}, result) From 1ee2c0dde01657e06c7f71fdb71124f4529068dd Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 2 Jun 2022 13:20:37 -0400 Subject: [PATCH 034/100] Switch nodeenrollment to staging branch --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c1d4fd75b..36d495fed 100644 --- a/go.mod +++ b/go.mod @@ -92,7 +92,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b - github.com/hashicorp/nodeenrollment v0.0.0-20220601055910-5718dde51fae + github.com/hashicorp/nodeenrollment v0.0.0-20220602171424-99579ebf597b ) require ( diff --git a/go.sum b/go.sum index a1d9084db..e49b0dcd9 100644 --- a/go.sum +++ b/go.sum @@ -740,8 +740,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220601055910-5718dde51fae h1:A8JdiMkZuic8905EgiTDw9cKOb6v1BSrfDMNlS9oejo= -github.com/hashicorp/nodeenrollment v0.0.0-20220601055910-5718dde51fae/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.0.0-20220602171424-99579ebf597b h1:viKFHuNiBbmK31L53FDQ7azuMU8ytiPyEQPRXLaLqwg= +github.com/hashicorp/nodeenrollment v0.0.0-20220602171424-99579ebf597b/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= From 642e41c9d74c249664ab92b254027082ee6f0f38 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Fri, 20 May 2022 14:36:00 -0400 Subject: [PATCH 035/100] feat(workerAuth): Implement nodeenrollment lib workerauth storage --- .../99/03_worker_authentication.up.sql | 10 +- .../servers/store/v1/worker_auth.proto | 6 +- internal/servers/query.go | 16 +- internal/servers/repository_workerauth.go | 755 ++++++++++++++++++ .../servers/repository_workerauth_test.go | 236 ++++++ internal/servers/store/worker_auth.pb.go | 42 +- internal/servers/testing.go | 2 +- internal/servers/worker.go | 20 + internal/servers/worker_auth.go | 12 +- internal/servers/workerauth_store_test.go | 8 +- 10 files changed, 1066 insertions(+), 41 deletions(-) create mode 100644 internal/servers/repository_workerauth.go create mode 100644 internal/servers/repository_workerauth_test.go diff --git a/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql b/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql index b2aca4b53..33d71f06e 100644 --- a/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql @@ -44,7 +44,7 @@ create trigger worker_auth_ca_update_time_column insert into worker_auth_ca(private_id) values('roots'); create trigger update_version_column after update on worker_auth_ca - for each row execute procedure update_version_column(); + for each row execute procedure update_version_column(private_id); comment on table worker_auth_ca is 'worker_auth_ca is a one-row versioned table used for locking for certificate rotation on the worker_auth_ca_certificate table.'; @@ -110,7 +110,7 @@ create table worker_auth_authorized( references kms_data_key_version (private_id) on delete restrict on update cascade, - nonce bytea + nonce bytea -- encrypted value ); create trigger @@ -124,7 +124,7 @@ comment on table worker_auth_authorized is 'worker_auth_authorized is a table where each row represents key and cert data associated with an authorized worker.'; create table worker_auth_certificate_bundle( - certificate_public_key bytea not null + root_certificate_public_key bytea not null constraint worker_auth_ca_certificate_fkey references worker_auth_ca_certificate(public_key) on delete cascade @@ -137,7 +137,7 @@ create table worker_auth_certificate_bundle( cert_bundle bytea constraint current_cert_bundle_must_not_be_empty check(length(cert_bundle) > 0), - primary key(certificate_public_key, worker_key_identifier) + primary key(root_certificate_public_key, worker_key_identifier) ); comment on table worker_auth_certificate_bundle is @@ -147,6 +147,6 @@ create trigger immutable_columns before update on worker_auth_certificate_bundle - for each row execute procedure immutable_columns('certificate_public_key', 'worker_key_identifier', 'cert_bundle'); + for each row execute procedure immutable_columns('root_certificate_public_key', 'worker_key_identifier', 'cert_bundle'); commit; \ No newline at end of file diff --git a/internal/proto/controller/storage/servers/store/v1/worker_auth.proto b/internal/proto/controller/storage/servers/store/v1/worker_auth.proto index 197ea4005..fd7d91d87 100644 --- a/internal/proto/controller/storage/servers/store/v1/worker_auth.proto +++ b/internal/proto/controller/storage/servers/store/v1/worker_auth.proto @@ -40,11 +40,11 @@ message WorkerAuth { // WorkerCertBundle contains all fields related to a WorkerCertBundle resource message WorkerCertBundle { // The public key of the issuing root certificate - // @inject_tag: `gorm:"not_null"` - bytes certificate_public_key = 10; + // @inject_tag: `gorm:"primary_key"` + bytes root_certificate_public_key = 10; // The WorkerAuth worker_key_identifier this cert bundle record is for - // @inject_tag: `gorm:"not_null"` + // @inject_tag: `gorm:"primary_key"` string worker_key_identifier = 20; // CertBundle is the marshaled protobuf certificate bundle for a WorkerAuth diff --git a/internal/servers/query.go b/internal/servers/query.go index 3d51150ee..d83f5428f 100644 --- a/internal/servers/query.go +++ b/internal/servers/query.go @@ -2,11 +2,25 @@ package servers const ( deleteWhereCreateTimeSql = `create_time < ?` - deleteTagsByWorkerIdSql = ` + + deleteTagsByWorkerIdSql = ` delete from server_worker_tag where source = ? and worker_id = ?` + + deleteWorkerAuthQuery = ` + delete from worker_auth_authorized + where worker_key_identifier = @worker_key_identifier; + ` + deleteWorkerCertBundlesQuery = ` + delete from worker_auth_certificate_bundle + where worker_key_identifier = @worker_key_identifier; + ` + deleteRootCertificateQuery = ` + delete from worker_auth_ca_certificate + where state = @state; + ` ) diff --git a/internal/servers/repository_workerauth.go b/internal/servers/repository_workerauth.go new file mode 100644 index 000000000..d266e16b5 --- /dev/null +++ b/internal/servers/repository_workerauth.go @@ -0,0 +1,755 @@ +package servers + +import ( + "context" + "crypto/x509" + "database/sql" + "fmt" + "strconv" + + "github.com/fatih/structs" + "google.golang.org/protobuf/types/known/structpb" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/go-dbw" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" + nodee "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/types" + "github.com/mitchellh/mapstructure" + "google.golang.org/protobuf/proto" +) + +// Ensure we implement the Storage interfaces +var ( + _ nodee.Storage = (*WorkerAuthRepositoryStorage)(nil) +) + +type rootCertificatesVersion struct { + Version uint32 +} + +// WorkerAuthRepositoryStorage is the Worker Auth database repository +type WorkerAuthRepositoryStorage struct { + reader db.Reader + writer db.Writer + kms *kms.Kms +} + +// NewRepositoryStorage creates a new WorkerAuthRepositoryStorage that implements the Storage interface +func NewRepositoryStorage(ctx context.Context, r db.Reader, w db.Writer, kms *kms.Kms) (*WorkerAuthRepositoryStorage, error) { + const op = "servers.(WorkerAuthRepositoryStorage).NewRepositoryStorage" + switch { + case isNil(r): + return nil, errors.New(ctx, errors.InvalidParameter, op, "reader is nil") + case isNil(w): + return nil, errors.New(ctx, errors.InvalidParameter, op, "writer is nil") + case isNil(kms): + return nil, errors.New(ctx, errors.InvalidParameter, op, "kms is nil") + } + + workerAuthRepoStorage := &WorkerAuthRepositoryStorage{ + reader: r, + writer: w, + kms: kms, + } + + return workerAuthRepoStorage, nil +} + +// Store implements the Storage interface +func (r *WorkerAuthRepositoryStorage) Store(ctx context.Context, msg nodee.MessageWithId) error { + const op = "servers.(WorkerAuthRepositoryStorage).Store" + if err := types.ValidateMessage(msg); err != nil { + return errors.Wrap(ctx, err, op) + } + if msg.GetId() == "" { + return errors.New(ctx, errors.InvalidParameter, op, "given message cannot be stored as it has no ID") + } + + // Determine type of message to store + switch t := msg.(type) { + case *types.NodeInformation: + err := r.storeNodeInformation(ctx, t) + if err != nil { + return errors.Wrap(ctx, err, op) + } + case *types.RootCertificates: + err := r.storeRootCertificates(ctx, t) + if err != nil { + return errors.Wrap(ctx, err, op) + } + default: + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("message type %T not supported for Store", msg)) + } + + return nil +} + +// Node information is stored in two parts: +// * the workerAuth record is stored with a reference to a worker +// * certificate bundles are stored with a reference to the workerAuth record and issuing root certificate +func (r *WorkerAuthRepositoryStorage) storeNodeInformation(ctx context.Context, node *types.NodeInformation) error { + const op = "servers.(WorkerAuthRepositoryStorage).storeNodeInformation" + if node == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing NodeInformation") + } + + nodeAuth := allocWorkerAuth() + nodeAuth.WorkerKeyIdentifier = node.Id + nodeAuth.WorkerEncryptionPubKey = node.EncryptionPublicKeyBytes + nodeAuth.WorkerSigningPubKey = node.CertificatePublicKeyPkix + + // Encrypt the private key + databaseWrapper, err := r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase) + if err != nil { + return errors.Wrap(ctx, err, op) + } + nodeAuth.KeyId, err = databaseWrapper.KeyId(ctx) + if err != nil { + return errors.Wrap(ctx, err, op) + } + nodeAuth.ControllerEncryptionPrivKey, err = encrypt(ctx, node.ServerEncryptionPrivateKeyBytes, databaseWrapper) + if err != nil { + return errors.Wrap(ctx, err, op) + } + nodeAuth.Nonce, err = encrypt(ctx, node.RegistrationNonce, databaseWrapper) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + // Get workerId from state passed in + var result workerAuthWorkerId + err = mapstructure.Decode(node.State.AsMap(), &result) + if err != nil { + return errors.Wrap(ctx, err, op) + } + nodeAuth.WorkerId = result.WorkerId + + if err := nodeAuth.ValidateNewWorkerAuth(ctx); err != nil { + return errors.Wrap(ctx, err, op) + } + + _, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(read db.Reader, w db.Writer) error { + // Store WorkerAuth + storeNodeAuth := nodeAuth.clone() + if err := w.Create(ctx, &storeNodeAuth); err != nil { + return errors.Wrap(ctx, err, op) + } + // Then store cert bundles associated with this WorkerAuth + for _, c := range node.CertificateBundles { + if err := r.storeWorkerCertBundle(ctx, c, nodeAuth.WorkerKeyIdentifier, w); err != nil { + return errors.Wrap(ctx, err, op) + } + } + return nil + }, + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + return nil +} + +func (r *WorkerAuthRepositoryStorage) storeWorkerCertBundle(ctx context.Context, bundle *types.CertificateBundle, + workerKeyIdentifier string, writer db.Writer, +) error { + const op = "servers.(WorkerAuthRepositoryStorage).storeWorkerCertBundle" + if bundle == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing CertificateBundle") + } + if workerKeyIdentifier == "" { + return errors.New(ctx, errors.InvalidParameter, op, "empty workerKeyIdentifier") + } + if writer == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing writer") + } + + workerCertBundle := allocWorkerCertBundle() + bundleBytes, err := proto.Marshal(bundle) + if err != nil { + return errors.New(ctx, errors.Encode, op, "error marshaling nodetypes.CertificateBundle", errors.WithWrap(err)) + } + + // Extract serial number from CA cert + caCert := bundle.CaCertificateDer + parsedCert, err := x509.ParseCertificate(caCert) + if err != nil { + return errors.Wrap(ctx, err, op) + } + workerCertBundle.RootCertificatePublicKey = parsedCert.AuthorityKeyId + workerCertBundle.CertBundle = bundleBytes + workerCertBundle.WorkerKeyIdentifier = workerKeyIdentifier + + err = workerCertBundle.ValidateNewWorkerCertBundle(ctx) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + err = writer.Create(ctx, &workerCertBundle) + + if err != nil { + return errors.Wrap(ctx, err, op) + } + return nil +} + +func (r *WorkerAuthRepositoryStorage) convertRootCertificate(ctx context.Context, cert *types.RootCertificate) (*RootCertificate, error) { + const op = "servers.(WorkerAuthRepositoryStorage).convertRootCertificate" + if cert == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing RootCertificate") + } + + rootCert := allocRootCertificate() + + parsedCert, err := x509.ParseCertificate(cert.CertificateDer) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + rootCert.SerialNumber = parsedCert.SerialNumber.Uint64() + rootCert.Certificate = cert.CertificateDer + rootCert.NotValidAfter = timestamp.New(cert.NotAfter.AsTime()) + rootCert.NotValidBefore = timestamp.New(cert.NotBefore.AsTime()) + rootCert.PublicKey = cert.PublicKeyPkix + rootCert.State = cert.Id + rootCert.IssuingCa = ca_id + + // Encrypt the private key + databaseWrapper, err := r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + rootCert.KeyId, err = databaseWrapper.KeyId(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + rootCert.PrivateKey, err = encrypt(ctx, cert.PrivateKeyPkcs8, databaseWrapper) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + err = rootCert.ValidateNewRootCertificate(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + return rootCert, nil +} + +func (r *WorkerAuthRepositoryStorage) storeRootCertificates(ctx context.Context, cert *types.RootCertificates) error { + const op = "servers.(WorkerAuthRepositoryStorage).storeRootCertificates" + if cert == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing RootCertificate") + } + + nextCert, err := r.convertRootCertificate(ctx, cert.Next) + currentCert, err := r.convertRootCertificate(ctx, cert.Current) + + // Use passed version + var result rootCertificatesVersion + err = mapstructure.Decode(cert.State.AsMap(), &result) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + version := uint32(1) + if result.Version != 0 { + version = result.Version + } + certAuthority := &CertificateAuthority{ + CertificateAuthority: &store.CertificateAuthority{ + PrivateId: ca_id, + Version: version + 1, + }, + } + + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(read db.Reader, w db.Writer) error { + // Delete the old certs + if err = r.removeRootCertificateWithWriter(ctx, string(CurrentState), w); err != nil { + return errors.Wrap(ctx, err, op) + } + if err = r.removeRootCertificateWithWriter(ctx, string(NextState), w); err != nil { + return errors.Wrap(ctx, err, op) + } + + // Update the certAuthority's version column + rowsUpdated, err := w.Update(ctx, certAuthority, []string{"Version"}, nil, db.WithVersion(&version)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rowsUpdated != 1 { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated CertificateAuthority and %d rows updated", rowsUpdated)) + } + + // Then insert the new certs + if nextCert != nil { + if err = w.Create(ctx, &nextCert); err != nil { + return errors.Wrap(ctx, err, op) + } + } + if currentCert != nil { + if err = w.Create(ctx, ¤tCert); err != nil { + return errors.Wrap(ctx, err, op) + } + } + return nil + }, + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + return nil +} + +// Load implements the Storage interface. +// Load loads values into the given message. The message must be populated +// with the ID value. If not found, the returned error should be ErrNotFound. +func (r *WorkerAuthRepositoryStorage) Load(ctx context.Context, msg nodee.MessageWithId) error { + const op = "servers.(WorkerAuthRepositoryStorage).Load" + if err := types.ValidateMessage(msg); err != nil { + return errors.Wrap(ctx, err, op) + } + if msg.GetId() == "" { + return errors.New(ctx, errors.InvalidParameter, op, "given message cannot be loaded as it has no ID") + } + + switch t := msg.(type) { + case *types.NodeInformation: + err := r.loadNodeInformation(ctx, t) + if err != nil { + return errors.Wrap(ctx, err, op) + } + case *types.RootCertificates: + err := r.loadRootCertificates(ctx, t) + if err != nil { + return errors.Wrap(ctx, err, op) + } + default: + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("message type %T not supported for Load", t)) + } + + return nil +} + +// Node information is loaded in two parts: +// * the workerAuth record +// * its certificate bundles +func (r *WorkerAuthRepositoryStorage) loadNodeInformation(ctx context.Context, node *types.NodeInformation) error { + const op = "servers.(WorkerAuthRepositoryStorage).loadNodeInformation" + if node == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing NodeInformation") + } + + authorizedWorker, err := r.findWorkerAuth(ctx, node) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if authorizedWorker == nil { + return nodee.ErrNotFound + } + + node.EncryptionPublicKeyBytes = authorizedWorker.WorkerEncryptionPubKey + node.CertificatePublicKeyPkix = authorizedWorker.WorkerSigningPubKey + + // Default values are used for key types + node.EncryptionPublicKeyType = types.KEYTYPE_X25519 + node.CertificatePublicKeyType = types.KEYTYPE_ED25519 + node.ServerEncryptionPrivateKeyType = types.KEYTYPE_X25519 + + // Decrypt private key + databaseWrapper, err := r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase, kms.WithKeyId(authorizedWorker.KeyId)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + node.ServerEncryptionPrivateKeyBytes, err = decrypt(ctx, authorizedWorker.ControllerEncryptionPrivKey, databaseWrapper) + if err != nil { + return errors.Wrap(ctx, err, op) + } + node.RegistrationNonce, err = decrypt(ctx, authorizedWorker.Nonce, databaseWrapper) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + // Get cert bundles from the other table + certBundles, err := r.findCertBundles(ctx, node.Id) + if err != nil { + return errors.Wrap(ctx, err, op) + } + node.CertificateBundles = certBundles + + return nil +} + +func (r *WorkerAuthRepositoryStorage) findCertBundles(ctx context.Context, workerKeyId string) ([]*types.CertificateBundle, error) { + const op = "servers.(WorkerAuthRepositoryStorage).findCertBundles" + if workerKeyId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty workerKeyId") + } + + var bundles []*WorkerCertBundle + err := r.reader.SearchWhere(ctx, &bundles, "worker_key_identifier = ?", []interface{}{workerKeyId}, db.WithLimit(-1)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + certBundle := []*types.CertificateBundle{} + for _, bundle := range bundles { + thisBundle := &types.CertificateBundle{} + if err := proto.Unmarshal(bundle.WorkerCertBundle.CertBundle, thisBundle); err != nil { + return nil, errors.New(ctx, errors.Decode, op, "error unmarshaling message", errors.WithWrap(err)) + } + certBundle = append(certBundle, thisBundle) + } + + return certBundle, nil +} + +func (r *WorkerAuthRepositoryStorage) findWorkerAuth(ctx context.Context, node *types.NodeInformation) (*WorkerAuth, error) { + const op = "servers.(WorkerAuthRepositoryStorage).findWorkerAuth" + if node == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "node is nil") + } + + worker := allocWorkerAuth() + worker.WorkerKeyIdentifier = node.Id + + err := r.reader.LookupById(ctx, worker) + if err != nil { + if errors.Is(err, dbw.ErrRecordNotFound) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op) + } + + return worker, nil +} + +func (r *WorkerAuthRepositoryStorage) loadRootCertificates(ctx context.Context, cert *types.RootCertificates) error { + const op = "servers.(WorkerAuthRepositoryStorage).loadRootCertificates" + if cert == nil { + return errors.New(ctx, errors.InvalidParameter, op, "cert is nil") + } + + // See if we have the CertificateAuthority first + certAuthority := &CertificateAuthority{ + CertificateAuthority: &store.CertificateAuthority{ + PrivateId: cert.Id, + }, + } + err := r.reader.LookupById(ctx, certAuthority) + if err != nil { + return nodee.ErrNotFound + } + + // Add version to state field of cert + versionMap := &rootCertificatesVersion{Version: certAuthority.Version} + stateOpt := structs.Map(versionMap) + state, err := structpb.NewStruct(stateOpt) + if err != nil { + return errors.Wrap(ctx, err, op) + } + cert.State = state + + // Load current and next certificates + certsToFind := []string{string(NextState), string(CurrentState)} + for _, c := range certsToFind { + + rootCertificate := allocRootCertificate() + rootCert := &types.RootCertificate{} + + if err := r.reader.SearchWhere(ctx, &rootCertificate, "state = ?", []interface{}{c}, db.WithLimit(-1)); err != nil { + return errors.Wrap(ctx, err, op) + } + + if rootCertificate.Certificate == nil { + return nodee.ErrNotFound + } + + rootCert.CertificateDer = rootCertificate.Certificate + rootCert.NotAfter = rootCertificate.NotValidAfter.Timestamp + rootCert.NotBefore = rootCertificate.NotValidBefore.Timestamp + rootCert.PublicKeyPkix = rootCertificate.PublicKey + rootCert.PrivateKeyType = types.KEYTYPE_ED25519 + + // decrypt private key + databaseWrapper, err := r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase, kms.WithKeyId(rootCertificate.KeyId)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + rootCert.PrivateKeyPkcs8, err = decrypt(ctx, rootCertificate.PrivateKey, databaseWrapper) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + if c == string(NextState) { + cert.Next = rootCert + } + if c == string(CurrentState) { + cert.Current = rootCert + } + } + + return nil +} + +// Remove implements the Storage interface. +// Remove removes the given message. Only the ID field of the message is considered. +func (r *WorkerAuthRepositoryStorage) Remove(ctx context.Context, msg nodee.MessageWithId) error { + const op = "servers.(WorkerAuthRepositoryStorage).Remove" + if err := types.ValidateMessage(msg); err != nil { + return errors.New(ctx, errors.InvalidParameter, op, "given message cannot be removed as it has no ID") + } + + // Determine type of message to remove + switch msg.(type) { + case *types.NodeInformation: + err := r.removeNodeInformation(ctx, msg.GetId()) + if err != nil { + return errors.Wrap(ctx, err, op) + } + case *types.RootCertificates: + err := r.removeCertificateAuthority(ctx, msg.(*types.RootCertificates)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + default: + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("message type %T not supported for Remove", msg)) + } + + return nil +} + +func (r *WorkerAuthRepositoryStorage) removeNodeInformation(ctx context.Context, id string) error { + const op = "servers.(WorkerAuthRepositoryStorage).removeNodeInformation" + if id == "" { + return errors.New(ctx, errors.InvalidParameter, op, "empty id") + } + + _, err := r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(reader db.Reader, w db.Writer) error { + var err error + _, err = w.Exec(ctx, deleteWorkerAuthQuery, []interface{}{sql.Named("worker_key_identifier", id)}) + if err != nil { + return errors.Wrap(ctx, err, op) + } + _, err = w.Exec(ctx, deleteWorkerCertBundlesQuery, []interface{}{sql.Named("worker_key_identifier", id)}) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete workerAuth")) + } + return nil + }, + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + return nil +} + +func (r *WorkerAuthRepositoryStorage) removeCertificateAuthority(ctx context.Context, cert *types.RootCertificates) error { + const op = "servers.(WorkerAuthRepositoryStorage).removeCertificateAuthority" + + var result rootCertificatesVersion + err := mapstructure.Decode(cert.State.AsMap(), &result) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + version := uint32(1) + if result.Version != 0 { + version = result.Version + } + certAuthority := &CertificateAuthority{ + CertificateAuthority: &store.CertificateAuthority{ + PrivateId: ca_id, + Version: version + 1, + }, + } + + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(read db.Reader, w db.Writer) error { + // Update the certAuthority's version column first + rowsUpdated, err := w.Update(ctx, certAuthority, []string{"Version"}, nil, db.WithVersion(&version)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rowsUpdated != 1 { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated CertificateAuthority and %d rows updated", rowsUpdated)) + } + + if err := r.removeRootCertificateWithWriter(ctx, string(CurrentState), w); err != nil { + return errors.Wrap(ctx, err, op) + } + if err := r.removeRootCertificateWithWriter(ctx, string(NextState), w); err != nil { + return errors.Wrap(ctx, err, op) + } + + return nil + }, + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + return nil +} + +func (r *WorkerAuthRepositoryStorage) removeRootCertificateWithWriter(ctx context.Context, id string, writer db.Writer) error { + const op = "servers.(WorkerAuthRepositoryStorage).removeRootCertificateWithWriter" + if id == "" { + return errors.New(ctx, errors.InvalidParameter, op, "empty id") + } + if writer == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing writer") + } + + rows, err := writer.Exec(ctx, deleteRootCertificateQuery, []interface{}{ + sql.Named("state", id), + }) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete root certificate")) + } + if rows > 1 { + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 root certificate would have been deleted") + } + return nil +} + +// List implements the Storage interface. +// List returns a list of IDs; the type of the message is used to disambiguate what to list. +func (r *WorkerAuthRepositoryStorage) List(ctx context.Context, msg proto.Message) ([]string, error) { + const op = "servers.(WorkerAuthRepositoryStorage).List" + + var err error + var ids []string + // Determine type of message to store + switch msg.(type) { + case *types.NodeInformation: + ids, err = r.listNodeInformation(ctx) + case *types.RootCertificate: + ids, err = r.listRootCertificates(ctx) + case *types.RootCertificates: + ids, err = r.listCertificateAuthority(ctx) + default: + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("message type %T not supported for List", msg)) + } + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return ids, nil +} + +// Returns a list of node auth IDs +func (r *WorkerAuthRepositoryStorage) listNodeInformation(ctx context.Context) ([]string, error) { + const op = "servers.(WorkerAuthRepositoryStorage).listNodeCertificates" + + var where string + var nodeAuths []*WorkerAuth + err := r.reader.SearchWhere(ctx, &nodeAuths, where, []interface{}{}, db.WithLimit(-1)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + var nodeIds []string + for _, auth := range nodeAuths { + nodeIds = append(nodeIds, auth.WorkerKeyIdentifier) + } + return nodeIds, nil +} + +// Returns a list of root certificates +func (r *WorkerAuthRepositoryStorage) listRootCertificates(ctx context.Context) ([]string, error) { + const op = "servers.(WorkerAuthRepositoryStorage).listRootCertificates" + + var where string + var rootCertificates []*RootCertificate + err := r.reader.SearchWhere(ctx, &rootCertificates, where, []interface{}{}, db.WithLimit(-1)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + var certIds []string + for _, cert := range rootCertificates { + certIds = append(certIds, strconv.FormatUint(cert.SerialNumber, 10)) + } + + return certIds, nil +} + +// Returns a list of certificate authorities +func (r *WorkerAuthRepositoryStorage) listCertificateAuthority(ctx context.Context) ([]string, error) { + const op = "servers.(WorkerAuthRepositoryStorage).listCertificateAuthority" + + var where string + var rootCertificates []*CertificateAuthority + err := r.reader.SearchWhere(ctx, &rootCertificates, where, []interface{}{}, db.WithLimit(-1)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + var certIds []string + for _, cert := range rootCertificates { + certIds = append(certIds, cert.PrivateId) + } + + return certIds, nil +} + +// encrypt value before writing it to the db +func encrypt(ctx context.Context, value []byte, wrapper wrapping.Wrapper) ([]byte, error) { + const op = "servers.(WorkerAuthRepositoryStorage).encrypt" + if value == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing value") + } + if wrapper == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing wrapper") + } + + blobInfo, err := wrapper.Encrypt(ctx, value) + if err != nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "error encrypting recovery info", errors.WithWrap(err)) + } + marshaledBlob, err := proto.Marshal(blobInfo) + if err != nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "error marshaling encrypted blob", errors.WithWrap(err)) + } + return marshaledBlob, nil +} + +func decrypt(ctx context.Context, value []byte, wrapper wrapping.Wrapper) ([]byte, error) { + const op = "servers.(WorkerAuthRepositoryStorage).decrypt" + if value == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing value") + } + if wrapper == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing wrapper") + } + + blobInfo := new(wrapping.BlobInfo) + if err := proto.Unmarshal(value, blobInfo); err != nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "error decoding encrypted blob", errors.WithWrap(err)) + } + + marshaledInfo, err := wrapper.Decrypt(ctx, blobInfo) + if err != nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "error decrypting recovery info", errors.WithWrap(err)) + } + + return marshaledInfo, nil +} diff --git a/internal/servers/repository_workerauth_test.go b/internal/servers/repository_workerauth_test.go new file mode 100644 index 000000000..3e752bf86 --- /dev/null +++ b/internal/servers/repository_workerauth_test.go @@ -0,0 +1,236 @@ +package servers + +import ( + "context" + "crypto/rand" + "testing" + + "github.com/fatih/structs" + "github.com/mitchellh/mapstructure" + "google.golang.org/protobuf/types/known/structpb" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/registration" + "github.com/hashicorp/nodeenrollment/rotation" + "github.com/hashicorp/nodeenrollment/storage/file" + "github.com/hashicorp/nodeenrollment/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/crypto/curve25519" +) + +// Test RootCertificate storage +func TestStoreRootCertificates(t *testing.T) { + require, assert := require.New(t), assert.New(t) + ctx := context.Background() + wrapper := db.TestWrapper(t) + conn, _ := db.TestSetup(t, "postgres") + kmsCache := kms.TestKms(t, conn, wrapper) + // Ensures the global scope contains a valid root key + err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(err) + wrapper, err = kmsCache.GetWrapper(context.Background(), scope.Global.String(), kms.KeyPurposeDatabase) + require.NoError(err) + require.NotNil(wrapper) + + rw := db.New(conn) + workerAuthRepo, err := NewRepositoryStorage(ctx, rw, rw, kmsCache) + require.NoError(err) + + // Fail to find root certificates prior to rotation/ creation + noCertAuth := &types.RootCertificates{Id: ca_id} + err = workerAuthRepo.Load(ctx, noCertAuth) + require.Error(err) + rootIds, err := workerAuthRepo.List(ctx, (*types.RootCertificate)(nil)) + require.NoError(err) + assert.Len(rootIds, 0) + + // Rotate will generate and store next and current, as we have none + roots, err := rotation.RotateRootCertificates(ctx, workerAuthRepo) + require.NoError(err) + rootIds, err = workerAuthRepo.List(ctx, (*types.RootCertificate)(nil)) + require.NoError(err) + assert.Len(rootIds, 2) + + // Load stored roots + certAuthority := &types.RootCertificates{Id: ca_id} + err = workerAuthRepo.Load(ctx, certAuthority) + require.NoError(err) + require.NotNil(certAuthority.GetNext()) + require.NotNil(certAuthority.GetCurrent()) + + // Read the next cert and validate the stored values are valid after encrypt and decrypt + assert.Equal(roots.Next.PrivateKeyPkcs8, certAuthority.Next.PrivateKeyPkcs8) + + // Read the current cert and validate the stored values are valid after encrypt and decrypt + assert.Equal(roots.Current.PrivateKeyPkcs8, certAuthority.Current.PrivateKeyPkcs8) + + // Fail to find root certificates under bogus id + bogusCertAuth := &types.RootCertificates{Id: "bogus"} + err = workerAuthRepo.Load(ctx, bogusCertAuth) + require.Error(err) +} + +func TestStoreCertAuthorityVersioning(t *testing.T) { + require, assert := require.New(t), assert.New(t) + ctx := context.Background() + wrapper := db.TestWrapper(t) + conn, _ := db.TestSetup(t, "postgres") + kmsCache := kms.TestKms(t, conn, wrapper) + // Ensures the global scope contains a valid root key + err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(err) + wrapper, err = kmsCache.GetWrapper(context.Background(), scope.Global.String(), kms.KeyPurposeDatabase) + require.NoError(err) + require.NotNil(wrapper) + + rw := db.New(conn) + workerAuthRepo, err := NewRepositoryStorage(ctx, rw, rw, kmsCache) + require.NoError(err) + + // Store CA and check that initial version updates + roots, err := rotation.RotateRootCertificates(ctx, workerAuthRepo) + require.NoError(err) + + cAuth2 := &types.RootCertificates{Id: ca_id} + err = workerAuthRepo.Load(ctx, cAuth2) + require.NoError(err) + var result2 rootCertificatesVersion + err = mapstructure.Decode(cAuth2.State.AsMap(), &result2) + require.NoError(err) + assert.Equal(uint32(2), result2.Version) + + // Store CA with old version, expect error + badVersion := &rootCertificatesVersion{Version: uint32(1)} + badStateOpt := structs.Map(badVersion) + badState, err := structpb.NewStruct(badStateOpt) + require.NoError(err) + newRoots2 := &types.RootCertificates{ + Id: ca_id, + Next: roots.Next, + Current: roots.Current, + } + newRoots2.State = badState + err = workerAuthRepo.Store(ctx, newRoots2) + require.Error(err) + + // Remove CA, expect updated version + cAuthRemove := &types.RootCertificates{Id: ca_id} + removeVersion := &rootCertificatesVersion{Version: uint32(2)} + removeOpt := structs.Map(removeVersion) + removeState, err := structpb.NewStruct(removeOpt) + require.NoError(err) + cAuthRemove.State = removeState + err = workerAuthRepo.Remove(ctx, cAuthRemove) + require.NoError(err) +} + +// Test WorkerAuth storage +func TestStoreWorkerAuth(t *testing.T) { + require, assert := require.New(t), assert.New(t) + ctx := context.Background() + wrapper := db.TestWrapper(t) + conn, _ := db.TestSetup(t, "postgres") + kmsCache := kms.TestKms(t, conn, wrapper) + + // Ensures the global scope contains a valid root key + err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(err) + wrapper, err = kmsCache.GetWrapper(context.Background(), scope.Global.String(), kms.KeyPurposeDatabase) + require.NoError(err) + require.NotNil(wrapper) + + worker := TestWorker(t, conn, wrapper) + + rw := db.New(conn) + rootStorage, err := NewRepositoryStorage(ctx, rw, rw, kmsCache) + require.NoError(err) + + _, err = rotation.RotateRootCertificates(ctx, rootStorage) + require.NoError(err) + + // Create struct to pass in with workerId that will be passed along to storage + state, err := AttachWorkerIdToState(ctx, worker.PublicId) + require.NoError(err) + + // This happens on the worker + fileStorage, err := file.NewFileStorage(ctx) + require.NoError(err) + nodeCreds, err := types.NewNodeCredentials(ctx, fileStorage) + require.NoError(err) + // Create request using worker id + fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(ctx) + require.NoError(err) + keyId, err := nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) + require.NoError(err) + nodePubKey, err := curve25519.X25519(nodeCreds.EncryptionPrivateKeyBytes, curve25519.Basepoint) + require.NoError(err) + + // Add in node information to storage so we have a key to use + nodeInfo := &types.NodeInformation{ + Id: keyId, + CertificatePublicKeyPkix: nodeCreds.CertificatePublicKeyPkix, + CertificatePublicKeyType: nodeCreds.CertificatePrivateKeyType, + EncryptionPublicKeyBytes: nodePubKey, + EncryptionPublicKeyType: nodeCreds.EncryptionPrivateKeyType, + RegistrationNonce: nodeCreds.RegistrationNonce, + State: state, + } + + // Create storage for authentication and pass it the worker + storage, err := NewRepositoryStorage(ctx, rw, rw, kmsCache) + require.NoError(err) + + // The AuthorizeNode request will result in a WorkerAuth record being stored + require.NoError(registration.AuthorizeNode(ctx, storage, fetchReq, nodeenrollment.WithState(state))) + + // We should now look for a node information value in storage and validate that it's populated + nodeInfos, err := storage.List(ctx, (*types.NodeInformation)(nil)) + require.NoError(err) + require.NotEmpty(nodeInfos) + assert.Len(nodeInfos, 1) + + // Validate the stored fields match those from the worker + nodeLookup := &types.NodeInformation{ + Id: keyId, + } + err = storage.Load(ctx, nodeLookup) + require.NoError(err) + assert.NotEmpty(nodeLookup) + assert.Equal(nodeInfo.EncryptionPublicKeyBytes, nodeLookup.EncryptionPublicKeyBytes) + assert.Equal(nodeInfo.RegistrationNonce, nodeLookup.RegistrationNonce) + assert.Equal(nodeInfo.CertificatePublicKeyPkix, nodeLookup.CertificatePublicKeyPkix) + + // Remove node + err = storage.Remove(ctx, nodeLookup) + require.NoError(err) + err = storage.Load(ctx, nodeLookup) + require.Error(err) +} + +func TestUnsupportedMessages(t *testing.T) { + require := require.New(t) + ctx := context.Background() + wrapper := db.TestWrapper(t) + conn, _ := db.TestSetup(t, "postgres") + kmsCache := kms.TestKms(t, conn, wrapper) + + rw := db.New(conn) + storage, err := NewRepositoryStorage(ctx, rw, rw, kmsCache) + require.NoError(err) + + err = storage.Store(ctx, &types.NodeCredentials{}) + require.Error(err) + + err = storage.Load(ctx, &types.NodeCredentials{Id: "bogus"}) + require.Error(err) + + _, err = storage.List(ctx, (*types.NodeCredentials)(nil)) + require.Error(err) + + err = storage.Remove(ctx, &types.NodeCredentials{Id: "bogus"}) + require.Error(err) +} diff --git a/internal/servers/store/worker_auth.pb.go b/internal/servers/store/worker_auth.pb.go index 119db8fee..88c48b18b 100644 --- a/internal/servers/store/worker_auth.pb.go +++ b/internal/servers/store/worker_auth.pb.go @@ -140,11 +140,11 @@ type WorkerCertBundle struct { unknownFields protoimpl.UnknownFields // The public key of the issuing root certificate - // @inject_tag: `gorm:"not_null"` - CertificatePublicKey []byte `protobuf:"bytes,10,opt,name=certificate_public_key,json=certificatePublicKey,proto3" json:"certificate_public_key,omitempty" gorm:"not_null"` + // @inject_tag: `gorm:"primary_key"` + RootCertificatePublicKey []byte `protobuf:"bytes,10,opt,name=root_certificate_public_key,json=rootCertificatePublicKey,proto3" json:"root_certificate_public_key,omitempty" gorm:"primary_key"` // The WorkerAuth worker_key_identifier this cert bundle record is for - // @inject_tag: `gorm:"not_null"` - WorkerKeyIdentifier string `protobuf:"bytes,20,opt,name=worker_key_identifier,json=workerKeyIdentifier,proto3" json:"worker_key_identifier,omitempty" gorm:"not_null"` + // @inject_tag: `gorm:"primary_key"` + WorkerKeyIdentifier string `protobuf:"bytes,20,opt,name=worker_key_identifier,json=workerKeyIdentifier,proto3" json:"worker_key_identifier,omitempty" gorm:"primary_key"` // CertBundle is the marshaled protobuf certificate bundle for a WorkerAuth // @inject_tag: `gorm:"not_null"` CertBundle []byte `protobuf:"bytes,30,opt,name=cert_bundle,json=certBundle,proto3" json:"cert_bundle,omitempty" gorm:"not_null"` @@ -182,9 +182,9 @@ func (*WorkerCertBundle) Descriptor() ([]byte, []int) { return file_controller_storage_servers_store_v1_worker_auth_proto_rawDescGZIP(), []int{1} } -func (x *WorkerCertBundle) GetCertificatePublicKey() []byte { +func (x *WorkerCertBundle) GetRootCertificatePublicKey() []byte { if x != nil { - return x.CertificatePublicKey + return x.RootCertificatePublicKey } return nil } @@ -231,22 +231,22 @@ var file_controller_storage_servers_store_v1_worker_auth_proto_rawDesc = []byte{ 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x69, 0x76, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, - 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x9d, + 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x42, 0x75, 0x6e, - 0x64, 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x14, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, - 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1f, 0x0a, - 0x0b, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x1e, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x65, 0x72, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x42, 0x3c, - 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, - 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x6c, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x18, 0x72, 0x6f, 0x6f, 0x74, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, + 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x62, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x65, 0x72, + 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/servers/testing.go b/internal/servers/testing.go index b027d7876..fee3a0333 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -26,7 +26,7 @@ func populateBytes(length int) []byte { return fieldBytes } -func TestKkmsKey(ctx context.Context, t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) string { +func TestKmsKey(ctx context.Context, t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) string { t.Helper() org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) kmsCache := kms.TestKms(t, conn, wrapper) diff --git a/internal/servers/worker.go b/internal/servers/worker.go index f7978fb0a..4789f810f 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -4,12 +4,32 @@ import ( "context" "strings" + "github.com/fatih/structs" "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/servers/store" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" ) +type workerAuthWorkerId struct { + WorkerId string +} + +// 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 = "servers.(Worker).AttachWorkerIdToState" + if workerId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing workerId") + } + + workerMap := &workerAuthWorkerId{WorkerId: workerId} + stateOpt := structs.Map(workerMap) + return structpb.NewStruct(stateOpt) +} + // 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. diff --git a/internal/servers/worker_auth.go b/internal/servers/worker_auth.go index d3bd84907..3c7c11b56 100644 --- a/internal/servers/worker_auth.go +++ b/internal/servers/worker_auth.go @@ -56,7 +56,7 @@ func newWorkerAuth(ctx context.Context, workerKeyIdentifier, workerId string, op return l, nil } -func AllocWorkerAuth() *WorkerAuth { +func allocWorkerAuth() *WorkerAuth { return &WorkerAuth{ WorkerAuth: &store.WorkerAuth{}, } @@ -132,15 +132,15 @@ func newWorkerCertBundle(ctx context.Context, certificatePublicKey []byte, worke l := &WorkerCertBundle{ WorkerCertBundle: &store.WorkerCertBundle{ - CertificatePublicKey: certificatePublicKey, - WorkerKeyIdentifier: workerKeyIdentifier, - CertBundle: certBundle, + RootCertificatePublicKey: certificatePublicKey, + WorkerKeyIdentifier: workerKeyIdentifier, + CertBundle: certBundle, }, } return l, nil } -func AllocWorkerCertBundle() *WorkerCertBundle { +func allocWorkerCertBundle() *WorkerCertBundle { return &WorkerCertBundle{ WorkerCertBundle: &store.WorkerCertBundle{}, } @@ -156,7 +156,7 @@ func (w *WorkerCertBundle) clone() *WorkerCertBundle { // Validate is called before storing a WorkerCertBundle in the db func (w *WorkerCertBundle) ValidateNewWorkerCertBundle(ctx context.Context) error { const op = "servers.(WorkerAuth).validateNewWorkerCertBundle" - if w.CertificatePublicKey == nil { + if w.RootCertificatePublicKey == nil { return errors.New(ctx, errors.InvalidParameter, op, "missing CertificatePublicKey") } if w.WorkerKeyIdentifier == "" { diff --git a/internal/servers/workerauth_store_test.go b/internal/servers/workerauth_store_test.go index 488f5fe6a..e66d6d67d 100644 --- a/internal/servers/workerauth_store_test.go +++ b/internal/servers/workerauth_store_test.go @@ -427,7 +427,7 @@ func TestWorkerCertBundle(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) - testKey := TestKkmsKey(ctx, t, conn, wrapper) + testKey := TestKmsKey(ctx, t, conn, wrapper) worker := TestWorker(t, conn, wrapper) workerAuth := TestWorkerAuth(ctx, t, conn, worker, testKey) @@ -454,9 +454,9 @@ func TestWorkerCertBundle(t *testing.T) { certificateBundle: certBundle, }, expected: &store.WorkerCertBundle{ - CertificatePublicKey: rootCA.PublicKey, - WorkerKeyIdentifier: workerAuth.WorkerKeyIdentifier, - CertBundle: certBundle, + RootCertificatePublicKey: rootCA.PublicKey, + WorkerKeyIdentifier: workerAuth.WorkerKeyIdentifier, + CertBundle: certBundle, }, wantErr: false, }, From 78ffd82581d16741f08ae769f018a768d9f50642 Mon Sep 17 00:00:00 2001 From: Jim Date: Fri, 3 Jun 2022 09:14:45 -0400 Subject: [PATCH 036/100] fix (servers): return unhandled err from storeRootCertificates(...) (#2127) --- internal/servers/repository_workerauth.go | 7 ++++++- internal/servers/workerauth_store_test.go | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/servers/repository_workerauth.go b/internal/servers/repository_workerauth.go index d266e16b5..95d22465d 100644 --- a/internal/servers/repository_workerauth.go +++ b/internal/servers/repository_workerauth.go @@ -248,8 +248,13 @@ func (r *WorkerAuthRepositoryStorage) storeRootCertificates(ctx context.Context, } nextCert, err := r.convertRootCertificate(ctx, cert.Next) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to convert next root certificate")) + } currentCert, err := r.convertRootCertificate(ctx, cert.Current) - + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to convert current root certificate")) + } // Use passed version var result rootCertificatesVersion err = mapstructure.Decode(cert.State.AsMap(), &result) diff --git a/internal/servers/workerauth_store_test.go b/internal/servers/workerauth_store_test.go index e66d6d67d..fdbe415d6 100644 --- a/internal/servers/workerauth_store_test.go +++ b/internal/servers/workerauth_store_test.go @@ -255,6 +255,7 @@ func TestDuplicateRootCert(t *testing.T) { current2, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, testKey, CurrentState) + require.NoError(t, err) next1, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, RootCertificateKeys{publicKey: populateBytes(defaultLength), privateKey: populateBytes(defaultLength)}, testKey, NextState) From cd9b10721f9459c043304f2750877fb56764b0fd Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 3 Jun 2022 11:05:07 -0400 Subject: [PATCH 037/100] Pull in new nodeenrollment lib to fix some bugs --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 36d495fed..8feafe862 100644 --- a/go.mod +++ b/go.mod @@ -92,7 +92,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b - github.com/hashicorp/nodeenrollment v0.0.0-20220602171424-99579ebf597b + github.com/hashicorp/nodeenrollment v0.0.0-20220603150451-f3e992e55fd8 ) require ( diff --git a/go.sum b/go.sum index e49b0dcd9..3b74234e0 100644 --- a/go.sum +++ b/go.sum @@ -740,8 +740,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220602171424-99579ebf597b h1:viKFHuNiBbmK31L53FDQ7azuMU8ytiPyEQPRXLaLqwg= -github.com/hashicorp/nodeenrollment v0.0.0-20220602171424-99579ebf597b/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.0.0-20220603150451-f3e992e55fd8 h1:stSjcYW8rtmRUtmoN/Kw7d+ciZULbeSxtBjdl6jz1Vk= +github.com/hashicorp/nodeenrollment v0.0.0-20220603150451-f3e992e55fd8/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= From 98449f25ebb7b107cd4c85bf31fcaf3b04e825f3 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 3 Jun 2022 11:09:02 -0400 Subject: [PATCH 038/100] Fix build --- internal/daemon/controller/controller.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index ae2955aed..79daf9700 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -421,5 +421,6 @@ func (c *Controller) AuthorizeNodeeWorker(request string) error { if err := proto.Unmarshal(reqBytes, req); err != nil { return fmt.Errorf("(%s) error unmarshaling common name value: %w", op, err) } - return registration.AuthorizeNode(c.baseContext, c.NodeeFileStorage, req) + _, err = registration.AuthorizeNode(c.baseContext, c.NodeeFileStorage, req) + return err } From 6cbb36f2b1d860c2e241525472c5e00c203768ce Mon Sep 17 00:00:00 2001 From: Jim Date: Fri, 3 Jun 2022 12:04:33 -0400 Subject: [PATCH 039/100] chore (servers): update nodeenrollment dep (#2129) The signature for registration.AuthorizeNode(...) changed --- internal/servers/repository_workerauth_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/servers/repository_workerauth_test.go b/internal/servers/repository_workerauth_test.go index 3e752bf86..7d90077ef 100644 --- a/internal/servers/repository_workerauth_test.go +++ b/internal/servers/repository_workerauth_test.go @@ -185,7 +185,8 @@ func TestStoreWorkerAuth(t *testing.T) { require.NoError(err) // The AuthorizeNode request will result in a WorkerAuth record being stored - require.NoError(registration.AuthorizeNode(ctx, storage, fetchReq, nodeenrollment.WithState(state))) + _, err = registration.AuthorizeNode(ctx, storage, fetchReq, nodeenrollment.WithState(state)) + require.NoError(err) // We should now look for a node information value in storage and validate that it's populated nodeInfos, err := storage.List(ctx, (*types.NodeInformation)(nil)) From 9da0faabd7888b31b65a991204accae2c6885ce3 Mon Sep 17 00:00:00 2001 From: Jim Date: Fri, 3 Jun 2022 12:28:54 -0400 Subject: [PATCH 040/100] refactor (servers/workerauth): add StoreNodeInformationTx(...) (#2125) refactor to export StoreNodeInformationTx(...) which can be used by other repositories within their transactions to store node information. --- internal/servers/repository_workerauth.go | 60 +++--- .../servers/repository_workerauth_test.go | 178 +++++++++++++++++- 2 files changed, 211 insertions(+), 27 deletions(-) diff --git a/internal/servers/repository_workerauth.go b/internal/servers/repository_workerauth.go index 95d22465d..44bd70520 100644 --- a/internal/servers/repository_workerauth.go +++ b/internal/servers/repository_workerauth.go @@ -74,10 +74,16 @@ func (r *WorkerAuthRepositoryStorage) Store(ctx context.Context, msg nodee.Messa // Determine type of message to store switch t := msg.(type) { case *types.NodeInformation: - err := r.storeNodeInformation(ctx, t) + // Encrypt the private key + databaseWrapper, err := r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase) if err != nil { return errors.Wrap(ctx, err, op) } + if _, err := r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(read db.Reader, w db.Writer) error { + return StoreNodeInformationTx(ctx, w, databaseWrapper, t) + }); err != nil { + return errors.Wrap(ctx, err, op) + } case *types.RootCertificates: err := r.storeRootCertificates(ctx, t) if err != nil { @@ -90,11 +96,26 @@ func (r *WorkerAuthRepositoryStorage) Store(ctx context.Context, msg nodee.Messa return nil } +// StoreNodeInformationTx stores NodeInformation. No options are currently +// supported. +// +// This function encapsulates all the work required within a dbw.TxHandler and +// allows this capability to be shared with other repositories or just called +// within a transaction. To be clear, this repository function doesn't include +// its own transaction and is intended to be used within a transaction provided +// by the caller. +// // Node information is stored in two parts: // * the workerAuth record is stored with a reference to a worker // * certificate bundles are stored with a reference to the workerAuth record and issuing root certificate -func (r *WorkerAuthRepositoryStorage) storeNodeInformation(ctx context.Context, node *types.NodeInformation) error { +func StoreNodeInformationTx(ctx context.Context, writer db.Writer, databaseWrapper wrapping.Wrapper, node *types.NodeInformation, _ ...Option) error { const op = "servers.(WorkerAuthRepositoryStorage).storeNodeInformation" + if isNil(writer) { + return errors.New(ctx, errors.InvalidParameter, op, "missing writer") + } + if isNil(databaseWrapper) { + return errors.New(ctx, errors.InvalidParameter, op, "missing database wrapper") + } if node == nil { return errors.New(ctx, errors.InvalidParameter, op, "missing NodeInformation") } @@ -104,11 +125,7 @@ func (r *WorkerAuthRepositoryStorage) storeNodeInformation(ctx context.Context, nodeAuth.WorkerEncryptionPubKey = node.EncryptionPublicKeyBytes nodeAuth.WorkerSigningPubKey = node.CertificatePublicKeyPkix - // Encrypt the private key - databaseWrapper, err := r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase) - if err != nil { - return errors.Wrap(ctx, err, op) - } + var err error nodeAuth.KeyId, err = databaseWrapper.KeyId(ctx) if err != nil { return errors.Wrap(ctx, err, op) @@ -134,30 +151,25 @@ func (r *WorkerAuthRepositoryStorage) storeNodeInformation(ctx context.Context, return errors.Wrap(ctx, err, op) } - _, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(read db.Reader, w db.Writer) error { - // Store WorkerAuth - storeNodeAuth := nodeAuth.clone() - if err := w.Create(ctx, &storeNodeAuth); err != nil { + // Store WorkerAuth + if err := writer.Create(ctx, &nodeAuth); err != nil { + return errors.Wrap(ctx, err, op) + } + // Then store cert bundles associated with this WorkerAuth + for _, c := range node.CertificateBundles { + if err := storeWorkerCertBundle(ctx, c, nodeAuth.WorkerKeyIdentifier, writer); err != nil { return errors.Wrap(ctx, err, op) } - // Then store cert bundles associated with this WorkerAuth - for _, c := range node.CertificateBundles { - if err := r.storeWorkerCertBundle(ctx, c, nodeAuth.WorkerKeyIdentifier, w); err != nil { - return errors.Wrap(ctx, err, op) - } - } - return nil - }, - ) - if err != nil { - return errors.Wrap(ctx, err, op) } return nil } -func (r *WorkerAuthRepositoryStorage) storeWorkerCertBundle(ctx context.Context, bundle *types.CertificateBundle, - workerKeyIdentifier string, writer db.Writer, +func storeWorkerCertBundle( + ctx context.Context, + bundle *types.CertificateBundle, + workerKeyIdentifier string, + writer db.Writer, ) error { const op = "servers.(WorkerAuthRepositoryStorage).storeWorkerCertBundle" if bundle == nil { diff --git a/internal/servers/repository_workerauth_test.go b/internal/servers/repository_workerauth_test.go index 7d90077ef..11f7bea3b 100644 --- a/internal/servers/repository_workerauth_test.go +++ b/internal/servers/repository_workerauth_test.go @@ -6,20 +6,21 @@ import ( "testing" "github.com/fatih/structs" - "github.com/mitchellh/mapstructure" - "google.golang.org/protobuf/types/known/structpb" - "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/types/scope" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/registration" "github.com/hashicorp/nodeenrollment/rotation" "github.com/hashicorp/nodeenrollment/storage/file" "github.com/hashicorp/nodeenrollment/types" + "github.com/mitchellh/mapstructure" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/crypto/curve25519" + "google.golang.org/protobuf/types/known/structpb" ) // Test RootCertificate storage @@ -235,3 +236,174 @@ func TestUnsupportedMessages(t *testing.T) { err = storage.Remove(ctx, &types.NodeCredentials{Id: "bogus"}) require.Error(err) } + +func TestStoreNodeInformationTx(t *testing.T) { + t.Parallel() + testCtx := context.Background() + + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + + testWrapper := db.TestWrapper(t) + testKeyId, err := testWrapper.KeyId(testCtx) + require.NoError(t, err) + + kmsCache := kms.TestKms(t, conn, testWrapper) + // Ensures the global scope contains a valid root key + err = kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(t, err) + databaseWrapper, err := kmsCache.GetWrapper(context.Background(), scope.Global.String(), kms.KeyPurposeDatabase) + require.NoError(t, err) + + testRootStorage, err := NewRepositoryStorage(testCtx, rw, rw, kmsCache) + require.NoError(t, err) + + _, err = rotation.RotateRootCertificates(testCtx, testRootStorage) + require.NoError(t, err) + + // Create struct to pass in with workerId that will be passed along to + // storage + testWorker := TestWorker(t, conn, testWrapper) + + testState, err := AttachWorkerIdToState(testCtx, testWorker.PublicId) + require.NoError(t, err) + + testNodeInfoFn := func() *types.NodeInformation { + // This happens on the worker + fileStorage, err := file.NewFileStorage(testCtx) + require.NoError(t, err) + nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage) + require.NoError(t, err) + + nodePubKey, err := curve25519.X25519(nodeCreds.EncryptionPrivateKeyBytes, curve25519.Basepoint) + require.NoError(t, err) + // Add in node information to storage so we have a key to use + nodeInfo := &types.NodeInformation{ + Id: testKeyId, + CertificatePublicKeyPkix: nodeCreds.CertificatePublicKeyPkix, + CertificatePublicKeyType: nodeCreds.CertificatePrivateKeyType, + EncryptionPublicKeyBytes: nodePubKey, + EncryptionPublicKeyType: nodeCreds.EncryptionPrivateKeyType, + ServerEncryptionPrivateKeyBytes: []byte("whatever"), + RegistrationNonce: nodeCreds.RegistrationNonce, + State: testState, + } + return nodeInfo + } + + tests := []struct { + name string + writer db.Writer + databaseWrapper wrapping.Wrapper + node *types.NodeInformation + wantErr bool + wantErrIs errors.Code + wantErrContains string + }{ + { + name: "missing-writer", + databaseWrapper: databaseWrapper, + node: testNodeInfoFn(), + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "missing writer", + }, + { + name: "missing-wrapper", + writer: rw, + node: testNodeInfoFn(), + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "missing database wrapper", + }, + { + name: "missing-node", + writer: rw, + databaseWrapper: databaseWrapper, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "missing NodeInformation", + }, + { + name: "key-id-error", + writer: rw, + databaseWrapper: &mockTestWrapper{err: errors.New(testCtx, errors.Internal, "testing", "key-id-error")}, + node: testNodeInfoFn(), + wantErr: true, + wantErrIs: errors.Internal, + wantErrContains: "key-id-error", + }, + { + name: "encrypt-error", + writer: rw, + databaseWrapper: &mockTestWrapper{ + encryptError: true, + err: errors.New(testCtx, errors.Encrypt, "testing", "encrypt-error"), + }, + node: testNodeInfoFn(), + wantErr: true, + wantErrIs: errors.Encrypt, + wantErrContains: "encrypt-error", + }, + { + name: "create-error", + writer: &db.Db{}, + databaseWrapper: databaseWrapper, + node: testNodeInfoFn(), + wantErr: true, + wantErrContains: "db.Create", + }, + { + name: "success", + writer: rw, + databaseWrapper: databaseWrapper, + node: testNodeInfoFn(), + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + err := StoreNodeInformationTx(testCtx, tc.writer, tc.databaseWrapper, tc.node) + if tc.wantErr { + require.Error(err) + if tc.wantErrIs != errors.Unknown { + assert.True(errors.Match(errors.T(tc.wantErrIs), err)) + } + if tc.wantErrContains != "" { + assert.Contains(err.Error(), tc.wantErrContains) + } + return + } + require.NoError(err) + }) + } +} + +type mockTestWrapper struct { + wrapping.Wrapper + decryptError bool + encryptError bool + err error + keyId string +} + +func (m *mockTestWrapper) KeyId(context.Context) (string, error) { + if m.err != nil { + return "", m.err + } + return m.keyId, nil +} + +func (m *mockTestWrapper) Encrypt(ctx context.Context, plaintext []byte, options ...wrapping.Option) (*wrapping.BlobInfo, error) { + if m.err != nil && m.encryptError { + return nil, m.err + } + panic("todo") +} + +func (m *mockTestWrapper) Decrypt(ctx context.Context, ciphertext *wrapping.BlobInfo, options ...wrapping.Option) ([]byte, error) { + if m.err != nil && m.decryptError { + return nil, m.err + } + return []byte("decrypted"), nil +} From 5c76017dd66905c271ebee1c67b8b6f7cd117570 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 3 Jun 2022 12:58:26 -0400 Subject: [PATCH 041/100] Fix nil storage --- internal/daemon/worker/worker.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 7da2e46cb..dce7259a1 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -178,10 +178,6 @@ func (w *Worker) Start() error { controllerResolver := manual.NewBuilderWithScheme(scheme) w.controllerResolver.Store(controllerResolver) - if err := w.startListeners(); err != nil { - return fmt.Errorf("error starting worker listeners: %w", err) - } - var err error w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext, nodeefile.WithBaseDirectory(w.conf.RawConfig.Worker.StoragePath)) @@ -189,6 +185,10 @@ func (w *Worker) Start() error { return err } + if err := w.startListeners(); err != nil { + return fmt.Errorf("error starting worker listeners: %w", err) + } + nodeCreds, err := types.NewNodeCredentials(w.baseContext, w.NodeeFileStorage) if err != nil { return fmt.Errorf("error generating new node creds: %w", err) From abbfe45419a12cee40a8548ab8ed3d3c80910bae Mon Sep 17 00:00:00 2001 From: Todd Date: Fri, 3 Jun 2022 11:19:04 -0700 Subject: [PATCH 042/100] Add the Read/List service functions (#2123) --- globals/fields.go | 6 + internal/daemon/controller/handler.go | 11 + .../handlers/workers/worker_service.go | 359 ++++++++++++++++++ .../handlers/workers/worker_service_test.go | 223 +++++++++++ internal/gen/controller.swagger.json | 13 +- .../api/resources/workers/v1/worker.proto | 8 +- internal/servers/testing.go | 34 +- internal/servers/testing_test.go | 11 +- .../api/resources/workers/worker.pb.go | 190 ++++----- 9 files changed, 747 insertions(+), 108 deletions(-) create mode 100644 internal/daemon/controller/handlers/workers/worker_service.go create mode 100644 internal/daemon/controller/handlers/workers/worker_service_test.go diff --git a/globals/fields.go b/globals/fields.go index 00589c64e..3d8d352c2 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -73,4 +73,10 @@ const ( CredentialTypeField = "credential_type" CredentialMappingOverridesField = "credential_mapping_overrides" MetricNamespace = "boundary" + LastStatusTimeField = "last_status_time" + AddressField = "address" + CanonicalAddressField = "canonical_address" + TagsField = "tags" + CanonicalTagsField = "canonical_tags" + ConfigurationField = "configuration" ) diff --git a/internal/daemon/controller/handler.go b/internal/daemon/controller/handler.go index d8ed85e48..d5757a81a 100644 --- a/internal/daemon/controller/handler.go +++ b/internal/daemon/controller/handler.go @@ -35,6 +35,7 @@ import ( "github.com/hashicorp/boundary/internal/daemon/controller/handlers/sessions" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/users" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers" "github.com/hashicorp/boundary/internal/daemon/controller/internal/metric" "github.com/hashicorp/boundary/internal/gen/controller/api/services" authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" @@ -224,6 +225,13 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error { } services.RegisterCredentialLibraryServiceServer(s, cl) } + if _, ok := currentServices[services.WorkerService_ServiceDesc.ServiceName]; !ok { + ws, err := workers.NewService(c.baseContext, c.ServersRepoFn, c.IamRepoFn) + if err != nil { + return fmt.Errorf("failed to create worker handler service: %w", err) + } + services.RegisterWorkerServiceServer(s, ws) + } if _, ok := s.GetServiceInfo()[opsservices.HealthService_ServiceDesc.ServiceName]; !ok { hs := health.NewService() opsservices.RegisterHealthServiceServer(s, hs) @@ -281,6 +289,9 @@ func registerGrpcGatewayEndpoints(ctx context.Context, gwMux *runtime.ServeMux, if err := services.RegisterCredentialLibraryServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil { return fmt.Errorf("failed to register credential library service handler: %w", err) } + if err := services.RegisterWorkerServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil { + return fmt.Errorf("failed to register worker service handler: %w", err) + } return nil } diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go new file mode 100644 index 000000000..4330e14e9 --- /dev/null +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -0,0 +1,359 @@ +package workers + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/globals" + "github.com/hashicorp/boundary/internal/daemon/controller/auth" + "github.com/hashicorp/boundary/internal/daemon/controller/common" + "github.com/hashicorp/boundary/internal/daemon/controller/common/scopeids" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers" + "github.com/hashicorp/boundary/internal/errors" + pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/perms" + "github.com/hashicorp/boundary/internal/requests" + "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" + "github.com/hashicorp/boundary/internal/types/scope" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" + "google.golang.org/grpc/codes" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +var ( + // IdActions contains the set of actions that can be performed on + // individual resources + IdActions = action.ActionSet{ + action.NoOp, + action.Read, + action.Update, + action.Delete, + } + + // CollectionActions contains the set of actions that can be performed on + // this collection + CollectionActions = action.ActionSet{ + action.Create, + action.List, + } +) + +// Service handles request as described by the pbs.WorkerServiceServer interface. +type Service struct { + pbs.UnimplementedWorkerServiceServer + + repoFn common.ServersRepoFactory + iamRepoFn common.IamRepoFactory +} + +// NewService returns a worker service which handles worker related requests to boundary. +func NewService(ctx context.Context, repo common.ServersRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) { + const op = "workers.NewService" + if repo == nil { + return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing servers repository") + } + if iamRepoFn == nil { + return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing iam repository") + } + return Service{repoFn: repo, iamRepoFn: iamRepoFn}, nil +} + +var _ pbs.WorkerServiceServer = Service{} + +// ListWorkers implements the interface pbs.WorkerServiceServer. +func (s Service) ListWorkers(ctx context.Context, req *pbs.ListWorkersRequest) (*pbs.ListWorkersResponse, error) { + if err := validateListRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetScopeId(), action.List) + if authResults.Error != nil { + // If it's forbidden, and it's a recursive request, and they're + // successfully authenticated but just not authorized, keep going as we + // may have authorization on downstream scopes. Or, if they've not + // authenticated, still process in case u_anon has permissions. + if (authResults.Error == handlers.ForbiddenError() || authResults.Error == handlers.UnauthenticatedError()) && + req.GetRecursive() && + authResults.AuthenticationFinished { + } else { + return nil, authResults.Error + } + } + + scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds( + ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.Worker, req.GetRecursive()) + if err != nil { + return nil, err + } + // If no scopes match, return an empty response + if len(scopeIds) == 0 { + return &pbs.ListWorkersResponse{}, nil + } + + ul, err := s.listFromRepo(ctx, scopeIds) + if err != nil { + return nil, err + } + if len(ul) == 0 { + return &pbs.ListWorkersResponse{}, nil + } + + filter, err := handlers.NewFilter(req.GetFilter()) + if err != nil { + return nil, err + } + finalItems := make([]*pb.Worker, 0, len(ul)) + res := perms.Resource{ + Type: resource.Worker, + } + for _, item := range ul { + res.Id = item.GetPublicId() + res.ScopeId = item.GetScopeId() + authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings() + if len(authorizedActions) == 0 { + continue + } + + outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId) + outputOpts := make([]handlers.Option, 0, 3) + outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if outputFields.Has(globals.ScopeField) { + outputOpts = append(outputOpts, handlers.WithScope(scopeInfoMap[item.GetScopeId()])) + } + if outputFields.Has(globals.AuthorizedActionsField) { + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions)) + } + + item, err := toProto(ctx, item, outputOpts...) + if err != nil { + return nil, err + } + + if filter.Match(item) { + finalItems = append(finalItems, item) + } + } + return &pbs.ListWorkersResponse{Items: finalItems}, nil +} + +// GetWorkers implements the interface pbs.WorkerServiceServer. +func (s Service) GetWorker(ctx context.Context, req *pbs.GetWorkerRequest) (*pbs.GetWorkerResponse, error) { + const op = "workers.(Service).GetWorker" + + if err := validateGetRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } + w, err := s.getFromRepo(ctx, req.GetId()) + if err != nil { + return nil, err + } + + outputFields, ok := requests.OutputFields(ctx) + if !ok { + return nil, errors.New(ctx, errors.Internal, op, "no request context found") + } + + outputOpts := make([]handlers.Option, 0, 3) + outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if outputFields.Has(globals.ScopeField) { + outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) + } + if outputFields.Has(globals.AuthorizedActionsField) { + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, w.GetPublicId(), IdActions).Strings())) + } + + item, err := toProto(ctx, w, outputOpts...) + if err != nil { + return nil, err + } + + return &pbs.GetWorkerResponse{Item: item}, nil +} + +func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*servers.Worker, error) { + repo, err := s.repoFn() + if err != nil { + return nil, err + } + wl, err := repo.ListWorkers(ctx, scopeIds, servers.WithLiveness(-1)) + if err != nil { + return nil, err + } + return wl, nil +} + +func (s Service) getFromRepo(ctx context.Context, id string) (*servers.Worker, error) { + repo, err := s.repoFn() + if err != nil { + return nil, err + } + w, err := repo.LookupWorker(ctx, id) + if err != nil { + if errors.IsNotFoundError(err) { + return nil, handlers.NotFoundErrorf("Worker %q doesn't exist.", id) + } + return nil, err + } + if w == nil { + return nil, handlers.NotFoundErrorf("Worker %q doesn't exist.", id) + } + return w, nil +} + +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + res := auth.VerifyResults{} + repo, err := s.repoFn() + if err != nil { + res.Error = err + return res + } + + var parentId string + opts := []auth.Option{auth.WithType(resource.Worker), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + default: + w, err := repo.LookupWorker(ctx, id) + if err != nil { + res.Error = err + return res + } + if w == nil { + res.Error = handlers.NotFoundError() + return res + } + parentId = w.GetScopeId() + opts = append(opts, auth.WithId(id)) + } + opts = append(opts, auth.WithScopeId(parentId)) + return auth.Verify(ctx, opts...) +} + +func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (*pb.Worker, error) { + const op = "workers.toProto" + opts := handlers.GetOpts(opt...) + if opts.WithOutputFields == nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building worker proto") + } + outputFields := *opts.WithOutputFields + + out := pb.Worker{} + if outputFields.Has(globals.IdField) { + out.Id = in.GetPublicId() + } + if outputFields.Has(globals.ScopeIdField) { + out.ScopeId = in.GetScopeId() + } + if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" { + out.Description = wrapperspb.String(in.GetDescription()) + } + if outputFields.Has(globals.NameField) && in.GetName() != "" { + out.Name = wrapperspb.String(in.GetName()) + } + if outputFields.Has(globals.CreatedTimeField) { + out.CreatedTime = in.GetCreateTime().GetTimestamp() + } + if outputFields.Has(globals.UpdatedTimeField) { + out.UpdatedTime = in.GetUpdateTime().GetTimestamp() + } + if outputFields.Has(globals.VersionField) { + out.Version = in.GetVersion() + } + if outputFields.Has(globals.ScopeField) { + out.Scope = opts.WithScope + } + if outputFields.Has(globals.AuthorizedActionsField) { + out.AuthorizedActions = opts.WithAuthorizedActions + } + if outputFields.Has(globals.AddressField) && in.GetAddress() != "" { + out.Address = wrapperspb.String(in.GetAddress()) + } + if outputFields.Has(globals.CanonicalAddressField) { + out.CanonicalAddress = in.CanonicalAddress() + } + if outputFields.Has(globals.LastStatusTimeField) { + out.LastStatusTime = in.GetLastStatusTime().GetTimestamp() + } + if outputFields.Has(globals.CanonicalTagsField) && len(in.CanonicalTags()) > 0 { + var err error + out.Tags, err = tagsToMapProto(in.CanonicalTags()) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing canonical tags proto")) + } + } + if outputFields.Has(globals.TagsField) && len(in.GetApiTags()) > 0 { + var err error + out.Tags, err = tagsToMapProto(in.GetApiTags()) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing api tags proto")) + } + } + if outputFields.Has(globals.ConfigurationField) { + if in.GetWorkerReportedAddress() != "" || + in.GetWorkerReportedName() != "" || + len(in.GetConfigTags()) > 0 { + out.WorkerConfig = &pb.WorkerConfig{} + } + if len(in.GetConfigTags()) > 0 { + var err error + out.GetWorkerConfig().Tags, err = tagsToMapProto(in.GetConfigTags()) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing config tags proto")) + } + } + out.GetWorkerConfig().Address = in.GetWorkerReportedAddress() + out.GetWorkerConfig().Name = in.GetWorkerReportedName() + } + + return &out, nil +} + +func tagsToMapProto(in map[string][]string) (map[string]*structpb.ListValue, error) { + b := make(map[string][]interface{}) + for k, v := range in { + result := make([]interface{}, 0, len(v)) + for _, t := range v { + result = append(result, t) + } + b[k] = result + } + ret := make(map[string]*structpb.ListValue) + var err error + for k, v := range b { + ret[k], err = structpb.NewList(v) + if err != nil { + return nil, err + } + } + return ret, nil +} + +// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce +// requirements on the structure of the request. They verify that: +// * The path passed in is correctly formatted +// * All required parameters are set +// * There are no conflicting parameters provided +func validateGetRequest(req *pbs.GetWorkerRequest) error { + return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, servers.WorkerPrefix) +} + +func validateListRequest(req *pbs.ListWorkersRequest) error { + badFields := map[string]string{} + if req.GetScopeId() != scope.Global.String() { + badFields["scope_id"] = "Must be 'global' when listing." + } + if _, err := handlers.NewFilter(req.GetFilter()); err != nil { + badFields["filter"] = fmt.Sprintf("This field could not be parsed. %v", err) + } + if len(badFields) > 0 { + return handlers.InvalidArgumentErrorf("Error in provided request.", badFields) + } + return nil +} diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go new file mode 100644 index 000000000..007c8da98 --- /dev/null +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -0,0 +1,223 @@ +package workers + +import ( + "context" + "fmt" + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/boundary/internal/daemon/controller/auth" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +var testAuthorizedActions = []string{"no-op", "read", "update", "delete"} + +func TestGet(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrap := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, wrap) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + rw := db.New(conn) + kms := kms.TestKms(t, conn, wrap) + repoFn := func() (*servers.Repository, error) { + return servers.NewRepository(rw, rw, kms) + } + + worker := servers.TestWorker(t, conn, wrap, + servers.WithName("test worker names"), + servers.WithDescription("test worker description"), + servers.WithAddress("test worker address")) + + wantWorker := &pb.Worker{ + Id: worker.GetPublicId(), + ScopeId: worker.GetScopeId(), + Scope: &scopes.ScopeInfo{Id: worker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"}, + CreatedTime: worker.CreateTime.GetTimestamp(), + UpdatedTime: worker.UpdateTime.GetTimestamp(), + Version: worker.GetVersion(), + Name: wrapperspb.String(worker.GetName()), + Description: wrapperspb.String(worker.GetDescription()), + Address: wrapperspb.String(worker.GetAddress()), + AuthorizedActions: testAuthorizedActions, + CanonicalAddress: worker.CanonicalAddress(), + LastStatusTime: worker.GetLastStatusTime().GetTimestamp(), + WorkerConfig: &pb.WorkerConfig{ + Address: worker.GetWorkerReportedAddress(), + Name: worker.GetWorkerReportedName(), + }, + } + + cases := []struct { + name string + scopeId string + req *pbs.GetWorkerRequest + res *pbs.GetWorkerResponse + err error + }{ + { + name: "Get an Existing Worker", + scopeId: worker.GetScopeId(), + req: &pbs.GetWorkerRequest{Id: worker.GetPublicId()}, + res: &pbs.GetWorkerResponse{Item: wantWorker}, + }, + { + name: "Get a non existant Worker", + req: &pbs.GetWorkerRequest{Id: servers.WorkerPrefix + "_DoesntExis"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.NotFound), + }, + { + name: "Wrong id prefix", + req: &pbs.GetWorkerRequest{Id: "j_1234567890"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "space in id", + req: &pbs.GetWorkerRequest{Id: servers.WorkerPrefix + "_1 23456789"}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + s, err := NewService(context.Background(), repoFn, iamRepoFn) + require.NoError(t, err, "Couldn't create new worker service.") + + got, err := s.GetWorker(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), tc.req) + if tc.err != nil { + require.Error(t, err) + assert.True(t, errors.Is(err, tc.err), "GetWorker(%+v) got error %v, wanted %v", tc.req, err, tc.err) + } else { + require.NoError(t, err) + } + assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform()), "GetWorker(%q) got response\n%q, wanted\n%q", tc.req, got, tc.res) + }) + } +} + +func TestList(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrap := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, wrap) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + rw := db.New(conn) + kms := kms.TestKms(t, conn, wrap) + repoFn := func() (*servers.Repository, error) { + return servers.NewRepository(rw, rw, kms) + } + + var wantWorkers []*pb.Worker + for i := 0; i < 10; i++ { + w := servers.TestWorker(t, conn, wrap, servers.WithName(fmt.Sprintf("worker%d", i))) + wantWorkers = append(wantWorkers, &pb.Worker{ + Id: w.GetPublicId(), + ScopeId: w.GetScopeId(), + Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"}, + CreatedTime: w.CreateTime.GetTimestamp(), + UpdatedTime: w.UpdateTime.GetTimestamp(), + Version: w.GetVersion(), + Name: wrapperspb.String(w.GetName()), + AuthorizedActions: testAuthorizedActions, + CanonicalAddress: w.CanonicalAddress(), + LastStatusTime: w.GetLastStatusTime().GetTimestamp(), + WorkerConfig: &pb.WorkerConfig{ + Address: w.GetWorkerReportedAddress(), + Name: w.GetWorkerReportedName(), + }, + }) + } + + cases := []struct { + name string + req *pbs.ListWorkersRequest + res *pbs.ListWorkersResponse + err error + }{ + { + name: "List All Workers", + req: &pbs.ListWorkersRequest{ScopeId: scope.Global.String()}, + res: &pbs.ListWorkersResponse{Items: wantWorkers}, + }, + { + name: "List global workers recursively", + req: &pbs.ListWorkersRequest{ScopeId: "global", Recursive: true}, + res: &pbs.ListWorkersResponse{ + Items: wantWorkers, + }, + }, + { + name: "Filter to a single workers", + req: &pbs.ListWorkersRequest{ScopeId: "global", Recursive: true, Filter: `"/item/name"=="worker2"`}, + res: &pbs.ListWorkersResponse{ + Items: wantWorkers[2:3], + }, + }, + { + name: "Filter to 2 workers", + req: &pbs.ListWorkersRequest{ScopeId: "global", Recursive: true, Filter: `"/item/name" matches "worker[23]"`}, + res: &pbs.ListWorkersResponse{ + Items: wantWorkers[2:4], + }, + }, + { + name: "Filter to no workers", + req: &pbs.ListWorkersRequest{ScopeId: "global", Recursive: true, Filter: `"/item/id"=="doesntmatch"`}, + res: &pbs.ListWorkersResponse{}, + }, + { + name: "Filter Bad Format", + req: &pbs.ListWorkersRequest{ScopeId: "global", Filter: `"//id/"=="bad"`}, + err: handlers.InvalidArgumentErrorf("bad format", nil), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + s, err := NewService(context.Background(), repoFn, iamRepoFn) + require.NoError(err, "Couldn't create new worker service.") + + // Test with a non-anon user + got, gErr := s.ListWorkers(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId()), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "ListWorkers(%q) got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err) + return + } + require.NoError(gErr) + sort.Slice(got.Items, func(i, j int) bool { + return got.Items[i].GetName().GetValue() < got.Items[j].GetName().GetValue() + }) + assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListWorkers(%q) got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res) + + // Test the anon case + got, gErr = s.ListWorkers(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId(), auth.WithUserId(auth.AnonymousUserId)), tc.req) + require.NoError(gErr) + assert.Len(got.Items, len(tc.res.Items)) + for _, item := range got.GetItems() { + require.Nil(item.CreatedTime) + require.Nil(item.UpdatedTime) + require.Zero(item.Version) + } + }) + } +} diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 76cb6f60d..abbb46a40 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -5579,6 +5579,14 @@ "$ref": "#/definitions/controller.api.resources.workers.v1.WorkerConfig", "description": "Output only. The set of information the worker daemon has reported about\nitself.", "readOnly": true + }, + "authorized_actions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Output only. The available actions on this resource for the requester.", + "readOnly": true } }, "title": "Worker contains all fields related to a Worker resource" @@ -5596,11 +5604,6 @@ "description": "Output only. The name the worker daemon reports itself having.", "readOnly": true }, - "version": { - "type": "string", - "description": "Output only. The boundary version the worker has reported itself as running.", - "readOnly": true - }, "tags": { "type": "object", "additionalProperties": { diff --git a/internal/proto/controller/api/resources/workers/v1/worker.proto b/internal/proto/controller/api/resources/workers/v1/worker.proto index 1f9112cb0..1b5a1fe45 100644 --- a/internal/proto/controller/api/resources/workers/v1/worker.proto +++ b/internal/proto/controller/api/resources/workers/v1/worker.proto @@ -18,9 +18,6 @@ message WorkerConfig { // Output only. The name the worker daemon reports itself having. string name = 20; - // Output only. The boundary version the worker has reported itself as running. - string version = 30; - // Output only. The tags reporteed by the worker daemon. map tags = 40; } @@ -87,5 +84,8 @@ message Worker { // itself. WorkerConfig worker_config = 140 [json_name = "worker_config"]; -// TODO: Add worker auth related fields. + // TODO: Add worker auth related fields. + + // Output only. The available actions on this resource for the requester. + repeated string authorized_actions = 300 [json_name = "authorized_actions"]; // @gotags: `class:"public"` } diff --git a/internal/servers/testing.go b/internal/servers/testing.go index fee3a0333..f09b65eae 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -81,22 +81,48 @@ func TestWorkerAuth(ctx context.Context, t *testing.T, conn *db.DB, worker *Work } // TestWorker inserts a worker into the db to satisfy foreign key constraints. -func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *Worker { +// The worker provided fields are auto generated. WithName, WithDescription, +// and WithAddress are applied to the resource name, description and address if +// present. +func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Worker { t.Helper() rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) serversRepo, err := NewRepository(rw, rw, kms) require.NoError(t, err) + ctx := context.Background() - id, err := newWorkerId(context.Background()) + namePart, err := newWorkerId(ctx) require.NoError(t, err) - name := "test-worker-" + strings.ToLower(id) + name := "test-worker-" + strings.ToLower(namePart) wrk := NewWorkerForStatus(scope.Global.String(), WithName(name), WithAddress("127.0.0.1")) - wrk, err = serversRepo.UpsertWorkerStatus(context.Background(), wrk) + wrk, err = serversRepo.UpsertWorkerStatus(ctx, wrk) require.NoError(t, err) require.NotNil(t, wrk) + opts := getOpts(opt...) + + var mask []string + if opts.withName != "" { + wrk.Name = opts.withName + mask = append(mask, "name") + } + if opts.withDescription != "" { + wrk.Description = opts.withDescription + mask = append(mask, "description") + } + if opts.withAddress != "" { + wrk.Address = opts.withAddress + mask = append(mask, "address") + } + if len(mask) > 0 { + var n int + wrk, n, err = serversRepo.UpdateWorker(ctx, wrk, wrk.Version, mask) + require.NoError(t, err) + require.Equal(t, 1, n) + require.NotNil(t, wrk) + } return wrk } diff --git a/internal/servers/testing_test.go b/internal/servers/testing_test.go index 4525dc678..c12687450 100644 --- a/internal/servers/testing_test.go +++ b/internal/servers/testing_test.go @@ -14,7 +14,12 @@ import ( func TestTestWorker(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) - tWorker := TestWorker(t, conn, wrapper) + const ( + name = "test name" + description = "test description" + address = "test address" + ) + tWorker := TestWorker(t, conn, wrapper, WithName(name), WithDescription(description), WithAddress(address)) assert.NotNil(t, tWorker) assert.True(t, strings.HasPrefix(tWorker.GetPublicId(), WorkerPrefix)) @@ -23,4 +28,8 @@ func TestTestWorker(t *testing.T) { rw := db.New(conn) require.NoError(t, rw.LookupById(context.Background(), lkpWorker)) assert.NotNil(t, lkpWorker) + assert.NotNil(t, lkpWorker.GetLastStatusTime()) + assert.Equal(t, name, lkpWorker.GetName()) + assert.Equal(t, description, lkpWorker.GetDescription()) + assert.Equal(t, address, lkpWorker.GetAddress()) } diff --git a/sdk/pbs/controller/api/resources/workers/worker.pb.go b/sdk/pbs/controller/api/resources/workers/worker.pb.go index a35883402..db30fc9a4 100644 --- a/sdk/pbs/controller/api/resources/workers/worker.pb.go +++ b/sdk/pbs/controller/api/resources/workers/worker.pb.go @@ -35,8 +35,6 @@ type WorkerConfig struct { Address string `protobuf:"bytes,10,opt,name=address,proto3" json:"address,omitempty"` // Output only. The name the worker daemon reports itself having. Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty"` - // Output only. The boundary version the worker has reported itself as running. - Version string `protobuf:"bytes,30,opt,name=version,proto3" json:"version,omitempty"` // Output only. The tags reporteed by the worker daemon. Tags map[string]*structpb.ListValue `protobuf:"bytes,40,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -87,13 +85,6 @@ func (x *WorkerConfig) GetName() string { return "" } -func (x *WorkerConfig) GetVersion() string { - if x != nil { - return x.Version - } - return "" -} - func (x *WorkerConfig) GetTags() map[string]*structpb.ListValue { if x != nil { return x.Tags @@ -141,6 +132,8 @@ type Worker struct { // Output only. The set of information the worker daemon has reported about // itself. WorkerConfig *WorkerConfig `protobuf:"bytes,140,opt,name=worker_config,proto3" json:"worker_config,omitempty"` + // Output only. The available actions on this resource for the requester. + AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty"` // @gotags: `class:"public"` } func (x *Worker) Reset() { @@ -273,6 +266,13 @@ func (x *Worker) GetWorkerConfig() *WorkerConfig { return nil } +func (x *Worker) GetAuthorizedActions() []string { + if x != nil { + return x.AuthorizedActions + } + return nil +} + var File_controller_api_resources_workers_v1_worker_proto protoreflect.FileDescriptor var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ @@ -293,95 +293,97 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xfc, 0x01, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x74, 0x6f, 0x22, 0xe2, 0x01, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x04, 0x74, - 0x61, 0x67, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x61, 0x67, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x53, 0x0a, 0x09, - 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0xac, 0x08, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, - 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, - 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, - 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x46, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, - 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, - 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x5a, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, - 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, - 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, - 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, - 0x61, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, - 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, - 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x61, 0x6e, - 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x10, 0x6c, 0x61, - 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x82, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x65, 0x12, 0x4f, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, + 0x67, 0x73, 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xdd, 0x08, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, + 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, + 0x6f, 0x70, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, + 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, + 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, - 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x53, 0x0a, - 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, - 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, - 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, - 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, + 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x61, + 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, + 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x0e, 0x63, 0x61, 0x6e, + 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x3e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, + 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0d, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, + 0x12, 0x47, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x77, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, + 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, + 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, + 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( From 5f66d8f2af83d59619e02416696c929b3ddb935a Mon Sep 17 00:00:00 2001 From: Jim Date: Fri, 3 Jun 2022 14:32:51 -0400 Subject: [PATCH 043/100] feature (worker): add WithFetchNodeCredentialsRequest to CreateWorker (#2131) * feature (errors): add support fmt specifier with operands to WithMsg(...) * feature (worker): add WithFetchNodeCredentialsRequest to CreateWorker * fixup! feature (worker): add WithFetchNodeCredentialsRequest to CreateWorker --- internal/errors/error.go | 2 +- internal/errors/option.go | 7 +++-- internal/errors/option_test.go | 7 +++++ internal/servers/options.go | 11 +++++++ internal/servers/repository_worker.go | 45 +++++++++++++++++++++------ 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/internal/errors/error.go b/internal/errors/error.go index 72494c72d..d505210d8 100644 --- a/internal/errors/error.go +++ b/internal/errors/error.go @@ -74,7 +74,7 @@ func E(ctx context.Context, opt ...Option) error { Code: code, Op: opts.withOp, Wrapped: opts.withErrWrapped, - Msg: opts.withErrMsg, + Msg: fmt.Sprintf(opts.withErrMsg, opts.withErrMsgArgs...), } if opts.withoutEvent { return err diff --git a/internal/errors/option.go b/internal/errors/option.go index 89bc577ab..1b70ba9f8 100644 --- a/internal/errors/option.go +++ b/internal/errors/option.go @@ -17,6 +17,7 @@ type Options struct { withCode Code withErrWrapped error withErrMsg string + withErrMsgArgs []any withOp Op withoutEvent bool } @@ -34,10 +35,12 @@ func WithWrap(e error) Option { } // WithMsg provides an option to provide a message when creating a new -// error. -func WithMsg(msg string) Option { +// error. If args are provided, the the msg string is used as a fmt specifier +// for the arguments and the resulting string is used as the msg. +func WithMsg(msg string, args ...any) Option { return func(o *Options) { o.withErrMsg = msg + o.withErrMsgArgs = args } } diff --git a/internal/errors/option_test.go b/internal/errors/option_test.go index 3d7a0ac7e..88912a408 100644 --- a/internal/errors/option_test.go +++ b/internal/errors/option_test.go @@ -22,6 +22,13 @@ func Test_getOpts(t *testing.T) { opts = GetOpts(WithMsg("test msg")) testOpts.withErrMsg = "test msg" assert.Equal(opts, testOpts) + + opts = GetOpts(WithMsg("%s msg", "test")) + testOpts.withErrMsg = "%s msg" + testOpts.withErrMsgArgs = []any{"test"} + assert.Equal(opts, testOpts) + + assert.Equal("#1 test msg: unknown: error #0", E(context.Background(), WithMsg("#%d %s msg", 1, "test")).Error()) }) t.Run("WithWrap", func(t *testing.T) { assert := assert.New(t) diff --git a/internal/servers/options.go b/internal/servers/options.go index 9fb7f6f2e..999782588 100644 --- a/internal/servers/options.go +++ b/internal/servers/options.go @@ -3,6 +3,8 @@ package servers import ( "context" "time" + + "github.com/hashicorp/nodeenrollment/types" ) // getOpts - iterate the inbound Options and return a struct @@ -32,6 +34,7 @@ type options struct { withKeyId string withNonce []byte withNewIdFunc func(context.Context) (string, error) + withFetchNodeCredentialsRequest *types.FetchNodeCredentialsRequest } func getDefaultOptions() options { @@ -132,3 +135,11 @@ func WithNewIdFunc(fn func(context.Context) (string, error)) Option { o.withNewIdFunc = fn } } + +// WithFetchNodeCredentialsRequest allows an optional +// FetchNodeCredentialsRequest to be specified. +func WithFetchNodeCredentialsRequest(req *types.FetchNodeCredentialsRequest) Option { + return func(o *options) { + o.withFetchNodeCredentialsRequest = req + } +} diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index 787521afe..3740af566 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -8,8 +8,12 @@ import ( "github.com/hashicorp/boundary/internal/db" dbcommon "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/types/scope" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/registration" ) // DeleteWorker will delete a worker from the repository. @@ -383,7 +387,8 @@ func (r *Repository) UpdateWorker(ctx context.Context, worker *Worker, version u // ReportedStatus and Tags are intentionally ignored when creating a worker (not // included). Currently, a worker can only be created in the global scope // -// Options supported: WithNewIdFunc (this option is likely only useful for tests) +// Options supported: WithFetchNodeCredentialsRequest and WithNewIdFunc (this +// option is likely only useful for tests) func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Option) (*Worker, error) { const op = "servers.CreateWorker" switch { @@ -409,25 +414,47 @@ func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Op return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to generate worker id")) } + var databaseWrapper wrapping.Wrapper + var workerAuthRepo *WorkerAuthRepositoryStorage + if opts.withFetchNodeCredentialsRequest != nil { + // used to encrypt the privKey within the NodeInformation + databaseWrapper, err = r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + } + var returnedWorker *Worker - _, err = r.writer.DoTx( + if _, err := r.writer.DoTx( ctx, db.StdRetryCnt, db.ExpBackoff{}, func(_ db.Reader, w db.Writer) error { returnedWorker = worker.clone() - err := w.Create( + if err := w.Create( ctx, returnedWorker, - ) - if err != nil { - return errors.Wrap(ctx, err, op) + ); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create worker")) + } + if opts.withFetchNodeCredentialsRequest != nil { + workerAuthRepo, err = NewRepositoryStorage(ctx, r.reader, r.writer, r.kms) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create worker auth repository")) + } + nodeInfo, err := registration.AuthorizeNode(ctx, workerAuthRepo, opts.withFetchNodeCredentialsRequest, nodeenrollment.WithSkipStorage(true)) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to authorize node")) + } + if err := StoreNodeInformationTx(ctx, w, databaseWrapper, nodeInfo); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to store node information")) + } } return nil }, - ) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create worker")) + ); err != nil { + return nil, errors.Wrap(ctx, err, op) } return returnedWorker, nil } From a1c2f9f4d89dec9536e45e9095bb1b4c9837ee4c Mon Sep 17 00:00:00 2001 From: Todd Date: Fri, 3 Jun 2022 11:34:52 -0700 Subject: [PATCH 044/100] Add delete service function implementation. (#2124) --- .../handlers/workers/worker_service.go | 38 ++++++++++- .../handlers/workers/worker_service_test.go | 63 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index 4330e14e9..372781051 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -138,7 +138,7 @@ func (s Service) ListWorkers(ctx context.Context, req *pbs.ListWorkersRequest) ( return &pbs.ListWorkersResponse{Items: finalItems}, nil } -// GetWorkers implements the interface pbs.WorkerServiceServer. +// GetWorker implements the interface pbs.WorkerServiceServer. func (s Service) GetWorker(ctx context.Context, req *pbs.GetWorkerRequest) (*pbs.GetWorkerResponse, error) { const op = "workers.(Service).GetWorker" @@ -176,6 +176,22 @@ func (s Service) GetWorker(ctx context.Context, req *pbs.GetWorkerRequest) (*pbs return &pbs.GetWorkerResponse{Item: item}, nil } +// DeleteWorker implements the interface pbs.WorkerServiceServer. +func (s Service) DeleteWorker(ctx context.Context, req *pbs.DeleteWorkerRequest) (*pbs.DeleteWorkerResponse, error) { + if err := validateDeleteRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } + _, err := s.deleteFromRepo(ctx, req.GetId()) + if err != nil { + return nil, err + } + return nil, nil +} + func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*servers.Worker, error) { repo, err := s.repoFn() if err != nil { @@ -206,6 +222,22 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*servers.Worker, e return w, nil } +func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) { + const op = "workers.(Service).deleteFromRepo" + repo, err := s.repoFn() + if err != nil { + return false, err + } + rows, err := repo.DeleteWorker(ctx, id) + if err != nil { + if errors.IsNotFoundError(err) { + return false, nil + } + return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete worker")) + } + return rows > 0, nil +} + func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { res := auth.VerifyResults{} repo, err := s.repoFn() @@ -357,3 +389,7 @@ func validateListRequest(req *pbs.ListWorkersRequest) error { } return nil } + +func validateDeleteRequest(req *pbs.DeleteWorkerRequest) error { + return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, servers.WorkerPrefix) +} diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index 007c8da98..98cb863a4 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -221,3 +221,66 @@ func TestList(t *testing.T) { }) } } + +func TestDelete(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrap := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, wrap) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + rw := db.New(conn) + kms := kms.TestKms(t, conn, wrap) + repoFn := func() (*servers.Repository, error) { + return servers.NewRepository(rw, rw, kms) + } + ctx := context.Background() + + s, err := NewService(ctx, repoFn, iamRepoFn) + require.NoError(t, err, "Error when getting new worker service.") + + w := servers.TestWorker(t, conn, wrap) + + cases := []struct { + name string + scopeId string + req *pbs.DeleteWorkerRequest + res *pbs.DeleteWorkerResponse + err error + }{ + { + name: "Delete an Existing Worker", + scopeId: w.GetScopeId(), + req: &pbs.DeleteWorkerRequest{ + Id: w.GetPublicId(), + }, + }, + { + name: "Delete bad worker id", + scopeId: w.GetScopeId(), + req: &pbs.DeleteWorkerRequest{ + Id: servers.WorkerPrefix + "_doesntexis", + }, + err: handlers.ApiErrorWithCode(codes.NotFound), + }, + { + name: "Bad Worker Id formatting", + scopeId: w.GetScopeId(), + req: &pbs.DeleteWorkerRequest{ + Id: "bad_format", + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, gErr := s.DeleteWorker(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "DeleteWorker(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) + } + assert.EqualValuesf(tc.res, got, "DeleteWorker(%+v) got response %q, wanted %q", tc.req, got, tc.res) + }) + } +} From 1987264bc357c5266a13053c0f350491ea6acaac Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 3 Jun 2022 14:44:53 -0400 Subject: [PATCH 045/100] Migrate storage to database repo (#2132) --- api/util.go | 8 --- internal/daemon/controller/common/common.go | 25 ++++---- internal/daemon/controller/controller.go | 62 ++++++++++++------- internal/daemon/controller/listeners.go | 10 ++- internal/daemon/controller/listeners_test.go | 4 -- internal/daemon/worker/worker.go | 13 ++-- internal/servers/repository_worker.go | 8 +++ internal/servers/repository_workerauth.go | 24 ++++--- .../servers/repository_workerauth_test.go | 14 ++++- internal/servers/worker.go | 2 +- 10 files changed, 102 insertions(+), 68 deletions(-) diff --git a/api/util.go b/api/util.go index 4dd69368c..67669db03 100644 --- a/api/util.go +++ b/api/util.go @@ -1,13 +1,5 @@ package api -import ( - "github.com/fatih/structs" -) - -func init() { - structs.DefaultTagName = "json" -} - type GenericResult interface { GetItem() interface{} GetResponse() *Response diff --git a/internal/daemon/controller/common/common.go b/internal/daemon/controller/common/common.go index 4945b44d9..f520d4f99 100644 --- a/internal/daemon/controller/common/common.go +++ b/internal/daemon/controller/common/common.go @@ -14,16 +14,17 @@ import ( ) type ( - AuthTokenRepoFactory = oidc.AuthTokenRepoFactory - VaultCredentialRepoFactory = func() (*vault.Repository, error) - IamRepoFactory func() (*iam.Repository, error) - OidcAuthRepoFactory = oidc.OidcRepoFactory - PasswordAuthRepoFactory func() (*password.Repository, error) - ServersRepoFactory func() (*servers.Repository, error) - StaticRepoFactory func() (*static.Repository, error) - PluginHostRepoFactory func() (*pluginhost.Repository, error) - HostPluginRepoFactory func() (*hostplugin.Repository, error) - SessionRepoFactory func() (*session.Repository, error) - ConnectionRepoFactory func() (*session.ConnectionRepository, error) - TargetRepoFactory func() (*target.Repository, error) + AuthTokenRepoFactory = oidc.AuthTokenRepoFactory + VaultCredentialRepoFactory = func() (*vault.Repository, error) + IamRepoFactory func() (*iam.Repository, error) + OidcAuthRepoFactory = oidc.OidcRepoFactory + PasswordAuthRepoFactory func() (*password.Repository, error) + ServersRepoFactory func() (*servers.Repository, error) + StaticRepoFactory func() (*static.Repository, error) + PluginHostRepoFactory func() (*pluginhost.Repository, error) + HostPluginRepoFactory func() (*hostplugin.Repository, error) + SessionRepoFactory func() (*session.Repository, error) + ConnectionRepoFactory func() (*session.ConnectionRepository, error) + TargetRepoFactory func() (*target.Repository, error) + WorkerAuthRepoStorageFactory func() (*servers.WorkerAuthRepositoryStorage, error) ) diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index 79daf9700..212fdcc55 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -27,6 +27,7 @@ import ( "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/scheduler/job" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/types/scope" @@ -36,9 +37,7 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/hashicorp/go-secure-stdlib/pluginutil/v2" - "github.com/hashicorp/nodeenrollment/registration" "github.com/hashicorp/nodeenrollment/rotation" - nodeefile "github.com/hashicorp/nodeenrollment/storage/file" "github.com/hashicorp/nodeenrollment/types" "github.com/mr-tron/base58" ua "go.uber.org/atomic" @@ -70,18 +69,19 @@ type Controller struct { apiGrpcGatewayTicket string // Repo factory methods - AuthTokenRepoFn common.AuthTokenRepoFactory - VaultCredentialRepoFn common.VaultCredentialRepoFactory - IamRepoFn common.IamRepoFactory - OidcRepoFn common.OidcAuthRepoFactory - PasswordAuthRepoFn common.PasswordAuthRepoFactory - ServersRepoFn common.ServersRepoFactory - SessionRepoFn common.SessionRepoFactory - ConnectionRepoFn common.ConnectionRepoFactory - StaticHostRepoFn common.StaticRepoFactory - PluginHostRepoFn common.PluginHostRepoFactory - HostPluginRepoFn common.HostPluginRepoFactory - TargetRepoFn common.TargetRepoFactory + AuthTokenRepoFn common.AuthTokenRepoFactory + VaultCredentialRepoFn common.VaultCredentialRepoFactory + IamRepoFn common.IamRepoFactory + OidcRepoFn common.OidcAuthRepoFactory + PasswordAuthRepoFn common.PasswordAuthRepoFactory + ServersRepoFn common.ServersRepoFactory + SessionRepoFn common.SessionRepoFactory + ConnectionRepoFn common.ConnectionRepoFactory + StaticHostRepoFn common.StaticRepoFactory + PluginHostRepoFn common.PluginHostRepoFactory + HostPluginRepoFn common.HostPluginRepoFactory + TargetRepoFn common.TargetRepoFactory + WorkerAuthRepoStorageFn common.WorkerAuthRepoStorageFactory scheduler *scheduler.Scheduler @@ -92,9 +92,6 @@ type Controller struct { // Used to signal the Health Service to start // replying to queries with "503 Service Unavailable". HealthService *health.Service - - // PoC: Testing bits for BYOW - NodeeFileStorage *nodeefile.FileStorage } func New(ctx context.Context, conf *Config) (*Controller, error) { @@ -304,6 +301,9 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { c.ConnectionRepoFn = func() (*session.ConnectionRepository, error) { return session.NewConnectionRepository(ctx, dbase, dbase, c.kms) } + c.WorkerAuthRepoStorageFn = func() (*servers.WorkerAuthRepositoryStorage, error) { + return servers.NewRepositoryStorage(ctx, dbase, dbase, c.kms) + } return c, nil } @@ -317,12 +317,11 @@ func (c *Controller) Start() error { c.baseContext, c.baseCancel = context.WithCancel(context.Background()) - var err error - c.NodeeFileStorage, err = nodeefile.NewFileStorage(c.baseContext) + workerAuthStorage, err := c.WorkerAuthRepoStorageFn() if err != nil { - return err + return fmt.Errorf("error fetching worker auth storage: %w", err) } - _, err = rotation.RotateRootCertificates(c.baseContext, c.NodeeFileStorage) + _, err = rotation.RotateRootCertificates(c.baseContext, workerAuthStorage) if err != nil { return err } @@ -410,17 +409,32 @@ func (c *Controller) WorkerStatusUpdateTimes() *sync.Map { return c.workerStatusUpdateTimes } +// This is a temporary function until the API is up func (c *Controller) AuthorizeNodeeWorker(request string) error { - const op = "controller.(Controller).AuthorizeWorker" + const op = "controller.(Controller).AuthorizeNodeeWorker" reqBytes, err := base58.FastBase58Decoding(request) if err != nil { - return fmt.Errorf("(%s) error base64-decoding fetch node creds next proto value", op) + return fmt.Errorf("(%s) error base58-decoding fetch node creds next proto value: %w", op, err) } // Decode the proto into the request req := new(types.FetchNodeCredentialsRequest) if err := proto.Unmarshal(reqBytes, req); err != nil { return fmt.Errorf("(%s) error unmarshaling common name value: %w", op, err) } - _, err = registration.AuthorizeNode(c.baseContext, c.NodeeFileStorage, req) + + serversRepo, err := c.ServersRepoFn() + if err != nil { + return fmt.Errorf("(%s) error fetching servers repo: %w", op, err) + } + + _, err = serversRepo.CreateWorker(c.baseContext, &servers.Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + }, + }, servers.WithFetchNodeCredentialsRequest(req)) + if err != nil { + return fmt.Errorf("(%s) error creating worker: %w", op, err) + } + return err } diff --git a/internal/daemon/controller/listeners.go b/internal/daemon/controller/listeners.go index b4bd1046a..3475819b2 100644 --- a/internal/daemon/controller/listeners.go +++ b/internal/daemon/controller/listeners.go @@ -106,10 +106,16 @@ func (c *Controller) configureForApi(ln *base.ServerListener) ([]func(), error) func (c *Controller) configureForCluster(ln *base.ServerListener) (func(), error) { const op = "controller.configureForCluster" + + workerAuthStorage, err := c.WorkerAuthRepoStorageFn() + if err != nil { + return nil, fmt.Errorf("error fetching worker auth storage: %w", err) + } + l, err := protocol.NewInterceptingListener( &protocol.InterceptingListenerConfiguration{ Context: c.baseContext, - Storage: c.NodeeFileStorage, + Storage: workerAuthStorage, BaseListener: ln.ClusterListener, BaseTlsConfiguration: &tls.Config{ GetConfigForClient: c.validateWorkerTls, @@ -143,7 +149,7 @@ func (c *Controller) configureForCluster(ln *base.ServerListener) (func(), error pbs.RegisterSessionServiceServer(workerServer, workerService) multihopService, err := handlers.NewMultihopServiceServer( - c.NodeeFileStorage, + workerAuthStorage, true, nil, ) diff --git a/internal/daemon/controller/listeners_test.go b/internal/daemon/controller/listeners_test.go index db1119291..30a3b769c 100644 --- a/internal/daemon/controller/listeners_test.go +++ b/internal/daemon/controller/listeners_test.go @@ -23,7 +23,6 @@ import ( "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-secure-stdlib/configutil/v2" "github.com/hashicorp/go-secure-stdlib/listenerutil" - nodeefile "github.com/hashicorp/nodeenrollment/storage/file" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/protobuf/proto" @@ -251,9 +250,6 @@ func TestStartListeners(t *testing.T) { c.baseContext = ctx c.baseCancel = cancel - c.NodeeFileStorage, err = nodeefile.NewFileStorage(c.baseContext) - require.NoError(t, err) - err = c.startListeners() require.NoError(t, err) diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index dce7259a1..5cca1f524 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -161,6 +161,12 @@ func New(conf *Config) (*Worker, error) { return nil, fmt.Errorf("exactly one proxy listener is required") } + w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext, + nodeefile.WithBaseDirectory(w.conf.RawConfig.Worker.StoragePath)) + if err != nil { + return nil, err + } + return w, nil } @@ -178,13 +184,6 @@ func (w *Worker) Start() error { controllerResolver := manual.NewBuilderWithScheme(scheme) w.controllerResolver.Store(controllerResolver) - var err error - w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext, - nodeefile.WithBaseDirectory(w.conf.RawConfig.Worker.StoragePath)) - if err != nil { - return err - } - if err := w.startListeners(); err != nil { return fmt.Errorf("error starting worker listeners: %w", err) } diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index 3740af566..e6f20568d 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -14,6 +14,7 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping/v2" "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/registration" + "google.golang.org/protobuf/types/known/structpb" ) // DeleteWorker will delete a worker from the repository. @@ -413,6 +414,12 @@ func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Op if worker.PublicId, err = opts.withNewIdFunc(ctx); err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to generate worker id")) } + state, err := structpb.NewStruct(map[string]any{ + "worker_id": worker.PublicId, + }) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error generating state struct", err)) + } var databaseWrapper wrapping.Wrapper var workerAuthRepo *WorkerAuthRepositoryStorage @@ -447,6 +454,7 @@ func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Op if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to authorize node")) } + nodeInfo.State = state if err := StoreNodeInformationTx(ctx, w, databaseWrapper, nodeInfo); err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to store node information")) } diff --git a/internal/servers/repository_workerauth.go b/internal/servers/repository_workerauth.go index 44bd70520..ef5db2bb9 100644 --- a/internal/servers/repository_workerauth.go +++ b/internal/servers/repository_workerauth.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-dbw" wrapping "github.com/hashicorp/go-kms-wrapping/v2" + "github.com/hashicorp/nodeenrollment" nodee "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/types" "github.com/mitchellh/mapstructure" @@ -30,7 +31,7 @@ var ( ) type rootCertificatesVersion struct { - Version uint32 + Version uint32 `mapstructure:"version" structs:"version"` } // WorkerAuthRepositoryStorage is the Worker Auth database repository @@ -340,21 +341,26 @@ func (r *WorkerAuthRepositoryStorage) Load(ctx context.Context, msg nodee.Messag return errors.New(ctx, errors.InvalidParameter, op, "given message cannot be loaded as it has no ID") } + var err error switch t := msg.(type) { case *types.NodeInformation: - err := r.loadNodeInformation(ctx, t) - if err != nil { - return errors.Wrap(ctx, err, op) - } + err = r.loadNodeInformation(ctx, t) + case *types.RootCertificates: - err := r.loadRootCertificates(ctx, t) - if err != nil { - return errors.Wrap(ctx, err, op) - } + err = r.loadRootCertificates(ctx, t) + default: return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("message type %T not supported for Load", t)) } + if err != nil { + if err == nodeenrollment.ErrNotFound { + // Don't wrap as this will confuse things + return err + } + return errors.Wrap(ctx, err, op) + } + return nil } diff --git a/internal/servers/repository_workerauth_test.go b/internal/servers/repository_workerauth_test.go index 11f7bea3b..f6d54a4be 100644 --- a/internal/servers/repository_workerauth_test.go +++ b/internal/servers/repository_workerauth_test.go @@ -346,13 +346,25 @@ func TestStoreNodeInformationTx(t *testing.T) { wantErrContains: "encrypt-error", }, { - name: "create-error", + name: "create-error-no-db", writer: &db.Db{}, databaseWrapper: databaseWrapper, node: testNodeInfoFn(), wantErr: true, wantErrContains: "db.Create", }, + { + name: "create-error-validation", + writer: rw, + databaseWrapper: databaseWrapper, + node: func() *types.NodeInformation { + ni := testNodeInfoFn() + ni.State = nil + return ni + }(), + wantErr: true, + wantErrContains: "missing WorkerId", + }, { name: "success", writer: rw, diff --git a/internal/servers/worker.go b/internal/servers/worker.go index 4789f810f..4d7beabfd 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -13,7 +13,7 @@ import ( ) type workerAuthWorkerId struct { - WorkerId string + WorkerId string `mapstructure:"worker_id" structs:"worker_id"` } // AttachWorkerIdToState accepts a workerId and creates a struct for use with the Nodeenrollment lib From 1830605a7e3e47d77b4004c864cda7c3b5bef8db Mon Sep 17 00:00:00 2001 From: Haotian Date: Fri, 3 Jun 2022 11:59:02 -0700 Subject: [PATCH 046/100] refact(servers) Update api for new worker and controller server tables (#2101) * Discontinue support for old Server message in ServerCoordinationService. Split message into ServerWorkerStatus and UpstreamServer (which currently contains controller addresses). Remove unnecessary fields. Modify workerStatusServer to use new fields. Co-authored-by: Todd --- .../daemon/cluster/handlers/worker_service.go | 55 ++- .../handlers/worker_service_status_test.go | 103 +++-- .../targets/tcp/target_service_test.go | 31 +- internal/daemon/worker/status.go | 18 +- internal/daemon/worker/testing.go | 2 +- internal/daemon/worker/worker.go | 13 +- .../daemon/worker/worker_proxy_service.go | 2 +- .../server_coordination_service.pb.go | 414 ++++++++++++------ .../v1/server_coordination_service.proto | 39 +- .../proto/controller/servers/v1/servers.proto | 47 +- internal/servers/options.go | 8 + internal/servers/options_test.go | 8 + internal/servers/repository_worker.go | 13 +- internal/servers/servers.pb.go | 234 ++++------ internal/tests/helper/testing_helper.go | 2 +- 15 files changed, 566 insertions(+), 423 deletions(-) diff --git a/internal/daemon/cluster/handlers/worker_service.go b/internal/daemon/cluster/handlers/worker_service.go index 1ddda90c5..ac7e03aee 100644 --- a/internal/daemon/cluster/handlers/worker_service.go +++ b/internal/daemon/cluster/handlers/worker_service.go @@ -58,30 +58,42 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques const op = "workers.(workerServiceServer).Status" // TODO: on the worker, if we get errors back from this repeatedly, do we // terminate all sessions since we can't know if they were canceled? - ws.updateTimes.Store(req.Worker.PrivateId, time.Now()) + + wStat := req.GetWorkerStatus() + switch { + case wStat.GetName() == "": + return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Name is not set in the request but is required.") + case wStat.GetAddress() == "": + return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Address is not set but is required.") + } + // This Store call is currently only for testing purposes + ws.updateTimes.Store(wStat.GetName(), time.Now()) + serverRepo, err := ws.serversRepoFn() if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error getting servers repo")) return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error acquiring repo to store worker status: %v", err) } - reqServer := req.GetWorker() // Convert API tags to storage tags - workerTags := make([]*servers.Tag, 0, len(reqServer.Tags)) - for k, v := range reqServer.Tags { - for _, val := range v.GetValues() { - workerTags = append(workerTags, &servers.Tag{ - Key: k, - Value: val, - }) - } + wTags := wStat.GetTags() + workerTags := make([]*servers.Tag, 0, len(wTags)) + for _, v := range wTags { + workerTags = append(workerTags, &servers.Tag{ + Key: v.GetKey(), + Value: v.GetValue(), + }) } wConf := servers.NewWorkerForStatus(scope.Global.String(), - servers.WithName(reqServer.PrivateId), - servers.WithAddress(reqServer.Address), + servers.WithName(wStat.GetName()), + servers.WithAddress(wStat.GetAddress()), servers.WithWorkerTags(workerTags...)) - wrk, err := serverRepo.UpsertWorkerStatus(ctx, wConf, servers.WithUpdateTags(req.GetUpdateTags())) + opts := []servers.Option{servers.WithUpdateTags(req.GetUpdateTags())} + if wStat.GetPublicId() != "" { + opts = append(opts, servers.WithPublicId(wStat.GetPublicId())) + } + wrk, err := serverRepo.UpsertWorkerStatus(ctx, wConf, opts...) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error storing worker status")) return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error storing worker status: %v", err) @@ -92,18 +104,17 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques return &pbs.StatusResponse{}, status.Errorf(codes.Internal, "Error getting current controllers: %v", err) } - responseControllers := []*servers.Server{} + responseControllers := []*pbs.UpstreamServer{} for _, c := range controllers { - thisController := &servers.Server{ - PrivateId: c.PrivateId, - Address: c.Address, - CreateTime: c.CreateTime, - UpdateTime: c.UpdateTime, + thisController := &pbs.UpstreamServer{ + Address: c.Address, + Type: pbs.UpstreamServer_TYPE_CONTROLLER, } responseControllers = append(responseControllers, thisController) } ret := &pbs.StatusResponse{ - Controllers: responseControllers, + CalculatedUpstreams: responseControllers, + WorkerId: wrk.GetPublicId(), } stateReport := make([]session.StateReport, 0, len(req.GetJobs())) @@ -152,8 +163,8 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques notActive, err := session.WorkerStatusReport(ctx, sessRepo, connectionRepo, wrk.GetPublicId(), stateReport) if err != nil { return nil, status.Errorf(codes.Internal, - "Error comparing state of sessions for worker %q with public id %q: %v", - req.Worker.PrivateId, wrk.GetPublicId(), err) + "Error comparing state of sessions for worker with public id %q: %v", + wrk.GetPublicId(), err) } for _, na := range notActive { var connChanges []*pbs.Connection diff --git a/internal/daemon/cluster/handlers/worker_service_status_test.go b/internal/daemon/cluster/handlers/worker_service_status_test.go index 67112c63d..d34924890 100644 --- a/internal/daemon/cluster/handlers/worker_service_status_test.go +++ b/internal/daemon/cluster/handlers/worker_service_status_test.go @@ -21,6 +21,8 @@ import ( "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) func TestStatus(t *testing.T) { @@ -98,31 +100,30 @@ func TestStatus(t *testing.T) { name: "No Sessions", wantErr: false, req: &pbs.StatusRequest{ - Worker: &servers.Server{ - PrivateId: worker1.GetWorkerReportedName(), - Address: worker1.CanonicalAddress(), - CreateTime: worker1.CreateTime, - UpdateTime: worker1.UpdateTime, + WorkerStatus: &servers.ServerWorkerStatus{ + PublicId: worker1.GetPublicId(), + Name: worker1.GetWorkerReportedName(), + Address: worker1.CanonicalAddress(), }, }, want: &pbs.StatusResponse{ - Controllers: []*servers.Server{ + CalculatedUpstreams: []*pbs.UpstreamServer{ { - PrivateId: "test_controller1", - Address: "127.0.0.1", + Type: pbs.UpstreamServer_TYPE_CONTROLLER, + Address: "127.0.0.1", }, }, + WorkerId: worker1.PublicId, }, }, { name: "Still Active", wantErr: false, req: &pbs.StatusRequest{ - Worker: &servers.Server{ - PrivateId: worker1.GetWorkerReportedName(), - Address: worker1.CanonicalAddress(), - CreateTime: worker1.CreateTime, - UpdateTime: worker1.UpdateTime, + WorkerStatus: &servers.ServerWorkerStatus{ + PublicId: worker1.GetPublicId(), + Name: worker1.GetWorkerReportedName(), + Address: worker1.CanonicalAddress(), }, Jobs: []*pbs.JobStatus{ { @@ -145,14 +146,37 @@ func TestStatus(t *testing.T) { }, }, want: &pbs.StatusResponse{ - Controllers: []*servers.Server{ + CalculatedUpstreams: []*pbs.UpstreamServer{ { - PrivateId: "test_controller1", - Address: "127.0.0.1", + Type: pbs.UpstreamServer_TYPE_CONTROLLER, + Address: "127.0.0.1", }, }, + WorkerId: worker1.PublicId, }, }, + { + name: "No Name", + wantErr: true, + req: &pbs.StatusRequest{ + WorkerStatus: &servers.ServerWorkerStatus{ + PublicId: worker1.GetPublicId(), + Address: worker1.CanonicalAddress(), + }, + }, + wantErrMsg: status.Error(codes.InvalidArgument, "Name is not set in the request but is required.").Error(), + }, + { + name: "No Address", + wantErr: true, + req: &pbs.StatusRequest{ + WorkerStatus: &servers.ServerWorkerStatus{ + PublicId: worker1.GetPublicId(), + Name: worker1.GetWorkerReportedName(), + }, + }, + wantErrMsg: status.Error(codes.InvalidArgument, "Address is not set but is required.").Error(), + }, } for _, tc := range cases { @@ -162,7 +186,7 @@ func TestStatus(t *testing.T) { got, err := s.Status(ctx, tc.req) if tc.wantErr { require.Error(err) - assert.Nil(got) + assert.Equal(got, &pbs.StatusResponse{}) assert.Equal(tc.wantErrMsg, err.Error()) return } @@ -172,14 +196,15 @@ func TestStatus(t *testing.T) { got, cmpopts.IgnoreUnexported( pbs.StatusResponse{}, - servers.Server{}, + servers.ServerWorkerStatus{}, + pbs.UpstreamServer{}, pbs.JobChangeRequest{}, pbs.Job{}, pbs.Job_SessionInfo{}, pbs.SessionJobInfo{}, pbs.Connection{}, ), - cmpopts.IgnoreFields(servers.Server{}, "CreateTime", "UpdateTime"), + cmpopts.IgnoreFields(servers.ServerWorkerStatus{}, "Tags"), ), ) }) @@ -278,11 +303,10 @@ func TestStatusSessionClosed(t *testing.T) { require.NoError(t, err) }, req: &pbs.StatusRequest{ - Worker: &servers.Server{ - PrivateId: worker1.GetWorkerReportedName(), - Address: worker1.CanonicalAddress(), - CreateTime: worker1.CreateTime, - UpdateTime: worker1.UpdateTime, + WorkerStatus: &servers.ServerWorkerStatus{ + PublicId: worker1.GetPublicId(), + Name: worker1.GetWorkerReportedName(), + Address: worker1.CanonicalAddress(), }, Jobs: []*pbs.JobStatus{ { @@ -305,10 +329,10 @@ func TestStatusSessionClosed(t *testing.T) { }, }, want: &pbs.StatusResponse{ - Controllers: []*servers.Server{ + CalculatedUpstreams: []*pbs.UpstreamServer{ { - PrivateId: "test_controller1", - Address: "127.0.0.1", + Type: pbs.UpstreamServer_TYPE_CONTROLLER, + Address: "127.0.0.1", }, }, JobsRequests: []*pbs.JobChangeRequest{ @@ -325,6 +349,7 @@ func TestStatusSessionClosed(t *testing.T) { RequestType: pbs.CHANGETYPE_CHANGETYPE_UPDATE_STATE, }, }, + WorkerId: worker1.PublicId, }, }, } @@ -349,14 +374,15 @@ func TestStatusSessionClosed(t *testing.T) { got, cmpopts.IgnoreUnexported( pbs.StatusResponse{}, - servers.Server{}, + servers.ServerWorkerStatus{}, + pbs.UpstreamServer{}, pbs.JobChangeRequest{}, pbs.Job{}, pbs.Job_SessionInfo{}, pbs.SessionJobInfo{}, pbs.Connection{}, ), - cmpopts.IgnoreFields(servers.Server{}, "CreateTime", "UpdateTime"), + cmpopts.IgnoreFields(servers.ServerWorkerStatus{}, "Tags"), ), ) }) @@ -444,9 +470,10 @@ func TestStatusDeadConnection(t *testing.T) { require.NotEqual(t, deadConn.PublicId, connection.PublicId) req := &pbs.StatusRequest{ - Worker: &servers.Server{ - PrivateId: worker1.GetWorkerReportedName(), - Address: worker1.GetWorkerReportedAddress(), + WorkerStatus: &servers.ServerWorkerStatus{ + PublicId: worker1.GetPublicId(), + Name: worker1.GetWorkerReportedName(), + Address: worker1.CanonicalAddress(), }, Jobs: []*pbs.JobStatus{ { @@ -469,12 +496,13 @@ func TestStatusDeadConnection(t *testing.T) { }, } want := &pbs.StatusResponse{ - Controllers: []*servers.Server{ + CalculatedUpstreams: []*pbs.UpstreamServer{ { - PrivateId: "test_controller1", - Address: "127.0.0.1", + Type: pbs.UpstreamServer_TYPE_CONTROLLER, + Address: "127.0.0.1", }, }, + WorkerId: worker1.PublicId, } got, err := s.Status(ctx, req) @@ -484,14 +512,15 @@ func TestStatusDeadConnection(t *testing.T) { got, cmpopts.IgnoreUnexported( pbs.StatusResponse{}, - servers.Server{}, + servers.ServerWorkerStatus{}, + pbs.UpstreamServer{}, pbs.JobChangeRequest{}, pbs.Job{}, pbs.Job_SessionInfo{}, pbs.SessionJobInfo{}, pbs.Connection{}, ), - cmpopts.IgnoreFields(servers.Server{}, "CreateTime", "UpdateTime"), + cmpopts.IgnoreFields(servers.ServerWorkerStatus{}, "Tags"), ), ) diff --git a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go index 26120d855..940963743 100644 --- a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go @@ -2646,9 +2646,6 @@ func TestAuthorizeSession(t *testing.T) { sessionRepoFn := func() (*session.Repository, error) { return session.NewRepository(rw, rw, kms) } - connectionRepoFn := func() (*session.ConnectionRepository, error) { - return session.NewConnectionRepository(ctx, rw, rw, kms) - } staticHostRepoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } @@ -2787,14 +2784,7 @@ func TestAuthorizeSession(t *testing.T) { require.NoError(t, err) // Tell our DB that there is a worker ready to serve the data - workerService := cluster.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) - _, err = workerService.Status(ctx, &spbs.StatusRequest{ - Worker: &spb.Server{ - PrivateId: "w_1234567890", - Address: "localhost:8457", - }, - }) - require.NoError(t, err) + servers.TestWorker(t, conn, wrapper) asRes1, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{ Id: tar.GetPublicId(), @@ -2903,9 +2893,6 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { sessionRepoFn := func() (*session.Repository, error) { return session.NewRepository(rw, rw, kms) } - connectionRepoFn := func() (*session.ConnectionRepository, error) { - return session.NewConnectionRepository(ctx, rw, rw, kms) - } staticHostRepoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } @@ -3075,14 +3062,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { require.NoError(t, err) // Tell our DB that there is a worker ready to serve the data - workerService := cluster.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) - _, err = workerService.Status(ctx, &spbs.StatusRequest{ - Worker: &spb.Server{ - PrivateId: "w_1234567890", - Address: "localhost:8457", - }, - }) - require.NoError(t, err) + servers.TestWorker(t, conn, wrapper) asRes, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{ Id: tar.GetPublicId(), @@ -3208,9 +3188,10 @@ func TestAuthorizeSession_Errors(t *testing.T) { workerExists := func(tar target.Target) (version uint32) { workerService := cluster.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, &sync.Map{}, kms) _, err := workerService.Status(context.Background(), &spbs.StatusRequest{ - Worker: &spb.Server{ - PrivateId: "w_1234567890", - Address: "localhost:123", + WorkerStatus: &spb.ServerWorkerStatus{ + PublicId: "w_1234567890", + Name: "w_1234567890", + Address: "localhost:123", }, }) require.NoError(t, err) diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index 4ddad3862..728e0fe63 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -135,21 +135,21 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { // Send status information client := w.controllerStatusConn.Load().(pbs.ServerCoordinationServiceClient) - var tags map[string]*servers.TagValues + var tags []*servers.TagPair // If we're not going to request a tag update, no reason to have these // marshaled on every status call. if w.updateTags.Load() { - tags = w.tags.Load().(map[string]*servers.TagValues) + tags = w.tags.Load().([]*servers.TagPair) } statusCtx, statusCancel := context.WithTimeout(cancelCtx, common.StatusTimeout) defer statusCancel() result, err := client.Status(statusCtx, &pbs.StatusRequest{ Jobs: activeJobs, - Worker: &servers.Server{ - PrivateId: w.conf.RawConfig.Worker.Name, - Address: w.conf.RawConfig.Worker.PublicAddr, - Tags: tags, + WorkerStatus: &servers.ServerWorkerStatus{ + Name: w.conf.RawConfig.Worker.Name, + Address: w.conf.RawConfig.Worker.PublicAddr, + Tags: tags, }, UpdateTags: w.updateTags.Load(), }) @@ -190,9 +190,9 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { } else { w.updateTags.Store(false) // This may be nil if we are in a multiple hop scenario - if len(result.Controllers) > 0 { - addrs := make([]resolver.Address, 0, len(result.Controllers)) - for _, v := range result.Controllers { + if len(result.CalculatedUpstreams) > 0 { + addrs := make([]resolver.Address, 0, len(result.CalculatedUpstreams)) + for _, v := range result.CalculatedUpstreams { addrs = append(addrs, resolver.Address{Addr: v.Address}) } w.Resolver().UpdateState(resolver.State{Addresses: addrs}) diff --git a/internal/daemon/worker/testing.go b/internal/daemon/worker/testing.go index d73145cc9..623cbd0d7 100644 --- a/internal/daemon/worker/testing.go +++ b/internal/daemon/worker/testing.go @@ -56,7 +56,7 @@ func (tw *TestWorker) Name() string { func (tw *TestWorker) ControllerAddrs() []string { var addrs []string lastStatus := tw.w.LastStatusSuccess() - for _, v := range lastStatus.GetControllers() { + for _, v := range lastStatus.GetCalculatedUpstreams() { addrs = append(addrs, v.Address) } diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 5cca1f524..26dc8c0dd 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -294,13 +294,16 @@ func (w *Worker) Resolver() *manual.Resolver { func (w *Worker) ParseAndStoreTags(incoming map[string][]string) { if len(incoming) == 0 { - w.tags.Store(map[string]*servers.TagValues{}) + w.tags.Store([]*servers.TagPair{}) return } - tags := make(map[string]*servers.TagValues, len(incoming)) - for k, v := range incoming { - tags[k] = &servers.TagValues{ - Values: append(make([]string, 0, len(v)), v...), + tags := []*servers.TagPair{} + for k, vals := range incoming { + for _, v := range vals { + tags = append(tags, &servers.TagPair{ + Key: k, + Value: v, + }) } } w.tags.Store(tags) diff --git a/internal/daemon/worker/worker_proxy_service.go b/internal/daemon/worker/worker_proxy_service.go index e70b4451a..1b4755658 100644 --- a/internal/daemon/worker/worker_proxy_service.go +++ b/internal/daemon/worker/worker_proxy_service.go @@ -36,7 +36,7 @@ func (ws *workerProxyServiceServer) Status(ctx context.Context, req *pbs.StatusR if resp != nil { // We don't currently support distributing new addreses to workers // multiple hops away so ensure they're stripped out - resp.Controllers = nil + resp.CalculatedUpstreams = nil } return resp, err diff --git a/internal/gen/controller/servers/services/server_coordination_service.pb.go b/internal/gen/controller/servers/services/server_coordination_service.pb.go index c893c7c2b..8fc3654e2 100644 --- a/internal/gen/controller/servers/services/server_coordination_service.pb.go +++ b/internal/gen/controller/servers/services/server_coordination_service.pb.go @@ -222,6 +222,55 @@ func (CHANGETYPE) EnumDescriptor() ([]byte, []int) { return file_controller_servers_services_v1_server_coordination_service_proto_rawDescGZIP(), []int{3} } +type UpstreamServer_TYPE int32 + +const ( + UpstreamServer_TYPE_UNSPECIFIED UpstreamServer_TYPE = 0 + UpstreamServer_TYPE_CONTROLLER UpstreamServer_TYPE = 1 + UpstreamServer_TYPE_WORKER UpstreamServer_TYPE = 2 +) + +// Enum value maps for UpstreamServer_TYPE. +var ( + UpstreamServer_TYPE_name = map[int32]string{ + 0: "TYPE_UNSPECIFIED", + 1: "TYPE_CONTROLLER", + 2: "TYPE_WORKER", + } + UpstreamServer_TYPE_value = map[string]int32{ + "TYPE_UNSPECIFIED": 0, + "TYPE_CONTROLLER": 1, + "TYPE_WORKER": 2, + } +) + +func (x UpstreamServer_TYPE) Enum() *UpstreamServer_TYPE { + p := new(UpstreamServer_TYPE) + *p = x + return p +} + +func (x UpstreamServer_TYPE) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (UpstreamServer_TYPE) Descriptor() protoreflect.EnumDescriptor { + return file_controller_servers_services_v1_server_coordination_service_proto_enumTypes[4].Descriptor() +} + +func (UpstreamServer_TYPE) Type() protoreflect.EnumType { + return &file_controller_servers_services_v1_server_coordination_service_proto_enumTypes[4] +} + +func (x UpstreamServer_TYPE) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use UpstreamServer_TYPE.Descriptor instead. +func (UpstreamServer_TYPE) EnumDescriptor() ([]byte, []int) { + return file_controller_servers_services_v1_server_coordination_service_proto_rawDescGZIP(), []int{4, 0} +} + type Connection struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -462,15 +511,70 @@ func (x *JobStatus) GetJob() *Job { return nil } +// UpstreamServer returns the controller address in the StatusResponse but may be used in the future to return +// worker address. +type UpstreamServer struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // type specifies between controller or worker. + Type UpstreamServer_TYPE `protobuf:"varint,10,opt,name=type,proto3,enum=controller.servers.services.v1.UpstreamServer_TYPE" json:"type,omitempty"` + // Address of the daemon + Address string `protobuf:"bytes,20,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *UpstreamServer) Reset() { + *x = UpstreamServer{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpstreamServer) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpstreamServer) ProtoMessage() {} + +func (x *UpstreamServer) ProtoReflect() protoreflect.Message { + mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpstreamServer.ProtoReflect.Descriptor instead. +func (*UpstreamServer) Descriptor() ([]byte, []int) { + return file_controller_servers_services_v1_server_coordination_service_proto_rawDescGZIP(), []int{4} +} + +func (x *UpstreamServer) GetType() UpstreamServer_TYPE { + if x != nil { + return x.Type + } + return UpstreamServer_TYPE_UNSPECIFIED +} + +func (x *UpstreamServer) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + type StatusRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The worker info. We could use information from the TLS connection but this - // is easier and going the other route doesn't provide much benefit -- if you - // get access to the key and spoof the connection, you're already compromised. - Worker *servers.Server `protobuf:"bytes,10,opt,name=worker,proto3" json:"worker,omitempty"` // Jobs which this worker wants to report the status. Jobs []*JobStatus `protobuf:"bytes,20,rep,name=jobs,proto3" json:"jobs,omitempty"` // Whether to update tags from the Server block on this RPC. We only need to @@ -478,12 +582,17 @@ type StatusRequest struct { // changed allows us to avoid constant database operations for something that // won't change very often, if ever. UpdateTags bool `protobuf:"varint,30,opt,name=update_tags,json=updateTags,proto3" json:"update_tags,omitempty"` + // Replaces the old worker field: + // The worker info. We could use information from the TLS connection but this + // is easier and going the other route doesn't provide much benefit -- if you + // get access to the key and spoof the connection, you're already compromised. + WorkerStatus *servers.ServerWorkerStatus `protobuf:"bytes,40,opt,name=worker_status,json=workerStatus,proto3" json:"worker_status,omitempty"` } func (x *StatusRequest) Reset() { *x = StatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[4] + mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -496,7 +605,7 @@ func (x *StatusRequest) String() string { func (*StatusRequest) ProtoMessage() {} func (x *StatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[4] + mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -509,14 +618,7 @@ func (x *StatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead. func (*StatusRequest) Descriptor() ([]byte, []int) { - return file_controller_servers_services_v1_server_coordination_service_proto_rawDescGZIP(), []int{4} -} - -func (x *StatusRequest) GetWorker() *servers.Server { - if x != nil { - return x.Worker - } - return nil + return file_controller_servers_services_v1_server_coordination_service_proto_rawDescGZIP(), []int{5} } func (x *StatusRequest) GetJobs() []*JobStatus { @@ -533,6 +635,13 @@ func (x *StatusRequest) GetUpdateTags() bool { return false } +func (x *StatusRequest) GetWorkerStatus() *servers.ServerWorkerStatus { + if x != nil { + return x.WorkerStatus + } + return nil +} + type JobChangeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -545,7 +654,7 @@ type JobChangeRequest struct { func (x *JobChangeRequest) Reset() { *x = JobChangeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[5] + mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -558,7 +667,7 @@ func (x *JobChangeRequest) String() string { func (*JobChangeRequest) ProtoMessage() {} func (x *JobChangeRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[5] + mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -571,7 +680,7 @@ func (x *JobChangeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use JobChangeRequest.ProtoReflect.Descriptor instead. func (*JobChangeRequest) Descriptor() ([]byte, []int) { - return file_controller_servers_services_v1_server_coordination_service_proto_rawDescGZIP(), []int{5} + return file_controller_servers_services_v1_server_coordination_service_proto_rawDescGZIP(), []int{6} } func (x *JobChangeRequest) GetJob() *Job { @@ -593,20 +702,23 @@ type StatusResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Active controllers. This can be used for connection management. - Controllers []*servers.Server `protobuf:"bytes,10,rep,name=controllers,proto3" json:"controllers,omitempty"` // List of jobs and the expected state changes. For example, this will // include jobs witch change type of canceled for jobs which are active on a // worker but should be canceled. This could also contain a request to start a // job such as a worker -> worker proxy for establishing a session through an // enclave. JobsRequests []*JobChangeRequest `protobuf:"bytes,20,rep,name=jobs_requests,json=jobsRequests,proto3" json:"jobs_requests,omitempty"` + // UpstreamServer currently returns the controller address in the StatusResponse. + CalculatedUpstreams []*UpstreamServer `protobuf:"bytes,30,rep,name=calculated_upstreams,json=calculatedUpstreams,proto3" json:"calculated_upstreams,omitempty"` + // The ID of the worker which made the request. The worker can send this value in subsequent requests so the + // controller does not need to do a database lookup for the id using the name field. + WorkerId string `protobuf:"bytes,40,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty"` } func (x *StatusResponse) Reset() { *x = StatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[6] + mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -619,7 +731,7 @@ func (x *StatusResponse) String() string { func (*StatusResponse) ProtoMessage() {} func (x *StatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[6] + mi := &file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -632,14 +744,7 @@ func (x *StatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusResponse.ProtoReflect.Descriptor instead. func (*StatusResponse) Descriptor() ([]byte, []int) { - return file_controller_servers_services_v1_server_coordination_service_proto_rawDescGZIP(), []int{6} -} - -func (x *StatusResponse) GetControllers() []*servers.Server { - if x != nil { - return x.Controllers - } - return nil + return file_controller_servers_services_v1_server_coordination_service_proto_rawDescGZIP(), []int{7} } func (x *StatusResponse) GetJobsRequests() []*JobChangeRequest { @@ -649,6 +754,20 @@ func (x *StatusResponse) GetJobsRequests() []*JobChangeRequest { return nil } +func (x *StatusResponse) GetCalculatedUpstreams() []*UpstreamServer { + if x != nil { + return x.CalculatedUpstreams + } + return nil +} + +func (x *StatusResponse) GetWorkerId() string { + if x != nil { + return x.WorkerId + } + return "" +} + var File_controller_servers_services_v1_server_coordination_service_proto protoreflect.FileDescriptor var file_controller_servers_services_v1_server_coordination_service_proto_rawDesc = []byte{ @@ -695,79 +814,99 @@ var file_controller_servers_services_v1_server_coordination_service_proto_rawDes 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, - 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0xa6, 0x01, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x3d, - 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, - 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x1f, 0x0a, - 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x1e, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x73, 0x22, 0x98, - 0x01, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x12, 0x4d, 0x0a, 0x0c, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x54, 0x59, 0x50, 0x45, 0x52, 0x0b, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0xa8, 0x01, 0x0a, 0x0e, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, + 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0xb7, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x47, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x59, 0x50, 0x45, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x42, 0x0a, 0x04, 0x54, + 0x59, 0x50, 0x45, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0f, + 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x45, 0x52, 0x10, 0x02, 0x22, + 0xcd, 0x01, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x3d, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, + 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, + 0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, + 0x73, 0x12, 0x4e, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x52, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x22, + 0x98, 0x01, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x12, 0x4d, 0x0a, 0x0c, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x54, 0x59, 0x50, 0x45, 0x52, 0x0b, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0xfa, 0x01, 0x0a, 0x0e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0d, 0x6a, 0x6f, 0x62, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0c, 0x6a, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x73, 0x2a, 0x92, 0x01, 0x0a, 0x10, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, - 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x4f, 0x4e, - 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1f, 0x0a, 0x1b, 0x43, - 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, - 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, - 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, - 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, - 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, - 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x10, 0x03, 0x2a, 0x9e, 0x01, 0x0a, 0x0d, 0x53, 0x45, - 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x12, 0x1d, 0x0a, 0x19, 0x53, - 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, - 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x45, - 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, - 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, - 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, - 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, - 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, - 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54, 0x45, - 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x54, 0x45, 0x44, 0x10, 0x04, 0x2a, 0x37, 0x0a, 0x07, 0x4a, 0x4f, - 0x42, 0x54, 0x59, 0x50, 0x45, 0x12, 0x17, 0x0a, 0x13, 0x4a, 0x4f, 0x42, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, - 0x0a, 0x0f, 0x4a, 0x4f, 0x42, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, - 0x4e, 0x10, 0x01, 0x2a, 0x45, 0x0a, 0x0a, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x54, 0x59, 0x50, - 0x45, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, - 0x17, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, - 0x54, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x01, 0x32, 0x86, 0x01, 0x0a, 0x19, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x69, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x65, 0x73, 0x74, 0x73, 0x12, 0x61, 0x0a, 0x14, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x1e, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x42, 0x51, 0x5a, 0x4f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, - 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, - 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x52, 0x13, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x55, 0x70, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x49, 0x64, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x2a, 0x92, 0x01, 0x0a, 0x10, 0x43, 0x4f, 0x4e, 0x4e, + 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x12, 0x20, 0x0a, 0x1c, + 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1f, + 0x0a, 0x1b, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, + 0x55, 0x53, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x01, 0x12, + 0x1e, 0x0a, 0x1a, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, + 0x1b, 0x0a, 0x17, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x10, 0x03, 0x2a, 0x9e, 0x01, 0x0a, + 0x0d, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x12, 0x1d, + 0x0a, 0x19, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, + 0x15, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, + 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x45, 0x53, 0x53, + 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, + 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, + 0x1c, 0x0a, 0x18, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x54, 0x45, 0x44, 0x10, 0x04, 0x2a, 0x37, 0x0a, + 0x07, 0x4a, 0x4f, 0x42, 0x54, 0x59, 0x50, 0x45, 0x12, 0x17, 0x0a, 0x13, 0x4a, 0x4f, 0x42, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4a, 0x4f, 0x42, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x53, + 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x2a, 0x45, 0x0a, 0x0a, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, + 0x54, 0x59, 0x50, 0x45, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, + 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x01, 0x32, 0x86, 0x01, + 0x0a, 0x19, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x69, 0x0a, 0x06, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x51, 0x5a, 0x4f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -782,42 +921,45 @@ func file_controller_servers_services_v1_server_coordination_service_proto_rawDe return file_controller_servers_services_v1_server_coordination_service_proto_rawDescData } -var file_controller_servers_services_v1_server_coordination_service_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_controller_servers_services_v1_server_coordination_service_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_controller_servers_services_v1_server_coordination_service_proto_enumTypes = make([]protoimpl.EnumInfo, 5) +var file_controller_servers_services_v1_server_coordination_service_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_controller_servers_services_v1_server_coordination_service_proto_goTypes = []interface{}{ - (CONNECTIONSTATUS)(0), // 0: controller.servers.services.v1.CONNECTIONSTATUS - (SESSIONSTATUS)(0), // 1: controller.servers.services.v1.SESSIONSTATUS - (JOBTYPE)(0), // 2: controller.servers.services.v1.JOBTYPE - (CHANGETYPE)(0), // 3: controller.servers.services.v1.CHANGETYPE - (*Connection)(nil), // 4: controller.servers.services.v1.Connection - (*SessionJobInfo)(nil), // 5: controller.servers.services.v1.SessionJobInfo - (*Job)(nil), // 6: controller.servers.services.v1.Job - (*JobStatus)(nil), // 7: controller.servers.services.v1.JobStatus - (*StatusRequest)(nil), // 8: controller.servers.services.v1.StatusRequest - (*JobChangeRequest)(nil), // 9: controller.servers.services.v1.JobChangeRequest - (*StatusResponse)(nil), // 10: controller.servers.services.v1.StatusResponse - (*servers.Server)(nil), // 11: controller.servers.v1.Server + (CONNECTIONSTATUS)(0), // 0: controller.servers.services.v1.CONNECTIONSTATUS + (SESSIONSTATUS)(0), // 1: controller.servers.services.v1.SESSIONSTATUS + (JOBTYPE)(0), // 2: controller.servers.services.v1.JOBTYPE + (CHANGETYPE)(0), // 3: controller.servers.services.v1.CHANGETYPE + (UpstreamServer_TYPE)(0), // 4: controller.servers.services.v1.UpstreamServer.TYPE + (*Connection)(nil), // 5: controller.servers.services.v1.Connection + (*SessionJobInfo)(nil), // 6: controller.servers.services.v1.SessionJobInfo + (*Job)(nil), // 7: controller.servers.services.v1.Job + (*JobStatus)(nil), // 8: controller.servers.services.v1.JobStatus + (*UpstreamServer)(nil), // 9: controller.servers.services.v1.UpstreamServer + (*StatusRequest)(nil), // 10: controller.servers.services.v1.StatusRequest + (*JobChangeRequest)(nil), // 11: controller.servers.services.v1.JobChangeRequest + (*StatusResponse)(nil), // 12: controller.servers.services.v1.StatusResponse + (*servers.ServerWorkerStatus)(nil), // 13: controller.servers.v1.ServerWorkerStatus } var file_controller_servers_services_v1_server_coordination_service_proto_depIdxs = []int32{ 0, // 0: controller.servers.services.v1.Connection.status:type_name -> controller.servers.services.v1.CONNECTIONSTATUS 1, // 1: controller.servers.services.v1.SessionJobInfo.status:type_name -> controller.servers.services.v1.SESSIONSTATUS - 4, // 2: controller.servers.services.v1.SessionJobInfo.connections:type_name -> controller.servers.services.v1.Connection + 5, // 2: controller.servers.services.v1.SessionJobInfo.connections:type_name -> controller.servers.services.v1.Connection 2, // 3: controller.servers.services.v1.Job.type:type_name -> controller.servers.services.v1.JOBTYPE - 5, // 4: controller.servers.services.v1.Job.session_info:type_name -> controller.servers.services.v1.SessionJobInfo - 6, // 5: controller.servers.services.v1.JobStatus.job:type_name -> controller.servers.services.v1.Job - 11, // 6: controller.servers.services.v1.StatusRequest.worker:type_name -> controller.servers.v1.Server - 7, // 7: controller.servers.services.v1.StatusRequest.jobs:type_name -> controller.servers.services.v1.JobStatus - 6, // 8: controller.servers.services.v1.JobChangeRequest.job:type_name -> controller.servers.services.v1.Job - 3, // 9: controller.servers.services.v1.JobChangeRequest.request_type:type_name -> controller.servers.services.v1.CHANGETYPE - 11, // 10: controller.servers.services.v1.StatusResponse.controllers:type_name -> controller.servers.v1.Server - 9, // 11: controller.servers.services.v1.StatusResponse.jobs_requests:type_name -> controller.servers.services.v1.JobChangeRequest - 8, // 12: controller.servers.services.v1.ServerCoordinationService.Status:input_type -> controller.servers.services.v1.StatusRequest - 10, // 13: controller.servers.services.v1.ServerCoordinationService.Status:output_type -> controller.servers.services.v1.StatusResponse - 13, // [13:14] is the sub-list for method output_type - 12, // [12:13] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name + 6, // 4: controller.servers.services.v1.Job.session_info:type_name -> controller.servers.services.v1.SessionJobInfo + 7, // 5: controller.servers.services.v1.JobStatus.job:type_name -> controller.servers.services.v1.Job + 4, // 6: controller.servers.services.v1.UpstreamServer.type:type_name -> controller.servers.services.v1.UpstreamServer.TYPE + 8, // 7: controller.servers.services.v1.StatusRequest.jobs:type_name -> controller.servers.services.v1.JobStatus + 13, // 8: controller.servers.services.v1.StatusRequest.worker_status:type_name -> controller.servers.v1.ServerWorkerStatus + 7, // 9: controller.servers.services.v1.JobChangeRequest.job:type_name -> controller.servers.services.v1.Job + 3, // 10: controller.servers.services.v1.JobChangeRequest.request_type:type_name -> controller.servers.services.v1.CHANGETYPE + 11, // 11: controller.servers.services.v1.StatusResponse.jobs_requests:type_name -> controller.servers.services.v1.JobChangeRequest + 9, // 12: controller.servers.services.v1.StatusResponse.calculated_upstreams:type_name -> controller.servers.services.v1.UpstreamServer + 10, // 13: controller.servers.services.v1.ServerCoordinationService.Status:input_type -> controller.servers.services.v1.StatusRequest + 12, // 14: controller.servers.services.v1.ServerCoordinationService.Status:output_type -> controller.servers.services.v1.StatusResponse + 14, // [14:15] is the sub-list for method output_type + 13, // [13:14] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_controller_servers_services_v1_server_coordination_service_proto_init() } @@ -875,7 +1017,7 @@ func file_controller_servers_services_v1_server_coordination_service_proto_init( } } file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatusRequest); i { + switch v := v.(*UpstreamServer); i { case 0: return &v.state case 1: @@ -887,7 +1029,7 @@ func file_controller_servers_services_v1_server_coordination_service_proto_init( } } file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobChangeRequest); i { + switch v := v.(*StatusRequest); i { case 0: return &v.state case 1: @@ -899,6 +1041,18 @@ func file_controller_servers_services_v1_server_coordination_service_proto_init( } } file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JobChangeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_servers_services_v1_server_coordination_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StatusResponse); i { case 0: return &v.state @@ -919,8 +1073,8 @@ func file_controller_servers_services_v1_server_coordination_service_proto_init( File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_servers_services_v1_server_coordination_service_proto_rawDesc, - NumEnums: 4, - NumMessages: 7, + NumEnums: 5, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/proto/controller/servers/services/v1/server_coordination_service.proto b/internal/proto/controller/servers/services/v1/server_coordination_service.proto index f571efe4a..6f47f3918 100644 --- a/internal/proto/controller/servers/services/v1/server_coordination_service.proto +++ b/internal/proto/controller/servers/services/v1/server_coordination_service.proto @@ -56,11 +56,25 @@ message JobStatus { Job job = 1; } +// UpstreamServer returns the controller address in the StatusResponse but may be used in the future to return +// worker address. +message UpstreamServer { + enum TYPE { + TYPE_UNSPECIFIED = 0; + TYPE_CONTROLLER = 1; + TYPE_WORKER = 2; + } + + // type specifies between controller or worker. + TYPE type = 10; + + // Address of the daemon + string address = 20; +} + message StatusRequest { - // The worker info. We could use information from the TLS connection but this - // is easier and going the other route doesn't provide much benefit -- if you - // get access to the key and spoof the connection, you're already compromised. - servers.v1.Server worker = 10; + reserved 10; + reserved "worker"; // Jobs which this worker wants to report the status. repeated JobStatus jobs = 20; @@ -70,6 +84,12 @@ message StatusRequest { // changed allows us to avoid constant database operations for something that // won't change very often, if ever. bool update_tags = 30; + + // Replaces the old worker field: + // The worker info. We could use information from the TLS connection but this + // is easier and going the other route doesn't provide much benefit -- if you + // get access to the key and spoof the connection, you're already compromised. + servers.v1.ServerWorkerStatus worker_status = 40; } enum CHANGETYPE { @@ -85,8 +105,8 @@ message JobChangeRequest { } message StatusResponse { - // Active controllers. This can be used for connection management. - repeated servers.v1.Server controllers = 10; + reserved 10; + reserved "controllers"; // List of jobs and the expected state changes. For example, this will // include jobs witch change type of canceled for jobs which are active on a @@ -94,4 +114,11 @@ message StatusResponse { // job such as a worker -> worker proxy for establishing a session through an // enclave. repeated JobChangeRequest jobs_requests = 20; + + // UpstreamServer currently returns the controller address in the StatusResponse. + repeated UpstreamServer calculated_upstreams = 30; + + // The ID of the worker which made the request. The worker can send this value in subsequent requests so the + // controller does not need to do a database lookup for the id using the name field. + string worker_id = 40; } diff --git a/internal/proto/controller/servers/v1/servers.proto b/internal/proto/controller/servers/v1/servers.proto index 493db240a..3339c9ba9 100644 --- a/internal/proto/controller/servers/v1/servers.proto +++ b/internal/proto/controller/servers/v1/servers.proto @@ -2,44 +2,25 @@ syntax = "proto3"; package controller.servers.v1; -import "controller/storage/timestamp/v1/timestamp.proto"; - option go_package = "github.com/hashicorp/boundary/internal/servers;servers"; -// Server contains all fields related to a Controller or Worker resource -message Server { - // Private ID of the resource - string private_id = 10; +// TagPair matches a key to a value. +message TagPair { + string key = 1; + string value = 2; +} - // Type of the resource (controller, worker) - string type = 20; +// ServerWorkerStatus is the new message used in place of Server to relay status request info. +message ServerWorkerStatus { + // Id of the worker. + string public_id = 10; - // Name of the resource. This is deprecated because we ended up setting the - // private ID and name to be identical, and there is currently no reason we - // can think of to allow a given server's name to match another within the - // same cluster. So we simply use the private ID and deprecate this. - // @inject_tag: `gorm:"-"` - string name = 30 [deprecated = true]; + // Name of the resource (optional) + string name = 20; - // Description of the resource - string description = 40; - - // Address for the server - string address = 50; - - // First seen time from the RDBMS - storage.timestamp.v1.Timestamp create_time = 60; - - // Last time there was an update - storage.timestamp.v1.Timestamp update_time = 70; + // Address for the worker. This must be set. + string address = 30; // Tags for workers - // @inject_tag: `gorm:"-"` - map tags = 80; -} - -// TagValues is used because map fields cannot be repeated but can be a -// message -message TagValues { - repeated string values = 10; + repeated TagPair tags = 40; } diff --git a/internal/servers/options.go b/internal/servers/options.go index 999782588..052ed55f5 100644 --- a/internal/servers/options.go +++ b/internal/servers/options.go @@ -22,6 +22,7 @@ type Option func(*options) // options = how options are represented type options struct { withName string + withPublicId string withDescription string withAddress string withLimit int @@ -57,6 +58,13 @@ func WithName(name string) Option { } } +// WithPublicId provides an optional public Id used for skipping one db call. +func WithPublicId(id string) Option { + return func(o *options) { + o.withPublicId = id + } +} + // WithAddress provides an optional address. func WithAddress(address string) Option { return func(o *options) { diff --git a/internal/servers/options_test.go b/internal/servers/options_test.go index 3a34e4a7d..a1ef9ad8e 100644 --- a/internal/servers/options_test.go +++ b/internal/servers/options_test.go @@ -21,6 +21,14 @@ func Test_GetOpts(t *testing.T) { testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) + t.Run("WithPublicId", func(t *testing.T) { + opts := getOpts(WithPublicId("test")) + testOpts := getDefaultOptions() + testOpts.withPublicId = "test" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil + assert.Equal(t, opts, testOpts) + }) t.Run("WithDescription", func(t *testing.T) { opts := getOpts(WithDescription("test desc")) testOpts := getDefaultOptions() diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index e6f20568d..c06fba951 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -178,7 +178,7 @@ func (r *Repository) ListWorkers(ctx context.Context, scopeIds []string, opt ... // UpsertWorkerStatus creates a new worker if one with the provided public id // doesn't already exist. If it does, UpsertWorkerStatus updates the worker // status. This returns the Worker object with the updated WorkerStatus applied. -// The WithUpdateTags option is the only ones used. All others are ignored. +// The WithPublicId and WithUpdateTags options are the only ones used. All others are ignored. // Workers are intentionally not oplogged. func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt ...Option) (*Worker, error) { const op = "servers.UpsertWorkerStatus" @@ -207,9 +207,14 @@ func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt opts := getOpts(opt...) var err error - worker.PublicId, err = newWorkerIdFromName(ctx, worker.GetWorkerReportedName()) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error generating worker id")) + switch { + case opts.withPublicId != "": + worker.PublicId = opts.withPublicId + default: + worker.PublicId, err = newWorkerIdFromName(ctx, worker.GetWorkerReportedName()) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error generating worker id")) + } } var ret *Worker diff --git a/internal/servers/servers.pb.go b/internal/servers/servers.pb.go index 22d492e8a..46f2879cc 100644 --- a/internal/servers/servers.pb.go +++ b/internal/servers/servers.pb.go @@ -7,7 +7,6 @@ package servers import ( - timestamp "github.com/hashicorp/boundary/internal/db/timestamp" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -21,39 +20,18 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// Server contains all fields related to a Controller or Worker resource -type Server struct { +// TagPair matches a key to a value. +type TagPair struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Private ID of the resource - PrivateId string `protobuf:"bytes,10,opt,name=private_id,json=privateId,proto3" json:"private_id,omitempty"` - // Type of the resource (controller, worker) - Type string `protobuf:"bytes,20,opt,name=type,proto3" json:"type,omitempty"` - // Name of the resource. This is deprecated because we ended up setting the - // private ID and name to be identical, and there is currently no reason we - // can think of to allow a given server's name to match another within the - // same cluster. So we simply use the private ID and deprecate this. - // @inject_tag: `gorm:"-"` - // - // Deprecated: Do not use. - Name string `protobuf:"bytes,30,opt,name=name,proto3" json:"name,omitempty" gorm:"-"` - // Description of the resource - Description string `protobuf:"bytes,40,opt,name=description,proto3" json:"description,omitempty"` - // Address for the server - Address string `protobuf:"bytes,50,opt,name=address,proto3" json:"address,omitempty"` - // First seen time from the RDBMS - CreateTime *timestamp.Timestamp `protobuf:"bytes,60,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` - // Last time there was an update - UpdateTime *timestamp.Timestamp `protobuf:"bytes,70,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` - // Tags for workers - // @inject_tag: `gorm:"-"` - Tags map[string]*TagValues `protobuf:"bytes,80,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" gorm:"-"` + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } -func (x *Server) Reset() { - *x = Server{} +func (x *TagPair) Reset() { + *x = TagPair{} if protoimpl.UnsafeEnabled { mi := &file_controller_servers_v1_servers_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -61,13 +39,13 @@ func (x *Server) Reset() { } } -func (x *Server) String() string { +func (x *TagPair) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Server) ProtoMessage() {} +func (*TagPair) ProtoMessage() {} -func (x *Server) ProtoReflect() protoreflect.Message { +func (x *TagPair) ProtoReflect() protoreflect.Message { mi := &file_controller_servers_v1_servers_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -79,80 +57,43 @@ func (x *Server) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Server.ProtoReflect.Descriptor instead. -func (*Server) Descriptor() ([]byte, []int) { +// Deprecated: Use TagPair.ProtoReflect.Descriptor instead. +func (*TagPair) Descriptor() ([]byte, []int) { return file_controller_servers_v1_servers_proto_rawDescGZIP(), []int{0} } -func (x *Server) GetPrivateId() string { +func (x *TagPair) GetKey() string { if x != nil { - return x.PrivateId + return x.Key } return "" } -func (x *Server) GetType() string { +func (x *TagPair) GetValue() string { if x != nil { - return x.Type + return x.Value } return "" } -// Deprecated: Do not use. -func (x *Server) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Server) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *Server) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -func (x *Server) GetCreateTime() *timestamp.Timestamp { - if x != nil { - return x.CreateTime - } - return nil -} - -func (x *Server) GetUpdateTime() *timestamp.Timestamp { - if x != nil { - return x.UpdateTime - } - return nil -} - -func (x *Server) GetTags() map[string]*TagValues { - if x != nil { - return x.Tags - } - return nil -} - -// TagValues is used because map fields cannot be repeated but can be a -// message -type TagValues struct { +// ServerWorkerStatus is the new message used in place of Server to relay status request info. +type ServerWorkerStatus struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Values []string `protobuf:"bytes,10,rep,name=values,proto3" json:"values,omitempty"` + // Id of the worker. + PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty"` + // Name of the resource (optional) + Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty"` + // Address for the worker. This must be set. + Address string `protobuf:"bytes,30,opt,name=address,proto3" json:"address,omitempty"` + // Tags for workers + Tags []*TagPair `protobuf:"bytes,40,rep,name=tags,proto3" json:"tags,omitempty"` } -func (x *TagValues) Reset() { - *x = TagValues{} +func (x *ServerWorkerStatus) Reset() { + *x = ServerWorkerStatus{} if protoimpl.UnsafeEnabled { mi := &file_controller_servers_v1_servers_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -160,13 +101,13 @@ func (x *TagValues) Reset() { } } -func (x *TagValues) String() string { +func (x *ServerWorkerStatus) String() string { return protoimpl.X.MessageStringOf(x) } -func (*TagValues) ProtoMessage() {} +func (*ServerWorkerStatus) ProtoMessage() {} -func (x *TagValues) ProtoReflect() protoreflect.Message { +func (x *ServerWorkerStatus) ProtoReflect() protoreflect.Message { mi := &file_controller_servers_v1_servers_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -178,14 +119,35 @@ func (x *TagValues) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use TagValues.ProtoReflect.Descriptor instead. -func (*TagValues) Descriptor() ([]byte, []int) { +// Deprecated: Use ServerWorkerStatus.ProtoReflect.Descriptor instead. +func (*ServerWorkerStatus) Descriptor() ([]byte, []int) { return file_controller_servers_v1_servers_proto_rawDescGZIP(), []int{1} } -func (x *TagValues) GetValues() []string { +func (x *ServerWorkerStatus) GetPublicId() string { if x != nil { - return x.Values + return x.PublicId + } + return "" +} + +func (x *ServerWorkerStatus) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ServerWorkerStatus) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *ServerWorkerStatus) GetTags() []*TagPair { + if x != nil { + return x.Tags } return nil } @@ -196,45 +158,24 @@ var file_controller_servers_v1_servers_proto_rawDesc = []byte{ 0x0a, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc1, 0x03, - 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x74, 0x61, 0x67, - 0x73, 0x18, 0x50, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x59, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x23, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, - 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x22, 0x31, 0x0a, 0x07, + 0x54, 0x61, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x93, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, + 0x04, 0x74, 0x61, 0x67, 0x73, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -249,23 +190,18 @@ func file_controller_servers_v1_servers_proto_rawDescGZIP() []byte { return file_controller_servers_v1_servers_proto_rawDescData } -var file_controller_servers_v1_servers_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_controller_servers_v1_servers_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_controller_servers_v1_servers_proto_goTypes = []interface{}{ - (*Server)(nil), // 0: controller.servers.v1.Server - (*TagValues)(nil), // 1: controller.servers.v1.TagValues - nil, // 2: controller.servers.v1.Server.TagsEntry - (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp + (*TagPair)(nil), // 0: controller.servers.v1.TagPair + (*ServerWorkerStatus)(nil), // 1: controller.servers.v1.ServerWorkerStatus } var file_controller_servers_v1_servers_proto_depIdxs = []int32{ - 3, // 0: controller.servers.v1.Server.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 1: controller.servers.v1.Server.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 2, // 2: controller.servers.v1.Server.tags:type_name -> controller.servers.v1.Server.TagsEntry - 1, // 3: controller.servers.v1.Server.TagsEntry.value:type_name -> controller.servers.v1.TagValues - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 0, // 0: controller.servers.v1.ServerWorkerStatus.tags:type_name -> controller.servers.v1.TagPair + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_controller_servers_v1_servers_proto_init() } @@ -275,7 +211,7 @@ func file_controller_servers_v1_servers_proto_init() { } if !protoimpl.UnsafeEnabled { file_controller_servers_v1_servers_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Server); i { + switch v := v.(*TagPair); i { case 0: return &v.state case 1: @@ -287,7 +223,7 @@ func file_controller_servers_v1_servers_proto_init() { } } file_controller_servers_v1_servers_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TagValues); i { + switch v := v.(*ServerWorkerStatus); i { case 0: return &v.state case 1: @@ -305,7 +241,7 @@ func file_controller_servers_v1_servers_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_servers_v1_servers_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/tests/helper/testing_helper.go b/internal/tests/helper/testing_helper.go index 05bacb66a..24b9cb602 100644 --- a/internal/tests/helper/testing_helper.go +++ b/internal/tests/helper/testing_helper.go @@ -34,7 +34,7 @@ import ( const ( DefaultGracePeriod = time.Second * 15 expectConnectionStateOnControllerTimeout = time.Minute * 2 - expectConnectionStateOnWorkerTimeout = DefaultGracePeriod * 2 + expectConnectionStateOnWorkerTimeout = DefaultGracePeriod * 3 // This is the interval that we check states on in the worker. It // needs to be particularly granular to ensure that we allow for From 631c174f47ea826eb77241855c9ac7a2069f316f Mon Sep 17 00:00:00 2001 From: Haotian Date: Fri, 3 Jun 2022 13:25:58 -0700 Subject: [PATCH 047/100] feat(worker): implements WorkerAuthStorage wrapper for worker credential encryption (#2076) * Adds WorkerAuthStorage wrapper for worker credential encryption using nodeenrollment library. * Adds AuthStoragePath field in worker config and dev mode to specify directory to store credentials. --- globals/kms.go | 9 +- internal/cmd/base/server_test.go | 9 +- internal/cmd/base/servers.go | 15 +- internal/cmd/commands/dev/dev.go | 18 ++ internal/cmd/commands/dev/flags_test.go | 72 +++++++ .../commands/server/listener_reload_test.go | 4 +- internal/cmd/config/config.go | 49 +++-- internal/cmd/config/config_test.go | 176 +++++++++++++++++- .../daemon/worker/controller_connection.go | 3 +- internal/daemon/worker/testing.go | 6 + internal/daemon/worker/testing_test.go | 27 +++ internal/daemon/worker/worker.go | 11 +- internal/daemon/worker/worker_test.go | 24 +++ internal/kms/const.go | 5 + internal/kms/external_wrappers_test.go | 3 +- internal/kms/kms.go | 3 +- internal/kms/kms_test.go | 3 +- internal/kms/options.go | 29 ++- 18 files changed, 418 insertions(+), 48 deletions(-) create mode 100644 internal/cmd/commands/dev/flags_test.go diff --git a/globals/kms.go b/globals/kms.go index 1787f7fe3..739d12e7d 100644 --- a/globals/kms.go +++ b/globals/kms.go @@ -1,8 +1,9 @@ package globals const ( - KmsPurposeRoot = "root" - KmsPurposeWorkerAuth = "worker-auth" - KmsPurposeRecovery = "recovery" - KmsPurposeConfig = "config" + KmsPurposeRoot = "root" + KmsPurposeWorkerAuth = "worker-auth" + KmsPurposeWorkerAuthStorage = "worker-auth-storage" + KmsPurposeRecovery = "recovery" + KmsPurposeConfig = "config" ) diff --git a/internal/cmd/base/server_test.go b/internal/cmd/base/server_test.go index a97303679..8f9d63833 100644 --- a/internal/cmd/base/server_test.go +++ b/internal/cmd/base/server_test.go @@ -55,8 +55,11 @@ func TestServer_SetupKMSes(t *testing.T) { purposes: []string{globals.KmsPurposeRoot}, }, { - name: "multi purpose", - purposes: []string{globals.KmsPurposeRoot, globals.KmsPurposeRecovery, globals.KmsPurposeWorkerAuth, globals.KmsPurposeConfig}, + name: "multi purpose", + purposes: []string{ + globals.KmsPurposeRoot, globals.KmsPurposeRecovery, globals.KmsPurposeWorkerAuth, + globals.KmsPurposeWorkerAuthStorage, globals.KmsPurposeConfig, + }, }, } logger := hclog.Default() @@ -89,6 +92,8 @@ func TestServer_SetupKMSes(t *testing.T) { assert.NotNil(s.RootKms) case globals.KmsPurposeWorkerAuth: assert.NotNil(s.WorkerAuthKms) + case globals.KmsPurposeWorkerAuthStorage: + assert.NotNil(s.WorkerAuthStorageKms) case globals.KmsPurposeRecovery: assert.NotNil(s.RecoveryKms) } diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index fd8b6b68f..ba09f71de 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -82,11 +82,12 @@ type Server struct { StderrLock *sync.Mutex Eventer *event.Eventer - RootKms wrapping.Wrapper - WorkerAuthKms wrapping.Wrapper - RecoveryKms wrapping.Wrapper - Kms *kms.Kms - SecureRandomReader io.Reader + RootKms wrapping.Wrapper + WorkerAuthKms wrapping.Wrapper + WorkerAuthStorageKms wrapping.Wrapper + RecoveryKms wrapping.Wrapper + Kms *kms.Kms + SecureRandomReader io.Reader PrometheusRegisterer prometheus.Registerer @@ -533,7 +534,7 @@ func (b *Server) SetupKMSes(ctx context.Context, ui cli.Ui, config *config.Confi if opts.withSkipWorkerAuthKmsInstantiation { continue } - case globals.KmsPurposeRoot, globals.KmsPurposeConfig: + case globals.KmsPurposeRoot, globals.KmsPurposeConfig, globals.KmsPurposeWorkerAuthStorage: case globals.KmsPurposeRecovery: if config.Controller != nil && config.DevRecoveryKey != "" { kms.Config["key"] = config.DevRecoveryKey @@ -597,6 +598,8 @@ func (b *Server) SetupKMSes(ctx context.Context, ui cli.Ui, config *config.Confi b.RootKms = wrapper case globals.KmsPurposeWorkerAuth: b.WorkerAuthKms = wrapper + case globals.KmsPurposeWorkerAuthStorage: + b.WorkerAuthStorageKms = wrapper case globals.KmsPurposeRecovery: b.RecoveryKms = wrapper case globals.KmsPurposeConfig: diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 0b70e3246..eccea78dd 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -86,6 +86,8 @@ type Command struct { flagCreateLoopbackHostPlugin bool flagPluginExecutionDir string flagUseEphemeralKmsWorkerAuthMethod bool + flagWorkerAuthStorageDir string + flagWorkerAuthStorageSkipCleanup bool } func (c *Command) Synopsis() string { @@ -335,6 +337,18 @@ func (c *Command) Flags() *base.FlagSets { Usage: "If set, the original \"ephemeral\" method of worker auth will be used to connect the initial dev worker to the controller.", }) + f.StringVar(&base.StringVar{ + Name: "worker-auth-storage-dir", + Target: &c.flagWorkerAuthStorageDir, + Usage: "Specifies the directory to store worker authentication credentials in dev mode.", + }) + + f.BoolVar(&base.BoolVar{ + Name: "worker-auth-storage-skip-cleanup", + Target: &c.flagWorkerAuthStorageSkipCleanup, + Usage: "Prevents deletion of temp worker credential storage directory if set", + }) + f.BoolVar(&base.BoolVar{ Name: "create-loopback-host-plugin", Target: &c.flagCreateLoopbackHostPlugin, @@ -391,6 +405,7 @@ func (c *Command) Run(args []string) int { c.DevUnprivilegedPassword = c.flagUnprivilegedPassword c.DevTargetDefaultPort = c.flagTargetDefaultPort c.Config.Plugins.ExecutionDir = c.flagPluginExecutionDir + c.Config.Worker.AuthStoragePath = c.flagWorkerAuthStorageDir if c.flagIdSuffix != "" { if len(c.flagIdSuffix) != 10 { c.UI.Error("Invalid ID suffix, must be exactly 10 characters") @@ -756,6 +771,9 @@ func (c *Command) Run(args []string) int { } if !c.flagControllerOnly { + if !c.flagWorkerAuthStorageSkipCleanup { + c.worker.NodeeFileStorage.Cleanup() + } if err := c.worker.Shutdown(); err != nil { c.UI.Error(fmt.Errorf("Error shutting down worker: %w", err).Error()) } diff --git a/internal/cmd/commands/dev/flags_test.go b/internal/cmd/commands/dev/flags_test.go new file mode 100644 index 000000000..5c17c48d3 --- /dev/null +++ b/internal/cmd/commands/dev/flags_test.go @@ -0,0 +1,72 @@ +package dev + +import ( + "bufio" + "os" + "testing" + + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/mitchellh/cli" + "github.com/stretchr/testify/assert" +) + +func TestCommand_Flags(t *testing.T) { + t.Parallel() + assert := assert.New(t) + + serverCmdUi := &base.BoundaryUI{ + Ui: &cli.ColoredUi{ + ErrorColor: cli.UiColorRed, + WarnColor: cli.UiColorYellow, + Ui: &cli.BasicUi{ + Reader: bufio.NewReader(os.Stdin), + Writer: os.Stdout, + }, + }, + Format: "table", + } + base.NewServer(base.NewCommand(serverCmdUi)) + + cmd := &Command{Server: base.NewServer(base.NewCommand(serverCmdUi))} + got := cmd.Flags() + completions := got.Completions() + + assert.Contains(completions, "-log-level") + assert.Contains(completions, "-log-format") + assert.Contains(completions, "-id-suffix") + assert.Contains(completions, "-password") + assert.Contains(completions, "-login-name") + assert.Contains(completions, "-unprivileged-password") + assert.Contains(completions, "-unprivileged-login-name") + assert.Contains(completions, "-api-listen-address") + assert.Contains(completions, "-host-address") + assert.Contains(completions, "-target-default-port") + assert.Contains(completions, "-target-session-connection-limit") + assert.Contains(completions, "-target-session-max-seconds") + assert.Contains(completions, "-cluster-listen-address") + assert.Contains(completions, "-controller-public-cluster-address") + assert.Contains(completions, "-ops-listen-address") + assert.Contains(completions, "-controller-only") + assert.Contains(completions, "-proxy-listen-address") + assert.Contains(completions, "-worker-public-address") + assert.Contains(completions, "-worker-auth-key") + assert.Contains(completions, "-disable-database-destruction") + assert.Contains(completions, "-combine-logs") + assert.Contains(completions, "-ui-passthrough-dir") + assert.Contains(completions, "-recovery-key") + assert.Contains(completions, "-database-url") + assert.Contains(completions, "-container-image") + assert.Contains(completions, "-event-format") + assert.Contains(completions, "-observation-events") + assert.Contains(completions, "-audit-events") + assert.Contains(completions, "-system-events") + assert.Contains(completions, "-audit-events") + assert.Contains(completions, "-event-allow-filter") + assert.Contains(completions, "-event-deny-filter") + assert.Contains(completions, "-plugin-execution-dir") + assert.Contains(completions, "-use-ephemeral-kms-worker-auth-method") + assert.Contains(completions, "-worker-auth-storage-dir") + assert.Contains(completions, "-worker-auth-storage-skip-cleanup") + + // keep adding assertions for other flags which should be set as a result of cmd.Flags() +} diff --git a/internal/cmd/commands/server/listener_reload_test.go b/internal/cmd/commands/server/listener_reload_test.go index 4f19e884f..8d77baf28 100644 --- a/internal/cmd/commands/server/listener_reload_test.go +++ b/internal/cmd/commands/server/listener_reload_test.go @@ -94,7 +94,9 @@ func TestServer_ReloadListener(t *testing.T) { } defer os.RemoveAll(td) - controllerKey, workerAuthKey, recoveryKey := config.DevKeyGeneration() + controllerKey := config.DevKeyGeneration() + workerAuthKey := config.DevKeyGeneration() + recoveryKey := config.DevKeyGeneration() cmd := testServerCommand(t, testServerCommandOpts{ CreateDevDatabase: true, diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index 6d1b85fba..e5282b6b4 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -57,6 +57,13 @@ kms "aead" { key_id = "global_worker-auth" } +kms "aead" { + purpose = "worker-auth-storage" + aead_type = "aes-gcm" + key = "%s" + key_id = "global_worker-auth-storage" +} + kms "aead" { purpose = "recovery" aead_type = "aes-gcm" @@ -105,11 +112,12 @@ type Config struct { Controller *Controller `hcl:"controller"` // Dev-related options - DevController bool `hcl:"-"` - DevUiPassthroughDir string `hcl:"-"` - DevControllerKey string `hcl:"-"` - DevWorkerAuthKey string `hcl:"-"` - DevRecoveryKey string `hcl:"-"` + DevController bool `hcl:"-"` + DevUiPassthroughDir string `hcl:"-"` + DevControllerKey string `hcl:"-"` + DevWorkerAuthKey string `hcl:"-"` + DevWorkerAuthStorageKey string `hcl:"-"` + DevRecoveryKey string `hcl:"-"` // Eventing configuration for the controller Eventing *event.EventerConfig `hcl:"events"` @@ -189,7 +197,7 @@ type Worker struct { StatusGracePeriodDuration time.Duration `hcl:"-"` // StoragePath represents the location a pki worker stores its node credentials, if set. - StoragePath string `hcl:"storage_path"` + AuthStoragePath string `hcl:"storage_path"` } func (w *Worker) InitNameIfEmpty() (string, error) { @@ -234,8 +242,8 @@ func DevWorker() (*Config, error) { return parsed, nil } -func DevKeyGeneration() (string, string, string) { - var numBytes int64 = 96 +func DevKeyGeneration() string { + var numBytes int64 = 32 randBuf := new(bytes.Buffer) n, err := randBuf.ReadFrom(&io.LimitedReader{ R: rand.Reader, @@ -245,21 +253,21 @@ func DevKeyGeneration() (string, string, string) { panic(err) } if n != numBytes { - panic(fmt.Errorf("expected to read 64 bytes, read %d", n)) + panic(fmt.Errorf("expected to read 32 bytes, read %d", n)) } - controllerKey := base64.StdEncoding.EncodeToString(randBuf.Bytes()[0:32]) - workerAuthKey := base64.StdEncoding.EncodeToString(randBuf.Bytes()[32:64]) - recoveryKey := base64.StdEncoding.EncodeToString(randBuf.Bytes()[64:numBytes]) - - return controllerKey, workerAuthKey, recoveryKey + devKey := base64.StdEncoding.EncodeToString(randBuf.Bytes())[:numBytes] + return devKey } // DevController is a Config that is used for dev mode of Boundary // controllers func DevController() (*Config, error) { - controllerKey, workerAuthKey, recoveryKey := DevKeyGeneration() + controllerKey := DevKeyGeneration() + workerAuthKey := DevKeyGeneration() + workerAuthStorageKey := DevKeyGeneration() + recoveryKey := DevKeyGeneration() - hclStr := fmt.Sprintf(devConfig+devControllerExtraConfig, controllerKey, workerAuthKey, recoveryKey) + hclStr := fmt.Sprintf(devConfig+devControllerExtraConfig, controllerKey, workerAuthKey, workerAuthStorageKey, recoveryKey) parsed, err := Parse(hclStr) if err != nil { return nil, fmt.Errorf("error parsing dev config: %w", err) @@ -267,13 +275,17 @@ func DevController() (*Config, error) { parsed.DevController = true parsed.DevControllerKey = controllerKey parsed.DevWorkerAuthKey = workerAuthKey + parsed.DevWorkerAuthStorageKey = workerAuthStorageKey parsed.DevRecoveryKey = recoveryKey return parsed, nil } func DevCombined() (*Config, error) { - controllerKey, workerAuthKey, recoveryKey := DevKeyGeneration() - hclStr := fmt.Sprintf(devConfig+devControllerExtraConfig+devWorkerExtraConfig, controllerKey, workerAuthKey, recoveryKey) + controllerKey := DevKeyGeneration() + workerAuthKey := DevKeyGeneration() + workerAuthStorageKey := DevKeyGeneration() + recoveryKey := DevKeyGeneration() + hclStr := fmt.Sprintf(devConfig+devControllerExtraConfig+devWorkerExtraConfig, controllerKey, workerAuthKey, workerAuthStorageKey, recoveryKey) parsed, err := Parse(hclStr) if err != nil { return nil, fmt.Errorf("error parsing dev config: %w", err) @@ -281,6 +293,7 @@ func DevCombined() (*Config, error) { parsed.DevController = true parsed.DevControllerKey = controllerKey parsed.DevWorkerAuthKey = workerAuthKey + parsed.DevWorkerAuthStorageKey = workerAuthStorageKey parsed.DevRecoveryKey = recoveryKey return parsed, nil } diff --git a/internal/cmd/config/config_test.go b/internal/cmd/config/config_test.go index 99ba8486a..570a71503 100644 --- a/internal/cmd/config/config_test.go +++ b/internal/cmd/config/config_test.go @@ -61,6 +61,14 @@ func TestDevController(t *testing.T) { "key_id": "global_worker-auth", }, }, + { + Type: "aead", + Purpose: []string{"worker-auth-storage"}, + Config: map[string]string{ + "aead_type": "aes-gcm", + "key_id": "global_worker-auth-storage", + }, + }, { Type: "aead", Purpose: []string{"recovery"}, @@ -84,9 +92,11 @@ func TestDevController(t *testing.T) { exp.Seals[0].Config["key"] = actual.Seals[0].Config["key"] exp.Seals[1].Config["key"] = actual.Seals[1].Config["key"] exp.Seals[2].Config["key"] = actual.Seals[2].Config["key"] + exp.Seals[3].Config["key"] = actual.Seals[3].Config["key"] exp.DevControllerKey = actual.Seals[0].Config["key"] exp.DevWorkerAuthKey = actual.Seals[1].Config["key"] - exp.DevRecoveryKey = actual.Seals[2].Config["key"] + exp.DevWorkerAuthStorageKey = actual.Seals[2].Config["key"] + exp.DevRecoveryKey = actual.Seals[3].Config["key"] assert.Equal(t, exp, actual) @@ -291,6 +301,170 @@ func TestDevWorker(t *testing.T) { assert.Error(t, err) } +func TestDevCombined(t *testing.T) { + actual, err := DevCombined() + if err != nil { + t.Fatal(err) + } + + truePointer := new(bool) + *truePointer = true + exp := &Config{ + Eventing: event.DefaultEventerConfig(), + SharedConfig: &configutil.SharedConfig{ + DisableMlock: true, + Listeners: []*listenerutil.ListenerConfig{ + { + Type: "tcp", + Purpose: []string{"api"}, + TLSDisable: true, + CorsEnabled: truePointer, + CorsAllowedOrigins: []string{"*"}, + }, + { + Type: "tcp", + Purpose: []string{"cluster"}, + }, + { + Type: "tcp", + Purpose: []string{"ops"}, + TLSDisable: true, + }, + { + Type: "tcp", + Purpose: []string{"proxy"}, + }, + }, + Seals: []*configutil.KMS{ + { + Type: "aead", + Purpose: []string{"root"}, + Config: map[string]string{ + "aead_type": "aes-gcm", + "key_id": "global_root", + }, + }, + { + Type: "aead", + Purpose: []string{"worker-auth"}, + Config: map[string]string{ + "aead_type": "aes-gcm", + "key_id": "global_worker-auth", + }, + }, + { + Type: "aead", + Purpose: []string{"worker-auth-storage"}, + Config: map[string]string{ + "aead_type": "aes-gcm", + "key_id": "global_worker-auth-storage", + }, + }, + { + Type: "aead", + Purpose: []string{"recovery"}, + Config: map[string]string{ + "aead_type": "aes-gcm", + "key_id": "global_recovery", + }, + }, + }, + }, + Controller: &Controller{ + Name: "dev-controller", + Description: "A default controller created in dev mode", + }, + DevController: true, + Worker: &Worker{ + Name: "w_1234567890", + Description: "A default worker created in dev mode", + Controllers: []string{"127.0.0.1"}, + ControllersRaw: []interface{}{"127.0.0.1"}, + Tags: map[string][]string{ + "type": {"dev", "local"}, + }, + }, + } + + exp.Listeners[0].RawConfig = actual.Listeners[0].RawConfig + exp.Listeners[1].RawConfig = actual.Listeners[1].RawConfig + exp.Listeners[2].RawConfig = actual.Listeners[2].RawConfig + exp.Listeners[3].RawConfig = actual.Listeners[3].RawConfig + exp.Seals[0].Config["key"] = actual.Seals[0].Config["key"] + exp.Seals[1].Config["key"] = actual.Seals[1].Config["key"] + exp.Seals[2].Config["key"] = actual.Seals[2].Config["key"] + exp.Seals[3].Config["key"] = actual.Seals[3].Config["key"] + exp.DevControllerKey = actual.Seals[0].Config["key"] + exp.DevWorkerAuthKey = actual.Seals[1].Config["key"] + exp.DevWorkerAuthStorageKey = actual.Seals[2].Config["key"] + exp.DevRecoveryKey = actual.Seals[3].Config["key"] + exp.Worker.TagsRaw = actual.Worker.TagsRaw + assert.Equal(t, exp, actual) +} + +func TestDevWorkerCredentialStorageDir(t *testing.T) { + t.Parallel() + tests := []struct { + name string + devWorkerConfig string + storagePath string + }{ + { + name: "Relative Storage Directory", + devWorkerConfig: ` + listener "tcp" { + purpose = "proxy" + } + + worker { + name = "w_1234567890" + description = "A default worker created in dev mode" + controllers = ["127.0.0.1"] + tags { + type = ["dev", "local"] + } + storage_path = ".." + } + `, + storagePath: "..", + }, + { + name: "Nonexistent Storage Directory", + devWorkerConfig: ` + listener "tcp" { + purpose = "proxy" + } + + worker { + name = "w_1234567890" + description = "A default worker created in dev mode" + controllers = ["127.0.0.1"] + tags { + type = ["dev", "local"] + } + storage_path = "nonexistent_dir/here" + } + `, + storagePath: "nonexistent_dir/here", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parsed, err := Parse(devConfig + tt.devWorkerConfig) + require.NoError(t, err) + require.Equal(t, tt.storagePath, parsed.Worker.AuthStoragePath) + }) + } +} + +func TestDevKeyGeneration(t *testing.T) { + t.Parallel() + dk := DevKeyGeneration() + numBytes := 32 + require.Equal(t, numBytes, len(dk)) + require.NotEqual(t, dk, DevKeyGeneration()) +} + func TestParsingName(t *testing.T) { t.Parallel() config := ` diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index 28dec4f0c..66b5fce6b 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -23,6 +23,7 @@ import ( pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/go-secure-stdlib/base62" + "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/multihop" "github.com/hashicorp/nodeenrollment/protocol" "google.golang.org/grpc" @@ -73,7 +74,7 @@ func (w *Worker) controllerDialerFunc() func(context.Context, string) (net.Conn, case w.conf.WorkerAuthKms != nil: conn, err = w.v1KmsAuthDialFn(ctx, addr) default: - conn, err = protocol.Dial(ctx, w.NodeeFileStorage, addr) + conn, err = protocol.Dial(ctx, w.NodeeFileStorage, addr, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) } if !w.everAuthenticated.Load() && err == nil && conn != nil { diff --git a/internal/daemon/worker/testing.go b/internal/daemon/worker/testing.go index 623cbd0d7..5025f9791 100644 --- a/internal/daemon/worker/testing.go +++ b/internal/daemon/worker/testing.go @@ -174,6 +174,9 @@ type TestWorkerOpts struct { // The worker auth KMS to use, or one will be created WorkerAuthKms wrapping.Wrapper + // The worker credential storage KMS to use, or one will be created + WorkerAuthStorageKms wrapping.Wrapper + // The name to use for the worker, otherwise one will be randomly // generated, unless provided in a non-nil Config Name string @@ -261,6 +264,8 @@ func NewTestWorker(t testing.TB, opts *TestWorkerOpts) *TestWorker { switch { case opts.WorkerAuthKms != nil: tw.b.WorkerAuthKms = opts.WorkerAuthKms + case opts.WorkerAuthStorageKms != nil: + tw.b.WorkerAuthStorageKms = opts.WorkerAuthStorageKms default: if err := tw.b.SetupKMSes(tw.b.Context, nil, opts.Config); err != nil { t.Fatal(err) @@ -310,6 +315,7 @@ func (tw *TestWorker) AddClusterWorkerMember(t testing.TB, opts *TestWorkerOpts) } nextOpts := &TestWorkerOpts{ WorkerAuthKms: tw.w.conf.WorkerAuthKms, + WorkerAuthStorageKms: tw.w.conf.WorkerAuthStorageKms, Name: opts.Name, InitialControllers: tw.ControllerAddrs(), Logger: tw.w.conf.Logger, diff --git a/internal/daemon/worker/testing_test.go b/internal/daemon/worker/testing_test.go index 0b9fe2536..b1fabe6c5 100644 --- a/internal/daemon/worker/testing_test.go +++ b/internal/daemon/worker/testing_test.go @@ -6,7 +6,9 @@ import ( "time" "github.com/hashicorp/boundary/internal/daemon/worker/session" + "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" "github.com/stretchr/testify/require" ) @@ -60,3 +62,28 @@ func TestTestWorkerLookupSessionMissing(t *testing.T) { require.False(ok) require.Equal(TestSessionInfo{}, actual) } + +func TestTestWorker_WorkerAuthStorageKms(t *testing.T) { + tests := []struct { + name string + wrapper wrapping.Wrapper + }{ + { + name: "Nil Wrapper", + wrapper: nil, + }, + { + name: "Valid Wrapper", + wrapper: db.TestWrapper(t), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + tw := NewTestWorker(t, &TestWorkerOpts{ + WorkerAuthStorageKms: tt.wrapper, + }) + require.Equal(tt.wrapper, tw.Config().WorkerAuthStorageKms) + }) + } +} diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 26dc8c0dd..cae39f101 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -162,7 +162,7 @@ func New(conf *Config) (*Worker, error) { } w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext, - nodeefile.WithBaseDirectory(w.conf.RawConfig.Worker.StoragePath)) + nodeefile.WithBaseDirectory(w.conf.RawConfig.Worker.AuthStoragePath)) if err != nil { return nil, err } @@ -184,11 +184,18 @@ func (w *Worker) Start() error { controllerResolver := manual.NewBuilderWithScheme(scheme) w.controllerResolver.Store(controllerResolver) + var err error + w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext, + nodeefile.WithBaseDirectory(w.conf.RawConfig.Worker.AuthStoragePath)) + if err != nil { + return err + } + if err := w.startListeners(); err != nil { return fmt.Errorf("error starting worker listeners: %w", err) } - nodeCreds, err := types.NewNodeCredentials(w.baseContext, w.NodeeFileStorage) + nodeCreds, err := types.NewNodeCredentials(w.baseContext, w.NodeeFileStorage, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) if err != nil { return fmt.Errorf("error generating new node creds: %w", err) } diff --git a/internal/daemon/worker/worker_test.go b/internal/daemon/worker/worker_test.go index 2f04c2fc2..6c18a4f3f 100644 --- a/internal/daemon/worker/worker_test.go +++ b/internal/daemon/worker/worker_test.go @@ -1,13 +1,16 @@ package worker import ( + "context" "testing" "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/config" + "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/configutil/v2" "github.com/hashicorp/go-secure-stdlib/listenerutil" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -123,3 +126,24 @@ func TestWorkerNew(t *testing.T) { }) } } + +func TestSetupWorkerCredentialStorage(t *testing.T) { + ctx := context.Background() + assert, require := assert.New(t), require.New(t) + ts := db.TestWrapper(t) + keyId, err := ts.KeyId(ctx) + + tw := NewTestWorker(t, &TestWorkerOpts{ + WorkerAuthStorageKms: ts, + DisableAutoStart: true, + }) + + err = tw.Worker().Start() + require.NoError(err) + wKeyId, err := tw.Config().WorkerAuthStorageKms.KeyId(ctx) + require.NoError(err) + assert.Equal(keyId, wKeyId) + + err = tw.Worker().Shutdown() + require.NoError(err) +} diff --git a/internal/kms/const.go b/internal/kms/const.go index a08fddf3d..a2349fc6d 100644 --- a/internal/kms/const.go +++ b/internal/kms/const.go @@ -27,6 +27,9 @@ const ( // KeyPurposeWorkerAuth is used for worker auth KeyPurposeWorkerAuth + // KeyPurposeWorkerAuthStorage is used for worker credential storage + KeyPurposeWorkerAuthStorage + // KeyPurposeTokens is used for token encryption KeyPurposeTokens @@ -56,6 +59,8 @@ func (k KeyPurpose) String() string { return "recovery" case KeyPurposeWorkerAuth: return "workerauth" + case KeyPurposeWorkerAuthStorage: + return "workerauthstorage" case KeyPurposeTokens: return "tokens" case KeyPurposeSessions: diff --git a/internal/kms/external_wrappers_test.go b/internal/kms/external_wrappers_test.go index be43c61a6..c0cbea429 100644 --- a/internal/kms/external_wrappers_test.go +++ b/internal/kms/external_wrappers_test.go @@ -20,9 +20,10 @@ func TestExternalWrappers(t *testing.T) { rootWrapper := db.TestWrapper(t) recoveryWrapper := db.TestWrapper(t) workerAuthWrapper := db.TestWrapper(t) + workerAuthStorageWrapper := db.TestWrapper(t) k := TestKms(t, conn, rootWrapper) - err := k.AddExternalWrappers(testCtx, WithRecoveryWrapper(recoveryWrapper), WithWorkerAuthWrapper(workerAuthWrapper)) + err := k.AddExternalWrappers(testCtx, WithRecoveryWrapper(recoveryWrapper), WithWorkerAuthWrapper(workerAuthWrapper), WithWorkerAuthStorageWrapper(workerAuthStorageWrapper)) require.NoError(err) assert.Equal(rootWrapper, k.GetExternalWrappers(testCtx).Root()) diff --git a/internal/kms/kms.go b/internal/kms/kms.go index 701329b75..c26e869bb 100644 --- a/internal/kms/kms.go +++ b/internal/kms/kms.go @@ -258,7 +258,8 @@ func stdNewKmsPurposes() []wrappingKms.KeyPurpose { for _, p := range ValidDekPurposes() { purposes = append(purposes, wrappingKms.KeyPurpose(p.String())) } - purposes = append(purposes, wrappingKms.KeyPurpose(KeyPurposeWorkerAuth.String()), wrappingKms.KeyPurpose(KeyPurposeRecovery.String())) + purposes = append(purposes, wrappingKms.KeyPurpose(KeyPurposeWorkerAuth.String()), wrappingKms.KeyPurpose(KeyPurposeWorkerAuthStorage.String()), + wrappingKms.KeyPurpose(KeyPurposeRecovery.String())) return purposes } diff --git a/internal/kms/kms_test.go b/internal/kms/kms_test.go index 83eb47522..f0c5bf91f 100644 --- a/internal/kms/kms_test.go +++ b/internal/kms/kms_test.go @@ -52,7 +52,8 @@ func Test_New(t *testing.T) { for _, p := range ValidDekPurposes() { purposes = append(purposes, wrappingKms.KeyPurpose(p.String())) } - purposes = append(purposes, wrappingKms.KeyPurpose(KeyPurposeWorkerAuth.String()), wrappingKms.KeyPurpose(KeyPurposeRecovery.String())) + purposes = append(purposes, wrappingKms.KeyPurpose(KeyPurposeWorkerAuth.String()), + wrappingKms.KeyPurpose(KeyPurposeWorkerAuthStorage.String()), wrappingKms.KeyPurpose(KeyPurposeRecovery.String())) r := dbw.New(rw.UnderlyingDB()) w := dbw.New(rw.UnderlyingDB()) diff --git a/internal/kms/options.go b/internal/kms/options.go index ab52ccaa1..6b97f8ed8 100644 --- a/internal/kms/options.go +++ b/internal/kms/options.go @@ -21,16 +21,17 @@ type Option func(*options) // options = how options are represented type options struct { - withLimit int - withRootWrapper wrapping.Wrapper - withWorkerAuthWrapper wrapping.Wrapper - withRecoveryWrapper wrapping.Wrapper - withOrderByVersion db.OrderBy - withKeyId string - withScopeIds []string - withRandomReader io.Reader - withReader db.Reader - withWriter db.Writer + withLimit int + withRootWrapper wrapping.Wrapper + withWorkerAuthWrapper wrapping.Wrapper + withWorkerAuthStorageWrapper wrapping.Wrapper + withRecoveryWrapper wrapping.Wrapper + withOrderByVersion db.OrderBy + withKeyId string + withScopeIds []string + withRandomReader io.Reader + withReader db.Reader + withWriter db.Writer } func getDefaultOptions() options { @@ -61,6 +62,14 @@ func WithWorkerAuthWrapper(w wrapping.Wrapper) Option { } } +// WithWorkerAuthStorageWrapper sets the external pki worker storage wrapper for a +// given scope +func WithWorkerAuthStorageWrapper(w wrapping.Wrapper) Option { + return func(o *options) { + o.withWorkerAuthStorageWrapper = w + } +} + // WithRecoveryWrapper sets the recovery wrapper for a given scope func WithRecoveryWrapper(w wrapping.Wrapper) Option { return func(o *options) { From f4642930b4b9863428e106b02f5a2ac8a17977d4 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 3 Jun 2022 17:32:33 -0400 Subject: [PATCH 048/100] Updates from review of 2076 (#2135) --- internal/cmd/config/config.go | 4 ++-- internal/cmd/config/config_test.go | 4 ++-- internal/daemon/worker/worker.go | 7 ------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index e5282b6b4..4d45c617a 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -196,8 +196,8 @@ type Worker struct { // TODO: This field is currently internal. StatusGracePeriodDuration time.Duration `hcl:"-"` - // StoragePath represents the location a pki worker stores its node credentials, if set. - AuthStoragePath string `hcl:"storage_path"` + // AuthStoragePath represents the location a worker stores its node credentials, if set + AuthStoragePath string `hcl:"auth_storage_path"` } func (w *Worker) InitNameIfEmpty() (string, error) { diff --git a/internal/cmd/config/config_test.go b/internal/cmd/config/config_test.go index 570a71503..6efbf8d2f 100644 --- a/internal/cmd/config/config_test.go +++ b/internal/cmd/config/config_test.go @@ -423,7 +423,7 @@ func TestDevWorkerCredentialStorageDir(t *testing.T) { tags { type = ["dev", "local"] } - storage_path = ".." + auth_storage_path = ".." } `, storagePath: "..", @@ -442,7 +442,7 @@ func TestDevWorkerCredentialStorageDir(t *testing.T) { tags { type = ["dev", "local"] } - storage_path = "nonexistent_dir/here" + auth_storage_path = "nonexistent_dir/here" } `, storagePath: "nonexistent_dir/here", diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index cae39f101..b4d5d417c 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -184,13 +184,6 @@ func (w *Worker) Start() error { controllerResolver := manual.NewBuilderWithScheme(scheme) w.controllerResolver.Store(controllerResolver) - var err error - w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext, - nodeefile.WithBaseDirectory(w.conf.RawConfig.Worker.AuthStoragePath)) - if err != nil { - return err - } - if err := w.startListeners(); err != nil { return fmt.Errorf("error starting worker listeners: %w", err) } From caf19f867edb9f60026d792ae53caf80e644cea7 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 3 Jun 2022 17:47:44 -0400 Subject: [PATCH 049/100] Rename some variables to remove Nodee --- go.mod | 2 +- go.sum | 4 ++-- internal/cmd/commands/dev/dev.go | 17 +++++++------ internal/cmd/commands/server/server.go | 10 ++++---- .../daemon/worker/controller_connection.go | 2 +- internal/daemon/worker/listeners.go | 24 +++++++++---------- internal/daemon/worker/worker.go | 18 +++++++------- .../servers/repository_workerauth_test.go | 4 ++-- 8 files changed, 42 insertions(+), 39 deletions(-) diff --git a/go.mod b/go.mod index 8feafe862..4ce1d09e1 100644 --- a/go.mod +++ b/go.mod @@ -92,7 +92,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b - github.com/hashicorp/nodeenrollment v0.0.0-20220603150451-f3e992e55fd8 + github.com/hashicorp/nodeenrollment v0.0.0-20220603214204-cead023172f3 ) require ( diff --git a/go.sum b/go.sum index 3b74234e0..9e8efeefa 100644 --- a/go.sum +++ b/go.sum @@ -740,8 +740,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220603150451-f3e992e55fd8 h1:stSjcYW8rtmRUtmoN/Kw7d+ciZULbeSxtBjdl6jz1Vk= -github.com/hashicorp/nodeenrollment v0.0.0-20220603150451-f3e992e55fd8/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.0.0-20220603214204-cead023172f3 h1:yGCgHpFf6g4VQKxP45r2vxQZXjequvcpm1x5XKg0oaU= +github.com/hashicorp/nodeenrollment v0.0.0-20220603214204-cead023172f3/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index eccea78dd..70055bd7d 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -29,6 +29,7 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/strutil" + nodeefile "github.com/hashicorp/nodeenrollment/storage/file" "github.com/mitchellh/cli" "github.com/posener/complete" "go.uber.org/atomic" @@ -691,15 +692,15 @@ func (c *Command) Run(args []string) int { } if !c.flagUseEphemeralKmsWorkerAuthMethod { - req := c.worker.NodeeRegistrationRequest + req := c.worker.WorkerAuthRegistrationRequest if req == "" { - c.UI.Error("No worker registration request found at worker start time") + c.UI.Error("No worker auth registration request found at worker start time") return base.CommandCliError } - c.InfoKeys = append(c.InfoKeys, "worker registration request") - c.Info["worker registration request"] = req - c.InfoKeys = append(c.InfoKeys, "worker current key id") - c.Info["worker current key id"] = c.worker.NodeeCurrentKeyId + c.InfoKeys = append(c.InfoKeys, "worker auth registration request") + c.Info["worker auth registration request"] = req + c.InfoKeys = append(c.InfoKeys, "worker auth current key id") + c.Info["worker auth current key id"] = c.worker.WorkerAuthCurrentKeyId go func() { for { select { @@ -772,7 +773,9 @@ func (c *Command) Run(args []string) int { if !c.flagControllerOnly { if !c.flagWorkerAuthStorageSkipCleanup { - c.worker.NodeeFileStorage.Cleanup() + if fileStorage, ok := c.worker.WorkerAuthStorage.(*nodeefile.FileStorage); ok { + fileStorage.Cleanup() + } } if err := c.worker.Shutdown(); err != nil { c.UI.Error(fmt.Errorf("Error shutting down worker: %w", err).Error()) diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index ca17036f4..a62cb8e9e 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -464,7 +464,7 @@ func (c *Command) Run(args []string) int { return base.CommandCliError } if c.WorkerAuthKms == nil { - req := c.worker.NodeeRegistrationRequest + req := c.worker.WorkerAuthRegistrationRequest if req == "" { retErr := fmt.Errorf("No worker registration request found at worker startup time") if err := c.worker.Shutdown(); err != nil { @@ -477,10 +477,10 @@ func (c *Command) Run(args []string) int { } return base.CommandCliError } - c.InfoKeys = append(c.InfoKeys, "worker registration request") - c.Info["worker registration request"] = req - c.InfoKeys = append(c.InfoKeys, "worker current key id") - c.Info["worker current key id"] = c.worker.NodeeCurrentKeyId + c.InfoKeys = append(c.InfoKeys, "worker auth registration request") + c.Info["worker auth registration request"] = req + c.InfoKeys = append(c.InfoKeys, "worker auth current key id") + c.Info["worker auth current key id"] = c.worker.WorkerAuthCurrentKeyId } } diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index 66b5fce6b..bd87c4457 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -74,7 +74,7 @@ func (w *Worker) controllerDialerFunc() func(context.Context, string) (net.Conn, case w.conf.WorkerAuthKms != nil: conn, err = w.v1KmsAuthDialFn(ctx, addr) default: - conn, err = protocol.Dial(ctx, w.NodeeFileStorage, addr, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) + conn, err = protocol.Dial(ctx, w.WorkerAuthStorage, addr, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) } if !w.everAuthenticated.Load() && err == nil && conn != nil { diff --git a/internal/daemon/worker/listeners.go b/internal/daemon/worker/listeners.go index ed98ac843..1aac3dfba 100644 --- a/internal/daemon/worker/listeners.go +++ b/internal/daemon/worker/listeners.go @@ -123,7 +123,7 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) interceptingListener, err := protocol.NewInterceptingListener( &protocol.InterceptingListenerConfiguration{ Context: w.baseContext, - Storage: w.NodeeFileStorage, + Storage: w.WorkerAuthStorage, BaseListener: ln.ProxyListener, BaseTlsConfiguration: &tls.Config{ GetConfigForClient: w.getSessionTls, @@ -135,14 +135,14 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) return nil, fmt.Errorf("error instantiating node auth listener: %w", err) } - w.nodeeSplitListener = splitlistener.New(interceptingListener) + w.workerAuthSplitListener = splitlistener.New(interceptingListener) downstreamServer := grpc.NewServer( grpc.MaxRecvMsgSize(math.MaxInt32), grpc.MaxSendMsgSize(math.MaxInt32), ) multihopService, err := handlers.NewMultihopServiceServer( - w.NodeeFileStorage, + w.WorkerAuthStorage, false, w.controllerMultihopConn, ) @@ -157,9 +157,9 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) ln.GrpcServer = downstreamServer return func() { - go w.nodeeSplitListener.Start() - go httpServer.Serve(w.nodeeSplitListener.OtherListener()) - go ln.GrpcServer.Serve(w.nodeeSplitListener.NodeEnrollmentListener()) + go w.workerAuthSplitListener.Start() + go httpServer.Serve(w.workerAuthSplitListener.OtherListener()) + go ln.GrpcServer.Serve(w.workerAuthSplitListener.NodeEnrollmentListener()) }, nil } @@ -172,21 +172,21 @@ func (w *Worker) stopServersAndListeners() error { // really likes to hang on closing. Maybe because it's never served a // connection? This is a workaround to force it until I can dig in. var cancel context.CancelFunc - if w.nodeeSplitListener != nil { + if w.workerAuthSplitListener != nil { var ctx context.Context ctx, cancel = context.WithTimeout(w.baseContext, 2*time.Second) go func() { <-ctx.Done() - w.nodeeSplitListener.Stop() + w.workerAuthSplitListener.Stop() cancel() }() } stopErrors := mg.Wait() - if w.nodeeSplitListener != nil { + if w.workerAuthSplitListener != nil { cancel() - err := w.nodeeSplitListener.Stop() + err := w.workerAuthSplitListener.Stop() if err != nil { stopErrors = multierror.Append(stopErrors, err) } @@ -237,8 +237,8 @@ func (w *Worker) stopAnyListeners() error { } var closeErrors *multierror.Error var err error - if w.nodeeSplitListener != nil { - err = w.nodeeSplitListener.Stop() + if w.workerAuthSplitListener != nil { + err = w.workerAuthSplitListener.Stop() } else if w.proxyListener.ProxyListener != nil { err = w.proxyListener.ProxyListener.Close() } diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index b4d5d417c..a8cee0726 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -74,11 +74,11 @@ type Worker struct { // SIGHUP. updateTags *ua.Bool - // PoC: Testing bits for BYOW - NodeeFileStorage *nodeefile.FileStorage - NodeeCurrentKeyId string - NodeeRegistrationRequest string - nodeeSplitListener *splitlistener.SplitListener + // The storage for node enrollment + WorkerAuthStorage nodeenrollment.Storage + WorkerAuthCurrentKeyId string + WorkerAuthRegistrationRequest string + workerAuthSplitListener *splitlistener.SplitListener // Test-specific options TestOverrideX509VerifyDnsName string @@ -161,7 +161,7 @@ func New(conf *Config) (*Worker, error) { return nil, fmt.Errorf("exactly one proxy listener is required") } - w.NodeeFileStorage, err = nodeefile.NewFileStorage(w.baseContext, + w.WorkerAuthStorage, err = nodeefile.New(w.baseContext, nodeefile.WithBaseDirectory(w.conf.RawConfig.Worker.AuthStoragePath)) if err != nil { return nil, err @@ -188,7 +188,7 @@ func (w *Worker) Start() error { return fmt.Errorf("error starting worker listeners: %w", err) } - nodeCreds, err := types.NewNodeCredentials(w.baseContext, w.NodeeFileStorage, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) + nodeCreds, err := types.NewNodeCredentials(w.baseContext, w.WorkerAuthStorage, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) if err != nil { return fmt.Errorf("error generating new node creds: %w", err) } @@ -201,11 +201,11 @@ func (w *Worker) Start() error { if err != nil { return fmt.Errorf("error marshaling fetch credentials request: %w", err) } - w.NodeeRegistrationRequest = base58.FastBase58Encoding(reqBytes) + w.WorkerAuthRegistrationRequest = base58.FastBase58Encoding(reqBytes) if err != nil { return fmt.Errorf("error encoding registration request: %w", err) } - w.NodeeCurrentKeyId, err = nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) + w.WorkerAuthCurrentKeyId, err = nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) if err != nil { return fmt.Errorf("error deriving key id: %w", err) } diff --git a/internal/servers/repository_workerauth_test.go b/internal/servers/repository_workerauth_test.go index f6d54a4be..061acbcd1 100644 --- a/internal/servers/repository_workerauth_test.go +++ b/internal/servers/repository_workerauth_test.go @@ -158,7 +158,7 @@ func TestStoreWorkerAuth(t *testing.T) { require.NoError(err) // This happens on the worker - fileStorage, err := file.NewFileStorage(ctx) + fileStorage, err := file.New(ctx) require.NoError(err) nodeCreds, err := types.NewNodeCredentials(ctx, fileStorage) require.NoError(err) @@ -270,7 +270,7 @@ func TestStoreNodeInformationTx(t *testing.T) { testNodeInfoFn := func() *types.NodeInformation { // This happens on the worker - fileStorage, err := file.NewFileStorage(testCtx) + fileStorage, err := file.New(testCtx) require.NoError(t, err) nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage) require.NoError(t, err) From c1445728e35f6863ee2cb77a8f29dfbfca305aa1 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 5 Jun 2022 14:50:15 -0400 Subject: [PATCH 050/100] tests (servers): additional CreateWorker(...) unit tests (#2134) --- internal/servers/repository_worker.go | 4 +- internal/servers/repository_worker_test.go | 88 +++++++++++++++++++++- 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index c06fba951..187ef216e 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -442,7 +442,7 @@ func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Op ctx, db.StdRetryCnt, db.ExpBackoff{}, - func(_ db.Reader, w db.Writer) error { + func(read db.Reader, w db.Writer) error { returnedWorker = worker.clone() if err := w.Create( ctx, @@ -451,7 +451,7 @@ func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Op return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create worker")) } if opts.withFetchNodeCredentialsRequest != nil { - workerAuthRepo, err = NewRepositoryStorage(ctx, r.reader, r.writer, r.kms) + workerAuthRepo, err = NewRepositoryStorage(ctx, read, w, r.kms) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create worker auth repository")) } diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 5096f786f..673a7aeb9 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/DATA-DOG/go-sqlmock" "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/timestamp" @@ -17,6 +18,10 @@ import ( "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-dbw" + "github.com/hashicorp/go-kms-wrapping/extras/kms/v2/migrations" + "github.com/hashicorp/nodeenrollment/rotation" + "github.com/hashicorp/nodeenrollment/storage/file" + "github.com/hashicorp/nodeenrollment/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/testing/protocmp" @@ -508,8 +513,8 @@ func TestRepository_CreateWorker(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) - kms := kms.TestKms(t, conn, wrapper) - testRepo, err := servers.NewRepository(rw, rw, kms) + testKms := kms.TestKms(t, conn, wrapper) + testRepo, err := servers.NewRepository(rw, rw, testKms) require.NoError(t, err) iamRepo := iam.TestRepo(t, conn, wrapper) @@ -519,10 +524,17 @@ func TestRepository_CreateWorker(t *testing.T) { return "", errors.New(testCtx, errors.Internal, "test", "testNewIdFn-error") } + rootStorage, err := servers.NewRepositoryStorage(testCtx, rw, rw, testKms) + require.NoError(t, err) + _, err = rotation.RotateRootCertificates(testCtx, rootStorage) + require.NoError(t, err) + tests := []struct { name string setup func() *servers.Worker repo *servers.Repository + fetchReq *types.FetchNodeCredentialsRequest + reader db.Reader opt []servers.Option wantErr bool wantErrIs errors.Code @@ -599,7 +611,7 @@ func TestRepository_CreateWorker(t *testing.T) { mock.ExpectBegin() mock.ExpectQuery(`INSERT`).WillReturnError(errors.New(testCtx, errors.Internal, "test", "create-error")) mock.ExpectRollback() - r, err := servers.NewRepository(rw, writer, kms) + r, err := servers.NewRepository(rw, writer, testKms) require.NoError(t, err) return r }(), @@ -643,11 +655,69 @@ func TestRepository_CreateWorker(t *testing.T) { wantErrContains: "last status time is not nil", }, { - name: "success", + name: "no-database-key", setup: func() *servers.Worker { w := servers.NewWorker(scope.Global.String()) return w }, + reader: rw, + fetchReq: func() *types.FetchNodeCredentialsRequest { + // This happens on the worker + fileStorage, err := file.NewFileStorage(testCtx) + require.NoError(t, err) + defer fileStorage.Cleanup() + + nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage) + require.NoError(t, err) + // Create request using worker id + fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(testCtx) + require.NoError(t, err) + return fetchReq + }(), + repo: func() *servers.Repository { + mockConn, mock := db.TestSetupWithMock(t) + mock.ExpectQuery(`SELECT`).WillReturnRows(sqlmock.NewRows([]string{"version", "create_time"}).AddRow(migrations.Version, time.Now())) + mock.ExpectQuery(`SELECT`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "no-database-key")) + k := kms.TestKms(t, mockConn, wrapper) + r, err := servers.NewRepository(rw, rw, k) + require.NoError(t, err) + return r + }(), + wantErr: true, + wantErrContains: "unable to get wrapper", + }, + { + name: "bad-fetch-node-req", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + return w + }, + reader: rw, + fetchReq: &types.FetchNodeCredentialsRequest{}, + repo: testRepo, + wantErr: true, + wantErrContains: "unable to authorize node", + }, + { + name: "success-with-fetch-node-req", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + return w + }, + reader: rw, + fetchReq: func() *types.FetchNodeCredentialsRequest { + // This happens on the worker + fileStorage, err := file.NewFileStorage(testCtx) + require.NoError(t, err) + defer fileStorage.Cleanup() + + nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage) + require.NoError(t, err) + // Create request using worker id + fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(testCtx) + require.NoError(t, err) + return fetchReq + }(), repo: testRepo, }, } @@ -655,6 +725,9 @@ func TestRepository_CreateWorker(t *testing.T) { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) testWorker := tc.setup() + + tc.opt = append(tc.opt, servers.WithFetchNodeCredentialsRequest(tc.fetchReq)) + got, err := tc.repo.CreateWorker(testCtx, testWorker, tc.opt...) if tc.wantErr { require.Error(err) @@ -681,6 +754,13 @@ func TestRepository_CreateWorker(t *testing.T) { err = rw.LookupByPublicId(testCtx, found) require.NoError(err) assert.Equal(got, found) + + if tc.fetchReq != nil { + worker := &servers.WorkerAuth{ + WorkerAuth: &store.WorkerAuth{}, + } + require.NoError(tc.reader.LookupWhere(testCtx, worker, "worker_id = ?", []any{found.PublicId})) + } }) } } From c59d0df611443c5b2c49803f6a6982602fa4f273 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sun, 5 Jun 2022 16:43:02 -0400 Subject: [PATCH 051/100] Add worker creation ACLs (#2138) This is a bit more than simply adding the action; we had some checks to ensure that list/create could only happen/not happen in some cases and those had to be modified to account for subactions. --- .../handlers/scopes/scope_service.go | 2 + .../handlers/scopes/scope_service_test.go | 6 +++ .../handlers/workers/worker_service.go | 2 +- internal/perms/acl.go | 31 +++++------ internal/perms/acl_test.go | 31 +++++++++++ internal/perms/grants.go | 23 +++++++-- internal/perms/grants_test.go | 51 ++++++++++++++++++- internal/types/action/action.go | 21 ++++++-- internal/types/action/action_test.go | 41 +++++++++++++++ internal/types/resource/resource.go | 2 +- 10 files changed, 181 insertions(+), 29 deletions(-) diff --git a/internal/daemon/controller/handlers/scopes/scope_service.go b/internal/daemon/controller/handlers/scopes/scope_service.go index 0c23f14c6..69a7148ba 100644 --- a/internal/daemon/controller/handlers/scopes/scope_service.go +++ b/internal/daemon/controller/handlers/scopes/scope_service.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/boundary/internal/daemon/controller/handlers/sessions" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/users" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers" "github.com/hashicorp/boundary/internal/errors" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/iam" @@ -63,6 +64,7 @@ var ( resource.Role: roles.CollectionActions, resource.Scope: CollectionActions, resource.User: users.CollectionActions, + resource.Worker: workers.CollectionActions, }, scope.Org.String(): { diff --git a/internal/daemon/controller/handlers/scopes/scope_service_test.go b/internal/daemon/controller/handlers/scopes/scope_service_test.go index 989f45e9d..0e1a82d37 100644 --- a/internal/daemon/controller/handlers/scopes/scope_service_test.go +++ b/internal/daemon/controller/handlers/scopes/scope_service_test.go @@ -95,6 +95,12 @@ var globalAuthorizedCollectionActions = map[string]*structpb.ListValue{ structpb.NewStringValue("list"), }, }, + "workers": { + Values: []*structpb.Value{ + structpb.NewStringValue("create:worker-request"), + structpb.NewStringValue("list"), + }, + }, } var orgAuthorizedCollectionActions = map[string]*structpb.ListValue{ diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index 372781051..ef579122e 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -36,7 +36,7 @@ var ( // CollectionActions contains the set of actions that can be performed on // this collection CollectionActions = action.ActionSet{ - action.Create, + action.CreateWorkerRequest, action.List, } ) diff --git a/internal/perms/acl.go b/internal/perms/acl.go index c4e813c1d..d31b1838a 100644 --- a/internal/perms/acl.go +++ b/internal/perms/acl.go @@ -1,18 +1,5 @@ package perms -/* -A really useful page to be aware of when looking at ACLs is -https://hashicorp.atlassian.net/wiki/spaces/ICU/pages/866976600/API+Actions+and+Permissions -speaking of which: TODO: put that chart in public docs. - -Anyways, from that page you can see that there are really only a few patterns of -ACLs that are ever allowed; see the Allowed function for a description along -with the code. - -This makes it actually quite simple to perform the ACL checking. Much of ACL -construction is thus synthesizing something reasonable from a set of Grants. -*/ - import ( "strings" @@ -114,6 +101,13 @@ func (a ACL) Allowed(r Resource, aType action.Type) (results ACLResults) { // If the action was not found above but we did find output fields in // patterns that match, we do not authorize the request, but we do build // up the output fields patterns. + // + // Note that when using IsActionOrParent it is merely to test whether it + // is an allowed format since some formats operate ony on collections + // (or don't operate at all on collections) and we want to ensure that + // it is/isn't a create or list command or subcommand to know whether + // that form is valid. The actual checking of whether the given action + // is granted to the user already happened above. var found bool switch { // id=;actions= where ID cannot be a wildcard; or @@ -123,8 +117,8 @@ func (a ACL) Allowed(r Resource, aType action.Type) (results ACLResults) { grant.id != "" && grant.id != "*" && grant.typ == resource.Unknown && - aType != action.List && - aType != action.Create: + !action.List.IsActionOrParent(aType) && + !action.Create.IsActionOrParent(aType): found = true @@ -137,8 +131,8 @@ func (a ACL) Allowed(r Resource, aType action.Type) (results ACLResults) { grant.typ == r.Type && grant.typ != resource.Unknown && topLevelType(r.Type) && - (aType == action.List || - aType == action.Create): + (action.List.IsActionOrParent(aType) || + action.Create.IsActionOrParent(aType)): found = true @@ -187,7 +181,8 @@ func topLevelType(typ resource.Type) bool { resource.Scope, resource.Session, resource.Target, - resource.User: + resource.User, + resource.Worker: return true } return false diff --git a/internal/perms/acl_test.go b/internal/perms/acl_test.go index c14edd65a..d32845112 100644 --- a/internal/perms/acl_test.go +++ b/internal/perms/acl_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/boundary/internal/intglobals" "github.com/hashicorp/boundary/internal/types/action" "github.com/hashicorp/boundary/internal/types/resource" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -315,6 +316,36 @@ func Test_ACLAllowed(t *testing.T) { {action: action.ReadSelf, authorized: true}, }, }, + { + name: "create worker with create", + resource: Resource{ScopeId: scope.Global.String(), Type: resource.Worker}, + scopeGrants: []scopeGrant{ + { + scope: scope.Global.String(), + grants: []string{ + "type=worker;actions=create", + }, + }, + }, + actionsAuthorized: []actionAuthorized{ + {action: action.CreateWorkerRequest, authorized: true}, + }, + }, + { + name: "create worker with request only", + resource: Resource{ScopeId: scope.Global.String(), Type: resource.Worker}, + scopeGrants: []scopeGrant{ + { + scope: scope.Global.String(), + grants: []string{ + "type=worker;actions=create:worker-request", + }, + }, + }, + actionsAuthorized: []actionAuthorized{ + {action: action.CreateWorkerRequest, authorized: true}, + }, + }, } for _, test := range tests { diff --git a/internal/perms/grants.go b/internal/perms/grants.go index 405f7de2b..279573f38 100644 --- a/internal/perms/grants.go +++ b/internal/perms/grants.go @@ -74,6 +74,20 @@ func (g Grant) Actions() (typs []action.Type, strs []string) { return } +// hasActionOrSubaction checks whether a grant's action set contains the given +// action or contains an action that is a subaction of the passed-in parameter. +// This is used for validation checking of parsed grants. N.B.: this is the +// opposite check of action.Type.IsActionOrParent, which is why the ordering is +// reversed going into that call. +func (g Grant) hasActionOrSubaction(act action.Type) bool { + for k := range g.actions { + if act.IsActionOrParent(k) { + return true + } + } + return false +} + func (g Grant) clone() *Grant { ret := &Grant{ scope: g.scope, @@ -376,12 +390,12 @@ func Parse(scopeId, grantString string, opt ...Option) (Grant, error) { return Grant{}, errors.NewDeprecated(errors.InvalidParameter, op, "parsed grant string contains no actions or output fields") } case 1: - if !grant.actions[action.Create] && - !grant.actions[action.List] { + if !grant.hasActionOrSubaction(action.Create) && + !grant.hasActionOrSubaction(action.List) { return Grant{}, errors.NewDeprecated(errors.InvalidParameter, op, "parsed grant string contains non-create or non-list action in a format that only allows these") } case 2: - if !grant.actions[action.Create] || !grant.actions[action.List] { + if !grant.hasActionOrSubaction(action.Create) || !grant.hasActionOrSubaction(action.List) { return Grant{}, errors.NewDeprecated(errors.InvalidParameter, op, "parsed grant string contains non-create or non-list action in a format that only allows these") } default: @@ -411,6 +425,7 @@ func Parse(scopeId, grantString string, opt ...Option) (Grant, error) { results := acl.Allowed(r, k) if results.Authorized { allowed = true + break } } if !allowed { @@ -429,7 +444,7 @@ func Parse(scopeId, grantString string, opt ...Option) (Grant, error) { func (g Grant) validateType() error { const op = "perms.(Grant).validateType" switch g.typ { - case resource.Controller, resource.Worker: + case resource.Controller: return errors.NewDeprecated(errors.InvalidParameter, op, fmt.Sprintf("unknown type specifier %q", g.typ)) } return nil diff --git a/internal/perms/grants_test.go b/internal/perms/grants_test.go index 19f807280..fd4c950c0 100644 --- a/internal/perms/grants_test.go +++ b/internal/perms/grants_test.go @@ -110,7 +110,7 @@ func Test_ValidateType(t *testing.T) { var g Grant for i := resource.Unknown; i <= resource.CredentialLibrary; i++ { g.typ = i - if i == resource.Controller || i == resource.Worker { + if i == resource.Controller { assert.Error(t, g.validateType()) } else { assert.NoError(t, g.validateType()) @@ -705,3 +705,52 @@ func Test_Parse(t *testing.T) { }) } } + +func TestHasActionOrSubaction(t *testing.T) { + tests := []struct { + name string + base []action.Type + act action.Type + want bool + }{ + { + name: "no actions", + base: []action.Type{}, + act: action.Read, + }, + { + name: "has direct action", + base: []action.Type{action.Cancel, action.Read}, + act: action.Read, + want: true, + }, + { + name: "has parent action", + base: []action.Type{action.Cancel, action.ReadSelf}, + act: action.Read, + want: true, + }, + { + name: "has direct sub action", + base: []action.Type{action.Cancel, action.ReadSelf}, + act: action.ReadSelf, + want: true, + }, + { + name: "has sub action needs parent", + base: []action.Type{action.Cancel, action.Read}, + act: action.ReadSelf, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := Grant{ + actions: make(map[action.Type]bool), + } + for _, act := range tt.base { + g.actions[act] = true + } + assert.Equal(t, tt.want, g.hasActionOrSubaction(tt.act)) + }) + } +} diff --git a/internal/types/action/action.go b/internal/types/action/action.go index e7d00aa7d..dec8cfc43 100644 --- a/internal/types/action/action.go +++ b/internal/types/action/action.go @@ -1,12 +1,12 @@ package action -import "strings" +import ( + "fmt" + "strings" +) -// Type defines a type for the Actions of Resources -// actions are also stored as a lookup db table named iam_action type Type uint -// not using iota intentionally, since the values are stored in the db as well. const ( Unknown Type = 0 List Type = 1 @@ -53,6 +53,7 @@ const ( AddHostSources Type = 42 SetHostSources Type = 43 RemoveHostSources Type = 44 + CreateWorkerRequest Type = 45 ) var Map = map[string]Type{ @@ -100,6 +101,7 @@ var Map = map[string]Type{ AddHostSources.String(): AddHostSources, SetHostSources.String(): SetHostSources, RemoveHostSources.String(): RemoveHostSources, + CreateWorkerRequest.String(): CreateWorkerRequest, } func (a Type) String() string { @@ -149,9 +151,20 @@ func (a Type) String() string { "add-host-sources", "set-host-sources", "remove-host-sources", + "create:worker-request", }[a] } +// IsActionOrParent tests whether the given action is either the same as the +// suspect or is a parent of the suspect. This is used in some parts of ACL +// checking. +func (act Type) IsActionOrParent(suspect Type) bool { + if act == suspect { + return true + } + return strings.HasPrefix(suspect.String(), fmt.Sprintf("%s:", act.String())) +} + // ActionSet stores a slice of action types type ActionSet []Type diff --git a/internal/types/action/action_test.go b/internal/types/action/action_test.go index c9de61572..c5086248e 100644 --- a/internal/types/action/action_test.go +++ b/internal/types/action/action_test.go @@ -95,6 +95,10 @@ func TestAction(t *testing.T) { action: NoOp, want: "no-op", }, + { + action: CreateWorkerRequest, + want: "create:worker-request", + }, } for _, tt := range tests { t.Run(tt.want, func(t *testing.T) { @@ -216,3 +220,40 @@ func TestOnlySelf(t *testing.T) { }) } } + +func TestIsActionOrParent(t *testing.T) { + tests := []struct { + name string + base Type + comp Type + want bool + }{ + { + name: "same", + base: Cancel, + comp: Cancel, + want: true, + }, + { + name: "different", + base: Cancel, + comp: Read, + }, + { + name: "different base and comp", + base: List, + comp: CancelSelf, + }, + { + name: "same base and comp", + base: Cancel, + comp: CancelSelf, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.base.IsActionOrParent(tt.comp)) + }) + } +} diff --git a/internal/types/resource/resource.go b/internal/types/resource/resource.go index 45edab66f..c8f40e9cc 100644 --- a/internal/types/resource/resource.go +++ b/internal/types/resource/resource.go @@ -28,7 +28,7 @@ const ( // NOTE: When adding a new type, be sure to update: // // * The Grant.validateType function and test - // * The perms.topLevelTypes function + // * The perms.topLevelType function // * The scopes service collection actions for appropriate scopes ) From 4117841cf9b0f343cd6bca1d3019605d62b15876 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sun, 5 Jun 2022 22:50:44 -0400 Subject: [PATCH 052/100] Use persistent storage for worker auth credentials (#2139) --- go.mod | 2 +- go.sum | 4 +- internal/cmd/commands/dev/dev.go | 7 +- internal/cmd/commands/server/server.go | 26 ++-- internal/daemon/worker/listeners.go | 4 +- internal/daemon/worker/testing.go | 5 +- internal/daemon/worker/worker.go | 116 +++++++++++++--- internal/daemon/worker/worker_test.go | 153 +++++++++++++++++++-- internal/servers/repository_worker_test.go | 4 +- 9 files changed, 261 insertions(+), 60 deletions(-) diff --git a/go.mod b/go.mod index 4ce1d09e1..955176dfc 100644 --- a/go.mod +++ b/go.mod @@ -92,7 +92,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b - github.com/hashicorp/nodeenrollment v0.0.0-20220603214204-cead023172f3 + github.com/hashicorp/nodeenrollment v0.0.0-20220605235506-351328414303 ) require ( diff --git a/go.sum b/go.sum index 9e8efeefa..62345953f 100644 --- a/go.sum +++ b/go.sum @@ -740,8 +740,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220603214204-cead023172f3 h1:yGCgHpFf6g4VQKxP45r2vxQZXjequvcpm1x5XKg0oaU= -github.com/hashicorp/nodeenrollment v0.0.0-20220603214204-cead023172f3/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.0.0-20220605235506-351328414303 h1:dWteo2hZzqmF7IMqIozEzLLx3J0GXGKhKU32hPERp4g= +github.com/hashicorp/nodeenrollment v0.0.0-20220605235506-351328414303/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 70055bd7d..f33611e0d 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -29,7 +29,6 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/strutil" - nodeefile "github.com/hashicorp/nodeenrollment/storage/file" "github.com/mitchellh/cli" "github.com/posener/complete" "go.uber.org/atomic" @@ -701,6 +700,8 @@ func (c *Command) Run(args []string) int { c.Info["worker auth registration request"] = req c.InfoKeys = append(c.InfoKeys, "worker auth current key id") c.Info["worker auth current key id"] = c.worker.WorkerAuthCurrentKeyId + c.InfoKeys = append(c.InfoKeys, "worker auth storage path") + c.Info["worker auth storage path"] = c.worker.WorkerAuthStorage.BaseDir() go func() { for { select { @@ -773,9 +774,7 @@ func (c *Command) Run(args []string) int { if !c.flagControllerOnly { if !c.flagWorkerAuthStorageSkipCleanup { - if fileStorage, ok := c.worker.WorkerAuthStorage.(*nodeefile.FileStorage); ok { - fileStorage.Cleanup() - } + c.worker.WorkerAuthStorage.Cleanup() } if err := c.worker.Shutdown(); err != nil { c.UI.Error(fmt.Errorf("Error shutting down worker: %w", err).Error()) diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index a62cb8e9e..70bb3a651 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -463,22 +463,10 @@ func (c *Command) Run(args []string) int { } return base.CommandCliError } - if c.WorkerAuthKms == nil { - req := c.worker.WorkerAuthRegistrationRequest - if req == "" { - retErr := fmt.Errorf("No worker registration request found at worker startup time") - if err := c.worker.Shutdown(); err != nil { - c.UI.Error(retErr.Error()) - retErr = fmt.Errorf("Error shutting down worker: %w", err) - } - c.UI.Error(retErr.Error()) - if err := c.controller.Shutdown(); err != nil { - c.UI.Error(fmt.Errorf("Error with controller shutdown: %w", err).Error()) - } - return base.CommandCliError - } + + if c.WorkerAuthKms == nil && c.worker.WorkerAuthRegistrationRequest != "" { c.InfoKeys = append(c.InfoKeys, "worker auth registration request") - c.Info["worker auth registration request"] = req + c.Info["worker auth registration request"] = c.worker.WorkerAuthRegistrationRequest c.InfoKeys = append(c.InfoKeys, "worker auth current key id") c.Info["worker auth current key id"] = c.worker.WorkerAuthCurrentKeyId } @@ -648,6 +636,14 @@ func (c *Command) StartWorker() error { return fmt.Errorf("Error initializing worker: %w", err) } + if c.WorkerAuthKms == nil { + if c.worker.WorkerAuthStorage == nil { + return fmt.Errorf("No worker auth KMS specified and no worker auth storage found") + } + c.InfoKeys = append(c.InfoKeys, "worker auth storage path") + c.Info["worker auth storage path"] = c.worker.WorkerAuthStorage.BaseDir() + } + if err := c.worker.Start(); err != nil { retErr := fmt.Errorf("Error starting worker: %w", err) if err := c.worker.Shutdown(); err != nil { diff --git a/internal/daemon/worker/listeners.go b/internal/daemon/worker/listeners.go index 1aac3dfba..7a8a9a5c6 100644 --- a/internal/daemon/worker/listeners.go +++ b/internal/daemon/worker/listeners.go @@ -19,9 +19,9 @@ import ( "github.com/hashicorp/go-multierror" nodee "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/multihop" + nodeenet "github.com/hashicorp/nodeenrollment/net" "github.com/hashicorp/nodeenrollment/protocol" "github.com/hashicorp/nodeenrollment/types" - "github.com/hashicorp/nodeenrollment/util/splitlistener" "github.com/hashicorp/nodeenrollment/util/temperror" "google.golang.org/grpc" ) @@ -135,7 +135,7 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) return nil, fmt.Errorf("error instantiating node auth listener: %w", err) } - w.workerAuthSplitListener = splitlistener.New(interceptingListener) + w.workerAuthSplitListener = nodeenet.NewSplitListener(interceptingListener) downstreamServer := grpc.NewServer( grpc.MaxRecvMsgSize(math.MaxInt32), diff --git a/internal/daemon/worker/testing.go b/internal/daemon/worker/testing.go index 5025f9791..5fe5387eb 100644 --- a/internal/daemon/worker/testing.go +++ b/internal/daemon/worker/testing.go @@ -32,7 +32,7 @@ type TestWorker struct { name string } -// Worker returns the underlying controller +// Worker returns the underlying worker func (tw *TestWorker) Worker() *Worker { return tw.w } @@ -177,6 +177,9 @@ type TestWorkerOpts struct { // The worker credential storage KMS to use, or one will be created WorkerAuthStorageKms wrapping.Wrapper + // The location of the worker's auth storage + WorkerAuthStoragePath string + // The name to use for the worker, otherwise one will be randomly // generated, unless provided in a non-nil Config Name string diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index a8cee0726..85fdf7ad7 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -28,9 +28,9 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-secure-stdlib/mlock" + nodeenet "github.com/hashicorp/nodeenrollment/net" nodeefile "github.com/hashicorp/nodeenrollment/storage/file" "github.com/hashicorp/nodeenrollment/types" - "github.com/hashicorp/nodeenrollment/util/splitlistener" ua "go.uber.org/atomic" "google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver/manual" @@ -75,10 +75,10 @@ type Worker struct { updateTags *ua.Bool // The storage for node enrollment - WorkerAuthStorage nodeenrollment.Storage + WorkerAuthStorage *nodeefile.Storage WorkerAuthCurrentKeyId string WorkerAuthRegistrationRequest string - workerAuthSplitListener *splitlistener.SplitListener + workerAuthSplitListener *nodeenet.SplitListener // Test-specific options TestOverrideX509VerifyDnsName string @@ -188,26 +188,98 @@ func (w *Worker) Start() error { return fmt.Errorf("error starting worker listeners: %w", err) } - nodeCreds, err := types.NewNodeCredentials(w.baseContext, w.WorkerAuthStorage, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) - if err != nil { - return fmt.Errorf("error generating new node creds: %w", err) - } + if w.conf.WorkerAuthKms == nil { + // In this section, we look for existing worker credentials. The two + // variables below store whether to create new credentials and whether + // to create a fetch request so it can be displayed in the worker + // startup info. These may be different because if initial creds have + // been generated on the worker side but not yet authorized/fetched from + // the controller, we don't want to invalidate that request on restart + // by generating a new set of credentials. However it's safe to output a + // new fetch request so we do in fact do that. + var createNodeAuthCreds bool + var createFetchRequest bool + nodeCreds, err := types.LoadNodeCredentials(w.baseContext, w.WorkerAuthStorage, nodeenrollment.CurrentId, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) + switch { + case err == nil: + // It's unclear why this would ever happen -- it shouldn't -- so + // this is simply safety against panics if something goes + // catastrophically wrong + if nodeCreds == nil { + event.WriteSysEvent(w.baseContext, op, "no error loading worker auth creds but nil creds, creating new creds for registration") + createNodeAuthCreds = true + createFetchRequest = true + break + } - req, err := nodeCreds.CreateFetchNodeCredentialsRequest(w.baseContext) - if err != nil { - return fmt.Errorf("error creating fetch credentials request: %w", err) - } - reqBytes, err := proto.Marshal(req) - if err != nil { - return fmt.Errorf("error marshaling fetch credentials request: %w", err) - } - w.WorkerAuthRegistrationRequest = base58.FastBase58Encoding(reqBytes) - if err != nil { - return fmt.Errorf("error encoding registration request: %w", err) - } - w.WorkerAuthCurrentKeyId, err = nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) - if err != nil { - return fmt.Errorf("error deriving key id: %w", err) + // Check that we have valid creds, or that we have generated creds but + // simply are still waiting on authentication (in which case we don't + // want to invalidate what we've already sent) + var validCreds bool + switch len(nodeCreds.CertificateBundles) { + case 0: + // Still waiting on initial creds, so don't invalidate the request + // by creating new credentials. However, we will generate and + // display a new valid request in case the first was lost. + createFetchRequest = true + + default: + now := time.Now() + for _, bundle := range nodeCreds.CertificateBundles { + if bundle.CertificateNotBefore.AsTime().Before(now) && bundle.CertificateNotAfter.AsTime().After(now) { + // If we have a certificate in its validity period, + // everything is fine + validCreds = true + break + } + } + + // Certificates are both expired, so create new credentials and + // output a request based on those + createNodeAuthCreds = !validCreds + createFetchRequest = !validCreds + } + + case errors.Is(err, nodeenrollment.ErrNotFound): + // Nothing was found on disk, so create + createNodeAuthCreds = true + createFetchRequest = true + + default: + // Some other type of error happened, bail out + return fmt.Errorf("error loading worker auth creds: %w", err) + } + + // NOTE: this block _must_ be before the `if createFetchRequest` block + // or the fetch request may have no credentials to work with + if createNodeAuthCreds { + nodeCreds, err = types.NewNodeCredentials(w.baseContext, w.WorkerAuthStorage, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) + if err != nil { + return fmt.Errorf("error generating new worker auth creds: %w", err) + } + } + + if createFetchRequest { + if nodeCreds == nil { + return fmt.Errorf("need to create fetch request but worker auth creds are nil: %w", err) + } + req, err := nodeCreds.CreateFetchNodeCredentialsRequest(w.baseContext) + if err != nil { + return fmt.Errorf("error creating worker auth fetch credentials request: %w", err) + } + reqBytes, err := proto.Marshal(req) + if err != nil { + return fmt.Errorf("error marshaling worker auth fetch credentials request: %w", err) + } + w.WorkerAuthRegistrationRequest = base58.FastBase58Encoding(reqBytes) + if err != nil { + return fmt.Errorf("error encoding worker auth registration request: %w", err) + } + w.WorkerAuthCurrentKeyId, err = nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) + if err != nil { + return fmt.Errorf("error deriving worker auth key id: %w", err) + } + } } if err := w.startControllerConnections(); err != nil { diff --git a/internal/daemon/worker/worker_test.go b/internal/daemon/worker/worker_test.go index 6c18a4f3f..fcd57d79a 100644 --- a/internal/daemon/worker/worker_test.go +++ b/internal/daemon/worker/worker_test.go @@ -2,7 +2,10 @@ package worker import ( "context" + "crypto/rand" + "os" "testing" + "time" "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/config" @@ -10,8 +13,15 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/configutil/v2" "github.com/hashicorp/go-secure-stdlib/listenerutil" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/registration" + "github.com/hashicorp/nodeenrollment/rotation" + nodeefile "github.com/hashicorp/nodeenrollment/storage/file" + "github.com/hashicorp/nodeenrollment/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" ) func TestWorkerNew(t *testing.T) { @@ -127,23 +137,144 @@ func TestWorkerNew(t *testing.T) { } } -func TestSetupWorkerCredentialStorage(t *testing.T) { +func TestSetupWorkerAuthStorage(t *testing.T) { + t.Parallel() + ctx := context.Background() - assert, require := assert.New(t), require.New(t) + ts := db.TestWrapper(t) keyId, err := ts.KeyId(ctx) + require.NoError(t, err) + // First, just test the key ID is populated + tmpDir, err := os.MkdirTemp("", "") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, os.RemoveAll(tmpDir)) }) tw := NewTestWorker(t, &TestWorkerOpts{ - WorkerAuthStorageKms: ts, - DisableAutoStart: true, + WorkerAuthStorageKms: ts, + WorkerAuthStoragePath: tmpDir, + DisableAutoStart: true, }) - + t.Cleanup(tw.Shutdown) err = tw.Worker().Start() - require.NoError(err) - wKeyId, err := tw.Config().WorkerAuthStorageKms.KeyId(ctx) - require.NoError(err) - assert.Equal(keyId, wKeyId) + require.NoError(t, err) - err = tw.Worker().Shutdown() - require.NoError(err) + wKeyId, err := tw.Config().WorkerAuthStorageKms.KeyId(ctx) + require.NoError(t, err) + assert.Equal(t, keyId, wKeyId) + + // Create a fresh persistent dir for the following tests + tmpDir, err = os.MkdirTemp("", "") + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, os.RemoveAll(tmpDir)) }) + + // Get an initial set of authorized node credentials + initStorage, err := nodeefile.New(ctx) + require.NoError(t, err) + t.Cleanup(initStorage.Cleanup) + _, err = rotation.RotateRootCertificates(ctx, initStorage) + require.NoError(t, err) + initNodeCreds, err := types.NewNodeCredentials(ctx, initStorage) + require.NoError(t, err) + req, err := initNodeCreds.CreateFetchNodeCredentialsRequest(ctx) + require.NoError(t, err) + _, err = registration.AuthorizeNode(ctx, initStorage, req) + require.NoError(t, err) + fetchResp, err := registration.FetchNodeCredentials(ctx, initStorage, req) + require.NoError(t, err) + initNodeCreds, err = initNodeCreds.HandleFetchNodeCredentialsResponse(ctx, initStorage, fetchResp) + require.NoError(t, err) + initKeyId, err := nodeenrollment.KeyIdFromPkix(initNodeCreds.CertificatePublicKeyPkix) + require.NoError(t, err) + + nonce := make([]byte, nodeenrollment.NonceSize) + _, err = rand.Reader.Read(nonce) + require.NoError(t, err) + + // What's going on here: in each test we are simulating a startup of a + // worker that has storage in various states. The input is a function to + // modify the current state of node credentials by using the worker's + // storage, but this happens before Start so we haven't done checking yet; + // the assertions check what the final result is. + tests := []struct { + name string + in func(t *testing.T, w *Worker) + expKeyId string // If set, the existing key ID to expect + expCreation bool // Whether we should expect a new key ID + expRegistrationRequest bool // Whether we should have seen a registration request generated + expError string // Some other error + }{ + { + name: "no creds", + in: func(t *testing.T, w *Worker) { + // Do nothing; in this case it will have already been cleared + }, + expCreation: true, + expRegistrationRequest: true, + }, + { + name: "valid creds", + in: func(t *testing.T, w *Worker) { + // Store the authorized creds + require.NoError(t, initNodeCreds.Store(ctx, w.WorkerAuthStorage)) + }, + }, + { + name: "existing but not validated", + in: func(t *testing.T, w *Worker) { + creds := proto.Clone(initNodeCreds).(*types.NodeCredentials) + creds.CertificateBundles = nil + creds.RegistrationNonce = nonce + require.NoError(t, creds.Store(ctx, w.WorkerAuthStorage)) + }, + expKeyId: initKeyId, + expRegistrationRequest: true, + }, + { + name: "existing and outside cert times", // Note that cert from next CA will already not be valid + in: func(t *testing.T, w *Worker) { + creds := proto.Clone(initNodeCreds).(*types.NodeCredentials) + creds.CertificateBundles[0].CertificateNotBefore = timestamppb.New(time.Time{}) + creds.CertificateBundles[0].CertificateNotAfter = timestamppb.New(time.Time{}) + require.NoError(t, creds.Store(ctx, w.WorkerAuthStorage)) + }, + expCreation: true, + expRegistrationRequest: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tw := NewTestWorker(t, &TestWorkerOpts{ + WorkerAuthStoragePath: tmpDir, + DisableAutoStart: true, + }) + t.Cleanup(tw.Shutdown) + + // Always clear out storage that was there before, ignore errors + _ = tw.Worker().WorkerAuthStorage.Remove(ctx, &types.NodeCredentials{Id: string(nodeenrollment.CurrentId)}) + + // Run node credentials modification + tt.in(t, tw.Worker()) + + // Start up to run logic + require.NoError(t, tw.Worker().Start()) + + // Validate state + if tt.expKeyId != "" { + assert.Equal(t, tt.expKeyId, tw.Worker().WorkerAuthCurrentKeyId) + } else { + if tt.expCreation { + assert.NotEmpty(t, tw.Worker().WorkerAuthCurrentKeyId) + } else { + assert.Empty(t, tw.Worker().WorkerAuthCurrentKeyId) + } + } + if tt.expRegistrationRequest { + assert.NotEmpty(t, tw.Worker().WorkerAuthRegistrationRequest) + } else { + assert.Empty(t, tw.Worker().WorkerAuthRegistrationRequest) + } + }) + } } diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 673a7aeb9..7daaffb5e 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -663,7 +663,7 @@ func TestRepository_CreateWorker(t *testing.T) { reader: rw, fetchReq: func() *types.FetchNodeCredentialsRequest { // This happens on the worker - fileStorage, err := file.NewFileStorage(testCtx) + fileStorage, err := file.New(testCtx) require.NoError(t, err) defer fileStorage.Cleanup() @@ -707,7 +707,7 @@ func TestRepository_CreateWorker(t *testing.T) { reader: rw, fetchReq: func() *types.FetchNodeCredentialsRequest { // This happens on the worker - fileStorage, err := file.NewFileStorage(testCtx) + fileStorage, err := file.New(testCtx) require.NoError(t, err) defer fileStorage.Cleanup() From 4c55305a49bf5e298a15ed5c594daffdb26c2f84 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Mon, 6 Jun 2022 11:20:04 -0400 Subject: [PATCH 053/100] Remove nonce encryption for workerAuth --- internal/servers/repository_workerauth.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/internal/servers/repository_workerauth.go b/internal/servers/repository_workerauth.go index ef5db2bb9..61970dec4 100644 --- a/internal/servers/repository_workerauth.go +++ b/internal/servers/repository_workerauth.go @@ -125,6 +125,7 @@ func StoreNodeInformationTx(ctx context.Context, writer db.Writer, databaseWrapp nodeAuth.WorkerKeyIdentifier = node.Id nodeAuth.WorkerEncryptionPubKey = node.EncryptionPublicKeyBytes nodeAuth.WorkerSigningPubKey = node.CertificatePublicKeyPkix + nodeAuth.Nonce = node.RegistrationNonce var err error nodeAuth.KeyId, err = databaseWrapper.KeyId(ctx) @@ -135,10 +136,6 @@ func StoreNodeInformationTx(ctx context.Context, writer db.Writer, databaseWrapp if err != nil { return errors.Wrap(ctx, err, op) } - nodeAuth.Nonce, err = encrypt(ctx, node.RegistrationNonce, databaseWrapper) - if err != nil { - return errors.Wrap(ctx, err, op) - } // Get workerId from state passed in var result workerAuthWorkerId @@ -383,6 +380,7 @@ func (r *WorkerAuthRepositoryStorage) loadNodeInformation(ctx context.Context, n node.EncryptionPublicKeyBytes = authorizedWorker.WorkerEncryptionPubKey node.CertificatePublicKeyPkix = authorizedWorker.WorkerSigningPubKey + node.RegistrationNonce = authorizedWorker.Nonce // Default values are used for key types node.EncryptionPublicKeyType = types.KEYTYPE_X25519 @@ -398,10 +396,6 @@ func (r *WorkerAuthRepositoryStorage) loadNodeInformation(ctx context.Context, n if err != nil { return errors.Wrap(ctx, err, op) } - node.RegistrationNonce, err = decrypt(ctx, authorizedWorker.Nonce, databaseWrapper) - if err != nil { - return errors.Wrap(ctx, err, op) - } // Get cert bundles from the other table certBundles, err := r.findCertBundles(ctx, node.Id) From 43bcbba47fbeebcc859503341cbed3e4206b1b18 Mon Sep 17 00:00:00 2001 From: Todd Date: Mon, 6 Jun 2022 10:52:50 -0700 Subject: [PATCH 054/100] Implement UpdateWorker service method (#2133) --- .../handlers/workers/worker_service.go | 99 ++++ .../handlers/workers/worker_service_test.go | 463 ++++++++++++++++++ .../api/resources/workers/v1/worker.proto | 8 +- .../storage/servers/store/v1/worker.proto | 16 +- internal/servers/store/worker.pb.go | 103 ++-- .../api/resources/workers/worker.pb.go | 97 ++-- 6 files changed, 687 insertions(+), 99 deletions(-) diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index ef579122e..a98316e03 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/boundary/internal/perms" "github.com/hashicorp/boundary/internal/requests" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/types/action" "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" @@ -24,6 +25,8 @@ import ( ) var ( + maskManager handlers.MaskManager + // IdActions contains the set of actions that can be performed on // individual resources IdActions = action.ActionSet{ @@ -41,6 +44,13 @@ var ( } ) +func init() { + var err error + if maskManager, err = handlers.NewMaskManager(handlers.MaskDestination{&store.Worker{}}, handlers.MaskSource{&pb.Worker{}}); err != nil { + panic(err) + } +} + // Service handles request as described by the pbs.WorkerServiceServer interface. type Service struct { pbs.UnimplementedWorkerServiceServer @@ -192,6 +202,44 @@ func (s Service) DeleteWorker(ctx context.Context, req *pbs.DeleteWorkerRequest) return nil, nil } +// UpdateWorker implements the interface pbs.WorkerServiceServer. +func (s Service) UpdateWorker(ctx context.Context, req *pbs.UpdateWorkerRequest) (*pbs.UpdateWorkerResponse, error) { + const op = "workers.(Service).UpdateWorker" + + if err := validateUpdateRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetId(), action.Update) + if authResults.Error != nil { + return nil, authResults.Error + } + w, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) + if err != nil { + return nil, err + } + + outputFields, ok := requests.OutputFields(ctx) + if !ok { + return nil, errors.New(ctx, errors.Internal, op, "no request context found") + } + + outputOpts := make([]handlers.Option, 0, 3) + outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if outputFields.Has(globals.ScopeField) { + outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) + } + if outputFields.Has(globals.AuthorizedActionsField) { + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, w.GetPublicId(), IdActions).Strings())) + } + + item, err := toProto(ctx, w, outputOpts...) + if err != nil { + return nil, err + } + + return &pbs.UpdateWorkerResponse{Item: item}, nil +} + func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*servers.Worker, error) { repo, err := s.repoFn() if err != nil { @@ -238,6 +286,38 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) { return rows > 0, nil } +func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Worker) (*servers.Worker, error) { + const op = "workers.(Service).updateInRepo" + var opts []servers.Option + if desc := item.GetDescription(); desc != nil { + opts = append(opts, servers.WithDescription(desc.GetValue())) + } + if name := item.GetName(); name != nil { + opts = append(opts, servers.WithName(name.GetValue())) + } + if addr := item.GetAddress(); addr != nil { + opts = append(opts, servers.WithAddress(addr.GetValue())) + } + w := servers.NewWorker(scopeId, opts...) + w.PublicId = id + dbMask := maskManager.Translate(mask) + if len(dbMask) == 0 { + return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."}) + } + repo, err := s.repoFn() + if err != nil { + return nil, err + } + out, rowsUpdated, err := repo.UpdateWorker(ctx, w, item.GetVersion(), dbMask) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update worker")) + } + if rowsUpdated == 0 { + return nil, handlers.NotFoundErrorf("Worker %q doesn't exist or incorrect version provided.", id) + } + return out, nil +} + func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { res := auth.VerifyResults{} repo, err := s.repoFn() @@ -393,3 +473,22 @@ func validateListRequest(req *pbs.ListWorkersRequest) error { func validateDeleteRequest(req *pbs.DeleteWorkerRequest) error { return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, servers.WorkerPrefix) } + +func validateUpdateRequest(req *pbs.UpdateWorkerRequest) error { + return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string { + badFields := map[string]string{} + if req.GetItem().GetWorkerConfig() != nil { + badFields[globals.ConfigurationField] = "This is a read only field." + } + if req.GetItem().GetCanonicalAddress() != "" { + badFields[globals.CanonicalAddressField] = "This is a read only field." + } + if req.GetItem().GetCanonicalTags() != nil { + badFields[globals.CanonicalAddressField] = "This is a read only field." + } + if req.GetItem().GetTags() != nil { + badFields[globals.TagsField] = "These values cannot be updated in this type of request." + } + return badFields + }, servers.WorkerPrefix) +} diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index 98cb863a4..64d9505bf 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -20,8 +20,12 @@ import ( pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/genproto/protobuf/field_mask" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -284,3 +288,462 @@ func TestDelete(t *testing.T) { }) } } + +func TestUpdate(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + ctx := context.Background() + rw := db.New(conn) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + repo, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + repoFn := func() (*servers.Repository, error) { + return repo, nil + } + + wkr := servers.TestWorker(t, conn, wrapper, + servers.WithName("default"), + servers.WithDescription("default"), + servers.WithAddress("default")) + + version := wkr.GetVersion() + + resetWorker := func() { + version++ + _, _, err = repo.UpdateWorker(context.Background(), wkr, version, []string{"Name", "Description", "Address"}) + require.NoError(t, err, "Failed to reset worker.") + version++ + } + + wCreated := wkr.GetCreateTime().GetTimestamp().AsTime() + toMerge := &pbs.UpdateWorkerRequest{ + Id: wkr.GetPublicId(), + } + workerService, err := NewService(ctx, repoFn, iamRepoFn) + require.NoError(t, err) + expectedScope := &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"} + expectedConfig := &pb.WorkerConfig{ + Address: wkr.GetWorkerReportedAddress(), + Name: wkr.GetWorkerReportedName(), + } + + cases := []struct { + name string + req *pbs.UpdateWorkerRequest + res *pbs.UpdateWorkerResponse + err error + }{ + { + name: "Update an Existing Worker", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name", "description", "address"}, + }, + Item: &pb.Worker{ + Name: wrapperspb.String("name"), + Description: wrapperspb.String("desc"), + Address: wrapperspb.String("address"), + }, + }, + res: &pbs.UpdateWorkerResponse{ + Item: &pb.Worker{ + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("name"), + Description: wrapperspb.String("desc"), + Address: wrapperspb.String("address"), + CanonicalAddress: "address", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerConfig: expectedConfig, + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + { + name: "Multiple Paths in single string", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name,description,type"}, + }, + Item: &pb.Worker{ + Name: wrapperspb.String("name"), + Description: wrapperspb.String("desc"), + }, + }, + res: &pbs.UpdateWorkerResponse{ + Item: &pb.Worker{ + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("name"), + Description: wrapperspb.String("desc"), + Address: wrapperspb.String("default"), + CanonicalAddress: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerConfig: expectedConfig, + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + { + name: "No Update Mask", + req: &pbs.UpdateWorkerRequest{ + Item: &pb.Worker{ + Name: wrapperspb.String("updated name"), + Description: wrapperspb.String("updated desc"), + }, + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Empty Path", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{Paths: []string{}}, + Item: &pb.Worker{ + Name: wrapperspb.String("updated name"), + Description: wrapperspb.String("updated desc"), + }, + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Update port to 0", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{Paths: []string{"default_port"}}, + Item: &pb.Worker{}, + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Only non-existant paths in Mask", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{Paths: []string{"nonexistant_field"}}, + Item: &pb.Worker{ + Name: wrapperspb.String("updated name"), + Description: wrapperspb.String("updated desc"), + }, + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Unset Name", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name"}, + }, + Item: &pb.Worker{ + Description: wrapperspb.String("ignored"), + }, + }, + res: &pbs.UpdateWorkerResponse{ + Item: &pb.Worker{ + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Description: wrapperspb.String("default"), + Address: wrapperspb.String("default"), + CanonicalAddress: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerConfig: expectedConfig, + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + { + name: "Unset Description", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"description"}, + }, + Item: &pb.Worker{ + Name: wrapperspb.String("ignored"), + }, + }, + res: &pbs.UpdateWorkerResponse{ + Item: &pb.Worker{ + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("default"), + Address: wrapperspb.String("default"), + CanonicalAddress: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerConfig: expectedConfig, + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + { + name: "Update Only Name", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name"}, + }, + Item: &pb.Worker{ + Name: wrapperspb.String("updated"), + Description: wrapperspb.String("ignored"), + }, + }, + res: &pbs.UpdateWorkerResponse{ + Item: &pb.Worker{ + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("updated"), + Description: wrapperspb.String("default"), + Address: wrapperspb.String("default"), + CanonicalAddress: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerConfig: expectedConfig, + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + { + name: "Update Only Description", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"description"}, + }, + Item: &pb.Worker{ + Name: wrapperspb.String("ignored"), + Description: wrapperspb.String("notignored"), + }, + }, + res: &pbs.UpdateWorkerResponse{ + Item: &pb.Worker{ + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("default"), + Description: wrapperspb.String("notignored"), + Address: wrapperspb.String("default"), + CanonicalAddress: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerConfig: expectedConfig, + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + { + name: "Update a Non Existing Worker", + req: &pbs.UpdateWorkerRequest{ + Id: servers.WorkerPrefix + "_DoesntExis", + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"description"}, + }, + Item: &pb.Worker{ + Description: wrapperspb.String("desc"), + }, + }, + err: handlers.ApiErrorWithCode(codes.NotFound), + }, + { + name: "Cant change Worker Defined Name", + req: &pbs.UpdateWorkerRequest{ + Id: wkr.GetPublicId(), + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"worker_config.name"}, + }, + Item: &pb.Worker{ + WorkerConfig: &pb.WorkerConfig{ + Name: "name", + }, + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Cant change Worker Defined Address", + req: &pbs.UpdateWorkerRequest{ + Id: wkr.GetPublicId(), + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"worker_config.address"}, + }, + Item: &pb.Worker{ + WorkerConfig: &pb.WorkerConfig{ + Address: "address", + }, + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Cant change Id", + req: &pbs.UpdateWorkerRequest{ + Id: wkr.GetPublicId(), + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"id"}, + }, + Item: &pb.Worker{ + Id: "w_somethinge", + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Cant specify Created Time", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"created_time"}, + }, + Item: &pb.Worker{ + CreatedTime: timestamppb.Now(), + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Cant specify Updated Time", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"updated_time"}, + }, + Item: &pb.Worker{ + UpdatedTime: timestamppb.Now(), + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Cant specify tags", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"tags"}, + }, + Item: &pb.Worker{ + Tags: map[string]*structpb.ListValue{ + "foo": func() *structpb.ListValue { + l, err := structpb.NewList([]interface{}{"bar"}) + require.NoError(t, err) + return l + }(), + }, + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Cant specify canonical tags", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"canonical_tags"}, + }, + Item: &pb.Worker{ + CanonicalTags: map[string]*structpb.ListValue{ + "foo": func() *structpb.ListValue { + l, err := structpb.NewList([]interface{}{"bar"}) + require.NoError(t, err) + return l + }(), + }, + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Cant specify canonical address", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"canonical_address"}, + }, + Item: &pb.Worker{ + CanonicalAddress: "should_fail", + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + tc.req.Item.Version = version + + req := proto.Clone(toMerge).(*pbs.UpdateWorkerRequest) + proto.Merge(req, tc.req) + + got, gErr := workerService.UpdateWorker(auth.DisabledAuthTestContext(iamRepoFn, scope.Global.String()), req) + if tc.err != nil { + require.Error(t, gErr) + assert.True(t, errors.Is(gErr, tc.err), "UpdateWorker(%+v) got error %v, wanted %v", req, gErr, tc.err) + } + + if tc.err == nil { + defer resetWorker() + } + + if got != nil { + assert.NotNilf(t, tc.res, "Expected UpdateWorker response to be nil, but was %v", got) + gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime() + // Verify updated set after it was created + assert.True(t, gotUpdateTime.After(wCreated), "Updated resource should have been updated after its creation. Was updated %v, which is after %v", gotUpdateTime, wCreated) + + // Clear all values which are hard to compare against. + got.Item.UpdatedTime, tc.res.Item.UpdatedTime = nil, nil + } + if tc.res != nil { + tc.res.Item.Version = version + 1 + } + assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform()), "UpdateWorker(%q) got response %q, wanted %q", req, got, tc.res) + }) + } +} + +func TestUpdate_BadVersion(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + + _, proj := iam.TestScopes(t, iamRepo) + + rw := db.New(conn) + repo, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err, "Couldn't create new worker repo.") + repoFn := func() (*servers.Repository, error) { + return repo, nil + } + ctx := context.Background() + + workerService, err := NewService(ctx, repoFn, iamRepoFn) + require.NoError(t, err, "Failed to create a new host set service.") + + wkr := servers.TestWorker(t, conn, wrapper) + + upTar, err := workerService.UpdateWorker(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), &pbs.UpdateWorkerRequest{ + Id: wkr.GetPublicId(), + Item: &pb.Worker{ + Description: wrapperspb.String("updated"), + Version: 72, + }, + UpdateMask: &field_mask.FieldMask{Paths: []string{"description"}}, + }) + assert.Nil(t, upTar) + assert.Error(t, err) + assert.True(t, errors.Is(err, handlers.NotFoundError()), "Got %v, wanted not found error.", err) +} diff --git a/internal/proto/controller/api/resources/workers/v1/worker.proto b/internal/proto/controller/api/resources/workers/v1/worker.proto index 1b5a1fe45..68a81b9da 100644 --- a/internal/proto/controller/api/resources/workers/v1/worker.proto +++ b/internal/proto/controller/api/resources/workers/v1/worker.proto @@ -62,7 +62,13 @@ message Worker { uint32 version = 80; // @gotags: `class:"public"` // The address that this worker is reachable at. - google.protobuf.StringValue address = 90; // @gotags: `class:"public"` + google.protobuf.StringValue address = 90 [ + (custom_options.v1.generate_sdk_option) = true, + (custom_options.v1.mask_mapping) = { + this: "address" + that: "address" + } + ]; // @gotags: `class:"public"` // Output only. This is the address used by authorized session. The value // is the same as the address field if set. Otherwise it uses the address the diff --git a/internal/proto/controller/storage/servers/store/v1/worker.proto b/internal/proto/controller/storage/servers/store/v1/worker.proto index e6c5dff3f..b988adb6c 100644 --- a/internal/proto/controller/storage/servers/store/v1/worker.proto +++ b/internal/proto/controller/storage/servers/store/v1/worker.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package controller.storage.servers.store.v1; +import "controller/custom_options/v1/options.proto"; import "controller/storage/timestamp/v1/timestamp.proto"; option go_package = "github.com/hashicorp/boundary/internal/servers/store;store"; @@ -14,15 +15,24 @@ message Worker { // Name of the resource (optional) // @inject_tag: `gorm:"default:null"` - string name = 20; + string name = 20 [(custom_options.v1.mask_mapping) = { + this: "name" + that: "name" + }]; // Description of the resource (optional) // @inject_tag: `gorm:"default:null"` - string description = 30; + string description = 30 [(custom_options.v1.mask_mapping) = { + this: "description" + that: "description" + }]; // Address for the worker. This is optional. // @inject_tag: `gorm:"default:null"` - string address = 40; + string address = 40 [(custom_options.v1.mask_mapping) = { + this: "address" + that: "address" + }]; // The create_time is set by the database. // @inject_tag: `gorm:"default:current_timestamp"` diff --git a/internal/servers/store/worker.pb.go b/internal/servers/store/worker.pb.go index c92cc11fc..0004e8869 100644 --- a/internal/servers/store/worker.pb.go +++ b/internal/servers/store/worker.pb.go @@ -8,6 +8,7 @@ package store import ( timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -261,53 +262,61 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x84, 0x04, 0x0a, 0x06, 0x57, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, - 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, - 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, - 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x30, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x36, 0x0a, 0x17, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x64, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x54, 0x0a, 0x10, 0x6c, 0x61, 0x73, - 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x6e, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x22, - 0x68, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x09, - 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xce, 0x04, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, + 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x16, 0xc2, 0xdd, 0x29, 0x12, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, + 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x5a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x54, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x54, + 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, + 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, + 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/pbs/controller/api/resources/workers/worker.pb.go b/sdk/pbs/controller/api/resources/workers/worker.pb.go index db30fc9a4..b11b1a93c 100644 --- a/sdk/pbs/controller/api/resources/workers/worker.pb.go +++ b/sdk/pbs/controller/api/resources/workers/worker.pb.go @@ -307,7 +307,7 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xdd, 0x08, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf9, 0x08, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, @@ -335,55 +335,56 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x61, - 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, - 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x0e, 0x63, 0x61, 0x6e, - 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x3e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, - 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x0d, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, - 0x12, 0x47, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x77, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, - 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, - 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, - 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, - 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x1a, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, + 0x29, 0x12, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, 0x0a, + 0x11, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, + 0x63, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x04, 0x74, 0x61, + 0x67, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, + 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x6f, 0x6e, + 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, + 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x10, + 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, + 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From 78150d88d955b13995fbe703504dc43cc5565e4a Mon Sep 17 00:00:00 2001 From: irenarindos Date: Fri, 3 Jun 2022 12:13:31 -0400 Subject: [PATCH 055/100] feature(WorkerAuth): Create RotateRoots domain and job --- internal/daemon/controller/controller.go | 14 +- internal/servers/job/jobs.go | 50 ++++ internal/servers/job/rotate_roots_job.go | 82 +++++++ internal/servers/job/rotate_roots_job_test.go | 221 ++++++++++++++++++ internal/servers/service_rotate_roots.go | 27 +++ internal/servers/service_rotate_roots_test.go | 88 +++++++ 6 files changed, 472 insertions(+), 10 deletions(-) create mode 100644 internal/servers/job/jobs.go create mode 100644 internal/servers/job/rotate_roots_job.go create mode 100644 internal/servers/job/rotate_roots_job_test.go create mode 100644 internal/servers/service_rotate_roots.go create mode 100644 internal/servers/service_rotate_roots_test.go diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index 212fdcc55..f2cb3363c 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -27,6 +27,7 @@ import ( "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/scheduler/job" "github.com/hashicorp/boundary/internal/servers" + serversjob "github.com/hashicorp/boundary/internal/servers/job" "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" @@ -37,7 +38,6 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/hashicorp/go-secure-stdlib/pluginutil/v2" - "github.com/hashicorp/nodeenrollment/rotation" "github.com/hashicorp/nodeenrollment/types" "github.com/mr-tron/base58" ua "go.uber.org/atomic" @@ -317,15 +317,6 @@ func (c *Controller) Start() error { c.baseContext, c.baseCancel = context.WithCancel(context.Background()) - workerAuthStorage, err := c.WorkerAuthRepoStorageFn() - if err != nil { - return fmt.Errorf("error fetching worker auth storage: %w", err) - } - _, err = rotation.RotateRootCertificates(c.baseContext, workerAuthStorage) - if err != nil { - return err - } - if err := c.registerJobs(); err != nil { return fmt.Errorf("error registering jobs: %w", err) } @@ -377,6 +368,9 @@ func (c *Controller) registerJobs() error { if err := session.RegisterJobs(c.baseContext, c.scheduler, rw, c.conf.StatusGracePeriodDuration); err != nil { return err } + if err := serversjob.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms); err != nil { + return err + } return nil } diff --git a/internal/servers/job/jobs.go b/internal/servers/job/jobs.go new file mode 100644 index 000000000..3c900a865 --- /dev/null +++ b/internal/servers/job/jobs.go @@ -0,0 +1,50 @@ +package servers + +import ( + "context" + "reflect" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/scheduler" +) + +// RegisterJobs registers the rotate roots job with the provided scheduler. +func RegisterJobs(ctx context.Context, scheduler *scheduler.Scheduler, r db.Reader, w db.Writer, kms *kms.Kms) error { + const op = "servers.(Jobs).RegisterJobs" + + if isNil(scheduler) { + return errors.New(ctx, errors.InvalidParameter, op, "missing scheduler") + } + if isNil(r) { + return errors.New(ctx, errors.InvalidParameter, op, "missing reader") + } + if isNil(w) { + return errors.New(ctx, errors.InvalidParameter, op, "missing writer") + } + if kms == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing kms") + } + + rotateRootsJob, err := newRotateRootsJob(ctx, r, w, kms) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if err = scheduler.RegisterJob(ctx, rotateRootsJob); err != nil { + return errors.Wrap(ctx, err, op) + } + + return nil +} + +func isNil(i interface{}) bool { + if i == nil { + return true + } + switch reflect.TypeOf(i).Kind() { + case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: + return reflect.ValueOf(i).IsNil() + } + return false +} diff --git a/internal/servers/job/rotate_roots_job.go b/internal/servers/job/rotate_roots_job.go new file mode 100644 index 000000000..ccdc83046 --- /dev/null +++ b/internal/servers/job/rotate_roots_job.go @@ -0,0 +1,82 @@ +package servers + +import ( + "context" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/scheduler" + "github.com/hashicorp/boundary/internal/servers" +) + +const rotateFrequency = time.Hour + +// rotateRootsJob defines a periodic job that initiates root certificate rotation +// It runs every hour; the root rotation function in the library is designed to not +// do anything if it's not time to rotate (roots are within their valid ranges) +type rotateRootsJob struct { + workerAuthRepo *servers.WorkerAuthRepositoryStorage + + totalRotates int +} + +// newRotateRootsJob instantiates the rotate roots job. +func newRotateRootsJob(ctx context.Context, r db.Reader, w db.Writer, kms *kms.Kms) (*rotateRootsJob, error) { + const op = "servers.newRotateRootsJob" + switch { + case isNil(r): + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing reader") + case isNil(w): + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing writer") + case kms == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing kms") + } + + workerAuthRepo, err := servers.NewRepositoryStorage(ctx, r, w, kms) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + return &rotateRootsJob{ + workerAuthRepo: workerAuthRepo, + totalRotates: 0, + }, nil +} + +// Name returns a short, unique name for the job. +func (r *rotateRootsJob) Name() string { return "rotate_roots" } + +// Description returns the description for the job. +func (r *rotateRootsJob) Description() string { + return "Rotate root certificates" +} + +// NextRunIn returns the next run time after a job is completed. +// This is represented by RotateFrequency +func (r *rotateRootsJob) NextRunIn(_ context.Context) (time.Duration, error) { + return rotateFrequency, nil +} + +// Status returns the status of the running job. +func (r *rotateRootsJob) Status() scheduler.JobStatus { + return scheduler.JobStatus{ + Completed: r.totalRotates, + Total: r.totalRotates, + } +} + +// Run executes the job by calling the rotateRoots domain function +func (r *rotateRootsJob) Run(ctx context.Context) error { + const op = "servers.(rotateRootsJob).Run" + + err := servers.RotateRoots(ctx, r.workerAuthRepo) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + r.totalRotates += 1 + + return nil +} diff --git a/internal/servers/job/rotate_roots_job_test.go b/internal/servers/job/rotate_roots_job_test.go new file mode 100644 index 000000000..bf32879c7 --- /dev/null +++ b/internal/servers/job/rotate_roots_job_test.go @@ -0,0 +1,221 @@ +package servers + +import ( + "context" + "crypto/rand" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/scheduler" + "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/nodeenrollment/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRotateRootsJob(t *testing.T) { + require, assert := require.New(t), assert.New(t) + ctx := context.Background() + wrapper := db.TestWrapper(t) + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + kmsCache := kms.TestKms(t, conn, wrapper) + err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(err) + + workerAuthRepo, err := servers.NewRepositoryStorage(ctx, rw, rw, kmsCache) + require.NoError(err) + + type args struct { + w db.Writer + r db.Reader + kms *kms.Kms + } + tests := []struct { + name string + args args + options []servers.Option + wantLimit int + wantErr bool + wantErrCode errors.Code + }{ + { + name: "nil writer", + args: args{ + r: rw, + kms: kmsCache, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "nil reader", + args: args{ + w: rw, + kms: kmsCache, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "nil kms", + args: args{ + w: rw, + r: rw, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "valid", + args: args{ + w: rw, + r: rw, + kms: kmsCache, + }, + wantLimit: db.DefaultLimit, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newRotateRootsJob(ctx, tt.args.r, tt.args.w, tt.args.kms) + if tt.wantErr { + require.Error(err) + assert.Nil(got) + assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "Unexpected error %s", err) + return + } + require.NoError(err) + require.NotNil(got) + require.Equal(0, got.totalRotates) + rootIds, err := workerAuthRepo.List(ctx, (*types.RootCertificate)(nil)) + require.NoError(err) + assert.Len(rootIds, 0) + assert.Equal("rotate_roots", got.Name()) + assert.Equal("Rotate root certificates", got.Description()) + nextRun, err := got.NextRunIn(ctx) + require.NoError(err) + assert.Equal(time.Hour, nextRun) + + // Run job and ensure rotation was performed + err = got.Run(ctx) + require.NoError(err) + require.Equal(1, got.totalRotates) + rootIds, err = workerAuthRepo.List(ctx, (*types.RootCertificate)(nil)) + require.NoError(err) + assert.Len(rootIds, 2) + }) + } +} + +func TestRotateRootsJobFailure(t *testing.T) { + require := require.New(t) + ctx := context.Background() + wrapper := db.TestWrapper(t) + conn, _ := db.TestSetup(t, "postgres") + kmsCache := kms.TestKms(t, conn, wrapper) + err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(err) + + got, err := newRotateRootsJob(ctx, &db.Db{}, &db.Db{}, kmsCache) + require.NoError(err) + + err = got.Run(ctx) + require.Error(err) +} + +func TestRegisterRotateRootsJob(t *testing.T) { + require, assert := require.New(t), assert.New(t) + ctx := context.Background() + wrapper := db.TestWrapper(t) + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + kmsCache := kms.TestKms(t, conn, wrapper) + err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(err) + + sched := scheduler.TestScheduler(t, conn, wrapper) + + type args struct { + s *scheduler.Scheduler + w db.Writer + r db.Reader + kms *kms.Kms + } + tests := []struct { + name string + args args + options []servers.Option + wantLimit int + wantErr bool + wantErrCode errors.Code + }{ + { + name: "nil scheduler", + args: args{ + w: rw, + r: rw, + kms: kmsCache, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "nil writer", + args: args{ + s: sched, + r: rw, + kms: kmsCache, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "nil reader", + args: args{ + s: sched, + w: rw, + kms: kmsCache, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "nil kms", + args: args{ + s: sched, + w: rw, + r: rw, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "valid", + args: args{ + s: sched, + w: rw, + r: rw, + kms: kmsCache, + }, + wantLimit: db.DefaultLimit, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := RegisterJobs(ctx, tt.args.s, tt.args.r, tt.args.w, tt.args.kms) + if tt.wantErr { + require.Error(err) + assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "Unexpected error %s", err) + return + } + require.NoError(err) + }) + } +} diff --git a/internal/servers/service_rotate_roots.go b/internal/servers/service_rotate_roots.go new file mode 100644 index 000000000..119962b10 --- /dev/null +++ b/internal/servers/service_rotate_roots.go @@ -0,0 +1,27 @@ +package servers + +import ( + "context" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/rotation" +) + +// RotateRoots is a domain service function that initiates a rotation of root certificates +// via a call to the nodenenrollment RotateRootCertificates function +// Accepts the nodeenrollment option, WithCertificateLifetime(time.Duration) to specify the lifetime +// of the generated cert(s) +func RotateRoots(ctx context.Context, workerAuthRepo *WorkerAuthRepositoryStorage, opt ...nodeenrollment.Option) error { + const op = "servers.RotateRoots" + if workerAuthRepo == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing workerAuthRepo") + } + + _, err := rotation.RotateRootCertificates(ctx, workerAuthRepo, opt...) + if err != nil { + return errors.Wrap(ctx, err, op) + } + + return nil +} diff --git a/internal/servers/service_rotate_roots_test.go b/internal/servers/service_rotate_roots_test.go new file mode 100644 index 000000000..7df8e488b --- /dev/null +++ b/internal/servers/service_rotate_roots_test.go @@ -0,0 +1,88 @@ +package servers + +import ( + "context" + "crypto/rand" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRotateRoots(t *testing.T) { + require, assert := require.New(t), assert.New(t) + ctx := context.Background() + wrapper := db.TestWrapper(t) + conn, _ := db.TestSetup(t, "postgres") + kmsCache := kms.TestKms(t, conn, wrapper) + err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(err) + + rw := db.New(conn) + workerAuthRepo, err := NewRepositoryStorage(ctx, rw, rw, kmsCache) + require.NoError(err) + + // Check that we have no roots first + rootIds, err := workerAuthRepo.List(ctx, (*types.RootCertificate)(nil)) + require.NoError(err) + assert.Len(rootIds, 0) + + // Generate roots + err = RotateRoots(ctx, workerAuthRepo, nodeenrollment.WithCertificateLifetime(time.Second*5)) + require.NoError(err) + + // Check that we have roots now + rootIds, err = workerAuthRepo.List(ctx, (*types.RootCertificate)(nil)) + require.NoError(err) + assert.Len(rootIds, 2) + certAuthority := &types.RootCertificates{Id: ca_id} + err = workerAuthRepo.Load(ctx, certAuthority) + require.NoError(err) + require.NotNil(certAuthority.GetNext()) + require.NotNil(certAuthority.GetCurrent()) + + initialNext := certAuthority.GetNext() + initialCurrent := certAuthority.GetCurrent() + + // Rotate roots and assert that they've rotated + err = RotateRoots(ctx, workerAuthRepo, nodeenrollment.WithCertificateLifetime(time.Second*5)) + require.NoError(err) + + certAuthority2 := &types.RootCertificates{Id: ca_id} + err = workerAuthRepo.Load(ctx, certAuthority2) + require.NoError(err) + require.NotNil(certAuthority2.GetNext()) + require.NotNil(certAuthority2.GetCurrent()) + rotatedNext := certAuthority2.GetNext() + rotatedCurrent := certAuthority2.GetCurrent() + + // Next and current should have changed + assert.NotEqual(initialNext, rotatedNext) + assert.NotEqual(initialCurrent, rotatedCurrent) + + // And the old next root should now be current + assert.Equal(initialNext, rotatedCurrent) +} + +func TestRotateRootsFailure(t *testing.T) { + require := require.New(t) + ctx := context.Background() + wrapper := db.TestWrapper(t) + conn, _ := db.TestSetup(t, "postgres") + + kmsCache := kms.TestKms(t, conn, wrapper) + err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(err) + + workerAuthRepo, err := NewRepositoryStorage(ctx, &db.Db{}, &db.Db{}, kmsCache) + require.NoError(err) + + err = RotateRoots(ctx, workerAuthRepo) + require.Error(err) +} From aa0afd325a5ad27592f62ffb37bae3834652cfdf Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 6 Jun 2022 14:52:47 -0400 Subject: [PATCH 056/100] Remove 'ephemeral' from KMS auth method bits (#2141) --- internal/cmd/commands/dev/dev.go | 84 ++++++++++++------------- internal/cmd/commands/dev/flags_test.go | 2 +- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index f33611e0d..63678737b 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -52,42 +52,42 @@ type Command struct { controller *controller.Controller worker *worker.Worker - flagLogLevel string - flagLogFormat string - flagCombineLogs bool - flagLoginName string - flagPassword string - flagUnprivilegedLoginName string - flagUnprivilegedPassword string - flagIdSuffix string - flagHostAddress string - flagTargetDefaultPort int - flagTargetSessionMaxSeconds int - flagTargetSessionConnectionLimit int - flagControllerAPIListenAddr string - flagControllerClusterListenAddr string - flagControllerPublicClusterAddr string - flagControllerOnly bool - flagWorkerAuthKey string - flagWorkerProxyListenAddr string - flagWorkerPublicAddr string - flagOpsListenAddr string - flagUiPassthroughDir string - flagRecoveryKey string - flagDatabaseUrl string - flagContainerImage string - flagDisableDatabaseDestruction bool - flagEventFormat string - flagAudit string - flagObservations string - flagSysEvents string - flagEveryEventAllowFilters []string - flagEveryEventDenyFilters []string - flagCreateLoopbackHostPlugin bool - flagPluginExecutionDir string - flagUseEphemeralKmsWorkerAuthMethod bool - flagWorkerAuthStorageDir string - flagWorkerAuthStorageSkipCleanup bool + flagLogLevel string + flagLogFormat string + flagCombineLogs bool + flagLoginName string + flagPassword string + flagUnprivilegedLoginName string + flagUnprivilegedPassword string + flagIdSuffix string + flagHostAddress string + flagTargetDefaultPort int + flagTargetSessionMaxSeconds int + flagTargetSessionConnectionLimit int + flagControllerAPIListenAddr string + flagControllerClusterListenAddr string + flagControllerPublicClusterAddr string + flagControllerOnly bool + flagWorkerAuthKey string + flagWorkerProxyListenAddr string + flagWorkerPublicAddr string + flagOpsListenAddr string + flagUiPassthroughDir string + flagRecoveryKey string + flagDatabaseUrl string + flagContainerImage string + flagDisableDatabaseDestruction bool + flagEventFormat string + flagAudit string + flagObservations string + flagSysEvents string + flagEveryEventAllowFilters []string + flagEveryEventDenyFilters []string + flagCreateLoopbackHostPlugin bool + flagPluginExecutionDir string + flagUseKmsWorkerAuthMethod bool + flagWorkerAuthStorageDir string + flagWorkerAuthStorageSkipCleanup bool } func (c *Command) Synopsis() string { @@ -332,9 +332,9 @@ func (c *Command) Flags() *base.FlagSets { Usage: "Specifies where Boundary should write plugins that it is executing; if not set defaults to system temp directory.", }) f.BoolVar(&base.BoolVar{ - Name: "use-ephemeral-kms-worker-auth-method", - Target: &c.flagUseEphemeralKmsWorkerAuthMethod, - Usage: "If set, the original \"ephemeral\" method of worker auth will be used to connect the initial dev worker to the controller.", + Name: "use-kms-worker-auth-method", + Target: &c.flagUseKmsWorkerAuthMethod, + Usage: "If set, the original KMS-based method of worker auth will be used to connect the initial dev worker to the controller.", }) f.StringVar(&base.StringVar{ @@ -562,7 +562,7 @@ func (c *Command) Run(args []string) int { if c.flagRecoveryKey != "" { c.Config.DevRecoveryKey = c.flagRecoveryKey } - if err := c.SetupKMSes(c.Context, c.UI, c.Config, base.WithSkipWorkerAuthKmsInstantiation(!c.flagUseEphemeralKmsWorkerAuthMethod)); err != nil { + if err := c.SetupKMSes(c.Context, c.UI, c.Config, base.WithSkipWorkerAuthKmsInstantiation(!c.flagUseKmsWorkerAuthMethod)); err != nil { c.UI.Error(err.Error()) return base.CommandUserError } @@ -570,7 +570,7 @@ func (c *Command) Run(args []string) int { c.UI.Error("Controller KMS not found after parsing KMS blocks") return base.CommandUserError } - if c.flagUseEphemeralKmsWorkerAuthMethod { + if c.flagUseKmsWorkerAuthMethod { if c.WorkerAuthKms == nil { c.UI.Error("Worker Auth KMS not found after parsing KMS blocks") return base.CommandUserError @@ -690,7 +690,7 @@ func (c *Command) Run(args []string) int { return base.CommandCliError } - if !c.flagUseEphemeralKmsWorkerAuthMethod { + if !c.flagUseKmsWorkerAuthMethod { req := c.worker.WorkerAuthRegistrationRequest if req == "" { c.UI.Error("No worker auth registration request found at worker start time") diff --git a/internal/cmd/commands/dev/flags_test.go b/internal/cmd/commands/dev/flags_test.go index 5c17c48d3..f52baea7d 100644 --- a/internal/cmd/commands/dev/flags_test.go +++ b/internal/cmd/commands/dev/flags_test.go @@ -64,7 +64,7 @@ func TestCommand_Flags(t *testing.T) { assert.Contains(completions, "-event-allow-filter") assert.Contains(completions, "-event-deny-filter") assert.Contains(completions, "-plugin-execution-dir") - assert.Contains(completions, "-use-ephemeral-kms-worker-auth-method") + assert.Contains(completions, "-use-kms-worker-auth-method") assert.Contains(completions, "-worker-auth-storage-dir") assert.Contains(completions, "-worker-auth-storage-skip-cleanup") From 0f7e1ee7fab9fa8b69f3f3ffc165ee2f5d305879 Mon Sep 17 00:00:00 2001 From: Todd Date: Mon, 6 Jun 2022 16:50:22 -0700 Subject: [PATCH 057/100] Fix canonical tags not getting assigned in the api resource. (#2144) --- .../handlers/workers/worker_service.go | 2 +- .../handlers/workers/worker_service_test.go | 52 ++++++++++++++----- internal/servers/repository_worker_test.go | 26 +++++++++- internal/servers/testing.go | 13 +++++ 4 files changed, 78 insertions(+), 15 deletions(-) diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index a98316e03..933201425 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -395,7 +395,7 @@ func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (* } if outputFields.Has(globals.CanonicalTagsField) && len(in.CanonicalTags()) > 0 { var err error - out.Tags, err = tagsToMapProto(in.CanonicalTags()) + out.CanonicalTags, err = tagsToMapProto(in.CanonicalTags()) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing canonical tags proto")) } diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index 64d9505bf..0613997bc 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -31,6 +31,17 @@ import ( var testAuthorizedActions = []string{"no-op", "read", "update", "delete"} +func structListValue(t *testing.T, ss ...string) *structpb.ListValue { + t.Helper() + var val []interface{} + for _, s := range ss { + val = append(val, s) + } + lv, err := structpb.NewList(val) + require.NoError(t, err) + return lv +} + func TestGet(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrap := db.TestWrapper(t) @@ -40,14 +51,29 @@ func TestGet(t *testing.T) { } rw := db.New(conn) kms := kms.TestKms(t, conn, wrap) + repo, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) repoFn := func() (*servers.Repository, error) { - return servers.NewRepository(rw, rw, kms) + return repo, nil } worker := servers.TestWorker(t, conn, wrap, servers.WithName("test worker names"), servers.WithDescription("test worker description"), - servers.WithAddress("test worker address")) + servers.WithAddress("test worker address"), + servers.WithWorkerTags(&servers.Tag{"key", "val"})) + // Add config tags to the created worker + worker, err = repo.UpsertWorkerStatus(context.Background(), + servers.NewWorkerForStatus(worker.GetScopeId(), + servers.WithName(worker.GetWorkerReportedName()), + servers.WithAddress(worker.GetWorkerReportedAddress()), + servers.WithWorkerTags(&servers.Tag{ + Key: "config", + Value: "test", + })), + servers.WithUpdateTags(true), + servers.WithPublicId(worker.GetPublicId())) + require.NoError(t, err) wantWorker := &pb.Worker{ Id: worker.GetPublicId(), @@ -62,9 +88,19 @@ func TestGet(t *testing.T) { AuthorizedActions: testAuthorizedActions, CanonicalAddress: worker.CanonicalAddress(), LastStatusTime: worker.GetLastStatusTime().GetTimestamp(), + CanonicalTags: map[string]*structpb.ListValue{ + "key": structListValue(t, "val"), + "config": structListValue(t, "test"), + }, + Tags: map[string]*structpb.ListValue{ + "key": structListValue(t, "val"), + }, WorkerConfig: &pb.WorkerConfig{ Address: worker.GetWorkerReportedAddress(), Name: worker.GetWorkerReportedName(), + Tags: map[string]*structpb.ListValue{ + "config": structListValue(t, "test"), + }, }, } @@ -631,11 +667,7 @@ func TestUpdate(t *testing.T) { }, Item: &pb.Worker{ Tags: map[string]*structpb.ListValue{ - "foo": func() *structpb.ListValue { - l, err := structpb.NewList([]interface{}{"bar"}) - require.NoError(t, err) - return l - }(), + "foo": structListValue(t, "bar"), }, }, }, @@ -650,11 +682,7 @@ func TestUpdate(t *testing.T) { }, Item: &pb.Worker{ CanonicalTags: map[string]*structpb.ListValue{ - "foo": func() *structpb.ListValue { - l, err := structpb.NewList([]interface{}{"bar"}) - require.NoError(t, err) - return l - }(), + "foo": structListValue(t, "bar"), }, }, }, diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 7daaffb5e..732aebc3f 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -145,11 +145,33 @@ func TestLookupWorker(t *testing.T) { require.NoError(t, err) ctx := context.Background() - w := servers.TestWorker(t, conn, wrapper) + w := servers.TestWorker(t, conn, wrapper, + servers.WithName("name"), + servers.WithDescription("description"), + servers.WithAddress("address"), + servers.WithWorkerTags(&servers.Tag{"key", "val"})) + w, err = repo.UpsertWorkerStatus(context.Background(), + servers.NewWorkerForStatus(w.GetScopeId(), + servers.WithName(w.GetWorkerReportedName()), + servers.WithAddress(w.GetWorkerReportedAddress()), + servers.WithWorkerTags(&servers.Tag{ + Key: "config", + Value: "test", + })), + servers.WithUpdateTags(true), + servers.WithPublicId(w.GetPublicId())) + require.NoError(t, err) + t.Run("success", func(t *testing.T) { got, err := repo.LookupWorker(ctx, w.GetPublicId()) require.NoError(t, err) - assert.Empty(t, cmp.Diff(w.Worker, got.Worker, protocmp.Transform())) + assert.Empty(t, cmp.Diff(w, got, protocmp.Transform())) + assert.Equal(t, map[string][]string{"key": {"val"}}, got.GetApiTags()) + assert.Equal(t, map[string][]string{"config": {"test"}}, got.GetConfigTags()) + assert.Equal(t, map[string][]string{ + "key": {"val"}, + "config": {"test"}, + }, got.CanonicalTags()) }) t.Run("not found", func(t *testing.T) { got, err := repo.LookupWorker(ctx, "w_unknownid") diff --git a/internal/servers/testing.go b/internal/servers/testing.go index f09b65eae..1cd081026 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/types/scope" wrapping "github.com/hashicorp/go-kms-wrapping/v2" "github.com/stretchr/testify/require" @@ -103,6 +104,18 @@ func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Opti require.NotNil(t, wrk) opts := getOpts(opt...) + if len(opts.withWorkerTags) > 0 { + var tags []interface{} + for _, t := range opts.withWorkerTags { + tags = append(tags, &store.WorkerTag{ + WorkerId: wrk.GetPublicId(), + Key: t.Key, + Value: t.Value, + Source: "api", + }) + } + require.NoError(t, rw.CreateItems(ctx, tags)) + } var mask []string if opts.withName != "" { wrk.Name = opts.withName From fdf43fc4fe2c7b7b4421a77ccd6a2dfb9eab3885 Mon Sep 17 00:00:00 2001 From: Jim Date: Tue, 7 Jun 2022 11:50:30 -0400 Subject: [PATCH 058/100] feature (worker): add CreateWorker(...) service (aka API) (#2143) --- globals/fields.go | 2 + .../handlers/scopes/scope_service_test.go | 2 +- .../handlers/workers/worker_service.go | 117 ++++- .../handlers/workers/worker_service_test.go | 399 ++++++++++++++++++ internal/gen/controller.swagger.json | 58 +-- .../api/services/worker_service.pb.go | 250 +++++------ .../api/services/worker_service.pb.gw.go | 38 +- .../api/services/worker_service_grpc.pb.go | 32 +- internal/perms/acl_test.go | 6 +- .../api/resources/workers/v1/worker.proto | 6 +- .../api/services/v1/worker_service.proto | 10 +- internal/types/action/action.go | 6 +- internal/types/action/action_test.go | 4 +- .../api/resources/workers/worker.pb.go | 76 ++-- 14 files changed, 773 insertions(+), 233 deletions(-) diff --git a/globals/fields.go b/globals/fields.go index 3d8d352c2..4fd02ee4d 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -79,4 +79,6 @@ const ( TagsField = "tags" CanonicalTagsField = "canonical_tags" ConfigurationField = "configuration" + WorkerAuthTokenField = "worker_auth_token" + WorkerConfigField = "worker_config" ) diff --git a/internal/daemon/controller/handlers/scopes/scope_service_test.go b/internal/daemon/controller/handlers/scopes/scope_service_test.go index 0e1a82d37..470df83a6 100644 --- a/internal/daemon/controller/handlers/scopes/scope_service_test.go +++ b/internal/daemon/controller/handlers/scopes/scope_service_test.go @@ -97,7 +97,7 @@ var globalAuthorizedCollectionActions = map[string]*structpb.ListValue{ }, "workers": { Values: []*structpb.Value{ - structpb.NewStringValue("create:worker-request"), + structpb.NewStringValue("create:worker-led"), structpb.NewStringValue("list"), }, }, diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index 933201425..bb82d654b 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -19,7 +19,10 @@ import ( "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" + "github.com/hashicorp/nodeenrollment/types" + "github.com/mr-tron/base58" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -39,7 +42,7 @@ var ( // CollectionActions contains the set of actions that can be performed on // this collection CollectionActions = action.ActionSet{ - action.CreateWorkerRequest, + action.CreateWorkerLed, action.List, } ) @@ -186,6 +189,53 @@ func (s Service) GetWorker(ctx context.Context, req *pbs.GetWorkerRequest) (*pbs return &pbs.GetWorkerResponse{Item: item}, nil } +// CreateWorker implements the interface pbs.WorkerServiceServer. +func (s Service) CreateWorkerLed(ctx context.Context, req *pbs.CreateWorkerLedRequest) (*pbs.CreateWorkerLedResponse, error) { + const op = "workers.(Service).CreateWorkerLed" + + if err := validateCreateRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.CreateWorkerLed) + if authResults.Error != nil { + return nil, authResults.Error + } + reqBytes, err := base58.FastBase58Decoding(req.GetItem().WorkerAuthToken.GetValue()) + if err != nil { + return nil, fmt.Errorf("%s: error decoding node_credentials_token: %w", op, err) + } + // Decode the proto into the request + creds := new(types.FetchNodeCredentialsRequest) + if err := proto.Unmarshal(reqBytes, creds); err != nil { + return nil, fmt.Errorf("%s: error unmarshaling node_credentials_token: %w", op, err) + } + created, err := s.createInRepo(ctx, req.GetItem(), creds) + if err != nil { + return nil, fmt.Errorf("%s: error creating worker: %w", op, err) + } + + outputFields, ok := requests.OutputFields(ctx) + if !ok { + return nil, errors.New(ctx, errors.Internal, op, "no request context found") + } + + outputOpts := make([]handlers.Option, 0, 3) + outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if outputFields.Has(globals.ScopeField) { + outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) + } + if outputFields.Has(globals.AuthorizedActionsField) { + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, created.GetPublicId(), IdActions).Strings())) + } + + item, err := toProto(ctx, created, outputOpts...) + if err != nil { + return nil, err + } + + return &pbs.CreateWorkerLedResponse{Item: item}, nil +} + // DeleteWorker implements the interface pbs.WorkerServiceServer. func (s Service) DeleteWorker(ctx context.Context, req *pbs.DeleteWorkerRequest) (*pbs.DeleteWorkerResponse, error) { if err := validateDeleteRequest(req); err != nil { @@ -270,6 +320,25 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*servers.Worker, e return w, nil } +func (s Service) createInRepo(ctx context.Context, worker *pb.Worker, creds *types.FetchNodeCredentialsRequest) (*servers.Worker, error) { + const op = "workers.(Service).createInRepo" + repo, err := s.repoFn() + if err != nil { + return nil, err + } + newWorker := servers.NewWorker( + worker.GetScopeId(), + servers.WithName(worker.GetName().GetValue()), + servers.WithDescription(worker.GetDescription().GetValue()), + servers.WithAddress(worker.GetAddress().GetValue()), + ) + retWorker, err := repo.CreateWorker(ctx, newWorker, servers.WithFetchNodeCredentialsRequest(creds)) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create worker")) + } + return retWorker, nil +} + func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) { const op = "workers.(Service).deleteFromRepo" repo, err := s.repoFn() @@ -329,7 +398,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth. var parentId string opts := []auth.Option{auth.WithType(resource.Worker), auth.WithAction(a)} switch a { - case action.List, action.Create: + case action.List, action.CreateWorkerLed: parentId = id default: w, err := repo.LookupWorker(ctx, id) @@ -420,8 +489,10 @@ func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (* return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing config tags proto")) } } - out.GetWorkerConfig().Address = in.GetWorkerReportedAddress() - out.GetWorkerConfig().Name = in.GetWorkerReportedName() + if out.WorkerConfig != nil { + out.GetWorkerConfig().Address = in.GetWorkerReportedAddress() + out.GetWorkerConfig().Name = in.GetWorkerReportedName() + } } return &out, nil @@ -492,3 +563,41 @@ func validateUpdateRequest(req *pbs.UpdateWorkerRequest) error { return badFields }, servers.WorkerPrefix) } + +func validateCreateRequest(req *pbs.CreateWorkerLedRequest) error { + return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string { + const ( + mustBeGlobalMsg = "Must be 'global'" + cannotBeEmptyMsg = "Cannot be empty." + readOnlyFieldMsg = "This is a read only field." + ) + badFields := map[string]string{} + if scope.Global.String() != req.GetItem().GetScopeId() { + badFields[globals.ScopeIdField] = mustBeGlobalMsg + } + // FIXME: in the future, we won't require this token since we'll support + // the server led flow where a token is returned when a worker is created. + if req.GetItem().WorkerAuthToken == nil { + badFields[globals.WorkerAuthTokenField] = cannotBeEmptyMsg + } + if req.GetItem().CanonicalAddress != "" { + badFields[globals.CanonicalAddressField] = readOnlyFieldMsg + } + if req.GetItem().Tags != nil { + badFields[globals.TagsField] = readOnlyFieldMsg + } + if req.GetItem().CanonicalTags != nil { + badFields[globals.CanonicalTagsField] = readOnlyFieldMsg + } + if req.GetItem().LastStatusTime != nil { + badFields[globals.LastStatusTimeField] = readOnlyFieldMsg + } + if req.GetItem().WorkerConfig != nil { + badFields[globals.WorkerConfigField] = readOnlyFieldMsg + } + if req.GetItem().AuthorizedActions != nil { + badFields[globals.AuthorizedActionsField] = readOnlyFieldMsg + } + return badFields + }) +} diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index 0613997bc..f6e200d61 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/daemon/controller/auth" "github.com/hashicorp/boundary/internal/daemon/controller/handlers" "github.com/hashicorp/boundary/internal/db" @@ -17,7 +18,12 @@ import ( "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" + "github.com/hashicorp/nodeenrollment/rotation" + "github.com/hashicorp/nodeenrollment/storage/file" + "github.com/hashicorp/nodeenrollment/types" + "github.com/mr-tron/base58" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/genproto/protobuf/field_mask" @@ -775,3 +781,396 @@ func TestUpdate_BadVersion(t *testing.T) { assert.Error(t, err) assert.True(t, errors.Is(err, handlers.NotFoundError()), "Got %v, wanted not found error.", err) } + +func TestCreateWorkerLed(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + testRootWrapper := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, testRootWrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + rw := db.New(conn) + testKms := kms.TestKms(t, conn, testRootWrapper) + repoFn := func() (*servers.Repository, error) { + return servers.NewRepository(rw, rw, testKms) + } + testCtx := context.Background() + + testSrv, err := NewService(testCtx, repoFn, iamRepoFn) + require.NoError(t, err, "Error when getting new worker service.") + + // Get an initial set of authorized node credentials + rootStorage, err := servers.NewRepositoryStorage(testCtx, rw, rw, testKms) + require.NoError(t, err) + _, err = rotation.RotateRootCertificates(testCtx, rootStorage) + require.NoError(t, err) + + fetchReqFn := func() string { + // This happens on the worker + fileStorage, err := file.New(testCtx) + require.NoError(t, err) + defer fileStorage.Cleanup() + + nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage) + require.NoError(t, err) + // Create request using worker id + fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(testCtx) + require.NoError(t, err) + + fetchEncoded, err := proto.Marshal(fetchReq) + require.NoError(t, err) + + return base58.Encode(fetchEncoded) + } + + tests := []struct { + name string + scopeId string + service Service + req *pbs.CreateWorkerLedRequest + res *pbs.CreateWorkerLedResponse + wantErr bool + wantErrIs error + wantErrContains string + }{ + { + name: "invalid-scope", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: "invalid-scope", + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: "Must be 'global'", + }, + { + name: "missing-node-credentials-token", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.WorkerAuthTokenField, + }, + { + name: "invalid-id", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + Id: "invalid-id", + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.IdField, + }, + { + name: "invalid-canonical-address", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + CanonicalAddress: "invalid-canonical-address", + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.CanonicalAddressField, + }, + { + name: "invalid-tags", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + Tags: map[string]*structpb.ListValue{ + "invalid": {Values: []*structpb.Value{ + structpb.NewStringValue("invalid-tags"), + }}, + }, + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.TagsField, + }, + { + name: "invalid-canonical-tags", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + CanonicalTags: map[string]*structpb.ListValue{ + "invalid": {Values: []*structpb.Value{ + structpb.NewStringValue("invalid-canonical-tags"), + }}, + }, + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.CanonicalTagsField, + }, + { + name: "invalid-last-status-time", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + LastStatusTime: timestamppb.Now(), + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.LastStatusTimeField, + }, + { + name: "invalid-worker-config", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + WorkerConfig: &pb.WorkerConfig{}, + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.WorkerConfigField, + }, + { + name: "invalid-authorized-actions", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + AuthorizedActions: []string{"invalid-authorized-actions"}, + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.AuthorizedActionsField, + }, + { + name: "invalid-create-time", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + CreatedTime: timestamppb.Now(), + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.CreatedTimeField, + }, + { + name: "invalid-update-time", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + UpdatedTime: timestamppb.Now(), + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.UpdatedTimeField, + }, + { + name: "invalid-version", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + Version: 1, + }, + }, + wantErr: true, + wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), + wantErrContains: globals.VersionField, + }, + { + name: "invalid-auth", + service: testSrv, + scopeId: "splat-auth-scope", + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + }, + }, + wantErr: true, + wantErrContains: "non-existent scope \"splat-auth-scope\"", + }, + { + name: "invalid-base58-encoding", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: "invalid;semicolon"}, + }, + }, + wantErr: true, + wantErrContains: "error decoding node_credentials_token", + }, + { + name: "invalid-marshal", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + WorkerAuthToken: &wrapperspb.StringValue{Value: "notNodeCreds"}, + }, + }, + wantErr: true, + wantErrContains: "error unmarshaling node_credentials_token", + }, + { + name: "create-error", + service: func() Service { + repoFn := func() (*servers.Repository, error) { + return servers.NewRepository(rw, &db.Db{}, testKms) + } + testSrv, err := NewService(testCtx, repoFn, iamRepoFn) + require.NoError(t, err, "Error when getting new worker service.") + return testSrv + }(), + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + Name: &wrapperspb.StringValue{Value: "success"}, + Description: &wrapperspb.StringValue{Value: "success-description"}, + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + }, + }, + wantErr: true, + wantErrContains: "error creating worker", + }, + { + name: "bad-repo-function-in-create", + service: func() Service { + // var cnt gives us a way for the repoFn to fail the 2nd time + // it's called so we can get past the AuthRequest check in + // CreateWorker(...) and test the createInRepo(...) failure + // path if the repoFn returns and err. + cnt := 0 + repoFn := func() (*servers.Repository, error) { + cnt = cnt + 1 + switch { + case cnt > 1: + return nil, errors.New(testCtx, errors.Internal, "bad-repo-function", "error creating repo") + default: + return servers.NewRepository(rw, rw, testKms) + } + } + testSrv, err := NewService(testCtx, repoFn, iamRepoFn) + require.NoError(t, err, "Error when getting new worker service.") + return testSrv + }(), + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + Name: &wrapperspb.StringValue{Value: "success"}, + Description: &wrapperspb.StringValue{Value: "success-description"}, + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + }, + }, + wantErr: true, + wantErrContains: "error creating worker: bad-repo-function: error creating repo", + }, + { + name: "success", + service: testSrv, + scopeId: scope.Global.String(), + req: &pbs.CreateWorkerLedRequest{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + Name: &wrapperspb.StringValue{Value: "success"}, + Description: &wrapperspb.StringValue{Value: "success-description"}, + WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + }, + }, + res: &pbs.CreateWorkerLedResponse{ + Item: &workers.Worker{ + ScopeId: scope.Global.String(), + Name: &wrapperspb.StringValue{Value: "success"}, + Description: &wrapperspb.StringValue{Value: "success-description"}, + Version: 1, + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, err := tc.service.CreateWorkerLed(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), tc.req) + if tc.wantErr { + require.Error(err) + assert.Nil(got) + if tc.wantErrIs != nil { + assert.ErrorIs(err, tc.wantErrIs) + } + if tc.wantErrContains != "" { + assert.Contains(err.Error(), tc.wantErrContains) + } + return + } + require.NoError(err) + require.NotNil(got) + { + // we only need to check that these "read-only" fields were set + // outbound. Other portions of the domain is responsible for + // testing that these values are correct and there's no reason + // to repeat those tests here. + assert.NotEmpty(got.GetItem().GetId()) + assert.NotEmpty(got.GetItem().CreatedTime) + assert.NotEmpty(got.GetItem().UpdatedTime) + assert.NotEmpty(got.GetItem().Scope) + assert.NotEmpty(got.GetItem().AuthorizedActions) + { + tc.res.Item.Id = got.GetItem().GetId() + tc.res.Item.CreatedTime = got.GetItem().GetCreatedTime() + tc.res.Item.UpdatedTime = got.GetItem().GetUpdatedTime() + tc.res.Item.Scope = got.GetItem().Scope + tc.res.Item.AuthorizedActions = got.GetItem().GetAuthorizedActions() + } + } + assert.Equal(tc.res, got) + }) + } +} diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index abbb46a40..c86653d6e 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -3688,31 +3688,6 @@ "tags": [ "controller.api.services.v1.WorkerService" ] - }, - "post": { - "summary": "Creates a single Worker.", - "operationId": "WorkerService_CreateWorker", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" - } - } - }, - "parameters": [ - { - "name": "item", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" - } - } - ], - "tags": [ - "controller.api.services.v1.WorkerService" - ] } }, "/v1/workers/{id}": { @@ -3799,6 +3774,33 @@ "controller.api.services.v1.WorkerService" ] } + }, + "/v1/workers:create:worker-led": { + "post": { + "summary": "Creates a single Worker.", + "operationId": "WorkerService_CreateWorkerLed", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + } + }, + "parameters": [ + { + "name": "item", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.resources.workers.v1.Worker" + } + } + ], + "tags": [ + "controller.api.services.v1.WorkerService" + ] + } } }, "definitions": { @@ -5580,6 +5582,10 @@ "description": "Output only. The set of information the worker daemon has reported about\nitself.", "readOnly": true }, + "worker_auth_token": { + "type": "string", + "title": "worker_auth_token is input only. Supports the worker led node\nenrollment flow where this credentials token is produced by a worker. This\ntoken is a base58 encoded types.FetchNodeCredentialsRequest from\nhttps://github.com/hashicorp/nodeenrollment" + }, "authorized_actions": { "type": "array", "items": { @@ -5882,7 +5888,7 @@ } } }, - "controller.api.services.v1.CreateWorkerResponse": { + "controller.api.services.v1.CreateWorkerLedResponse": { "type": "object", "properties": { "uri": { diff --git a/internal/gen/controller/api/services/worker_service.pb.go b/internal/gen/controller/api/services/worker_service.pb.go index bff4323ee..3f698b141 100644 --- a/internal/gen/controller/api/services/worker_service.pb.go +++ b/internal/gen/controller/api/services/worker_service.pb.go @@ -228,7 +228,7 @@ func (x *ListWorkersResponse) GetItems() []*workers.Worker { return nil } -type CreateWorkerRequest struct { +type CreateWorkerLedRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -236,8 +236,8 @@ type CreateWorkerRequest struct { Item *workers.Worker `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` } -func (x *CreateWorkerRequest) Reset() { - *x = CreateWorkerRequest{} +func (x *CreateWorkerLedRequest) Reset() { + *x = CreateWorkerLedRequest{} if protoimpl.UnsafeEnabled { mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -245,13 +245,13 @@ func (x *CreateWorkerRequest) Reset() { } } -func (x *CreateWorkerRequest) String() string { +func (x *CreateWorkerLedRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateWorkerRequest) ProtoMessage() {} +func (*CreateWorkerLedRequest) ProtoMessage() {} -func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { +func (x *CreateWorkerLedRequest) ProtoReflect() protoreflect.Message { mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -263,19 +263,19 @@ func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateWorkerRequest.ProtoReflect.Descriptor instead. -func (*CreateWorkerRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateWorkerLedRequest.ProtoReflect.Descriptor instead. +func (*CreateWorkerLedRequest) Descriptor() ([]byte, []int) { return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{4} } -func (x *CreateWorkerRequest) GetItem() *workers.Worker { +func (x *CreateWorkerLedRequest) GetItem() *workers.Worker { if x != nil { return x.Item } return nil } -type CreateWorkerResponse struct { +type CreateWorkerLedResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -284,8 +284,8 @@ type CreateWorkerResponse struct { Item *workers.Worker `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` } -func (x *CreateWorkerResponse) Reset() { - *x = CreateWorkerResponse{} +func (x *CreateWorkerLedResponse) Reset() { + *x = CreateWorkerLedResponse{} if protoimpl.UnsafeEnabled { mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -293,13 +293,13 @@ func (x *CreateWorkerResponse) Reset() { } } -func (x *CreateWorkerResponse) String() string { +func (x *CreateWorkerLedResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateWorkerResponse) ProtoMessage() {} +func (*CreateWorkerLedResponse) ProtoMessage() {} -func (x *CreateWorkerResponse) ProtoReflect() protoreflect.Message { +func (x *CreateWorkerLedResponse) ProtoReflect() protoreflect.Message { mi := &file_controller_api_services_v1_worker_service_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -311,19 +311,19 @@ func (x *CreateWorkerResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateWorkerResponse.ProtoReflect.Descriptor instead. -func (*CreateWorkerResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateWorkerLedResponse.ProtoReflect.Descriptor instead. +func (*CreateWorkerLedResponse) Descriptor() ([]byte, []int) { return file_controller_api_services_v1_worker_service_proto_rawDescGZIP(), []int{5} } -func (x *CreateWorkerResponse) GetUri() string { +func (x *CreateWorkerLedResponse) GetUri() string { if x != nil { return x.Uri } return "" } -func (x *CreateWorkerResponse) GetItem() *workers.Worker { +func (x *CreateWorkerLedResponse) GetItem() *workers.Worker { if x != nil { return x.Item } @@ -562,98 +562,100 @@ var file_controller_api_services_v1_worker_service_proto_rawDesc = []byte{ 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x56, 0x0a, 0x13, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x22, 0x69, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, - 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x3f, 0x0a, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xa4, 0x01, - 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x3c, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, - 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x57, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x25, 0x0a, - 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xd7, 0x06, 0x0a, - 0x0d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xa2, - 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x2c, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x92, 0x41, 0x17, 0x12, 0x15, - 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x57, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x10, 0x2f, 0x76, 0x31, - 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x12, 0x9a, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, - 0x65, 0x72, 0x73, 0x12, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x92, 0x41, 0x14, 0x12, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x73, - 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, - 0x12, 0xaf, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x6b, 0x65, 0x72, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x59, 0x0a, 0x16, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x65, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x6c, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x72, 0x69, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x22, 0xa4, 0x01, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3f, 0x0a, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x3c, 0x0a, 0x0b, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0b, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x57, 0x0a, 0x14, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x22, 0x25, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x32, 0xf2, 0x06, 0x0a, 0x0d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0xa2, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x12, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, 0x92, 0x41, 0x1a, 0x12, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x57, 0x6f, 0x72, 0x6b, - 0x65, 0x72, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x77, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x12, 0xad, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x38, 0x92, 0x41, 0x17, 0x12, 0x15, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, + 0x67, 0x6c, 0x65, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x18, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x9a, 0x01, 0x0a, 0x0b, 0x4c, 0x69, + 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x92, 0x41, 0x14, 0x12, + 0x12, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x77, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0xca, 0x01, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x65, 0x64, 0x12, 0x32, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x4e, 0x92, 0x41, 0x1a, 0x12, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x3a, 0x77, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x2d, 0x6c, 0x65, 0x64, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x12, 0xad, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x92, 0x41, 0x13, 0x12, 0x11, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1e, 0x32, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x12, 0xa1, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x92, 0x41, 0x13, 0x12, 0x11, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x32, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x12, 0xa1, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x92, 0x41, 0x13, 0x12, 0x11, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x12, 0x2a, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x92, 0x41, 0x13, 0x12, 0x11, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x2a, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -670,35 +672,35 @@ func file_controller_api_services_v1_worker_service_proto_rawDescGZIP() []byte { var file_controller_api_services_v1_worker_service_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_controller_api_services_v1_worker_service_proto_goTypes = []interface{}{ - (*GetWorkerRequest)(nil), // 0: controller.api.services.v1.GetWorkerRequest - (*GetWorkerResponse)(nil), // 1: controller.api.services.v1.GetWorkerResponse - (*ListWorkersRequest)(nil), // 2: controller.api.services.v1.ListWorkersRequest - (*ListWorkersResponse)(nil), // 3: controller.api.services.v1.ListWorkersResponse - (*CreateWorkerRequest)(nil), // 4: controller.api.services.v1.CreateWorkerRequest - (*CreateWorkerResponse)(nil), // 5: controller.api.services.v1.CreateWorkerResponse - (*UpdateWorkerRequest)(nil), // 6: controller.api.services.v1.UpdateWorkerRequest - (*UpdateWorkerResponse)(nil), // 7: controller.api.services.v1.UpdateWorkerResponse - (*DeleteWorkerRequest)(nil), // 8: controller.api.services.v1.DeleteWorkerRequest - (*DeleteWorkerResponse)(nil), // 9: controller.api.services.v1.DeleteWorkerResponse - (*workers.Worker)(nil), // 10: controller.api.resources.workers.v1.Worker - (*fieldmaskpb.FieldMask)(nil), // 11: google.protobuf.FieldMask + (*GetWorkerRequest)(nil), // 0: controller.api.services.v1.GetWorkerRequest + (*GetWorkerResponse)(nil), // 1: controller.api.services.v1.GetWorkerResponse + (*ListWorkersRequest)(nil), // 2: controller.api.services.v1.ListWorkersRequest + (*ListWorkersResponse)(nil), // 3: controller.api.services.v1.ListWorkersResponse + (*CreateWorkerLedRequest)(nil), // 4: controller.api.services.v1.CreateWorkerLedRequest + (*CreateWorkerLedResponse)(nil), // 5: controller.api.services.v1.CreateWorkerLedResponse + (*UpdateWorkerRequest)(nil), // 6: controller.api.services.v1.UpdateWorkerRequest + (*UpdateWorkerResponse)(nil), // 7: controller.api.services.v1.UpdateWorkerResponse + (*DeleteWorkerRequest)(nil), // 8: controller.api.services.v1.DeleteWorkerRequest + (*DeleteWorkerResponse)(nil), // 9: controller.api.services.v1.DeleteWorkerResponse + (*workers.Worker)(nil), // 10: controller.api.resources.workers.v1.Worker + (*fieldmaskpb.FieldMask)(nil), // 11: google.protobuf.FieldMask } var file_controller_api_services_v1_worker_service_proto_depIdxs = []int32{ 10, // 0: controller.api.services.v1.GetWorkerResponse.item:type_name -> controller.api.resources.workers.v1.Worker 10, // 1: controller.api.services.v1.ListWorkersResponse.items:type_name -> controller.api.resources.workers.v1.Worker - 10, // 2: controller.api.services.v1.CreateWorkerRequest.item:type_name -> controller.api.resources.workers.v1.Worker - 10, // 3: controller.api.services.v1.CreateWorkerResponse.item:type_name -> controller.api.resources.workers.v1.Worker + 10, // 2: controller.api.services.v1.CreateWorkerLedRequest.item:type_name -> controller.api.resources.workers.v1.Worker + 10, // 3: controller.api.services.v1.CreateWorkerLedResponse.item:type_name -> controller.api.resources.workers.v1.Worker 10, // 4: controller.api.services.v1.UpdateWorkerRequest.item:type_name -> controller.api.resources.workers.v1.Worker 11, // 5: controller.api.services.v1.UpdateWorkerRequest.update_mask:type_name -> google.protobuf.FieldMask 10, // 6: controller.api.services.v1.UpdateWorkerResponse.item:type_name -> controller.api.resources.workers.v1.Worker 0, // 7: controller.api.services.v1.WorkerService.GetWorker:input_type -> controller.api.services.v1.GetWorkerRequest 2, // 8: controller.api.services.v1.WorkerService.ListWorkers:input_type -> controller.api.services.v1.ListWorkersRequest - 4, // 9: controller.api.services.v1.WorkerService.CreateWorker:input_type -> controller.api.services.v1.CreateWorkerRequest + 4, // 9: controller.api.services.v1.WorkerService.CreateWorkerLed:input_type -> controller.api.services.v1.CreateWorkerLedRequest 6, // 10: controller.api.services.v1.WorkerService.UpdateWorker:input_type -> controller.api.services.v1.UpdateWorkerRequest 8, // 11: controller.api.services.v1.WorkerService.DeleteWorker:input_type -> controller.api.services.v1.DeleteWorkerRequest 1, // 12: controller.api.services.v1.WorkerService.GetWorker:output_type -> controller.api.services.v1.GetWorkerResponse 3, // 13: controller.api.services.v1.WorkerService.ListWorkers:output_type -> controller.api.services.v1.ListWorkersResponse - 5, // 14: controller.api.services.v1.WorkerService.CreateWorker:output_type -> controller.api.services.v1.CreateWorkerResponse + 5, // 14: controller.api.services.v1.WorkerService.CreateWorkerLed:output_type -> controller.api.services.v1.CreateWorkerLedResponse 7, // 15: controller.api.services.v1.WorkerService.UpdateWorker:output_type -> controller.api.services.v1.UpdateWorkerResponse 9, // 16: controller.api.services.v1.WorkerService.DeleteWorker:output_type -> controller.api.services.v1.DeleteWorkerResponse 12, // [12:17] is the sub-list for method output_type @@ -763,7 +765,7 @@ func file_controller_api_services_v1_worker_service_proto_init() { } } file_controller_api_services_v1_worker_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateWorkerRequest); i { + switch v := v.(*CreateWorkerLedRequest); i { case 0: return &v.state case 1: @@ -775,7 +777,7 @@ func file_controller_api_services_v1_worker_service_proto_init() { } } file_controller_api_services_v1_worker_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateWorkerResponse); i { + switch v := v.(*CreateWorkerLedResponse); i { case 0: return &v.state case 1: diff --git a/internal/gen/controller/api/services/worker_service.pb.gw.go b/internal/gen/controller/api/services/worker_service.pb.gw.go index 7a5e1897a..a9bfb14f3 100644 --- a/internal/gen/controller/api/services/worker_service.pb.gw.go +++ b/internal/gen/controller/api/services/worker_service.pb.gw.go @@ -119,8 +119,8 @@ func local_request_WorkerService_ListWorkers_0(ctx context.Context, marshaler ru } -func request_WorkerService_CreateWorker_0(ctx context.Context, marshaler runtime.Marshaler, client WorkerServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq CreateWorkerRequest +func request_WorkerService_CreateWorkerLed_0(ctx context.Context, marshaler runtime.Marshaler, client WorkerServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateWorkerLedRequest var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) @@ -131,13 +131,13 @@ func request_WorkerService_CreateWorker_0(ctx context.Context, marshaler runtime return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.CreateWorker(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.CreateWorkerLed(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_WorkerService_CreateWorker_0(ctx context.Context, marshaler runtime.Marshaler, server WorkerServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq CreateWorkerRequest +func local_request_WorkerService_CreateWorkerLed_0(ctx context.Context, marshaler runtime.Marshaler, server WorkerServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateWorkerLedRequest var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) @@ -148,7 +148,7 @@ func local_request_WorkerService_CreateWorker_0(ctx context.Context, marshaler r return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.CreateWorker(ctx, &protoReq) + msg, err := server.CreateWorkerLed(ctx, &protoReq) return msg, metadata, err } @@ -359,19 +359,19 @@ func RegisterWorkerServiceHandlerServer(ctx context.Context, mux *runtime.ServeM }) - mux.Handle("POST", pattern_WorkerService_CreateWorker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_WorkerService_CreateWorkerLed_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error - ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/CreateWorker", runtime.WithHTTPPathPattern("/v1/workers")) + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/CreateWorkerLed", runtime.WithHTTPPathPattern("/v1/workers:create:worker-led")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_WorkerService_CreateWorker_0(ctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WorkerService_CreateWorkerLed_0(ctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { @@ -379,7 +379,7 @@ func RegisterWorkerServiceHandlerServer(ctx context.Context, mux *runtime.ServeM return } - forward_WorkerService_CreateWorker_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_CreateWorker_0{resp}, mux.GetForwardResponseOptions()...) + forward_WorkerService_CreateWorkerLed_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_CreateWorkerLed_0{resp}, mux.GetForwardResponseOptions()...) }) @@ -514,24 +514,24 @@ func RegisterWorkerServiceHandlerClient(ctx context.Context, mux *runtime.ServeM }) - mux.Handle("POST", pattern_WorkerService_CreateWorker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_WorkerService_CreateWorkerLed_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error - ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/CreateWorker", runtime.WithHTTPPathPattern("/v1/workers")) + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.WorkerService/CreateWorkerLed", runtime.WithHTTPPathPattern("/v1/workers:create:worker-led")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_WorkerService_CreateWorker_0(ctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_WorkerService_CreateWorkerLed_0(ctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_WorkerService_CreateWorker_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_CreateWorker_0{resp}, mux.GetForwardResponseOptions()...) + forward_WorkerService_CreateWorkerLed_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_CreateWorkerLed_0{resp}, mux.GetForwardResponseOptions()...) }) @@ -589,12 +589,12 @@ func (m response_WorkerService_GetWorker_0) XXX_ResponseBody() interface{} { return response.Item } -type response_WorkerService_CreateWorker_0 struct { +type response_WorkerService_CreateWorkerLed_0 struct { proto.Message } -func (m response_WorkerService_CreateWorker_0) XXX_ResponseBody() interface{} { - response := m.Message.(*CreateWorkerResponse) +func (m response_WorkerService_CreateWorkerLed_0) XXX_ResponseBody() interface{} { + response := m.Message.(*CreateWorkerLedResponse) return response.Item } @@ -612,7 +612,7 @@ var ( pattern_WorkerService_ListWorkers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "workers"}, "")) - pattern_WorkerService_CreateWorker_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "workers"}, "")) + pattern_WorkerService_CreateWorkerLed_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "workers:create"}, "worker-led")) pattern_WorkerService_UpdateWorker_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "workers", "id"}, "")) @@ -624,7 +624,7 @@ var ( forward_WorkerService_ListWorkers_0 = runtime.ForwardResponseMessage - forward_WorkerService_CreateWorker_0 = runtime.ForwardResponseMessage + forward_WorkerService_CreateWorkerLed_0 = runtime.ForwardResponseMessage forward_WorkerService_UpdateWorker_0 = runtime.ForwardResponseMessage diff --git a/internal/gen/controller/api/services/worker_service_grpc.pb.go b/internal/gen/controller/api/services/worker_service_grpc.pb.go index 6002ef699..ee1a181b2 100644 --- a/internal/gen/controller/api/services/worker_service_grpc.pb.go +++ b/internal/gen/controller/api/services/worker_service_grpc.pb.go @@ -28,12 +28,12 @@ type WorkerServiceClient interface { // If the scope ID is missing, malformed, or reference a non existing scope, // an error is returned. ListWorkers(ctx context.Context, in *ListWorkersRequest, opts ...grpc.CallOption) (*ListWorkersResponse, error) - // CreateWorker creates and stores a Worker in boundary. The provided + // CreateWorkerLed creates and stores a Worker in boundary. The provided // request must include the Scope id in which the Worker will be created. // If the Scope id is missing, malformed or references a non existing // resource, an error is returned. If a name is provided that is in // use in another Worker in the same scope, an error is returned. - CreateWorker(ctx context.Context, in *CreateWorkerRequest, opts ...grpc.CallOption) (*CreateWorkerResponse, error) + CreateWorkerLed(ctx context.Context, in *CreateWorkerLedRequest, opts ...grpc.CallOption) (*CreateWorkerLedResponse, error) // UpdateWorker updates an existing Worker in boundary. The provided // Worker must not have any read only fields set. The update mask must be // included in the request and contain at least 1 mutable field. To unset @@ -74,9 +74,9 @@ func (c *workerServiceClient) ListWorkers(ctx context.Context, in *ListWorkersRe return out, nil } -func (c *workerServiceClient) CreateWorker(ctx context.Context, in *CreateWorkerRequest, opts ...grpc.CallOption) (*CreateWorkerResponse, error) { - out := new(CreateWorkerResponse) - err := c.cc.Invoke(ctx, "/controller.api.services.v1.WorkerService/CreateWorker", in, out, opts...) +func (c *workerServiceClient) CreateWorkerLed(ctx context.Context, in *CreateWorkerLedRequest, opts ...grpc.CallOption) (*CreateWorkerLedResponse, error) { + out := new(CreateWorkerLedResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.WorkerService/CreateWorkerLed", in, out, opts...) if err != nil { return nil, err } @@ -115,12 +115,12 @@ type WorkerServiceServer interface { // If the scope ID is missing, malformed, or reference a non existing scope, // an error is returned. ListWorkers(context.Context, *ListWorkersRequest) (*ListWorkersResponse, error) - // CreateWorker creates and stores a Worker in boundary. The provided + // CreateWorkerLed creates and stores a Worker in boundary. The provided // request must include the Scope id in which the Worker will be created. // If the Scope id is missing, malformed or references a non existing // resource, an error is returned. If a name is provided that is in // use in another Worker in the same scope, an error is returned. - CreateWorker(context.Context, *CreateWorkerRequest) (*CreateWorkerResponse, error) + CreateWorkerLed(context.Context, *CreateWorkerLedRequest) (*CreateWorkerLedResponse, error) // UpdateWorker updates an existing Worker in boundary. The provided // Worker must not have any read only fields set. The update mask must be // included in the request and contain at least 1 mutable field. To unset @@ -146,8 +146,8 @@ func (UnimplementedWorkerServiceServer) GetWorker(context.Context, *GetWorkerReq func (UnimplementedWorkerServiceServer) ListWorkers(context.Context, *ListWorkersRequest) (*ListWorkersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListWorkers not implemented") } -func (UnimplementedWorkerServiceServer) CreateWorker(context.Context, *CreateWorkerRequest) (*CreateWorkerResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateWorker not implemented") +func (UnimplementedWorkerServiceServer) CreateWorkerLed(context.Context, *CreateWorkerLedRequest) (*CreateWorkerLedResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateWorkerLed not implemented") } func (UnimplementedWorkerServiceServer) UpdateWorker(context.Context, *UpdateWorkerRequest) (*UpdateWorkerResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateWorker not implemented") @@ -204,20 +204,20 @@ func _WorkerService_ListWorkers_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } -func _WorkerService_CreateWorker_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateWorkerRequest) +func _WorkerService_CreateWorkerLed_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateWorkerLedRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(WorkerServiceServer).CreateWorker(ctx, in) + return srv.(WorkerServiceServer).CreateWorkerLed(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/controller.api.services.v1.WorkerService/CreateWorker", + FullMethod: "/controller.api.services.v1.WorkerService/CreateWorkerLed", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(WorkerServiceServer).CreateWorker(ctx, req.(*CreateWorkerRequest)) + return srv.(WorkerServiceServer).CreateWorkerLed(ctx, req.(*CreateWorkerLedRequest)) } return interceptor(ctx, in, info, handler) } @@ -274,8 +274,8 @@ var WorkerService_ServiceDesc = grpc.ServiceDesc{ Handler: _WorkerService_ListWorkers_Handler, }, { - MethodName: "CreateWorker", - Handler: _WorkerService_CreateWorker_Handler, + MethodName: "CreateWorkerLed", + Handler: _WorkerService_CreateWorkerLed_Handler, }, { MethodName: "UpdateWorker", diff --git a/internal/perms/acl_test.go b/internal/perms/acl_test.go index d32845112..194bb5904 100644 --- a/internal/perms/acl_test.go +++ b/internal/perms/acl_test.go @@ -328,7 +328,7 @@ func Test_ACLAllowed(t *testing.T) { }, }, actionsAuthorized: []actionAuthorized{ - {action: action.CreateWorkerRequest, authorized: true}, + {action: action.CreateWorkerLed, authorized: true}, }, }, { @@ -338,12 +338,12 @@ func Test_ACLAllowed(t *testing.T) { { scope: scope.Global.String(), grants: []string{ - "type=worker;actions=create:worker-request", + "type=worker;actions=create:worker-led", }, }, }, actionsAuthorized: []actionAuthorized{ - {action: action.CreateWorkerRequest, authorized: true}, + {action: action.CreateWorkerLed, authorized: true}, }, }, } diff --git a/internal/proto/controller/api/resources/workers/v1/worker.proto b/internal/proto/controller/api/resources/workers/v1/worker.proto index 68a81b9da..a7c16253e 100644 --- a/internal/proto/controller/api/resources/workers/v1/worker.proto +++ b/internal/proto/controller/api/resources/workers/v1/worker.proto @@ -90,7 +90,11 @@ message Worker { // itself. WorkerConfig worker_config = 140 [json_name = "worker_config"]; - // TODO: Add worker auth related fields. + // worker_auth_token is input only. Supports the worker led node + // enrollment flow where this credentials token is produced by a worker. This + // token is a base58 encoded types.FetchNodeCredentialsRequest from + // https://github.com/hashicorp/nodeenrollment + google.protobuf.StringValue worker_auth_token = 150 [json_name = "worker_auth_token"]; // @gotags: `class:"public"` // Output only. The available actions on this resource for the requester. repeated string authorized_actions = 300 [json_name = "authorized_actions"]; // @gotags: `class:"public"` diff --git a/internal/proto/controller/api/services/v1/worker_service.proto b/internal/proto/controller/api/services/v1/worker_service.proto index fea2c3d9a..f1aac2936 100644 --- a/internal/proto/controller/api/services/v1/worker_service.proto +++ b/internal/proto/controller/api/services/v1/worker_service.proto @@ -37,14 +37,14 @@ service WorkerService { }; } - // CreateWorker creates and stores a Worker in boundary. The provided + // CreateWorkerLed creates and stores a Worker in boundary. The provided // request must include the Scope id in which the Worker will be created. // If the Scope id is missing, malformed or references a non existing // resource, an error is returned. If a name is provided that is in // use in another Worker in the same scope, an error is returned. - rpc CreateWorker(CreateWorkerRequest) returns (CreateWorkerResponse) { + rpc CreateWorkerLed(CreateWorkerLedRequest) returns (CreateWorkerLedResponse) { option (google.api.http) = { - post: "/v1/workers" + post: "/v1/workers:create:worker-led" body: "item" response_body: "item" }; @@ -102,11 +102,11 @@ message ListWorkersResponse { repeated resources.workers.v1.Worker items = 1; } -message CreateWorkerRequest { +message CreateWorkerLedRequest { resources.workers.v1.Worker item = 1; } -message CreateWorkerResponse { +message CreateWorkerLedResponse { string uri = 1; // @gotags: `class:"public"` resources.workers.v1.Worker item = 2; } diff --git a/internal/types/action/action.go b/internal/types/action/action.go index dec8cfc43..010f3a699 100644 --- a/internal/types/action/action.go +++ b/internal/types/action/action.go @@ -53,7 +53,7 @@ const ( AddHostSources Type = 42 SetHostSources Type = 43 RemoveHostSources Type = 44 - CreateWorkerRequest Type = 45 + CreateWorkerLed Type = 45 ) var Map = map[string]Type{ @@ -101,7 +101,7 @@ var Map = map[string]Type{ AddHostSources.String(): AddHostSources, SetHostSources.String(): SetHostSources, RemoveHostSources.String(): RemoveHostSources, - CreateWorkerRequest.String(): CreateWorkerRequest, + CreateWorkerLed.String(): CreateWorkerLed, } func (a Type) String() string { @@ -151,7 +151,7 @@ func (a Type) String() string { "add-host-sources", "set-host-sources", "remove-host-sources", - "create:worker-request", + "create:worker-led", }[a] } diff --git a/internal/types/action/action_test.go b/internal/types/action/action_test.go index c5086248e..e644d3976 100644 --- a/internal/types/action/action_test.go +++ b/internal/types/action/action_test.go @@ -96,8 +96,8 @@ func TestAction(t *testing.T) { want: "no-op", }, { - action: CreateWorkerRequest, - want: "create:worker-request", + action: CreateWorkerLed, + want: "create:worker-led", }, } for _, tt := range tests { diff --git a/sdk/pbs/controller/api/resources/workers/worker.pb.go b/sdk/pbs/controller/api/resources/workers/worker.pb.go index b11b1a93c..751e63812 100644 --- a/sdk/pbs/controller/api/resources/workers/worker.pb.go +++ b/sdk/pbs/controller/api/resources/workers/worker.pb.go @@ -132,6 +132,11 @@ type Worker struct { // Output only. The set of information the worker daemon has reported about // itself. WorkerConfig *WorkerConfig `protobuf:"bytes,140,opt,name=worker_config,proto3" json:"worker_config,omitempty"` + // worker_auth_token is input only. Supports the worker led node + // enrollment flow where this credentials token is produced by a worker. This + // token is a base58 encoded types.FetchNodeCredentialsRequest from + // https://github.com/hashicorp/nodeenrollment + WorkerAuthToken *wrapperspb.StringValue `protobuf:"bytes,150,opt,name=worker_auth_token,proto3" json:"worker_auth_token,omitempty"` // @gotags: `class:"public"` // Output only. The available actions on this resource for the requester. AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty"` // @gotags: `class:"public"` } @@ -266,6 +271,13 @@ func (x *Worker) GetWorkerConfig() *WorkerConfig { return nil } +func (x *Worker) GetWorkerAuthToken() *wrapperspb.StringValue { + if x != nil { + return x.WorkerAuthToken + } + return nil +} + func (x *Worker) GetAuthorizedActions() []string { if x != nil { return x.AuthorizedActions @@ -307,7 +319,7 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf9, 0x08, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc6, 0x09, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, @@ -365,26 +377,31 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, - 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, - 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4b, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x96, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2f, 0x0a, 0x12, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, + 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, + 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, + 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -423,14 +440,15 @@ var file_controller_api_resources_workers_v1_worker_proto_depIdxs = []int32{ 4, // 8: controller.api.resources.workers.v1.Worker.canonical_tags:type_name -> controller.api.resources.workers.v1.Worker.CanonicalTagsEntry 7, // 9: controller.api.resources.workers.v1.Worker.last_status_time:type_name -> google.protobuf.Timestamp 0, // 10: controller.api.resources.workers.v1.Worker.worker_config:type_name -> controller.api.resources.workers.v1.WorkerConfig - 8, // 11: controller.api.resources.workers.v1.WorkerConfig.TagsEntry.value:type_name -> google.protobuf.ListValue - 8, // 12: controller.api.resources.workers.v1.Worker.TagsEntry.value:type_name -> google.protobuf.ListValue - 8, // 13: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry.value:type_name -> google.protobuf.ListValue - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 6, // 11: controller.api.resources.workers.v1.Worker.worker_auth_token:type_name -> google.protobuf.StringValue + 8, // 12: controller.api.resources.workers.v1.WorkerConfig.TagsEntry.value:type_name -> google.protobuf.ListValue + 8, // 13: controller.api.resources.workers.v1.Worker.TagsEntry.value:type_name -> google.protobuf.ListValue + 8, // 14: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry.value:type_name -> google.protobuf.ListValue + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_controller_api_resources_workers_v1_worker_proto_init() } From 1e3c941be11086e4fa3cbba3e90cb160377103b3 Mon Sep 17 00:00:00 2001 From: Todd Date: Tue, 7 Jun 2022 11:21:39 -0700 Subject: [PATCH 059/100] Add active session count to the worker (#2145) --- globals/fields.go | 1 + .../handlers/workers/worker_service.go | 3 + .../99/02_worker_controller_tables.up.sql | 55 --------------- .../oss/postgres/99/04_views.up.sql | 69 +++++++++++++++++++ internal/gen/controller.swagger.json | 6 ++ .../api/resources/workers/v1/worker.proto | 3 + internal/servers/repository_worker_test.go | 35 ++++++++++ internal/servers/worker.go | 17 ++++- .../api/resources/workers/worker.pb.go | 57 +++++++++------ 9 files changed, 167 insertions(+), 79 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/99/04_views.up.sql diff --git a/globals/fields.go b/globals/fields.go index 4fd02ee4d..491b00c8d 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -81,4 +81,5 @@ const ( ConfigurationField = "configuration" WorkerAuthTokenField = "worker_auth_token" WorkerConfigField = "worker_config" + ActiveConnectionCountField = "active_session_count" ) diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index bb82d654b..98f054bab 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -462,6 +462,9 @@ func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (* if outputFields.Has(globals.LastStatusTimeField) { out.LastStatusTime = in.GetLastStatusTime().GetTimestamp() } + if outputFields.Has(globals.ActiveConnectionCountField) { + out.ActiveConnectionCount = in.ActiveConnectionCount() + } if outputFields.Has(globals.CanonicalTagsField) && len(in.CanonicalTags()) > 0 { var err error out.CanonicalTags, err = tagsToMapProto(in.CanonicalTags()) diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index a9a70096d..1e8088baa 100644 --- a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -150,41 +150,6 @@ create table server_worker_tag ( primary key(worker_id, key, value, source) ); --- worker_aggregate view allows the worker and configuration to be read at the --- same time. -create view server_worker_aggregate as - with worker_config_tags(worker_id, source, tags) as ( - select - ct.worker_id, - ct.source, - -- keys and tags can be any lowercase printable character so use uppercase characters as delimitors. - string_agg(distinct concat_ws('Y', ct.key, ct.value), 'Z') as tags - from server_worker_tag ct - group by ct.worker_id, ct.source - ) -select - w.public_id, - w.scope_id, - w.description, - w.name, - w.address, - w.create_time, - w.update_time, - w.version, - w.worker_reported_name, - w.worker_reported_address, - w.last_status_time, - -- keys and tags can be any lowercase printable character so use uppercase characters as delimitors. - wt.tags as api_tags, - ct.tags as worker_config_tags -from server_worker w - left join worker_config_tags wt on - w.public_id = wt.worker_id and wt.source = 'api' - left join worker_config_tags ct on - w.public_id = ct.worker_id and ct.source = 'configuration'; -comment on view server_worker_aggregate is -'server_worker_aggregate contains the worker resource with its worker provided config values and its configuration and api provided tags.'; - -- Aaand drop server_tag drop table server_tag; @@ -245,26 +210,6 @@ set where status = 'running'; - --- Replaces the view created in 9/01. --- Remove the worker id from this view. In actuality this is almost a no-op --- because no server information was ever getting populated here due to a bug --- in the update mask when updating a session at the time we activate a session. -create view session_list as - select - s.public_id, s.user_id, s.host_id, s.target_id, - s.host_set_id, s.auth_token_id, s.scope_id, s.certificate,s.expiration_time, - s.connection_limit, s.tofu_token, s.key_id, s.termination_reason, s.version, - s.create_time, s.update_time, s.endpoint, s.worker_filter, - ss.state, ss.previous_end_time, ss.start_time, ss.end_time, sc.public_id as connection_id, - sc.client_tcp_address, sc.client_tcp_port, sc.endpoint_tcp_address, sc.endpoint_tcp_port, - sc.bytes_up, sc.bytes_down, sc.closed_reason - from session s - join session_state ss on - s.public_id = ss.session_id - left join session_connection sc on - s.public_id = sc.session_id; - -- Drop the server and server_type_enm tables drop table server; drop table server_type_enm; diff --git a/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql b/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql new file mode 100644 index 000000000..8ac26e22b --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql @@ -0,0 +1,69 @@ +begin; + +-- worker_aggregate view allows the worker and configuration to be read at the +-- same time. +create view server_worker_aggregate as +with worker_config_tags(worker_id, source, tags) as ( + select + ct.worker_id, + ct.source, + -- keys and tags can be any lowercase printable character so use uppercase characters as delimitors. + string_agg(distinct concat_ws('Y', ct.key, ct.value), 'Z') as tags + from server_worker_tag ct + group by ct.worker_id, ct.source +), + connection_count (worker_id, count) as ( + select + worker_id, + count(1) as count + from session_connection + where closed_reason is null + group by worker_id + ) +select + w.public_id, + w.scope_id, + w.description, + w.name, + w.address, + w.create_time, + w.update_time, + w.version, + w.worker_reported_name, + w.worker_reported_address, + w.last_status_time, + cc.count as active_connection_count, + -- keys and tags can be any lowercase printable character so use uppercase characters as delimitors. + wt.tags as api_tags, + ct.tags as worker_config_tags +from server_worker w + left join worker_config_tags wt on + w.public_id = wt.worker_id and wt.source = 'api' + left join worker_config_tags ct on + w.public_id = ct.worker_id and ct.source = 'configuration' + left join connection_count as cc on + w.public_id = cc.worker_id; +comment on view server_worker_aggregate is + 'server_worker_aggregate contains the worker resource with its worker provided config values and its configuration and api provided tags.'; + + +-- Replaces the view created in 9/01. +-- Remove the worker id from this view. In actuality this is almost a no-op +-- because no server information was ever getting populated here due to a bug +-- in the update mask when updating a session at the time we activate a session. +create view session_list as +select + s.public_id, s.user_id, s.host_id, s.target_id, + s.host_set_id, s.auth_token_id, s.scope_id, s.certificate,s.expiration_time, + s.connection_limit, s.tofu_token, s.key_id, s.termination_reason, s.version, + s.create_time, s.update_time, s.endpoint, s.worker_filter, + ss.state, ss.previous_end_time, ss.start_time, ss.end_time, sc.public_id as connection_id, + sc.client_tcp_address, sc.client_tcp_port, sc.endpoint_tcp_address, sc.endpoint_tcp_port, + sc.bytes_up, sc.bytes_down, sc.closed_reason +from session s + join session_state ss on + s.public_id = ss.session_id + left join session_connection sc on + s.public_id = sc.session_id; + +commit; \ No newline at end of file diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index c86653d6e..750f1d0e9 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -5586,6 +5586,12 @@ "type": "string", "title": "worker_auth_token is input only. Supports the worker led node\nenrollment flow where this credentials token is produced by a worker. This\ntoken is a base58 encoded types.FetchNodeCredentialsRequest from\nhttps://github.com/hashicorp/nodeenrollment" }, + "active_connection_count": { + "type": "integer", + "format": "int64", + "description": "Output only. The number of connections that this worker is currently handling.", + "readOnly": true + }, "authorized_actions": { "type": "array", "items": { diff --git a/internal/proto/controller/api/resources/workers/v1/worker.proto b/internal/proto/controller/api/resources/workers/v1/worker.proto index a7c16253e..ce4cb28dd 100644 --- a/internal/proto/controller/api/resources/workers/v1/worker.proto +++ b/internal/proto/controller/api/resources/workers/v1/worker.proto @@ -96,6 +96,9 @@ message Worker { // https://github.com/hashicorp/nodeenrollment google.protobuf.StringValue worker_auth_token = 150 [json_name = "worker_auth_token"]; // @gotags: `class:"public"` + // Output only. The number of connections that this worker is currently handling. + uint32 active_connection_count = 160 [json_name = "active_connection_count"]; // @gotags: `class:"public"` + // Output only. The available actions on this resource for the requester. repeated string authorized_actions = 300 [json_name = "authorized_actions"]; // @gotags: `class:"public"` } diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 732aebc3f..be0807d12 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-dbw" "github.com/hashicorp/go-kms-wrapping/extras/kms/v2/migrations" @@ -25,6 +26,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/timestamppb" ) func TestDeleteWorker(t *testing.T) { @@ -162,10 +164,43 @@ func TestLookupWorker(t *testing.T) { servers.WithPublicId(w.GetPublicId())) require.NoError(t, err) + sessRepo, err := session.NewRepository(rw, rw, kms) + require.NoError(t, err) + connRepo, err := session.NewConnectionRepository(ctx, rw, rw, kms, session.WithWorkerStateDelay(0)) + require.NoError(t, err) + // 1 session with 2 connections + { + composedOf := session.TestSessionParams(t, conn, wrapper, iam.TestRepo(t, conn, wrapper)) + future := timestamppb.New(time.Now().Add(time.Hour)) + exp := ×tamp.Timestamp{Timestamp: future} + composedOf.ConnectionLimit = -1 + sess := session.TestSession(t, conn, wrapper, composedOf, session.WithDbOpts(db.WithSkipVetForWrite(true)), session.WithExpirationTime(exp)) + sess, _, err = sessRepo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, []byte("foo")) + require.NoError(t, err) + c, _, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), w.GetPublicId()) + require.NoError(t, err) + require.NotNil(t, c) + c, _, err = connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), w.GetPublicId()) + require.NoError(t, err) + require.NotNil(t, c) + } + + // 1 session with 1 connection + { + sess2 := session.TestDefaultSession(t, conn, wrapper, iam.TestRepo(t, conn, wrapper), + session.WithDbOpts(db.WithSkipVetForWrite(true))) + sess2, _, err = sessRepo.ActivateSession(ctx, sess2.GetPublicId(), sess2.Version, []byte("foo")) + require.NoError(t, err) + c, _, err := connRepo.AuthorizeConnection(ctx, sess2.GetPublicId(), w.GetPublicId()) + require.NoError(t, err) + require.NotNil(t, c) + } + t.Run("success", func(t *testing.T) { got, err := repo.LookupWorker(ctx, w.GetPublicId()) require.NoError(t, err) assert.Empty(t, cmp.Diff(w, got, protocmp.Transform())) + assert.Equal(t, uint32(3), got.ActiveConnectionCount()) assert.Equal(t, map[string][]string{"key": {"val"}}, got.GetApiTags()) assert.Equal(t, map[string][]string{"config": {"test"}}, got.GetConfigTags()) assert.Equal(t, map[string][]string{ diff --git a/internal/servers/worker.go b/internal/servers/worker.go index 4d7beabfd..895c73e6a 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -36,8 +36,9 @@ func AttachWorkerIdToState(ctx context.Context, workerId string) (*structpb.Stru type Worker struct { *store.Worker - apiTags []*Tag `gorm:"-"` - configTags []*Tag `gorm:"-"` + activeConnectionCount uint32 `gorm:"-"` + apiTags []*Tag `gorm:"-"` + configTags []*Tag `gorm:"-"` } // NewWorker returns a new Worker. Valid options are WithName, WithDescription @@ -112,6 +113,12 @@ func (w *Worker) CanonicalAddress() string { return w.GetWorkerReportedAddress() } +// ActiveConnectionCount is the current number of sessions this worker is handling +// according to the controllers. +func (w *Worker) ActiveConnectionCount() uint32 { + return w.activeConnectionCount +} + // 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. func (w *Worker) CanonicalTags() map[string][]string { @@ -129,6 +136,7 @@ func (w *Worker) CanonicalTags() map[string][]string { return tags } +// GetApiTags returns the tags for this worker which has been set by the api. func (w *Worker) GetApiTags() map[string][]string { tags := make(map[string][]string) for _, t := range w.apiTags { @@ -137,6 +145,8 @@ func (w *Worker) GetApiTags() map[string][]string { return tags } +// 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 { @@ -172,6 +182,8 @@ type workerAggregate struct { Address string Version uint32 ApiTags string + ActiveConnectionCount uint32 + // Config Fields WorkerReportedName string WorkerReportedAddress string LastStatusTime *timestamp.Timestamp @@ -194,6 +206,7 @@ func (a *workerAggregate) toWorker(ctx context.Context) (*Worker, error) { WorkerReportedName: a.WorkerReportedName, LastStatusTime: a.LastStatusTime, }, + activeConnectionCount: a.ActiveConnectionCount, } tags, err := tagsFromAggregatedTagString(ctx, a.ApiTags) if err != nil { diff --git a/sdk/pbs/controller/api/resources/workers/worker.pb.go b/sdk/pbs/controller/api/resources/workers/worker.pb.go index 751e63812..570705428 100644 --- a/sdk/pbs/controller/api/resources/workers/worker.pb.go +++ b/sdk/pbs/controller/api/resources/workers/worker.pb.go @@ -137,6 +137,8 @@ type Worker struct { // token is a base58 encoded types.FetchNodeCredentialsRequest from // https://github.com/hashicorp/nodeenrollment WorkerAuthToken *wrapperspb.StringValue `protobuf:"bytes,150,opt,name=worker_auth_token,proto3" json:"worker_auth_token,omitempty"` // @gotags: `class:"public"` + // Output only. The number of connections that this worker is currently handling. + ActiveConnectionCount uint32 `protobuf:"varint,160,opt,name=active_connection_count,proto3" json:"active_connection_count,omitempty"` // @gotags: `class:"public"` // Output only. The available actions on this resource for the requester. AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty"` // @gotags: `class:"public"` } @@ -278,6 +280,13 @@ func (x *Worker) GetWorkerAuthToken() *wrapperspb.StringValue { return nil } +func (x *Worker) GetActiveConnectionCount() uint32 { + if x != nil { + return x.ActiveConnectionCount + } + return 0 +} + func (x *Worker) GetAuthorizedActions() []string { if x != nil { return x.AuthorizedActions @@ -319,7 +328,7 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc6, 0x09, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x81, 0x0a, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, @@ -381,27 +390,31 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x96, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2f, 0x0a, 0x12, - 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, - 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, - 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, - 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, - 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x39, 0x0a, 0x17, + 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xa0, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x17, + 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, + 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5c, 0x0a, + 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x50, 0x5a, 0x4e, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, + 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From d1b3b2441f9811ab5c5c907ccac939c61f974349 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 7 Jun 2022 14:26:33 -0400 Subject: [PATCH 060/100] Add node rotation (#2142) --- go.mod | 2 +- go.sum | 4 +- internal/cmd/commands/dev/dev.go | 11 + .../cluster/handlers/multihop_service.go | 31 +++ internal/daemon/worker/auth_rotation.go | 196 ++++++++++++++++++ internal/daemon/worker/auth_rotation_test.go | 115 ++++++++++ .../daemon/worker/controller_connection.go | 7 +- internal/daemon/worker/testing.go | 5 + internal/daemon/worker/worker.go | 81 +++++++- internal/servers/repository_workerauth.go | 8 + .../servers/repository_workerauth_test.go | 1 + 11 files changed, 451 insertions(+), 10 deletions(-) create mode 100644 internal/daemon/worker/auth_rotation.go create mode 100644 internal/daemon/worker/auth_rotation_test.go diff --git a/go.mod b/go.mod index 955176dfc..eed02b301 100644 --- a/go.mod +++ b/go.mod @@ -92,7 +92,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b - github.com/hashicorp/nodeenrollment v0.0.0-20220605235506-351328414303 + github.com/hashicorp/nodeenrollment v0.0.0-20220607152131-4f253e6da747 ) require ( diff --git a/go.sum b/go.sum index 62345953f..31a89704a 100644 --- a/go.sum +++ b/go.sum @@ -740,8 +740,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220605235506-351328414303 h1:dWteo2hZzqmF7IMqIozEzLLx3J0GXGKhKU32hPERp4g= -github.com/hashicorp/nodeenrollment v0.0.0-20220605235506-351328414303/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.0.0-20220607152131-4f253e6da747 h1:/S1ePrvxgLI3jgL6XLVVQAOm3Ua0wS4PvRhmiTgG6hQ= +github.com/hashicorp/nodeenrollment v0.0.0-20220607152131-4f253e6da747/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 63678737b..4dbccaf9d 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -88,6 +88,7 @@ type Command struct { flagUseKmsWorkerAuthMethod bool flagWorkerAuthStorageDir string flagWorkerAuthStorageSkipCleanup bool + flagWorkerAuthRotationInterval time.Duration } func (c *Command) Synopsis() string { @@ -355,6 +356,12 @@ func (c *Command) Flags() *base.FlagSets { Hidden: true, }) + f.DurationVar(&base.DurationVar{ + Name: "worker-auth-rotation-interval", + Target: &c.flagWorkerAuthRotationInterval, + Hidden: true, + }) + return set } @@ -677,6 +684,10 @@ func (c *Command) Run(args []string) int { return base.CommandCliError } + if c.flagWorkerAuthRotationInterval > 0 { + c.worker.TestOverrideAuthRotationPeriod = c.flagWorkerAuthRotationInterval + } + if err := c.worker.Start(); err != nil { retErr := fmt.Errorf("Error starting worker: %w", err) if err := c.worker.Shutdown(); err != nil { diff --git a/internal/daemon/cluster/handlers/multihop_service.go b/internal/daemon/cluster/handlers/multihop_service.go index 2fe742f18..a5407b4d0 100644 --- a/internal/daemon/cluster/handlers/multihop_service.go +++ b/internal/daemon/cluster/handlers/multihop_service.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/nodeenrollment" "github.com/hashicorp/nodeenrollment/multihop" "github.com/hashicorp/nodeenrollment/registration" + "github.com/hashicorp/nodeenrollment/rotation" "github.com/hashicorp/nodeenrollment/tls" "github.com/hashicorp/nodeenrollment/types" "github.com/hashicorp/nodeenrollment/util/temperror" @@ -23,6 +24,8 @@ type multihopServiceServer struct { options []nodeenrollment.Option } +// NewMultihopServiceServer creates a new service implementing +// MultihopServiceServer, storing values used for the implementing functions. func NewMultihopServiceServer(storage nodeenrollment.Storage, direct bool, client *atomic.Value, opt ...nodeenrollment.Option) (*multihopServiceServer, error) { const op = "cluster.handlers.NewMultihopServiceServer" @@ -43,6 +46,9 @@ func NewMultihopServiceServer(storage nodeenrollment.Storage, direct bool, clien var _ multihop.MultihopServiceServer = (*multihopServiceServer)(nil) +// FetchNodeCredentials implements the MultihopServiceServer interface. If it's +// direct (e.g. running on a controller) it handles the request directly, +// otherwise sends it to its next hop. func (m *multihopServiceServer) FetchNodeCredentials(ctx context.Context, req *types.FetchNodeCredentialsRequest) (*types.FetchNodeCredentialsResponse, error) { const op = "cluster.handlers.(multihopServiceServer).FetchNodeCredentials" switch m.direct { @@ -62,6 +68,9 @@ func (m *multihopServiceServer) FetchNodeCredentials(ctx context.Context, req *t } } +// GenerateServerCertificates implements the MultihopServiceServer interface. If +// it's direct (e.g. running on a controller) it handles the request directly, +// otherwise sends it to its next hop. func (m *multihopServiceServer) GenerateServerCertificates(ctx context.Context, req *types.GenerateServerCertificatesRequest) (*types.GenerateServerCertificatesResponse, error) { const op = "cluster.handlers.(multihopServiceServer).GenerateServerCertificates" switch m.direct { @@ -80,3 +89,25 @@ func (m *multihopServiceServer) GenerateServerCertificates(ctx context.Context, return multihopClient.GenerateServerCertificates(ctx, req) } } + +// RotateNodeCredentials implements the MultihopServiceServer interface. If it's +// direct (e.g. running on a controller) it handles the request directly, +// otherwise sends it to its next hop. +func (m *multihopServiceServer) RotateNodeCredentials(ctx context.Context, req *types.RotateNodeCredentialsRequest) (*types.RotateNodeCredentialsResponse, error) { + const op = "cluster.handlers.(multihopServiceServer).RotateNodeCredentials" + switch m.direct { + case true: + return rotation.RotateNodeCredentials(ctx, m.storage, req, m.options...) + + default: + client := m.client.Load() + if client == nil { + return nil, fmt.Errorf("%s: error fetching multihop connection, client is nil", op) + } + multihopClient, ok := client.(multihop.MultihopServiceClient) + if !ok { + return nil, temperror.New(errors.New("client could not be understood as a multihop service client")) + } + return multihopClient.RotateNodeCredentials(ctx, req) + } +} diff --git a/internal/daemon/worker/auth_rotation.go b/internal/daemon/worker/auth_rotation.go new file mode 100644 index 000000000..ae3b7c28e --- /dev/null +++ b/internal/daemon/worker/auth_rotation.go @@ -0,0 +1,196 @@ +package worker + +import ( + "context" + "errors" + "time" + + berrors "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/observability/event" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/multihop" + "github.com/hashicorp/nodeenrollment/types" +) + +func (w *Worker) startAuthRotationTicking(cancelCtx context.Context) { + const op = "worker.(Worker).startAuthRotationTicking" + if w.conf.WorkerAuthKms != nil { + // Nothing to do here, just immediately exit + // FIXME: Write a system event + return + } + + // The default time to use when we encounter an error or some other reason + // we can't get a better reset time + const defaultResetDuration = time.Hour + timer := time.NewTimer(defaultResetDuration) + // You're not supposed to call reset on timers that haven't been stopped or + // expired, so we stop it here so it resets in the loop below. That way if + // we want to adjust time, e.g. for tests, we can set the resetDuration + // value from within the loop + timer.Stop() + var resetDuration time.Duration // will start at 0, so we run immediately + for { + if w.TestOverrideAuthRotationPeriod != 0 { + resetDuration = w.TestOverrideAuthRotationPeriod + } + timer.Reset(resetDuration) + + select { + case <-cancelCtx.Done(): + event.WriteSysEvent(cancelCtx, op, "auth rotation ticking shutting down") + return + + case <-timer.C: + resetDuration = defaultResetDuration + + // Check if it's time to rotate and if not don't do anything + currentNodeCreds, err := types.LoadNodeCredentials(cancelCtx, w.WorkerAuthStorage, nodeenrollment.CurrentId, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) + if err != nil { + if errors.Is(err, nodeenrollment.ErrNotFound) { + // Be silent + continue + } + event.WriteError(cancelCtx, op, err) + continue + } + + if currentNodeCreds == nil { + event.WriteSysEvent(cancelCtx, op, "no error loading worker auth creds but nil creds, skipping rotation") + continue + } + + // Check the certificates to see if it's time + if len(currentNodeCreds.CertificateBundles) != 2 { + // Likely this means we haven't been authorized yet, and + // unclear what to do in any other situation + continue + } + + var earliestValid, latestValid time.Time + // Go through the bundles and find the full range of time that we + // have valid certificates + for _, bundle := range currentNodeCreds.CertificateBundles { + if curr := bundle.CertificateNotBefore.AsTime(); earliestValid.IsZero() || curr.Before(earliestValid) { + earliestValid = curr + } + if curr := bundle.CertificateNotAfter.AsTime(); latestValid.IsZero() || curr.After(latestValid) { + latestValid = curr + } + } + // Ensure that our time periods aren't somehow quite messed up + now := time.Now() + if earliestValid.After(now) || latestValid.Before(now) || earliestValid.After(latestValid) { + // We basically have no valid creds so we can't rotate. + // TODO (maybe): Have this trigger a new set of creds and request? + event.WriteSysEvent(cancelCtx, op, "not within node creds validity period, unsure what to do, not rotating") + continue + } + + // Figure out the midpoint; if we're after it, try to rotate + var shouldRotate bool + switch { + case w.TestOverrideAuthRotationPeriod != 0: + shouldRotate = true + default: + delta := latestValid.Sub(earliestValid) + shouldRotate = now.Before(earliestValid.Add(delta / 2)) + } + + if !shouldRotate { + continue + } + + if err := rotateWorkerAuth(cancelCtx, w, currentNodeCreds); err != nil { + event.WriteError(cancelCtx, op, err) + continue + } + + // TODO (maybe): Calculate new delta and set a custom retry time on the timer? + } + } +} + +// rotateWorkerAuth is a one-stop shop to perform a rotation, given a valid set +// of current node credentials. Note that it doesn't really sanity check inputs; +// it is meant to be called from other functions that have validated these +// inputs and is separated out purely for (a) cleanliness/readability of the +// ticking function and (b) possible future re-use. +func rotateWorkerAuth(ctx context.Context, w *Worker, currentNodeCreds *types.NodeCredentials) error { + const op = "worker.rotateWorkerAuth" + + randReaderOpt := nodeenrollment.WithRandomReader(w.conf.SecureRandomReader) + + // Ensure we can get some needed values prior to actually doing generation + currentKeyId, err := nodeenrollment.KeyIdFromPkix(currentNodeCreds.CertificatePublicKeyPkix) + if err != nil { + return berrors.Wrap(ctx, err, op) + } + client := w.controllerMultihopConn.Load() + if client == nil { + return berrors.Wrap(ctx, err, op) + } + multihopClient, ok := client.(multihop.MultihopServiceClient) + if !ok { + return berrors.Wrap(ctx, err, op) + } + + // Generate a new set of credentials but don't persist them yet + newNodeCreds, err := types.NewNodeCredentials( + ctx, + w.WorkerAuthStorage, + randReaderOpt, + nodeenrollment.WithWrapper(w.conf.WorkerAuthKms), + nodeenrollment.WithSkipStorage(true), + ) + if err != nil { + return berrors.Wrap(ctx, err, op) + } + + // Get a signed request from the new credentials + fetchReq, err := newNodeCreds.CreateFetchNodeCredentialsRequest(ctx, randReaderOpt) + if err != nil { + return berrors.Wrap(ctx, err, op) + } + + // Encrypt the values to the server + encFetchReq, err := nodeenrollment.EncryptMessage(ctx, currentKeyId, fetchReq, currentNodeCreds, randReaderOpt) + if err != nil { + return berrors.Wrap(ctx, err, op) + } + + resp, err := multihopClient.RotateNodeCredentials(ctx, &types.RotateNodeCredentialsRequest{ + CertificatePublicKeyPkix: currentNodeCreds.CertificatePublicKeyPkix, + EncryptedFetchNodeCredentialsRequest: encFetchReq, + }) + if err != nil { + return berrors.Wrap(ctx, err, op) + } + + // Decrypt the response using current credentials + fetchResp := new(types.FetchNodeCredentialsResponse) + if err := nodeenrollment.DecryptMessage( + ctx, + currentKeyId, + resp.EncryptedFetchNodeCredentialsResponse, + currentNodeCreds, + fetchResp, + ); err != nil { + return berrors.Wrap(ctx, err, op) + } + + // Call handle on the inner response, which will be encrypted to the + // new node credentials. This will also store the result, + // overwriting the current credentials. + _, err = newNodeCreds.HandleFetchNodeCredentialsResponse( + ctx, + w.WorkerAuthStorage, + fetchResp, + nodeenrollment.WithWrapper(w.conf.WorkerAuthKms), + ) + if err != nil { + return berrors.Wrap(ctx, err, op) + } + + return nil +} diff --git a/internal/daemon/worker/auth_rotation_test.go b/internal/daemon/worker/auth_rotation_test.go new file mode 100644 index 000000000..f5468c406 --- /dev/null +++ b/internal/daemon/worker/auth_rotation_test.go @@ -0,0 +1,115 @@ +package worker_test + +import ( + "testing" + "time" + + "github.com/hashicorp/boundary/internal/cmd/config" + "github.com/hashicorp/boundary/internal/daemon/controller" + "github.com/hashicorp/boundary/internal/daemon/worker" + "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/types" + "github.com/mr-tron/base58" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/resolver" + "google.golang.org/protobuf/proto" +) + +// TestRotationTicking ensures that we see new credential information for a +// worker on both the controller side and worker side in a periodic fashion +func TestRotationTicking(t *testing.T) { + t.Parallel() + + require, assert := require.New(t), assert.New(t) + logger := hclog.New(&hclog.LoggerOptions{ + Level: hclog.Trace, + }) + + conf, err := config.DevController() + require.NoError(err) + + c := controller.NewTestController(t, &controller.TestControllerOpts{ + Config: conf, + Logger: logger.Named("controller"), + }) + t.Cleanup(c.Shutdown) + + const rotationPeriod = 20 * time.Second + + w := worker.NewTestWorker(t, &worker.TestWorkerOpts{ + InitialControllers: c.ClusterAddrs(), + Logger: logger.Named("worker"), + AuthRotationPeriod: rotationPeriod, + }) + t.Cleanup(w.Shutdown) + + // Get a servers repo and worker auth repo + serversRepo, err := c.Controller().ServersRepoFn() + require.NoError(err) + workerAuthRepo, err := c.Controller().WorkerAuthRepoStorageFn() + require.NoError(err) + + // Give time for the job to ensure that roots are created and offset us from + // the rotation period + time.Sleep(rotationPeriod / 2) + + // Verify that there are no nodes authorized yet + auths, err := workerAuthRepo.List(c.Context(), (*types.NodeInformation)(nil)) + require.NoError(err) + assert.Len(auths, 0) + + // Perform initial authentication of worker to controller + reqBytes, err := base58.FastBase58Decoding(w.Worker().WorkerAuthRegistrationRequest) + require.NoError(err) + + // Decode the proto into the request and create the worker + req := new(types.FetchNodeCredentialsRequest) + require.NoError(proto.Unmarshal(reqBytes, req)) + _, err = serversRepo.CreateWorker(c.Context(), &servers.Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + }, + }, servers.WithFetchNodeCredentialsRequest(req)) + require.NoError(err) + + // Verify we see one authorized set of credentials now + auths, err = workerAuthRepo.List(c.Context(), (*types.NodeInformation)(nil)) + require.NoError(err) + assert.Len(auths, 1) + // Fetch creds and store current key + currNodeCreds, err := types.LoadNodeCredentials(w.Context(), w.Worker().WorkerAuthStorage, nodeenrollment.CurrentId) + require.NoError(err) + currKey := currNodeCreds.CertificatePublicKeyPkix + + // Now we wait and check that we see new values in the DB and different + // creds on the worker after each rotation period + for i := 2; i < 5; i++ { + time.Sleep(rotationPeriod) + + // Verify we see the expected number, since we aren't expiring any it + // should be equal to the number of times we did rotations + auths, err = workerAuthRepo.List(c.Context(), (*types.NodeInformation)(nil)) + require.NoError(err) + assert.Len(auths, i) + + // Fetch creds and compare current key + currNodeCreds, err := types.LoadNodeCredentials(w.Context(), w.Worker().WorkerAuthStorage, nodeenrollment.CurrentId) + require.NoError(err) + assert.NotEqual(currKey, currNodeCreds.CertificatePublicKeyPkix) + currKey = currNodeCreds.CertificatePublicKeyPkix + + // Stop and start the client connections to ensure the new credentials + // are valid; if not, we won't establish a new connection and rotation + // will fail + w.Worker().Resolver().UpdateState(resolver.State{Addresses: []resolver.Address{}}) + cc, err := w.Worker().GrpcClientConn() + require.NoError(err) + require.NoError(cc.Close()) + require.NoError(w.Worker().StartControllerConnections()) + } +} diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index bd87c4457..175d178ca 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -33,8 +33,10 @@ import ( "google.golang.org/protobuf/proto" ) -func (w *Worker) startControllerConnections() error { - const op = "worker.(Worker).startControllerConnections" +// StartControllerConnections starts up the resolver and initiates controller +// connection client creation +func (w *Worker) StartControllerConnections() error { + const op = "worker.(Worker).StartControllerConnections" initialAddrs := make([]resolver.Address, 0, len(w.conf.RawConfig.Worker.Controllers)) for _, addr := range w.conf.RawConfig.Worker.Controllers { switch { @@ -162,6 +164,7 @@ func (w *Worker) createClientConn(addr string) error { if err != nil { return fmt.Errorf("error dialing controller for worker auth: %w", err) } + w.grpcClientConn.Store(cc) w.controllerStatusConn.Store(pbs.NewServerCoordinationServiceClient(cc)) w.controllerSessionConn.Store(pbs.NewSessionServiceClient(cc)) diff --git a/internal/daemon/worker/testing.go b/internal/daemon/worker/testing.go index 5fe5387eb..d2711b7d9 100644 --- a/internal/daemon/worker/testing.go +++ b/internal/daemon/worker/testing.go @@ -198,6 +198,9 @@ type TestWorkerOpts struct { // Overrides worker's nonceFn, for cases where we want to have control // over the nonce we send to the Controller NonceFn randFn + + // If set, override the normal auth rotation period + AuthRotationPeriod time.Duration } func NewTestWorker(t testing.TB, opts *TestWorkerOpts) *TestWorker { @@ -297,6 +300,8 @@ func NewTestWorker(t testing.TB, opts *TestWorkerOpts) *TestWorker { t.Fatal(err) } + tw.w.TestOverrideAuthRotationPeriod = opts.AuthRotationPeriod + if opts.NonceFn != nil { tw.w.nonceFn = opts.NonceFn } diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 85fdf7ad7..31b95380c 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -28,10 +28,12 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-secure-stdlib/mlock" + "github.com/hashicorp/nodeenrollment/multihop" nodeenet "github.com/hashicorp/nodeenrollment/net" nodeefile "github.com/hashicorp/nodeenrollment/storage/file" "github.com/hashicorp/nodeenrollment/types" ua "go.uber.org/atomic" + "google.golang.org/grpc" "google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver/manual" "google.golang.org/protobuf/proto" @@ -49,6 +51,8 @@ type Worker struct { tickerWg sync.WaitGroup + grpcClientConn *atomic.Value + controllerStatusConn *atomic.Value everAuthenticated *ua.Bool lastStatusSuccess *atomic.Value @@ -80,9 +84,10 @@ type Worker struct { WorkerAuthRegistrationRequest string workerAuthSplitListener *nodeenet.SplitListener - // Test-specific options + // Test-specific options (and possibly hidden dev-mode flags) TestOverrideX509VerifyDnsName string TestOverrideX509VerifyCertPool *x509.CertPool + TestOverrideAuthRotationPeriod time.Duration } func New(conf *Config) (*Worker, error) { @@ -94,6 +99,7 @@ func New(conf *Config) (*Worker, error) { conf: conf, logger: conf.Logger.Named("worker"), started: ua.NewBool(false), + grpcClientConn: new(atomic.Value), controllerStatusConn: new(atomic.Value), everAuthenticated: ua.NewBool(false), lastStatusSuccess: new(atomic.Value), @@ -253,7 +259,12 @@ func (w *Worker) Start() error { // NOTE: this block _must_ be before the `if createFetchRequest` block // or the fetch request may have no credentials to work with if createNodeAuthCreds { - nodeCreds, err = types.NewNodeCredentials(w.baseContext, w.WorkerAuthStorage, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) + nodeCreds, err = types.NewNodeCredentials( + w.baseContext, + w.WorkerAuthStorage, + nodeenrollment.WithRandomReader(w.conf.SecureRandomReader), + nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms), + ) if err != nil { return fmt.Errorf("error generating new worker auth creds: %w", err) } @@ -263,7 +274,7 @@ func (w *Worker) Start() error { if nodeCreds == nil { return fmt.Errorf("need to create fetch request but worker auth creds are nil: %w", err) } - req, err := nodeCreds.CreateFetchNodeCredentialsRequest(w.baseContext) + req, err := nodeCreds.CreateFetchNodeCredentialsRequest(w.baseContext, nodeenrollment.WithRandomReader(w.conf.SecureRandomReader)) if err != nil { return fmt.Errorf("error creating worker auth fetch credentials request: %w", err) } @@ -282,15 +293,23 @@ func (w *Worker) Start() error { } } - if err := w.startControllerConnections(); err != nil { + if err := w.StartControllerConnections(); err != nil { return fmt.Errorf("error making controller connections: %w", err) } - w.tickerWg.Add(1) + // Rather than deal with some of the potential error conditions for Add on + // the waitgroup vs. Done (in case a function exits immediately), we will + // always start rotation and simply exit early if we're using KMS, and + // always add 2 here. + w.tickerWg.Add(2) go func() { defer w.tickerWg.Done() w.startStatusTicking(w.baseContext) }() + go func() { + defer w.tickerWg.Done() + w.startAuthRotationTicking(w.baseContext) + }() w.workerStartTime = time.Now() w.started.Store(true) @@ -382,6 +401,24 @@ func (w *Worker) ParseAndStoreTags(incoming map[string][]string) { w.updateTags.Store(true) } +// GrpcClientConn returns the underlying client connection +func (w *Worker) GrpcClientConn() (*grpc.ClientConn, error) { + rawConn := w.grpcClientConn.Load() + if rawConn == nil { + return nil, errors.New("unable to load grpc client conn") + } + cc, ok := rawConn.(*grpc.ClientConn) + if !ok { + return nil, fmt.Errorf("invalid grpc client connection %T", rawConn) + } + if cc == nil { + return nil, fmt.Errorf("client connection is nil") + } + + return cc, nil +} + +// ControllerSessionConn returns the underlying session service client func (w *Worker) ControllerSessionConn() (pbs.SessionServiceClient, error) { rawConn := w.controllerSessionConn.Load() if rawConn == nil { @@ -398,6 +435,40 @@ func (w *Worker) ControllerSessionConn() (pbs.SessionServiceClient, error) { return sessClient, nil } +// ControllerServerCoordinationServiceConn returns the underlying server coordination service client +func (w *Worker) ControllerServerCoordinationServiceConn() (pbs.ServerCoordinationServiceClient, error) { + rawConn := w.controllerStatusConn.Load() + if rawConn == nil { + return nil, errors.New("unable to load controller service coordination service connection") + } + statusClient, ok := rawConn.(pbs.ServerCoordinationServiceClient) + if !ok { + return nil, fmt.Errorf("invalid service coordination service client %T", rawConn) + } + if statusClient == nil { + return nil, fmt.Errorf("service coordination service client is nil") + } + + return statusClient, nil +} + +// ControllerMultihopConn returns the underlying multihop service client +func (w *Worker) ControllerMultihopConn() (multihop.MultihopServiceClient, error) { + rawConn := w.controllerMultihopConn.Load() + if rawConn == nil { + return nil, errors.New("unable to load controller multihop service connection") + } + multihopClient, ok := rawConn.(multihop.MultihopServiceClient) + if !ok { + return nil, fmt.Errorf("invalid multihop client %T", rawConn) + } + if multihopClient == nil { + return nil, fmt.Errorf("session multihop is nil") + } + + return multihopClient, nil +} + func (w *Worker) getSessionTls(hello *tls.ClientHelloInfo) (*tls.Config, error) { const op = "worker.(Worker).getSessionTls" ctx := w.baseContext diff --git a/internal/servers/repository_workerauth.go b/internal/servers/repository_workerauth.go index 61970dec4..cdcd34b80 100644 --- a/internal/servers/repository_workerauth.go +++ b/internal/servers/repository_workerauth.go @@ -397,6 +397,14 @@ func (r *WorkerAuthRepositoryStorage) loadNodeInformation(ctx context.Context, n return errors.Wrap(ctx, err, op) } + workerIdInfo := workerAuthWorkerId{WorkerId: authorizedWorker.GetWorkerId()} + workerIdMap := structs.Map(workerIdInfo) + state, err := structpb.NewStruct(workerIdMap) + if err != nil { + return errors.Wrap(ctx, err, op) + } + node.State = state + // Get cert bundles from the other table certBundles, err := r.findCertBundles(ctx, node.Id) if err != nil { diff --git a/internal/servers/repository_workerauth_test.go b/internal/servers/repository_workerauth_test.go index 061acbcd1..94aa141bc 100644 --- a/internal/servers/repository_workerauth_test.go +++ b/internal/servers/repository_workerauth_test.go @@ -205,6 +205,7 @@ func TestStoreWorkerAuth(t *testing.T) { assert.Equal(nodeInfo.EncryptionPublicKeyBytes, nodeLookup.EncryptionPublicKeyBytes) assert.Equal(nodeInfo.RegistrationNonce, nodeLookup.RegistrationNonce) assert.Equal(nodeInfo.CertificatePublicKeyPkix, nodeLookup.CertificatePublicKeyPkix) + assert.Equal(nodeInfo.State.AsMap(), nodeLookup.State.AsMap()) // Remove node err = storage.Remove(ctx, nodeLookup) From 9736b79d4e454d69f3b4b99a73af2e496675535b Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 7 Jun 2022 14:56:04 -0400 Subject: [PATCH 061/100] Remove temporary node authorization functions (#2147) --- internal/cmd/commands/dev/dev.go | 35 +++++++++++++++++++++- internal/daemon/controller/controller.go | 34 --------------------- internal/daemon/controller/handler.go | 38 ------------------------ 3 files changed, 34 insertions(+), 73 deletions(-) diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 4dbccaf9d..a7ec5b9a7 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -25,13 +25,18 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/intglobals" "github.com/hashicorp/boundary/internal/observability/event" + "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/target/tcp" "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/hashicorp/nodeenrollment/types" "github.com/mitchellh/cli" + "github.com/mr-tron/base58" "github.com/posener/complete" "go.uber.org/atomic" + "google.golang.org/protobuf/proto" ) var ( @@ -719,7 +724,7 @@ func (c *Command) Run(args []string) int { case <-c.Context.Done(): return case <-time.After(time.Second): - if err := c.controller.AuthorizeNodeeWorker(req); err != nil { + if err := authorizeWorker(c.Context, c.controller, req); err != nil { c.UI.Error(fmt.Errorf("Error authorizing node: %w", err).Error()) errorEncountered.Store(true) return @@ -805,3 +810,31 @@ func (c *Command) Run(args []string) int { return base.CommandSuccess } + +func authorizeWorker(ctx context.Context, c *controller.Controller, request string) error { + reqBytes, err := base58.FastBase58Decoding(request) + if err != nil { + return fmt.Errorf("error base58-decoding fetch node creds next proto value: %w", err) + } + // Decode the proto into the request + req := new(types.FetchNodeCredentialsRequest) + if err := proto.Unmarshal(reqBytes, req); err != nil { + return fmt.Errorf("error unmarshaling common name value: %w", err) + } + + serversRepo, err := c.ServersRepoFn() + if err != nil { + return fmt.Errorf("error fetching servers repo: %w", err) + } + + _, err = serversRepo.CreateWorker(ctx, &servers.Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + }, + }, servers.WithFetchNodeCredentialsRequest(req)) + if err != nil { + return fmt.Errorf("error creating worker: %w", err) + } + + return err +} diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index f2cb3363c..1d590ee04 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -28,7 +28,6 @@ import ( "github.com/hashicorp/boundary/internal/scheduler/job" "github.com/hashicorp/boundary/internal/servers" serversjob "github.com/hashicorp/boundary/internal/servers/job" - "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/types/scope" @@ -38,11 +37,8 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/hashicorp/go-secure-stdlib/pluginutil/v2" - "github.com/hashicorp/nodeenrollment/types" - "github.com/mr-tron/base58" ua "go.uber.org/atomic" "google.golang.org/grpc" - "google.golang.org/protobuf/proto" ) type Controller struct { @@ -402,33 +398,3 @@ func (c *Controller) Shutdown() error { func (c *Controller) WorkerStatusUpdateTimes() *sync.Map { return c.workerStatusUpdateTimes } - -// This is a temporary function until the API is up -func (c *Controller) AuthorizeNodeeWorker(request string) error { - const op = "controller.(Controller).AuthorizeNodeeWorker" - reqBytes, err := base58.FastBase58Decoding(request) - if err != nil { - return fmt.Errorf("(%s) error base58-decoding fetch node creds next proto value: %w", op, err) - } - // Decode the proto into the request - req := new(types.FetchNodeCredentialsRequest) - if err := proto.Unmarshal(reqBytes, req); err != nil { - return fmt.Errorf("(%s) error unmarshaling common name value: %w", op, err) - } - - serversRepo, err := c.ServersRepoFn() - if err != nil { - return fmt.Errorf("(%s) error fetching servers repo: %w", op, err) - } - - _, err = serversRepo.CreateWorker(c.baseContext, &servers.Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - }, - }, servers.WithFetchNodeCredentialsRequest(req)) - if err != nil { - return fmt.Errorf("(%s) error creating worker: %w", op, err) - } - - return err -} diff --git a/internal/daemon/controller/handler.go b/internal/daemon/controller/handler.go index d5757a81a..c06218f44 100644 --- a/internal/daemon/controller/handler.go +++ b/internal/daemon/controller/handler.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "io" "io/ioutil" "net/http" "net/textproto" @@ -67,7 +66,6 @@ func (c *Controller) apiHandler(props HandlerProperties) (http.Handler, error) { if err != nil { return nil, err } - mux.Handle("/v1/nodes", handleNodes(c)) mux.Handle("/v1/", grpcGwMux) mux.Handle("/", handleUi(c)) @@ -574,42 +572,6 @@ func wrapHandlerWithCallbackInterceptor(h http.Handler, c *Controller) http.Hand }) } -func handleNodes(c *Controller) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - switch req.Method { - - case http.MethodPost: - body, err := io.ReadAll(req.Body) - req.Body.Close() - if err != nil { - _, _ = w.Write([]byte(err.Error())) - w.WriteHeader(500) - return - } - type val struct { - Request string `json:"request"` - } - var currVal val - if err := json.Unmarshal(body, &currVal); err != nil { - _, _ = w.Write([]byte(err.Error())) - w.WriteHeader(500) - return - } - if err := c.AuthorizeNodeeWorker(currVal.Request); err != nil { - _, _ = w.Write([]byte(err.Error())) - w.WriteHeader(500) - return - } - w.WriteHeader(204) - return - - default: - w.WriteHeader(http.StatusBadRequest) - return - } - }) -} - /* func WrapForwardedForHandler(h http.Handler, authorizedAddrs []*sockaddr.SockAddrMarshaler, rejectNotPresent, rejectNonAuthz bool, hopSkips int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { From 37cc61e4c9d41c2764d0c470f0bdc63164e9a48d Mon Sep 17 00:00:00 2001 From: Jim Date: Thu, 9 Jun 2022 06:43:58 -0400 Subject: [PATCH 062/100] feature (worker): Add audit data classification to CreateWorker(...) api (#2153) --- Makefile | 2 ++ .../api/services/worker_service.pb.go | 14 ++++----- .../api/resources/workers/worker.pb.go | 30 +++++++++---------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 750ea7f46..83c16cbc9 100644 --- a/Makefile +++ b/Makefile @@ -193,6 +193,8 @@ protobuild: @protoc-go-inject-tag -input=./internal/gen/controller/api/services/session_service.pb.go @protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/users/user.pb.go @protoc-go-inject-tag -input=./internal/gen/controller/api/services/user_service.pb.go + @protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/workers/worker.pb.go + @protoc-go-inject-tag -input=./internal/gen/controller/api/services/worker_service.pb.go # these protos, services and openapi artifacts are purely for testing purposes @protoc-go-inject-tag -input=./internal/gen/testing/event/event.pb.go diff --git a/internal/gen/controller/api/services/worker_service.pb.go b/internal/gen/controller/api/services/worker_service.pb.go index 3f698b141..aa1a7ff14 100644 --- a/internal/gen/controller/api/services/worker_service.pb.go +++ b/internal/gen/controller/api/services/worker_service.pb.go @@ -29,7 +29,7 @@ type GetWorkerRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: `class:"public"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *GetWorkerRequest) Reset() { @@ -123,9 +123,9 @@ type ListWorkersRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ScopeId string `protobuf:"bytes,1,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty"` // @gotags: `class:"public"` - Recursive bool `protobuf:"varint,20,opt,name=recursive,proto3" json:"recursive,omitempty"` // @gotags: `class:"public"` - Filter string `protobuf:"bytes,30,opt,name=filter,proto3" json:"filter,omitempty"` // @gotags: `class:"sensitive"` + ScopeId string `protobuf:"bytes,1,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" class:"public"` // @gotags: `class:"public"` + Recursive bool `protobuf:"varint,20,opt,name=recursive,proto3" json:"recursive,omitempty" class:"public"` // @gotags: `class:"public"` + Filter string `protobuf:"bytes,30,opt,name=filter,proto3" json:"filter,omitempty" class:"sensitive"` // @gotags: `class:"sensitive"` } func (x *ListWorkersRequest) Reset() { @@ -280,7 +280,7 @@ type CreateWorkerLedResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"` // @gotags: `class:"public"` + Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty" class:"public"` // @gotags: `class:"public"` Item *workers.Worker `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` } @@ -335,7 +335,7 @@ type UpdateWorkerRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: `class:"public"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` Item *workers.Worker `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=update_mask,proto3" json:"update_mask,omitempty"` } @@ -445,7 +445,7 @@ type DeleteWorkerRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // @gotags: `class:"public"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *DeleteWorkerRequest) Reset() { diff --git a/sdk/pbs/controller/api/resources/workers/worker.pb.go b/sdk/pbs/controller/api/resources/workers/worker.pb.go index 570705428..637231736 100644 --- a/sdk/pbs/controller/api/resources/workers/worker.pb.go +++ b/sdk/pbs/controller/api/resources/workers/worker.pb.go @@ -99,36 +99,36 @@ type Worker struct { unknownFields protoimpl.UnknownFields // Output only. The ID of the User. - Id string `protobuf:"bytes,10,opt,name=id,proto3" json:"id,omitempty"` // @gotags: `class:"public"` + Id string `protobuf:"bytes,10,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` // The ID of the Scope this resource is in. - ScopeId string `protobuf:"bytes,20,opt,name=scope_id,proto3" json:"scope_id,omitempty"` // @gotags: `class:"public"` + ScopeId string `protobuf:"bytes,20,opt,name=scope_id,proto3" json:"scope_id,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. Scope information for this resource. Scope *scopes.ScopeInfo `protobuf:"bytes,30,opt,name=scope,proto3" json:"scope,omitempty"` // Optional name for identification purposes. - Name *wrapperspb.StringValue `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty"` // @gotags: `class:"sensitive"` + Name *wrapperspb.StringValue `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty" class:"sensitive"` // @gotags: `class:"sensitive"` // Optional user-set description for identification purposes. - Description *wrapperspb.StringValue `protobuf:"bytes,50,opt,name=description,proto3" json:"description,omitempty"` // @gotags: `class:"sensitive"` + Description *wrapperspb.StringValue `protobuf:"bytes,50,opt,name=description,proto3" json:"description,omitempty" class:"sensitive"` // @gotags: `class:"sensitive"` // Output only. The time this resource was created. - CreatedTime *timestamppb.Timestamp `protobuf:"bytes,60,opt,name=created_time,proto3" json:"created_time,omitempty"` // @gotags: `class:"public"` + CreatedTime *timestamppb.Timestamp `protobuf:"bytes,60,opt,name=created_time,proto3" json:"created_time,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The time this resource was last updated. - UpdatedTime *timestamppb.Timestamp `protobuf:"bytes,70,opt,name=updated_time,proto3" json:"updated_time,omitempty"` // @gotags: `class:"public"` + UpdatedTime *timestamppb.Timestamp `protobuf:"bytes,70,opt,name=updated_time,proto3" json:"updated_time,omitempty" class:"public"` // @gotags: `class:"public"` // Version is used in mutation requests, after the initial creation, to ensure this resource has not changed. // The mutation will fail if the version does not match the latest known good version. - Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty"` // @gotags: `class:"public"` + Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"` // The address that this worker is reachable at. - Address *wrapperspb.StringValue `protobuf:"bytes,90,opt,name=address,proto3" json:"address,omitempty"` // @gotags: `class:"public"` + Address *wrapperspb.StringValue `protobuf:"bytes,90,opt,name=address,proto3" json:"address,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. This is the address used by authorized session. The value // is the same as the address field if set. Otherwise it uses the address the // worker reports. - CanonicalAddress string `protobuf:"bytes,100,opt,name=canonical_address,json=canonicalAddress,proto3" json:"canonical_address,omitempty"` // @gotags: `class:"public"` + CanonicalAddress string `protobuf:"bytes,100,opt,name=canonical_address,json=canonicalAddress,proto3" json:"canonical_address,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The tags attached to this worker. - Tags map[string]*structpb.ListValue `protobuf:"bytes,110,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // @gotags: `class:"public"` + Tags map[string]*structpb.ListValue `protobuf:"bytes,110,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" class:"public"` // @gotags: `class:"public"` // Output only. The deduplicated union of the tags reported by the worker and // the tags added through the user facing api. This is used when applying // worker filters. - CanonicalTags map[string]*structpb.ListValue `protobuf:"bytes,120,rep,name=canonical_tags,json=canonicalTags,proto3" json:"canonical_tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // @gotags: `class:"public"` + CanonicalTags map[string]*structpb.ListValue `protobuf:"bytes,120,rep,name=canonical_tags,json=canonicalTags,proto3" json:"canonical_tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" class:"public"` // @gotags: `class:"public"` // Output only. The time this worker daemon last reported its status. - LastStatusTime *timestamppb.Timestamp `protobuf:"bytes,130,opt,name=last_status_time,proto3" json:"last_status_time,omitempty"` // @gotags: `class:"public"` + LastStatusTime *timestamppb.Timestamp `protobuf:"bytes,130,opt,name=last_status_time,proto3" json:"last_status_time,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The set of information the worker daemon has reported about // itself. WorkerConfig *WorkerConfig `protobuf:"bytes,140,opt,name=worker_config,proto3" json:"worker_config,omitempty"` @@ -136,11 +136,11 @@ type Worker struct { // enrollment flow where this credentials token is produced by a worker. This // token is a base58 encoded types.FetchNodeCredentialsRequest from // https://github.com/hashicorp/nodeenrollment - WorkerAuthToken *wrapperspb.StringValue `protobuf:"bytes,150,opt,name=worker_auth_token,proto3" json:"worker_auth_token,omitempty"` // @gotags: `class:"public"` + WorkerAuthToken *wrapperspb.StringValue `protobuf:"bytes,150,opt,name=worker_auth_token,proto3" json:"worker_auth_token,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The number of connections that this worker is currently handling. - ActiveConnectionCount uint32 `protobuf:"varint,160,opt,name=active_connection_count,proto3" json:"active_connection_count,omitempty"` // @gotags: `class:"public"` + ActiveConnectionCount uint32 `protobuf:"varint,160,opt,name=active_connection_count,proto3" json:"active_connection_count,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The available actions on this resource for the requester. - AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty"` // @gotags: `class:"public"` + AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *Worker) Reset() { From 594e6e82b5b017d556fda2e77e695f0e1ddf4143 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Wed, 8 Jun 2022 12:18:00 -0400 Subject: [PATCH 063/100] feat(worker): save workerAuth request to a file --- internal/cmd/base/servers.go | 27 +++++++++++++++++++ internal/cmd/commands/dev/dev.go | 13 +++++++++ internal/cmd/commands/server/server.go | 15 +++++++++++ .../daemon/worker/controller_connection.go | 11 ++++++++ 4 files changed, 66 insertions(+) diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index ba09f71de..a39941cc0 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -10,6 +10,7 @@ import ( "net" "os" "os/signal" + "path/filepath" "sort" "strconv" "strings" @@ -61,6 +62,9 @@ const ( // TODO: This value is temporary, it will be removed once we have a better // story/direction on attributes and system defaults. statusGracePeriodEnvVar = "BOUNDARY_STATUS_GRACE_PERIOD" + + // File name to use for storing workerAuth requests + WorkerAuthReqFile = "auth_request_token" ) func init() { @@ -342,6 +346,29 @@ func (b *Server) RemovePidFile(pidPath string) error { return os.Remove(pidPath) } +func (b *Server) StoreWorkerAuthReq(authReq, workerAuthReqPath string) error { + // Quit fast if no workerAuthReqFile + if workerAuthReqPath == "" { + return nil + } + workerAuthReqFilePath := filepath.Join(workerAuthReqPath, WorkerAuthReqFile) + + // Open the workerAuthReq file + workerAuthReqFile, err := os.OpenFile(workerAuthReqFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o640) + if err != nil { + return fmt.Errorf("could not open file for worker auth request: %w", err) + } + defer workerAuthReqFile.Close() + + // Write out the workerAuthRequest + _, err = workerAuthReqFile.WriteString(authReq) + if err != nil { + return fmt.Errorf("could not write to file for worker auth request: %w", err) + } + + return nil +} + func (b *Server) PrintInfo(ui cli.Ui) { verInfo := version.Get() if verInfo.Version != "" { diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index a7ec5b9a7..76b42fe93 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -718,6 +718,19 @@ func (c *Command) Run(args []string) int { c.Info["worker auth current key id"] = c.worker.WorkerAuthCurrentKeyId c.InfoKeys = append(c.InfoKeys, "worker auth storage path") c.Info["worker auth storage path"] = c.worker.WorkerAuthStorage.BaseDir() + if err := c.StoreWorkerAuthReq(c.worker.WorkerAuthRegistrationRequest, c.worker.WorkerAuthStorage.BaseDir()); err != nil { + // Shutdown on failure + retErr := fmt.Errorf("Error storing worker auth request: %w", err) + if err := c.worker.Shutdown(); err != nil { + c.UI.Error(retErr.Error()) + retErr = fmt.Errorf("Error shutting down worker: %w", err) + } + c.UI.Error(retErr.Error()) + if err := c.controller.Shutdown(); err != nil { + c.UI.Error(fmt.Errorf("Error with controller shutdown: %w", err).Error()) + } + return base.CommandCliError + } go func() { for { select { diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index 70bb3a651..ef519e882 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -469,6 +469,21 @@ func (c *Command) Run(args []string) int { c.Info["worker auth registration request"] = c.worker.WorkerAuthRegistrationRequest c.InfoKeys = append(c.InfoKeys, "worker auth current key id") c.Info["worker auth current key id"] = c.worker.WorkerAuthCurrentKeyId + + // Write the WorkerAuth request to a file + if err := c.StoreWorkerAuthReq(c.worker.WorkerAuthRegistrationRequest, c.worker.WorkerAuthStorage.BaseDir()); err != nil { + // Shutdown on failure + retErr := fmt.Errorf("Error storing worker auth request: %w", err) + if err := c.worker.Shutdown(); err != nil { + c.UI.Error(retErr.Error()) + retErr = fmt.Errorf("Error shutting down worker: %w", err) + } + c.UI.Error(retErr.Error()) + if err := c.controller.Shutdown(); err != nil { + c.UI.Error(fmt.Errorf("Error with controller shutdown: %w", err).Error()) + } + return base.CommandCliError + } } } diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index 175d178ca..1851536dd 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -14,6 +14,8 @@ import ( "math/big" mathrand "math/rand" "net" + "os" + "path/filepath" "strings" "time" @@ -77,6 +79,15 @@ func (w *Worker) controllerDialerFunc() func(context.Context, string) (net.Conn, conn, err = w.v1KmsAuthDialFn(ctx, addr) default: conn, err = protocol.Dial(ctx, w.WorkerAuthStorage, addr, nodeenrollment.WithWrapper(w.conf.WorkerAuthStorageKms)) + // No error and a valid connection means the WorkerAuthRegistrationRequest was populated + // We can remove the stored workerAuthRequest file + if err == nil && conn != nil { + if w.WorkerAuthStorage.BaseDir() != "" { + workerAuthReqFilePath := filepath.Join(w.WorkerAuthStorage.BaseDir(), base.WorkerAuthReqFile) + // Intentionally ignoring any error removing this file + _ = os.Remove(workerAuthReqFilePath) + } + } } if !w.everAuthenticated.Load() && err == nil && conn != nil { From f6c4e3c89652712b3cbc9d4b9c508b77d8e8ff88 Mon Sep 17 00:00:00 2001 From: Jim Date: Thu, 9 Jun 2022 11:28:12 -0400 Subject: [PATCH 064/100] feature (worker): add worker events around authentication (#2157) --- go.mod | 2 +- go.sum | 4 ++-- internal/daemon/worker/controller_connection.go | 12 ++++++++++++ internal/daemon/worker/status.go | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index eed02b301..b3164431e 100644 --- a/go.mod +++ b/go.mod @@ -92,7 +92,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b - github.com/hashicorp/nodeenrollment v0.0.0-20220607152131-4f253e6da747 + github.com/hashicorp/nodeenrollment v0.0.0-20220609044242-598c08a89c45 ) require ( diff --git a/go.sum b/go.sum index 31a89704a..61ee52032 100644 --- a/go.sum +++ b/go.sum @@ -740,8 +740,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220607152131-4f253e6da747 h1:/S1ePrvxgLI3jgL6XLVVQAOm3Ua0wS4PvRhmiTgG6hQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220607152131-4f253e6da747/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.0.0-20220609044242-598c08a89c45 h1:vnRJfKOMBoan6+PFw5P5CItnl3Z6lJJI1JaBM1tlLCo= +github.com/hashicorp/nodeenrollment v0.0.0-20220609044242-598c08a89c45/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index 1851536dd..e75ac8a62 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -71,6 +71,7 @@ func (w *Worker) StartControllerConnections() error { } func (w *Worker) controllerDialerFunc() func(context.Context, string) (net.Conn, error) { + const op = "worker.(Worker).controllerDialerFunc" return func(ctx context.Context, addr string) (net.Conn, error) { var conn net.Conn var err error @@ -89,9 +90,20 @@ func (w *Worker) controllerDialerFunc() func(context.Context, string) (net.Conn, } } } + switch err { + case nil: + case nodeenrollment.ErrNotAuthorized: + // We don't event in this case, because the function retries often + // and will spam the logs. The status function will event indicating + // that it can't send status because it's not authorized, so that + // will be a fine hint to the user as to the issue. + default: + event.WriteError(ctx, op, err) + } if !w.everAuthenticated.Load() && err == nil && conn != nil { w.everAuthenticated.Store(true) + event.WriteSysEvent(ctx, op, "worker has successfully authenticated") } return conn, err diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index 728e0fe63..8890ef111 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -95,6 +95,7 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { // any session information anyways and this will produce a ton of noise in // observability, so suppress it if !w.everAuthenticated.Load() { + event.WriteSysEvent(cancelCtx, op, "worker is not authenticated to an upstream, not sending status") return } From 7c4f495c2514c2d34e7365699df7104bdc8a2caa Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 9 Jun 2022 14:30:33 -0400 Subject: [PATCH 065/100] Add workers SDK API bits (#2159) --- api/workers/option.gen.go | 111 +++++++ api/workers/worker.gen.go | 310 ++++++++++++++++++ api/workers/worker_config.gen.go | 8 + internal/api/genapi/input.go | 132 ++++++-- internal/api/genapi/parse.go | 2 +- internal/api/genapi/templates.go | 42 ++- .../api/resources/workers/v1/worker.proto | 4 +- .../api/resources/workers/worker.pb.go | 104 +++--- 8 files changed, 611 insertions(+), 102 deletions(-) create mode 100644 api/workers/option.gen.go create mode 100644 api/workers/worker.gen.go create mode 100644 api/workers/worker_config.gen.go diff --git a/api/workers/option.gen.go b/api/workers/option.gen.go new file mode 100644 index 000000000..22be13b4b --- /dev/null +++ b/api/workers/option.gen.go @@ -0,0 +1,111 @@ +package workers + +import ( + "strings" + + "github.com/hashicorp/boundary/api" +) + +// Option is a func that sets optional attributes for a call. This does not need +// to be used directly, but instead option arguments are built from the +// functions in this package. WithX options set a value to that given in the +// argument; DefaultX options indicate that the value should be set to its +// default. When an API call is made options are processed in ther order they +// appear in the function call, so for a given argument X, a succession of WithX +// or DefaultX calls will result in the last call taking effect. +type Option func(*options) + +type options struct { + postMap map[string]interface{} + queryMap map[string]string + withAutomaticVersioning bool + withSkipCurlOutput bool + withFilter string +} + +func getDefaultOptions() options { + return options{ + postMap: make(map[string]interface{}), + queryMap: make(map[string]string), + } +} + +func getOpts(opt ...Option) (options, []api.Option) { + opts := getDefaultOptions() + for _, o := range opt { + if o != nil { + o(&opts) + } + } + var apiOpts []api.Option + if opts.withSkipCurlOutput { + apiOpts = append(apiOpts, api.WithSkipCurlOutput(true)) + } + if opts.withFilter != "" { + opts.queryMap["filter"] = opts.withFilter + } + return opts, apiOpts +} + +// If set, and if the version is zero during an update, the API will perform a +// fetch to get the current version of the resource and populate it during the +// update call. This is convenient but opens up the possibility for subtle +// order-of-modification issues, so use carefully. +func WithAutomaticVersioning(enable bool) Option { + return func(o *options) { + o.withAutomaticVersioning = enable + } +} + +// WithSkipCurlOutput tells the API to not use the current call for cURL output. +// Useful for when we need to look up versions. +func WithSkipCurlOutput(skip bool) Option { + return func(o *options) { + o.withSkipCurlOutput = true + } +} + +// WithFilter tells the API to filter the items returned using the provided +// filter term. The filter should be in a format supported by +// hashicorp/go-bexpr. +func WithFilter(filter string) Option { + return func(o *options) { + o.withFilter = strings.TrimSpace(filter) + } +} + +func WithAddress(inAddress string) Option { + return func(o *options) { + o.postMap["address"] = inAddress + } +} + +func DefaultAddress() Option { + return func(o *options) { + o.postMap["address"] = nil + } +} + +func WithDescription(inDescription string) Option { + return func(o *options) { + o.postMap["description"] = inDescription + } +} + +func DefaultDescription() Option { + return func(o *options) { + o.postMap["description"] = nil + } +} + +func WithName(inName string) Option { + return func(o *options) { + o.postMap["name"] = inName + } +} + +func DefaultName() Option { + return func(o *options) { + o.postMap["name"] = nil + } +} diff --git a/api/workers/worker.gen.go b/api/workers/worker.gen.go new file mode 100644 index 000000000..430122ee0 --- /dev/null +++ b/api/workers/worker.gen.go @@ -0,0 +1,310 @@ +// Code generated by "make api"; DO NOT EDIT. +package workers + +import ( + "context" + "fmt" + "net/url" + "time" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/scopes" +) + +type Worker struct { + Id string `json:"id,omitempty"` + ScopeId string `json:"scope_id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + CreatedTime time.Time `json:"created_time,omitempty"` + UpdatedTime time.Time `json:"updated_time,omitempty"` + Version uint32 `json:"version,omitempty"` + Address string `json:"address,omitempty"` + CanonicalAddress string `json:"canonical_address,omitempty"` + Tags map[string][]string `json:"tags,omitempty"` + CanonicalTags map[string][]string `json:"canonical_tags,omitempty"` + LastStatusTime time.Time `json:"last_status_time,omitempty"` + WorkerConfig *WorkerConfig `json:"worker_config,omitempty"` + WorkerAuthToken string `json:"worker_auth_token,omitempty"` + ActiveConnectionCount uint32 `json:"active_connection_count,omitempty"` + AuthorizedActions []string `json:"authorized_actions,omitempty"` + + response *api.Response +} + +type WorkerReadResult struct { + Item *Worker + response *api.Response +} + +func (n WorkerReadResult) GetItem() interface{} { + return n.Item +} + +func (n WorkerReadResult) GetResponse() *api.Response { + return n.response +} + +type WorkerCreateResult = WorkerReadResult +type WorkerUpdateResult = WorkerReadResult + +type WorkerDeleteResult struct { + response *api.Response +} + +// GetItem will always be nil for WorkerDeleteResult +func (n WorkerDeleteResult) GetItem() interface{} { + return nil +} + +func (n WorkerDeleteResult) GetResponse() *api.Response { + return n.response +} + +type WorkerListResult struct { + Items []*Worker + response *api.Response +} + +func (n WorkerListResult) GetItems() interface{} { + return n.Items +} + +func (n WorkerListResult) GetResponse() *api.Response { + return n.response +} + +// Client is a client for this collection +type Client struct { + client *api.Client +} + +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. +func NewClient(c *api.Client) *Client { + return &Client{client: c.Clone()} +} + +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client +} + +func (c *Client) CreateWorkerLed(ctx context.Context, workerAuthToken string, scopeId string, opt ...Option) (*WorkerCreateResult, error) { + if scopeId == "" { + return nil, fmt.Errorf("empty scopeId value passed into CreateWorkerLed request") + } + + opts, apiOpts := getOpts(opt...) + + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + if workerAuthToken == "" { + return nil, fmt.Errorf("empty workerAuthToken value passed into CreateWorkerLed request") + } else { + opts.postMap["worker_auth_token"] = workerAuthToken + } + + opts.postMap["scope_id"] = scopeId + + req, err := c.client.NewRequest(ctx, "POST", "workers:create:worker-led", opts.postMap, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating CreateWorkerLed request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during CreateWorkerLed call: %w", err) + } + + target := new(WorkerCreateResult) + target.Item = new(Worker) + apiErr, err := resp.Decode(target.Item) + if err != nil { + return nil, fmt.Errorf("error decoding CreateWorkerLed response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.response = resp + return target, nil +} + +func (c *Client) Read(ctx context.Context, id string, opt ...Option) (*WorkerReadResult, error) { + if id == "" { + return nil, fmt.Errorf("empty id value passed into Read request") + } + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + + req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("workers/%s", url.PathEscape(id)), nil, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating Read request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error performing client request during Read call: %w", err) + } + + target := new(WorkerReadResult) + target.Item = new(Worker) + apiErr, err := resp.Decode(target.Item) + if err != nil { + return nil, fmt.Errorf("error decoding Read response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.response = resp + return target, nil +} + +func (c *Client) Update(ctx context.Context, id string, version uint32, opt ...Option) (*WorkerUpdateResult, error) { + if id == "" { + return nil, fmt.Errorf("empty id value passed into Update request") + } + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + + opts.postMap["version"] = version + + req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("workers/%s", url.PathEscape(id)), opts.postMap, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating Update request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during Update call: %w", err) + } + + target := new(WorkerUpdateResult) + target.Item = new(Worker) + apiErr, err := resp.Decode(target.Item) + if err != nil { + return nil, fmt.Errorf("error decoding Update response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.response = resp + return target, nil +} + +func (c *Client) Delete(ctx context.Context, id string, opt ...Option) (*WorkerDeleteResult, error) { + if id == "" { + return nil, fmt.Errorf("empty id value passed into Delete request") + } + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + + req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("workers/%s", url.PathEscape(id)), nil, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating Delete request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during Delete call: %w", err) + } + + apiErr, err := resp.Decode(nil) + if err != nil { + return nil, fmt.Errorf("error decoding Delete response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + + target := &WorkerDeleteResult{ + response: resp, + } + return target, nil +} + +func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*WorkerListResult, error) { + if scopeId == "" { + return nil, fmt.Errorf("empty scopeId value passed into List request") + } + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + opts.queryMap["scope_id"] = scopeId + + req, err := c.client.NewRequest(ctx, "GET", "workers", nil, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating List request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during List call: %w", err) + } + + target := new(WorkerListResult) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, fmt.Errorf("error decoding List response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.response = resp + return target, nil +} diff --git a/api/workers/worker_config.gen.go b/api/workers/worker_config.gen.go new file mode 100644 index 000000000..1e31a3681 --- /dev/null +++ b/api/workers/worker_config.gen.go @@ -0,0 +1,8 @@ +// Code generated by "make api"; DO NOT EDIT. +package workers + +type WorkerConfig struct { + Address string `json:"address,omitempty"` + Name string `json:"name,omitempty"` + Tags map[string][]string `json:"tags,omitempty"` +} diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index b7eaab89e..943c1b85f 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/sessions" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/users" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" "google.golang.org/protobuf/proto" ) @@ -34,6 +35,12 @@ type structureInfo struct { fields []fieldInfo } +type requiredParam struct { + Name string + Typ string + PostType string +} + type fieldInfo struct { Name string ProtoName string @@ -87,9 +94,8 @@ type structInfo struct { // useful to avoid collisions nameOverride string - // typeOnCreate indicates that create will be creating a concrete - // implementation of an abstract type and thus a type field is necessary - typeOnCreate bool + // extraRequiredParams allows adding extra required parameters to templates + extraRequiredParams []requiredParam // recursiveListing indicates that the collection supports recursion when // listing @@ -149,7 +155,7 @@ var inputStructs = []*structInfo{ outFile: "scopes/scope.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -185,7 +191,7 @@ var inputStructs = []*structInfo{ outFile: "users/user.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -213,7 +219,7 @@ var inputStructs = []*structInfo{ outFile: "groups/group.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -251,7 +257,7 @@ var inputStructs = []*structInfo{ outFile: "roles/role.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -293,14 +299,20 @@ var inputStructs = []*structInfo{ outFile: "authmethods/authmethods.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, listTemplate, }, - pluralResourceName: "auth-methods", - typeOnCreate: true, + pluralResourceName: "auth-methods", + extraRequiredParams: []requiredParam{ + { + Name: "resourceType", + Typ: "string", + PostType: "type", + }, + }, versionEnabled: true, createResponseTypes: true, recursiveListing: true, @@ -321,7 +333,7 @@ var inputStructs = []*structInfo{ outFile: "accounts/account.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -349,7 +361,7 @@ var inputStructs = []*structInfo{ outFile: "managedgroups/managedgroups.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -385,15 +397,21 @@ var inputStructs = []*structInfo{ outFile: "credentialstores/credential_store.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, listTemplate, }, - pluralResourceName: "credential-stores", - parentTypeName: "scope", - typeOnCreate: true, + pluralResourceName: "credential-stores", + parentTypeName: "scope", + extraRequiredParams: []requiredParam{ + { + Name: "resourceType", + Typ: "string", + PostType: "type", + }, + }, versionEnabled: true, createResponseTypes: true, recursiveListing: true, @@ -424,7 +442,7 @@ var inputStructs = []*structInfo{ outFile: "credentiallibraries/credential_library.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -446,15 +464,21 @@ var inputStructs = []*structInfo{ outFile: "credentialstores/credential_store.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, listTemplate, }, - pluralResourceName: "credential-stores", - parentTypeName: "scope", - typeOnCreate: true, + pluralResourceName: "credential-stores", + parentTypeName: "scope", + extraRequiredParams: []requiredParam{ + { + Name: "resourceType", + Typ: "string", + PostType: "type", + }, + }, versionEnabled: true, createResponseTypes: true, recursiveListing: true, @@ -485,7 +509,7 @@ var inputStructs = []*structInfo{ outFile: "credentiallibraries/credential_library.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -502,7 +526,7 @@ var inputStructs = []*structInfo{ outFile: "hostcatalogs/host_catalog.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -523,8 +547,14 @@ var inputStructs = []*structInfo{ SkipDefault: true, }, }, - pluralResourceName: "host-catalogs", - typeOnCreate: true, + pluralResourceName: "host-catalogs", + extraRequiredParams: []requiredParam{ + { + Name: "resourceType", + Typ: "string", + PostType: "type", + }, + }, versionEnabled: true, createResponseTypes: true, recursiveListing: true, @@ -534,7 +564,7 @@ var inputStructs = []*structInfo{ outFile: "hosts/host.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -555,7 +585,7 @@ var inputStructs = []*structInfo{ outFile: "hostsets/host_set.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -622,7 +652,7 @@ var inputStructs = []*structInfo{ outFile: "targets/target.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate, + commonCreateTemplate, readTemplate, updateTemplate, deleteTemplate, @@ -676,8 +706,14 @@ var inputStructs = []*structInfo{ FieldType: "[]string", }, }, - versionEnabled: true, - typeOnCreate: true, + versionEnabled: true, + extraRequiredParams: []requiredParam{ + { + Name: "resourceType", + Typ: "string", + PostType: "type", + }, + }, createResponseTypes: true, recursiveListing: true, }, @@ -702,4 +738,40 @@ var inputStructs = []*structInfo{ fieldFilter: []string{"private_key"}, recursiveListing: true, }, + { + inProto: &workers.Worker{}, + outFile: "workers/worker.gen.go", + templates: []*template.Template{ + clientTemplate, + template.Must(template.New("").Funcs( + template.FuncMap{ + "snakeCase": snakeCase, + "funcName": func() string { + return "CreateWorkerLed" + }, + "apiAction": func() string { + return ":create:worker-led" + }, + }, + ).Parse(createTemplateStr)), + readTemplate, + updateTemplate, + deleteTemplate, + listTemplate, + }, + extraRequiredParams: []requiredParam{ + { + Name: "workerAuthToken", + Typ: "string", + PostType: "worker_auth_token", + }, + }, + pluralResourceName: "workers", + createResponseTypes: true, + recursiveListing: true, + }, + { + inProto: &workers.WorkerConfig{}, + outFile: "workers/worker_config.gen.go", + }, } diff --git a/internal/api/genapi/parse.go b/internal/api/genapi/parse.go index e60c703f7..564b48c2e 100644 --- a/internal/api/genapi/parse.go +++ b/internal/api/genapi/parse.go @@ -70,7 +70,7 @@ func parsePBs() { name = fmt.Sprintf("%s.%s", pkg, name) } switch name { - case "v1.AuthorizedCollectionActionsEntry": + case "v1.AuthorizedCollectionActionsEntry", "v1.CanonicalTagsEntry", "v1.TagsEntry": fi.FieldType = "map[string][]string" default: fi.FieldType = sliceText + ptr + name diff --git a/internal/api/genapi/templates.go b/internal/api/genapi/templates.go index 71a6086df..0fce42d72 100644 --- a/internal/api/genapi/templates.go +++ b/internal/api/genapi/templates.go @@ -46,7 +46,7 @@ type templateInput struct { SliceSubtypes map[string]sliceSubtypeInfo ExtraFields []fieldInfo VersionEnabled bool - TypeOnCreate bool + ExtraRequiredParams []requiredParam CreateResponseTypes bool RecursiveListing bool } @@ -65,7 +65,7 @@ func fillTemplates() { ParentTypeName: in.parentTypeName, ExtraFields: in.extraFields, VersionEnabled: in.versionEnabled, - TypeOnCreate: in.typeOnCreate, + ExtraRequiredParams: in.extraRequiredParams, CreateResponseTypes: in.createResponseTypes, RecursiveListing: in.recursiveListing, } @@ -346,14 +346,10 @@ func (c *Client) Delete(ctx context.Context, id string, opt... Option) (*{{ .Nam } `)) -var createTemplate = template.Must(template.New("").Funcs( - template.FuncMap{ - "snakeCase": snakeCase, - }, -).Parse(` -func (c *Client) Create (ctx context.Context, {{ if .TypeOnCreate }} resourceType string, {{ end }} {{ .CollectionFunctionArg }} string, opt... Option) (*{{ .Name }}CreateResult, error) { +const createTemplateStr = ` +func (c *Client) {{ funcName }} (ctx context.Context, {{ range .ExtraRequiredParams }} {{ .Name }} {{ .Typ }}, {{ end }} {{ .CollectionFunctionArg }} string, opt... Option) (*{{ .Name }}CreateResult, error) { if {{ .CollectionFunctionArg }} == "" { - return nil, fmt.Errorf("empty {{ .CollectionFunctionArg }} value passed into Create request") + return nil, fmt.Errorf("empty {{ .CollectionFunctionArg }} value passed into {{ funcName }} request") } opts, apiOpts := getOpts(opt...) @@ -361,17 +357,17 @@ func (c *Client) Create (ctx context.Context, {{ if .TypeOnCreate }} resourceTyp if c.client == nil { return nil, fmt.Errorf("nil client") } - {{ if .TypeOnCreate }} if resourceType == "" { - return nil, fmt.Errorf("empty resourceType value passed into Create request") + {{ range .ExtraRequiredParams }} if {{ .Name }} == "" { + return nil, fmt.Errorf("empty {{ .Name }} value passed into {{ funcName }} request") } else { - opts.postMap["type"] = resourceType + opts.postMap["{{ .PostType }}"] = {{ .Name }} }{{ end }} opts.postMap["{{ snakeCase .CollectionFunctionArg }}"] = {{ .CollectionFunctionArg }} - req, err := c.client.NewRequest(ctx, "POST", "{{ .CollectionPath }}", opts.postMap, apiOpts...) + req, err := c.client.NewRequest(ctx, "POST", "{{ .CollectionPath }}{{ apiAction }}", opts.postMap, apiOpts...) if err != nil { - return nil, fmt.Errorf("error creating Create request: %w", err) + return nil, fmt.Errorf("error creating {{ funcName }} request: %w", err) } {{ if ( eq .CollectionPath "\"scopes\"" ) }} opts.queryMap["scope_id"] = scopeId @@ -386,14 +382,14 @@ func (c *Client) Create (ctx context.Context, {{ if .TypeOnCreate }} resourceTyp resp, err := c.client.Do(req) if err != nil { - return nil, fmt.Errorf("error performing client request during Create call: %w", err) + return nil, fmt.Errorf("error performing client request during {{ funcName }} call: %w", err) } target := new({{ .Name }}CreateResult) target.Item = new({{ .Name }}) apiErr, err := resp.Decode(target.Item) if err != nil { - return nil, fmt.Errorf("error decoding Create response: %w", err) + return nil, fmt.Errorf("error decoding {{ funcName }} response: %w", err) } if apiErr != nil { return nil, apiErr @@ -401,7 +397,19 @@ func (c *Client) Create (ctx context.Context, {{ if .TypeOnCreate }} resourceTyp target.response = resp return target, nil } -`)) +` + +var commonCreateTemplate = template.Must(template.New("").Funcs( + template.FuncMap{ + "snakeCase": snakeCase, + "funcName": func() string { + return "Create" + }, + "apiAction": func() string { + return "" + }, + }, +).Parse(createTemplateStr)) var updateTemplate = template.Must(template.New("").Parse(` func (c *Client) Update(ctx context.Context, id string, version uint32, opt... Option) (*{{ .Name }}UpdateResult, error) { diff --git a/internal/proto/controller/api/resources/workers/v1/worker.proto b/internal/proto/controller/api/resources/workers/v1/worker.proto index ce4cb28dd..6a925c9a6 100644 --- a/internal/proto/controller/api/resources/workers/v1/worker.proto +++ b/internal/proto/controller/api/resources/workers/v1/worker.proto @@ -73,7 +73,7 @@ message Worker { // Output only. This is the address used by authorized session. The value // is the same as the address field if set. Otherwise it uses the address the // worker reports. - string canonical_address = 100; // @gotags: `class:"public"` + string canonical_address = 100 [json_name = "canonical_address"]; // @gotags: `class:"public"` // Output only. The tags attached to this worker. map tags = 110; // @gotags: `class:"public"` @@ -81,7 +81,7 @@ message Worker { // Output only. The deduplicated union of the tags reported by the worker and // the tags added through the user facing api. This is used when applying // worker filters. - map canonical_tags = 120; // @gotags: `class:"public"` + map canonical_tags = 120 [json_name = "canonical_tags"]; // @gotags: `class:"public"` // Output only. The time this worker daemon last reported its status. google.protobuf.Timestamp last_status_time = 130 [json_name = "last_status_time"]; // @gotags: `class:"public"` diff --git a/sdk/pbs/controller/api/resources/workers/worker.pb.go b/sdk/pbs/controller/api/resources/workers/worker.pb.go index 637231736..bf4791563 100644 --- a/sdk/pbs/controller/api/resources/workers/worker.pb.go +++ b/sdk/pbs/controller/api/resources/workers/worker.pb.go @@ -120,13 +120,13 @@ type Worker struct { // Output only. This is the address used by authorized session. The value // is the same as the address field if set. Otherwise it uses the address the // worker reports. - CanonicalAddress string `protobuf:"bytes,100,opt,name=canonical_address,json=canonicalAddress,proto3" json:"canonical_address,omitempty" class:"public"` // @gotags: `class:"public"` + CanonicalAddress string `protobuf:"bytes,100,opt,name=canonical_address,proto3" json:"canonical_address,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The tags attached to this worker. Tags map[string]*structpb.ListValue `protobuf:"bytes,110,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" class:"public"` // @gotags: `class:"public"` // Output only. The deduplicated union of the tags reported by the worker and // the tags added through the user facing api. This is used when applying // worker filters. - CanonicalTags map[string]*structpb.ListValue `protobuf:"bytes,120,rep,name=canonical_tags,json=canonicalTags,proto3" json:"canonical_tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" class:"public"` // @gotags: `class:"public"` + CanonicalTags map[string]*structpb.ListValue `protobuf:"bytes,120,rep,name=canonical_tags,proto3" json:"canonical_tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" class:"public"` // @gotags: `class:"public"` // Output only. The time this worker daemon last reported its status. LastStatusTime *timestamppb.Timestamp `protobuf:"bytes,130,opt,name=last_status_time,proto3" json:"last_status_time,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The set of information the worker daemon has reported about @@ -328,7 +328,7 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x81, 0x0a, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x83, 0x0a, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, @@ -361,60 +361,60 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x1a, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x12, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, 0x0a, + 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, - 0x63, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x04, 0x74, 0x61, - 0x67, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, - 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x6f, 0x6e, - 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, - 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x10, - 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x4b, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x96, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x39, 0x0a, 0x17, - 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xa0, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x17, - 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, - 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, + 0x73, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, + 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x04, 0x74, + 0x61, 0x67, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x66, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, + 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x6f, + 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, + 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x12, 0x47, + 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x4b, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, + 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x96, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x11, 0x77, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x39, + 0x0a, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xa0, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, + 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5c, 0x0a, - 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x50, 0x5a, 0x4e, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, - 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x50, 0x5a, + 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, + 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, + 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, + 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From 3aa7d11419f439a2298544ad80020f2e614c999e Mon Sep 17 00:00:00 2001 From: irenarindos Date: Thu, 9 Jun 2022 16:04:13 -0400 Subject: [PATCH 066/100] feat(worker): Map cluster ID in config to upstream address --- internal/cmd/commands/server/server.go | 9 ++++ internal/cmd/config/config.go | 3 ++ .../daemon/worker/controller_connection.go | 16 ++++-- internal/daemon/worker/status.go | 52 ++++++++++++++++--- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index ef519e882..ce2d41469 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -4,6 +4,7 @@ import ( "context" stderrors "errors" "fmt" + "github.com/hashicorp/go-uuid" "net" "os" "os/signal" @@ -330,6 +331,14 @@ func (c *Command) Run(args []string) int { } } } + + if c.Config.Worker.HCPBClusterId != "" { + _, err := uuid.ParseUUID(c.Config.Worker.HCPBClusterId) + if err != nil { + c.UI.Error(fmt.Errorf("Invalid HCPB cluster id %q: %w", c.Config.Worker.HCPBClusterId, err).Error()) + return base.CommandUserError + } + } } if err := c.SetupListeners(c.UI, c.Config.SharedConfig, []string{"api", "cluster", "proxy", "ops"}); err != nil { c.UI.Error(err.Error()) diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index 4d45c617a..4f2603c12 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -198,6 +198,9 @@ type Worker struct { // AuthStoragePath represents the location a worker stores its node credentials, if set AuthStoragePath string `hcl:"auth_storage_path"` + + // Internal field for use with HCP deployments. Will be used if controllers/ initial_upstreams is not set + HCPBClusterId string `hcl:"hcp_boundary_cluster_id"` } func (w *Worker) InitNameIfEmpty() (string, error) { diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index e75ac8a62..6c7039d79 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -35,6 +35,8 @@ import ( "google.golang.org/protobuf/proto" ) +const hcpb_url_suffix = ".proxy.boundary.hashicorp.cloud" + // StartControllerConnections starts up the resolver and initiates controller // connection client creation func (w *Worker) StartControllerConnections() error { @@ -57,7 +59,13 @@ func (w *Worker) StartControllerConnections() error { } if len(initialAddrs) == 0 { - return errors.New("no initial controller addresses found") + if w.conf.RawConfig.Worker.HCPBClusterId != "" { + clusterAddress := fmt.Sprintf("%s%s", w.conf.RawConfig.Worker.HCPBClusterId, hcpb_url_suffix) + initialAddrs = append(initialAddrs, resolver.Address{Addr: clusterAddress}) + event.WriteSysEvent(context.TODO(), op, fmt.Sprintf("Setting HCPB Cluster address %s as upstream address", clusterAddress)) + } else { + return errors.New("no initial controller addresses found") + } } w.Resolver().InitialState(resolver.State{ @@ -101,8 +109,10 @@ func (w *Worker) controllerDialerFunc() func(context.Context, string) (net.Conn, event.WriteError(ctx, op, err) } - if !w.everAuthenticated.Load() && err == nil && conn != nil { - w.everAuthenticated.Store(true) + if err == nil && conn != nil { + if !w.everAuthenticated.Load() { + w.everAuthenticated.Store(true) + } event.WriteSysEvent(ctx, op, "worker has successfully authenticated") } diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index 8890ef111..3f875df9b 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -3,21 +3,24 @@ package worker import ( "context" "errors" + "fmt" "math/rand" + "sort" + "strings" "time" - "github.com/hashicorp/boundary/internal/servers" - "github.com/hashicorp/boundary/internal/daemon/worker/common" "github.com/hashicorp/boundary/internal/daemon/worker/session" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" + "github.com/hashicorp/boundary/internal/servers" "google.golang.org/grpc/resolver" ) type LastStatusInformation struct { *pbs.StatusResponse - StatusTime time.Time + StatusTime time.Time + LastCalculatedUpstreams []resolver.Address } func (w *Worker) startStatusTicking(cancelCtx context.Context) { @@ -191,14 +194,29 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { } else { w.updateTags.Store(false) // This may be nil if we are in a multiple hop scenario + var addrs []resolver.Address if len(result.CalculatedUpstreams) > 0 { - addrs := make([]resolver.Address, 0, len(result.CalculatedUpstreams)) + addrs = make([]resolver.Address, 0, len(result.CalculatedUpstreams)) for _, v := range result.CalculatedUpstreams { addrs = append(addrs, resolver.Address{Addr: v.Address}) } - w.Resolver().UpdateState(resolver.State{Addresses: addrs}) + lastStatus := w.lastStatusSuccess.Load().(*LastStatusInformation) + // Compare upstreams; update resolver if there is a difference, and emit an event with old and new addresses + if lastStatus != nil && upstreamsHasChanged(lastStatus.LastCalculatedUpstreams, addrs) { + var oldUpstreams []string + for _, v := range lastStatus.LastCalculatedUpstreams { + oldUpstreams = append(oldUpstreams, v.Addr) + } + var newUpstreams []string + for _, v := range addrs { + newUpstreams = append(newUpstreams, v.Addr) + } + upstreamsMessage := fmt.Sprintf("Upstreams has changed; old upstreams were: %s, new upstreams are: %s", oldUpstreams, newUpstreams) + event.WriteSysEvent(context.TODO(), op, upstreamsMessage) + w.Resolver().UpdateState(resolver.State{Addresses: addrs}) + } } - w.lastStatusSuccess.Store(&LastStatusInformation{StatusResponse: result, StatusTime: time.Now()}) + w.lastStatusSuccess.Store(&LastStatusInformation{StatusResponse: result, StatusTime: time.Now(), LastCalculatedUpstreams: addrs}) for _, request := range result.GetJobsRequests() { switch request.GetRequestType() { @@ -239,6 +257,28 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { w.cleanupConnections(cancelCtx, false) } +func upstreamsHasChanged(oldUpstreams, newUpstreams []resolver.Address) bool { + if len(oldUpstreams) != len(newUpstreams) { + return true + } + + // Sort both upstreams + sort.Slice(oldUpstreams, func(i, j int) bool { + return strings.Compare(oldUpstreams[i].Addr, oldUpstreams[j].Addr) < 0 + }) + sort.Slice(newUpstreams, func(i, j int) bool { + return strings.Compare(newUpstreams[i].Addr, newUpstreams[j].Addr) < 0 + }) + + // Compare and return true if change detected + for i, _ := range oldUpstreams { + if oldUpstreams[i].Addr != newUpstreams[i].Addr { + return true + } + } + return false +} + // cleanupConnections walks all sessions and shuts down connections. // Additionally, sessions without connections are cleaned up from the // local worker's state. From 5b978d7fa78a09371f81be57065549d87fc1b9b9 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 9 Jun 2022 16:27:17 -0400 Subject: [PATCH 067/100] Adjust parameter naming (#2161) * Adjust parameter naming for some worker resource fields * Add eventing listener to event when authentication is successful from a downstream worker --- api/workers/worker.gen.go | 42 +-- ...o => worker_provided_configuration.gen.go} | 2 +- globals/fields.go | 4 +- internal/api/genapi/input.go | 8 +- .../server/worker_shutdown_reload_test.go | 4 +- internal/cmd/config/config_test.go | 12 +- .../handlers/workers/worker_service.go | 22 +- .../handlers/workers/worker_service_test.go | 248 ++++++++--------- .../controller/intercepting_listener.go | 2 +- internal/daemon/worker/listeners.go | 40 ++- internal/gen/controller.swagger.json | 12 +- .../api/resources/workers/v1/worker.proto | 10 +- .../api/resources/workers/worker.pb.go | 262 +++++++++--------- 13 files changed, 356 insertions(+), 312 deletions(-) rename api/workers/{worker_config.gen.go => worker_provided_configuration.gen.go} (84%) diff --git a/api/workers/worker.gen.go b/api/workers/worker.gen.go index 430122ee0..7064228a3 100644 --- a/api/workers/worker.gen.go +++ b/api/workers/worker.gen.go @@ -12,23 +12,23 @@ import ( ) type Worker struct { - Id string `json:"id,omitempty"` - ScopeId string `json:"scope_id,omitempty"` - Scope *scopes.ScopeInfo `json:"scope,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - CreatedTime time.Time `json:"created_time,omitempty"` - UpdatedTime time.Time `json:"updated_time,omitempty"` - Version uint32 `json:"version,omitempty"` - Address string `json:"address,omitempty"` - CanonicalAddress string `json:"canonical_address,omitempty"` - Tags map[string][]string `json:"tags,omitempty"` - CanonicalTags map[string][]string `json:"canonical_tags,omitempty"` - LastStatusTime time.Time `json:"last_status_time,omitempty"` - WorkerConfig *WorkerConfig `json:"worker_config,omitempty"` - WorkerAuthToken string `json:"worker_auth_token,omitempty"` - ActiveConnectionCount uint32 `json:"active_connection_count,omitempty"` - AuthorizedActions []string `json:"authorized_actions,omitempty"` + Id string `json:"id,omitempty"` + ScopeId string `json:"scope_id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + CreatedTime time.Time `json:"created_time,omitempty"` + UpdatedTime time.Time `json:"updated_time,omitempty"` + Version uint32 `json:"version,omitempty"` + Address string `json:"address,omitempty"` + CanonicalAddress string `json:"canonical_address,omitempty"` + Tags map[string][]string `json:"tags,omitempty"` + CanonicalTags map[string][]string `json:"canonical_tags,omitempty"` + LastStatusTime time.Time `json:"last_status_time,omitempty"` + WorkerProvidedConfiguration *WorkerProvidedConfiguration `json:"worker_provided_configuration,omitempty"` + WorkerGeneratedAuthToken string `json:"worker_generated_auth_token,omitempty"` + ActiveConnectionCount uint32 `json:"active_connection_count,omitempty"` + AuthorizedActions []string `json:"authorized_actions,omitempty"` response *api.Response } @@ -93,7 +93,7 @@ func (c *Client) ApiClient() *api.Client { return c.client } -func (c *Client) CreateWorkerLed(ctx context.Context, workerAuthToken string, scopeId string, opt ...Option) (*WorkerCreateResult, error) { +func (c *Client) CreateWorkerLed(ctx context.Context, workerGeneratedAuthToken string, scopeId string, opt ...Option) (*WorkerCreateResult, error) { if scopeId == "" { return nil, fmt.Errorf("empty scopeId value passed into CreateWorkerLed request") } @@ -103,10 +103,10 @@ func (c *Client) CreateWorkerLed(ctx context.Context, workerAuthToken string, sc if c.client == nil { return nil, fmt.Errorf("nil client") } - if workerAuthToken == "" { - return nil, fmt.Errorf("empty workerAuthToken value passed into CreateWorkerLed request") + if workerGeneratedAuthToken == "" { + return nil, fmt.Errorf("empty workerGeneratedAuthToken value passed into CreateWorkerLed request") } else { - opts.postMap["worker_auth_token"] = workerAuthToken + opts.postMap["worker_generated_auth_token"] = workerGeneratedAuthToken } opts.postMap["scope_id"] = scopeId diff --git a/api/workers/worker_config.gen.go b/api/workers/worker_provided_configuration.gen.go similarity index 84% rename from api/workers/worker_config.gen.go rename to api/workers/worker_provided_configuration.gen.go index 1e31a3681..4a276211a 100644 --- a/api/workers/worker_config.gen.go +++ b/api/workers/worker_provided_configuration.gen.go @@ -1,7 +1,7 @@ // Code generated by "make api"; DO NOT EDIT. package workers -type WorkerConfig struct { +type WorkerProvidedConfiguration struct { Address string `json:"address,omitempty"` Name string `json:"name,omitempty"` Tags map[string][]string `json:"tags,omitempty"` diff --git a/globals/fields.go b/globals/fields.go index 491b00c8d..84c23f810 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -79,7 +79,7 @@ const ( TagsField = "tags" CanonicalTagsField = "canonical_tags" ConfigurationField = "configuration" - WorkerAuthTokenField = "worker_auth_token" - WorkerConfigField = "worker_config" + WorkerGeneratedAuthTokenField = "worker_generated_auth_token" + WorkerProvidedConfigurationField = "worker_provided_configuration" ActiveConnectionCountField = "active_session_count" ) diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index 943c1b85f..46d90b06a 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -761,9 +761,9 @@ var inputStructs = []*structInfo{ }, extraRequiredParams: []requiredParam{ { - Name: "workerAuthToken", + Name: "workerGeneratedAuthToken", Typ: "string", - PostType: "worker_auth_token", + PostType: "worker_generated_auth_token", }, }, pluralResourceName: "workers", @@ -771,7 +771,7 @@ var inputStructs = []*structInfo{ recursiveListing: true, }, { - inProto: &workers.WorkerConfig{}, - outFile: "workers/worker_config.gen.go", + inProto: &workers.WorkerProvidedConfiguration{}, + outFile: "workers/worker_provided_configuration.gen.go", }, } diff --git a/internal/cmd/commands/server/worker_shutdown_reload_test.go b/internal/cmd/commands/server/worker_shutdown_reload_test.go index bf77ad451..7876e870e 100644 --- a/internal/cmd/commands/server/worker_shutdown_reload_test.go +++ b/internal/cmd/commands/server/worker_shutdown_reload_test.go @@ -25,7 +25,7 @@ import ( _ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets/tcp" ) -const shutdownReloadWorkerConfig = ` +const shutdownReloadWorkerProvidedConfiguration = ` disable_mlock = true worker { @@ -58,7 +58,7 @@ func TestServer_ShutdownWorker(t *testing.T) { // Start the worker workerCmd := testServerCommand(t, testServerCommandOpts{}) - workerCmd.presetConfig = atomic.NewString(fmt.Sprintf(shutdownReloadWorkerConfig, testController.ClusterAddrs()[0], key)) + workerCmd.presetConfig = atomic.NewString(fmt.Sprintf(shutdownReloadWorkerProvidedConfiguration, testController.ClusterAddrs()[0], key)) workerCodeChan := make(chan int) go func() { diff --git a/internal/cmd/config/config_test.go b/internal/cmd/config/config_test.go index 6efbf8d2f..44bbc5e34 100644 --- a/internal/cmd/config/config_test.go +++ b/internal/cmd/config/config_test.go @@ -405,13 +405,13 @@ func TestDevCombined(t *testing.T) { func TestDevWorkerCredentialStorageDir(t *testing.T) { t.Parallel() tests := []struct { - name string - devWorkerConfig string - storagePath string + name string + devWorkerProvidedConfiguration string + storagePath string }{ { name: "Relative Storage Directory", - devWorkerConfig: ` + devWorkerProvidedConfiguration: ` listener "tcp" { purpose = "proxy" } @@ -430,7 +430,7 @@ func TestDevWorkerCredentialStorageDir(t *testing.T) { }, { name: "Nonexistent Storage Directory", - devWorkerConfig: ` + devWorkerProvidedConfiguration: ` listener "tcp" { purpose = "proxy" } @@ -450,7 +450,7 @@ func TestDevWorkerCredentialStorageDir(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - parsed, err := Parse(devConfig + tt.devWorkerConfig) + parsed, err := Parse(devConfig + tt.devWorkerProvidedConfiguration) require.NoError(t, err) require.Equal(t, tt.storagePath, parsed.Worker.AuthStoragePath) }) diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index 98f054bab..b19540974 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -200,7 +200,7 @@ func (s Service) CreateWorkerLed(ctx context.Context, req *pbs.CreateWorkerLedRe if authResults.Error != nil { return nil, authResults.Error } - reqBytes, err := base58.FastBase58Decoding(req.GetItem().WorkerAuthToken.GetValue()) + reqBytes, err := base58.FastBase58Decoding(req.GetItem().WorkerGeneratedAuthToken.GetValue()) if err != nil { return nil, fmt.Errorf("%s: error decoding node_credentials_token: %w", op, err) } @@ -483,18 +483,18 @@ func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (* if in.GetWorkerReportedAddress() != "" || in.GetWorkerReportedName() != "" || len(in.GetConfigTags()) > 0 { - out.WorkerConfig = &pb.WorkerConfig{} + out.WorkerProvidedConfiguration = &pb.WorkerProvidedConfiguration{} } if len(in.GetConfigTags()) > 0 { var err error - out.GetWorkerConfig().Tags, err = tagsToMapProto(in.GetConfigTags()) + out.GetWorkerProvidedConfiguration().Tags, err = tagsToMapProto(in.GetConfigTags()) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing config tags proto")) } } - if out.WorkerConfig != nil { - out.GetWorkerConfig().Address = in.GetWorkerReportedAddress() - out.GetWorkerConfig().Name = in.GetWorkerReportedName() + if out.WorkerProvidedConfiguration != nil { + out.GetWorkerProvidedConfiguration().Address = in.GetWorkerReportedAddress() + out.GetWorkerProvidedConfiguration().Name = in.GetWorkerReportedName() } } @@ -551,7 +551,7 @@ func validateDeleteRequest(req *pbs.DeleteWorkerRequest) error { func validateUpdateRequest(req *pbs.UpdateWorkerRequest) error { return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string { badFields := map[string]string{} - if req.GetItem().GetWorkerConfig() != nil { + if req.GetItem().GetWorkerProvidedConfiguration() != nil { badFields[globals.ConfigurationField] = "This is a read only field." } if req.GetItem().GetCanonicalAddress() != "" { @@ -580,8 +580,8 @@ func validateCreateRequest(req *pbs.CreateWorkerLedRequest) error { } // FIXME: in the future, we won't require this token since we'll support // the server led flow where a token is returned when a worker is created. - if req.GetItem().WorkerAuthToken == nil { - badFields[globals.WorkerAuthTokenField] = cannotBeEmptyMsg + if req.GetItem().WorkerGeneratedAuthToken == nil { + badFields[globals.WorkerGeneratedAuthTokenField] = cannotBeEmptyMsg } if req.GetItem().CanonicalAddress != "" { badFields[globals.CanonicalAddressField] = readOnlyFieldMsg @@ -595,8 +595,8 @@ func validateCreateRequest(req *pbs.CreateWorkerLedRequest) error { if req.GetItem().LastStatusTime != nil { badFields[globals.LastStatusTimeField] = readOnlyFieldMsg } - if req.GetItem().WorkerConfig != nil { - badFields[globals.WorkerConfigField] = readOnlyFieldMsg + if req.GetItem().WorkerProvidedConfiguration != nil { + badFields[globals.WorkerProvidedConfigurationField] = readOnlyFieldMsg } if req.GetItem().AuthorizedActions != nil { badFields[globals.AuthorizedActionsField] = readOnlyFieldMsg diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index f6e200d61..20ef70d9c 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -67,7 +67,7 @@ func TestGet(t *testing.T) { servers.WithName("test worker names"), servers.WithDescription("test worker description"), servers.WithAddress("test worker address"), - servers.WithWorkerTags(&servers.Tag{"key", "val"})) + servers.WithWorkerTags(&servers.Tag{Key: "key", Value: "val"})) // Add config tags to the created worker worker, err = repo.UpsertWorkerStatus(context.Background(), servers.NewWorkerForStatus(worker.GetScopeId(), @@ -101,7 +101,7 @@ func TestGet(t *testing.T) { Tags: map[string]*structpb.ListValue{ "key": structListValue(t, "val"), }, - WorkerConfig: &pb.WorkerConfig{ + WorkerProvidedConfiguration: &pb.WorkerProvidedConfiguration{ Address: worker.GetWorkerReportedAddress(), Name: worker.GetWorkerReportedName(), Tags: map[string]*structpb.ListValue{ @@ -124,7 +124,7 @@ func TestGet(t *testing.T) { res: &pbs.GetWorkerResponse{Item: wantWorker}, }, { - name: "Get a non existant Worker", + name: "Get a non-existent Worker", req: &pbs.GetWorkerRequest{Id: servers.WorkerPrefix + "_DoesntExis"}, res: nil, err: handlers.ApiErrorWithCode(codes.NotFound), @@ -136,7 +136,7 @@ func TestGet(t *testing.T) { err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { - name: "space in id", + name: "Space in id", req: &pbs.GetWorkerRequest{Id: servers.WorkerPrefix + "_1 23456789"}, res: nil, err: handlers.ApiErrorWithCode(codes.InvalidArgument), @@ -186,7 +186,7 @@ func TestList(t *testing.T) { AuthorizedActions: testAuthorizedActions, CanonicalAddress: w.CanonicalAddress(), LastStatusTime: w.GetLastStatusTime().GetTimestamp(), - WorkerConfig: &pb.WorkerConfig{ + WorkerProvidedConfiguration: &pb.WorkerProvidedConfiguration{ Address: w.GetWorkerReportedAddress(), Name: w.GetWorkerReportedName(), }, @@ -370,7 +370,7 @@ func TestUpdate(t *testing.T) { workerService, err := NewService(ctx, repoFn, iamRepoFn) require.NoError(t, err) expectedScope := &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"} - expectedConfig := &pb.WorkerConfig{ + expectedConfig := &pb.WorkerProvidedConfiguration{ Address: wkr.GetWorkerReportedAddress(), Name: wkr.GetWorkerReportedName(), } @@ -395,17 +395,17 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Name: wrapperspb.String("name"), - Description: wrapperspb.String("desc"), - Address: wrapperspb.String("address"), - CanonicalAddress: "address", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerConfig: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("name"), + Description: wrapperspb.String("desc"), + Address: wrapperspb.String("address"), + CanonicalAddress: "address", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerProvidedConfiguration: expectedConfig, + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -422,17 +422,17 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Name: wrapperspb.String("name"), - Description: wrapperspb.String("desc"), - Address: wrapperspb.String("default"), - CanonicalAddress: "default", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerConfig: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("name"), + Description: wrapperspb.String("desc"), + Address: wrapperspb.String("default"), + CanonicalAddress: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerProvidedConfiguration: expectedConfig, + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -488,16 +488,16 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Description: wrapperspb.String("default"), - Address: wrapperspb.String("default"), - CanonicalAddress: "default", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerConfig: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Description: wrapperspb.String("default"), + Address: wrapperspb.String("default"), + CanonicalAddress: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerProvidedConfiguration: expectedConfig, + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -513,16 +513,16 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Name: wrapperspb.String("default"), - Address: wrapperspb.String("default"), - CanonicalAddress: "default", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerConfig: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("default"), + Address: wrapperspb.String("default"), + CanonicalAddress: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerProvidedConfiguration: expectedConfig, + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -539,17 +539,17 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Name: wrapperspb.String("updated"), - Description: wrapperspb.String("default"), - Address: wrapperspb.String("default"), - CanonicalAddress: "default", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerConfig: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("updated"), + Description: wrapperspb.String("default"), + Address: wrapperspb.String("default"), + CanonicalAddress: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerProvidedConfiguration: expectedConfig, + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -566,17 +566,17 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Name: wrapperspb.String("default"), - Description: wrapperspb.String("notignored"), - Address: wrapperspb.String("default"), - CanonicalAddress: "default", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerConfig: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("default"), + Description: wrapperspb.String("notignored"), + Address: wrapperspb.String("default"), + CanonicalAddress: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + WorkerProvidedConfiguration: expectedConfig, + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -598,10 +598,10 @@ func TestUpdate(t *testing.T) { req: &pbs.UpdateWorkerRequest{ Id: wkr.GetPublicId(), UpdateMask: &field_mask.FieldMask{ - Paths: []string{"worker_config.name"}, + Paths: []string{"worker_provided_configuration.name"}, }, Item: &pb.Worker{ - WorkerConfig: &pb.WorkerConfig{ + WorkerProvidedConfiguration: &pb.WorkerProvidedConfiguration{ Name: "name", }, }, @@ -614,10 +614,10 @@ func TestUpdate(t *testing.T) { req: &pbs.UpdateWorkerRequest{ Id: wkr.GetPublicId(), UpdateMask: &field_mask.FieldMask{ - Paths: []string{"worker_config.address"}, + Paths: []string{"worker_provided_configuration.address"}, }, Item: &pb.Worker{ - WorkerConfig: &pb.WorkerConfig{ + WorkerProvidedConfiguration: &pb.WorkerProvidedConfiguration{ Address: "address", }, }, @@ -840,8 +840,8 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: "invalid-scope", - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + ScopeId: "invalid-scope", + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, }, }, wantErr: true, @@ -859,7 +859,7 @@ func TestCreateWorkerLed(t *testing.T) { }, wantErr: true, wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), - wantErrContains: globals.WorkerAuthTokenField, + wantErrContains: globals.WorkerGeneratedAuthTokenField, }, { name: "invalid-id", @@ -867,9 +867,9 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - Id: "invalid-id", + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + Id: "invalid-id", }, }, wantErr: true, @@ -882,9 +882,9 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - CanonicalAddress: "invalid-canonical-address", + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + CanonicalAddress: "invalid-canonical-address", }, }, wantErr: true, @@ -897,8 +897,8 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, Tags: map[string]*structpb.ListValue{ "invalid": {Values: []*structpb.Value{ structpb.NewStringValue("invalid-tags"), @@ -916,8 +916,8 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, CanonicalTags: map[string]*structpb.ListValue{ "invalid": {Values: []*structpb.Value{ structpb.NewStringValue("invalid-canonical-tags"), @@ -935,9 +935,9 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - LastStatusTime: timestamppb.Now(), + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + LastStatusTime: timestamppb.Now(), }, }, wantErr: true, @@ -950,14 +950,14 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - WorkerConfig: &pb.WorkerConfig{}, + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + WorkerProvidedConfiguration: &pb.WorkerProvidedConfiguration{}, }, }, wantErr: true, wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), - wantErrContains: globals.WorkerConfigField, + wantErrContains: globals.WorkerProvidedConfigurationField, }, { name: "invalid-authorized-actions", @@ -965,9 +965,9 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - AuthorizedActions: []string{"invalid-authorized-actions"}, + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + AuthorizedActions: []string{"invalid-authorized-actions"}, }, }, wantErr: true, @@ -980,9 +980,9 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - CreatedTime: timestamppb.Now(), + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + CreatedTime: timestamppb.Now(), }, }, wantErr: true, @@ -995,9 +995,9 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - UpdatedTime: timestamppb.Now(), + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + UpdatedTime: timestamppb.Now(), }, }, wantErr: true, @@ -1010,9 +1010,9 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - Version: 1, + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + Version: 1, }, }, wantErr: true, @@ -1025,8 +1025,8 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: "splat-auth-scope", req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, }, }, wantErr: true, @@ -1038,8 +1038,8 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: "invalid;semicolon"}, + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: "invalid;semicolon"}, }, }, wantErr: true, @@ -1051,8 +1051,8 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerAuthToken: &wrapperspb.StringValue{Value: "notNodeCreds"}, + ScopeId: scope.Global.String(), + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: "notNodeCreds"}, }, }, wantErr: true, @@ -1071,10 +1071,10 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - Name: &wrapperspb.StringValue{Value: "success"}, - Description: &wrapperspb.StringValue{Value: "success-description"}, - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + ScopeId: scope.Global.String(), + Name: &wrapperspb.StringValue{Value: "success"}, + Description: &wrapperspb.StringValue{Value: "success-description"}, + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, }, }, wantErr: true, @@ -1104,10 +1104,10 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - Name: &wrapperspb.StringValue{Value: "success"}, - Description: &wrapperspb.StringValue{Value: "success-description"}, - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + ScopeId: scope.Global.String(), + Name: &wrapperspb.StringValue{Value: "success"}, + Description: &wrapperspb.StringValue{Value: "success-description"}, + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, }, }, wantErr: true, @@ -1119,10 +1119,10 @@ func TestCreateWorkerLed(t *testing.T) { scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ - ScopeId: scope.Global.String(), - Name: &wrapperspb.StringValue{Value: "success"}, - Description: &wrapperspb.StringValue{Value: "success-description"}, - WorkerAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, + ScopeId: scope.Global.String(), + Name: &wrapperspb.StringValue{Value: "success"}, + Description: &wrapperspb.StringValue{Value: "success-description"}, + WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, }, }, res: &pbs.CreateWorkerLedResponse{ diff --git a/internal/daemon/controller/intercepting_listener.go b/internal/daemon/controller/intercepting_listener.go index 8d0d08c54..6a95f18ae 100644 --- a/internal/daemon/controller/intercepting_listener.go +++ b/internal/daemon/controller/intercepting_listener.go @@ -86,7 +86,7 @@ func (m *interceptingListener) Accept() (net.Conn, error) { } return nil, newTempError(fmt.Errorf("error deriving key id from newly-authenticated node: %w", err)) } - event.WriteSysEvent(ctx, op, "worker successfully authed", "key_id", keyId) + event.WriteSysEvent(ctx, op, "worker successfully authenticated", "key_id", keyId) return conn, nil case strings.HasPrefix(tlsConn.ConnectionState().NegotiatedProtocol, "v1workerauth"): diff --git a/internal/daemon/worker/listeners.go b/internal/daemon/worker/listeners.go index 7a8a9a5c6..54ca63ea6 100644 --- a/internal/daemon/worker/listeners.go +++ b/internal/daemon/worker/listeners.go @@ -159,7 +159,12 @@ func (w *Worker) configureForWorker(ln *base.ServerListener, logger *log.Logger) return func() { go w.workerAuthSplitListener.Start() go httpServer.Serve(w.workerAuthSplitListener.OtherListener()) - go ln.GrpcServer.Serve(w.workerAuthSplitListener.NodeEnrollmentListener()) + go ln.GrpcServer.Serve( + &eventingListener{ + ctx: cancelCtx, + baseLn: w.workerAuthSplitListener.NodeEnrollmentListener(), + }, + ) }, nil } @@ -270,3 +275,36 @@ func listenerCloseErrorCheck(lnType string, err error) error { return err } + +type eventingListener struct { + ctx context.Context + baseLn net.Listener +} + +func (e *eventingListener) Accept() (net.Conn, error) { + const op = "worker.(eventingListener).Accept" + conn, err := e.baseLn.Accept() + if err != nil || conn == nil { + return conn, err + } + + // This is all best-effort; anything going wrong here shouldn't disrupt the + // connection, so on error simply stop trying to get to an event + tlsConn, ok := conn.(*tls.Conn) + if ok && len(tlsConn.ConnectionState().PeerCertificates) > 0 { + keyId, err := nodee.KeyIdFromPkix(tlsConn.ConnectionState().PeerCertificates[0].SubjectKeyId) + if err == nil { + event.WriteSysEvent(e.ctx, op, "worker successfully authenticated", "key_id", keyId) + } + } + + return conn, err +} + +func (e *eventingListener) Close() error { + return e.baseLn.Close() +} + +func (e *eventingListener) Addr() net.Addr { + return e.baseLn.Addr() +} diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 750f1d0e9..98dffffe8 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -5577,14 +5577,14 @@ "description": "Output only. The time this worker daemon last reported its status.", "readOnly": true }, - "worker_config": { - "$ref": "#/definitions/controller.api.resources.workers.v1.WorkerConfig", + "worker_provided_configuration": { + "$ref": "#/definitions/controller.api.resources.workers.v1.WorkerProvidedConfiguration", "description": "Output only. The set of information the worker daemon has reported about\nitself.", "readOnly": true }, - "worker_auth_token": { + "worker_generated_auth_token": { "type": "string", - "title": "worker_auth_token is input only. Supports the worker led node\nenrollment flow where this credentials token is produced by a worker. This\ntoken is a base58 encoded types.FetchNodeCredentialsRequest from\nhttps://github.com/hashicorp/nodeenrollment" + "title": "worker_generated_auth_token is input only. Supports the worker led node\nenrollment flow where this credentials token is produced by a worker. This\ntoken is a base58 encoded types.FetchNodeCredentialsRequest from\nhttps://github.com/hashicorp/nodeenrollment" }, "active_connection_count": { "type": "integer", @@ -5603,7 +5603,7 @@ }, "title": "Worker contains all fields related to a Worker resource" }, - "controller.api.resources.workers.v1.WorkerConfig": { + "controller.api.resources.workers.v1.WorkerProvidedConfiguration": { "type": "object", "properties": { "address": { @@ -5628,7 +5628,7 @@ "readOnly": true } }, - "description": "WorkerConfig contains configuration values reported by the worker daemon." + "description": "WorkerProvidedConfiguration contains configuration values reported by the worker daemon." }, "controller.api.services.v1.AddGroupMembersResponse": { "type": "object", diff --git a/internal/proto/controller/api/resources/workers/v1/worker.proto b/internal/proto/controller/api/resources/workers/v1/worker.proto index 6a925c9a6..f54a7d955 100644 --- a/internal/proto/controller/api/resources/workers/v1/worker.proto +++ b/internal/proto/controller/api/resources/workers/v1/worker.proto @@ -10,8 +10,8 @@ import "google/protobuf/wrappers.proto"; option go_package = "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers;workers"; -// WorkerConfig contains configuration values reported by the worker daemon. -message WorkerConfig { +// WorkerProvidedConfiguration contains configuration values reported by the worker daemon. +message WorkerProvidedConfiguration { // Output only. The address the worker daemon reports itself as reachable at. string address = 10; @@ -88,13 +88,13 @@ message Worker { // Output only. The set of information the worker daemon has reported about // itself. - WorkerConfig worker_config = 140 [json_name = "worker_config"]; + WorkerProvidedConfiguration worker_provided_configuration = 140 [json_name = "worker_provided_configuration"]; - // worker_auth_token is input only. Supports the worker led node + // worker_generated_auth_token is input only. Supports the worker led node // enrollment flow where this credentials token is produced by a worker. This // token is a base58 encoded types.FetchNodeCredentialsRequest from // https://github.com/hashicorp/nodeenrollment - google.protobuf.StringValue worker_auth_token = 150 [json_name = "worker_auth_token"]; // @gotags: `class:"public"` + google.protobuf.StringValue worker_generated_auth_token = 150 [json_name = "worker_generated_auth_token"]; // @gotags: `class:"public"` // Output only. The number of connections that this worker is currently handling. uint32 active_connection_count = 160 [json_name = "active_connection_count"]; // @gotags: `class:"public"` diff --git a/sdk/pbs/controller/api/resources/workers/worker.pb.go b/sdk/pbs/controller/api/resources/workers/worker.pb.go index bf4791563..57e01edab 100644 --- a/sdk/pbs/controller/api/resources/workers/worker.pb.go +++ b/sdk/pbs/controller/api/resources/workers/worker.pb.go @@ -25,8 +25,8 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// WorkerConfig contains configuration values reported by the worker daemon. -type WorkerConfig struct { +// WorkerProvidedConfiguration contains configuration values reported by the worker daemon. +type WorkerProvidedConfiguration struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -39,8 +39,8 @@ type WorkerConfig struct { Tags map[string]*structpb.ListValue `protobuf:"bytes,40,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (x *WorkerConfig) Reset() { - *x = WorkerConfig{} +func (x *WorkerProvidedConfiguration) Reset() { + *x = WorkerProvidedConfiguration{} if protoimpl.UnsafeEnabled { mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -48,13 +48,13 @@ func (x *WorkerConfig) Reset() { } } -func (x *WorkerConfig) String() string { +func (x *WorkerProvidedConfiguration) String() string { return protoimpl.X.MessageStringOf(x) } -func (*WorkerConfig) ProtoMessage() {} +func (*WorkerProvidedConfiguration) ProtoMessage() {} -func (x *WorkerConfig) ProtoReflect() protoreflect.Message { +func (x *WorkerProvidedConfiguration) ProtoReflect() protoreflect.Message { mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -66,26 +66,26 @@ func (x *WorkerConfig) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use WorkerConfig.ProtoReflect.Descriptor instead. -func (*WorkerConfig) Descriptor() ([]byte, []int) { +// Deprecated: Use WorkerProvidedConfiguration.ProtoReflect.Descriptor instead. +func (*WorkerProvidedConfiguration) Descriptor() ([]byte, []int) { return file_controller_api_resources_workers_v1_worker_proto_rawDescGZIP(), []int{0} } -func (x *WorkerConfig) GetAddress() string { +func (x *WorkerProvidedConfiguration) GetAddress() string { if x != nil { return x.Address } return "" } -func (x *WorkerConfig) GetName() string { +func (x *WorkerProvidedConfiguration) GetName() string { if x != nil { return x.Name } return "" } -func (x *WorkerConfig) GetTags() map[string]*structpb.ListValue { +func (x *WorkerProvidedConfiguration) GetTags() map[string]*structpb.ListValue { if x != nil { return x.Tags } @@ -131,12 +131,12 @@ type Worker struct { LastStatusTime *timestamppb.Timestamp `protobuf:"bytes,130,opt,name=last_status_time,proto3" json:"last_status_time,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The set of information the worker daemon has reported about // itself. - WorkerConfig *WorkerConfig `protobuf:"bytes,140,opt,name=worker_config,proto3" json:"worker_config,omitempty"` - // worker_auth_token is input only. Supports the worker led node + WorkerProvidedConfiguration *WorkerProvidedConfiguration `protobuf:"bytes,140,opt,name=worker_provided_configuration,proto3" json:"worker_provided_configuration,omitempty"` + // worker_generated_auth_token is input only. Supports the worker led node // enrollment flow where this credentials token is produced by a worker. This // token is a base58 encoded types.FetchNodeCredentialsRequest from // https://github.com/hashicorp/nodeenrollment - WorkerAuthToken *wrapperspb.StringValue `protobuf:"bytes,150,opt,name=worker_auth_token,proto3" json:"worker_auth_token,omitempty" class:"public"` // @gotags: `class:"public"` + WorkerGeneratedAuthToken *wrapperspb.StringValue `protobuf:"bytes,150,opt,name=worker_generated_auth_token,proto3" json:"worker_generated_auth_token,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The number of connections that this worker is currently handling. ActiveConnectionCount uint32 `protobuf:"varint,160,opt,name=active_connection_count,proto3" json:"active_connection_count,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The available actions on this resource for the requester. @@ -266,16 +266,16 @@ func (x *Worker) GetLastStatusTime() *timestamppb.Timestamp { return nil } -func (x *Worker) GetWorkerConfig() *WorkerConfig { +func (x *Worker) GetWorkerProvidedConfiguration() *WorkerProvidedConfiguration { if x != nil { - return x.WorkerConfig + return x.WorkerProvidedConfiguration } return nil } -func (x *Worker) GetWorkerAuthToken() *wrapperspb.StringValue { +func (x *Worker) GetWorkerGeneratedAuthToken() *wrapperspb.StringValue { if x != nil { - return x.WorkerAuthToken + return x.WorkerGeneratedAuthToken } return nil } @@ -314,107 +314,113 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xe2, 0x01, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x4f, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, - 0x67, 0x73, 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x83, 0x0a, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, - 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, - 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, - 0x6f, 0x70, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, - 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, - 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, - 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x1a, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, - 0x29, 0x12, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, - 0x11, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, - 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x04, 0x74, - 0x61, 0x67, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x66, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, - 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, + 0x74, 0x6f, 0x22, 0x80, 0x02, 0x0a, 0x1b, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x5e, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x6f, - 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, - 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x12, 0x47, - 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x4b, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x96, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x11, 0x77, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x39, - 0x0a, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xa0, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, + 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc7, 0x0a, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, + 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, + 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, + 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, + 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x1a, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x12, + 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x63, + 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, + 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x04, 0x74, 0x61, 0x67, + 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, + 0x74, 0x61, 0x67, 0x73, 0x12, 0x66, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, + 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, + 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x63, 0x61, + 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x10, + 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x87, 0x01, 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x5f, 0x0a, 0x1b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x96, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x1b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x39, 0x0a, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xa0, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x12, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x09, + 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x50, 0x5a, - 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, - 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, - 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, + 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, + 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -431,18 +437,18 @@ func file_controller_api_resources_workers_v1_worker_proto_rawDescGZIP() []byte var file_controller_api_resources_workers_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_controller_api_resources_workers_v1_worker_proto_goTypes = []interface{}{ - (*WorkerConfig)(nil), // 0: controller.api.resources.workers.v1.WorkerConfig - (*Worker)(nil), // 1: controller.api.resources.workers.v1.Worker - nil, // 2: controller.api.resources.workers.v1.WorkerConfig.TagsEntry - nil, // 3: controller.api.resources.workers.v1.Worker.TagsEntry - nil, // 4: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry - (*scopes.ScopeInfo)(nil), // 5: controller.api.resources.scopes.v1.ScopeInfo - (*wrapperspb.StringValue)(nil), // 6: google.protobuf.StringValue - (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp - (*structpb.ListValue)(nil), // 8: google.protobuf.ListValue + (*WorkerProvidedConfiguration)(nil), // 0: controller.api.resources.workers.v1.WorkerProvidedConfiguration + (*Worker)(nil), // 1: controller.api.resources.workers.v1.Worker + nil, // 2: controller.api.resources.workers.v1.WorkerProvidedConfiguration.TagsEntry + nil, // 3: controller.api.resources.workers.v1.Worker.TagsEntry + nil, // 4: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry + (*scopes.ScopeInfo)(nil), // 5: controller.api.resources.scopes.v1.ScopeInfo + (*wrapperspb.StringValue)(nil), // 6: google.protobuf.StringValue + (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp + (*structpb.ListValue)(nil), // 8: google.protobuf.ListValue } var file_controller_api_resources_workers_v1_worker_proto_depIdxs = []int32{ - 2, // 0: controller.api.resources.workers.v1.WorkerConfig.tags:type_name -> controller.api.resources.workers.v1.WorkerConfig.TagsEntry + 2, // 0: controller.api.resources.workers.v1.WorkerProvidedConfiguration.tags:type_name -> controller.api.resources.workers.v1.WorkerProvidedConfiguration.TagsEntry 5, // 1: controller.api.resources.workers.v1.Worker.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo 6, // 2: controller.api.resources.workers.v1.Worker.name:type_name -> google.protobuf.StringValue 6, // 3: controller.api.resources.workers.v1.Worker.description:type_name -> google.protobuf.StringValue @@ -452,9 +458,9 @@ var file_controller_api_resources_workers_v1_worker_proto_depIdxs = []int32{ 3, // 7: controller.api.resources.workers.v1.Worker.tags:type_name -> controller.api.resources.workers.v1.Worker.TagsEntry 4, // 8: controller.api.resources.workers.v1.Worker.canonical_tags:type_name -> controller.api.resources.workers.v1.Worker.CanonicalTagsEntry 7, // 9: controller.api.resources.workers.v1.Worker.last_status_time:type_name -> google.protobuf.Timestamp - 0, // 10: controller.api.resources.workers.v1.Worker.worker_config:type_name -> controller.api.resources.workers.v1.WorkerConfig - 6, // 11: controller.api.resources.workers.v1.Worker.worker_auth_token:type_name -> google.protobuf.StringValue - 8, // 12: controller.api.resources.workers.v1.WorkerConfig.TagsEntry.value:type_name -> google.protobuf.ListValue + 0, // 10: controller.api.resources.workers.v1.Worker.worker_provided_configuration:type_name -> controller.api.resources.workers.v1.WorkerProvidedConfiguration + 6, // 11: controller.api.resources.workers.v1.Worker.worker_generated_auth_token:type_name -> google.protobuf.StringValue + 8, // 12: controller.api.resources.workers.v1.WorkerProvidedConfiguration.TagsEntry.value:type_name -> google.protobuf.ListValue 8, // 13: controller.api.resources.workers.v1.Worker.TagsEntry.value:type_name -> google.protobuf.ListValue 8, // 14: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry.value:type_name -> google.protobuf.ListValue 15, // [15:15] is the sub-list for method output_type @@ -471,7 +477,7 @@ func file_controller_api_resources_workers_v1_worker_proto_init() { } if !protoimpl.UnsafeEnabled { file_controller_api_resources_workers_v1_worker_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkerConfig); i { + switch v := v.(*WorkerProvidedConfiguration); i { case 0: return &v.state case 1: From 256498d40fcb91d2b8a83c94cb887662d8ec7e89 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Thu, 9 Jun 2022 16:39:20 -0400 Subject: [PATCH 068/100] fixup! feat(worker): Map cluster ID in config to upstream address --- internal/cmd/commands/server/server.go | 8 ++-- internal/cmd/config/config.go | 6 +-- .../daemon/worker/controller_connection.go | 6 +-- internal/daemon/worker/status.go | 46 ++++--------------- 4 files changed, 20 insertions(+), 46 deletions(-) diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index ce2d41469..f0aed5ed9 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -4,7 +4,6 @@ import ( "context" stderrors "errors" "fmt" - "github.com/hashicorp/go-uuid" "net" "os" "os/signal" @@ -33,6 +32,7 @@ import ( "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/pluginutil/v2" + "github.com/hashicorp/go-uuid" "github.com/mitchellh/cli" "github.com/posener/complete" "go.uber.org/atomic" @@ -332,10 +332,10 @@ func (c *Command) Run(args []string) int { } } - if c.Config.Worker.HCPBClusterId != "" { - _, err := uuid.ParseUUID(c.Config.Worker.HCPBClusterId) + if c.Config.HCPBClusterId != "" { + _, err := uuid.ParseUUID(c.Config.HCPBClusterId) if err != nil { - c.UI.Error(fmt.Errorf("Invalid HCPB cluster id %q: %w", c.Config.Worker.HCPBClusterId, err).Error()) + c.UI.Error(fmt.Errorf("Invalid HCPB cluster id %q: %w", c.Config.HCPBClusterId, err).Error()) return base.CommandUserError } } diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index 4f2603c12..7e054b082 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -124,6 +124,9 @@ type Config struct { // Plugin-related options Plugins Plugins `hcl:"plugins"` + + // Internal field for use with HCP deployments. Used if controllers/ initial_upstreams is not set + HCPBClusterId string `hcl:"hcp_boundary_cluster_id"` } type Controller struct { @@ -198,9 +201,6 @@ type Worker struct { // AuthStoragePath represents the location a worker stores its node credentials, if set AuthStoragePath string `hcl:"auth_storage_path"` - - // Internal field for use with HCP deployments. Will be used if controllers/ initial_upstreams is not set - HCPBClusterId string `hcl:"hcp_boundary_cluster_id"` } func (w *Worker) InitNameIfEmpty() (string, error) { diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index 6c7039d79..82e410e4e 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -35,7 +35,7 @@ import ( "google.golang.org/protobuf/proto" ) -const hcpb_url_suffix = ".proxy.boundary.hashicorp.cloud" +const hcpbUrlSuffix = ".proxy.boundary.hashicorp.cloud" // StartControllerConnections starts up the resolver and initiates controller // connection client creation @@ -59,8 +59,8 @@ func (w *Worker) StartControllerConnections() error { } if len(initialAddrs) == 0 { - if w.conf.RawConfig.Worker.HCPBClusterId != "" { - clusterAddress := fmt.Sprintf("%s%s", w.conf.RawConfig.Worker.HCPBClusterId, hcpb_url_suffix) + if w.conf.RawConfig.HCPBClusterId != "" { + clusterAddress := fmt.Sprintf("%s%s", w.conf.RawConfig.HCPBClusterId, hcpbUrlSuffix) initialAddrs = append(initialAddrs, resolver.Address{Addr: clusterAddress}) event.WriteSysEvent(context.TODO(), op, fmt.Sprintf("Setting HCPB Cluster address %s as upstream address", clusterAddress)) } else { diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index 3f875df9b..14b53e083 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -4,9 +4,8 @@ import ( "context" "errors" "fmt" + "github.com/hashicorp/go-secure-stdlib/strutil" "math/rand" - "sort" - "strings" "time" "github.com/hashicorp/boundary/internal/daemon/worker/common" @@ -20,7 +19,7 @@ import ( type LastStatusInformation struct { *pbs.StatusResponse StatusTime time.Time - LastCalculatedUpstreams []resolver.Address + LastCalculatedUpstreams []string } func (w *Worker) startStatusTicking(cancelCtx context.Context) { @@ -195,28 +194,25 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { w.updateTags.Store(false) // This may be nil if we are in a multiple hop scenario var addrs []resolver.Address + var newUpstreams []string if len(result.CalculatedUpstreams) > 0 { addrs = make([]resolver.Address, 0, len(result.CalculatedUpstreams)) for _, v := range result.CalculatedUpstreams { addrs = append(addrs, resolver.Address{Addr: v.Address}) + newUpstreams = append(newUpstreams, v.Address) } lastStatus := w.lastStatusSuccess.Load().(*LastStatusInformation) // Compare upstreams; update resolver if there is a difference, and emit an event with old and new addresses - if lastStatus != nil && upstreamsHasChanged(lastStatus.LastCalculatedUpstreams, addrs) { - var oldUpstreams []string - for _, v := range lastStatus.LastCalculatedUpstreams { - oldUpstreams = append(oldUpstreams, v.Addr) - } - var newUpstreams []string - for _, v := range addrs { - newUpstreams = append(newUpstreams, v.Addr) - } - upstreamsMessage := fmt.Sprintf("Upstreams has changed; old upstreams were: %s, new upstreams are: %s", oldUpstreams, newUpstreams) + if lastStatus != nil && !strutil.EquivalentSlices(lastStatus.LastCalculatedUpstreams, newUpstreams) { + upstreamsMessage := fmt.Sprintf("Upstreams has changed; old upstreams were: %s, new upstreams are: %s", lastStatus.LastCalculatedUpstreams, newUpstreams) event.WriteSysEvent(context.TODO(), op, upstreamsMessage) w.Resolver().UpdateState(resolver.State{Addresses: addrs}) + } else if lastStatus == nil { + w.Resolver().UpdateState(resolver.State{Addresses: addrs}) + event.WriteSysEvent(context.TODO(), op, fmt.Sprintf("Upstreams after first status set to: %s", newUpstreams)) } } - w.lastStatusSuccess.Store(&LastStatusInformation{StatusResponse: result, StatusTime: time.Now(), LastCalculatedUpstreams: addrs}) + w.lastStatusSuccess.Store(&LastStatusInformation{StatusResponse: result, StatusTime: time.Now(), LastCalculatedUpstreams: newUpstreams}) for _, request := range result.GetJobsRequests() { switch request.GetRequestType() { @@ -257,28 +253,6 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { w.cleanupConnections(cancelCtx, false) } -func upstreamsHasChanged(oldUpstreams, newUpstreams []resolver.Address) bool { - if len(oldUpstreams) != len(newUpstreams) { - return true - } - - // Sort both upstreams - sort.Slice(oldUpstreams, func(i, j int) bool { - return strings.Compare(oldUpstreams[i].Addr, oldUpstreams[j].Addr) < 0 - }) - sort.Slice(newUpstreams, func(i, j int) bool { - return strings.Compare(newUpstreams[i].Addr, newUpstreams[j].Addr) < 0 - }) - - // Compare and return true if change detected - for i, _ := range oldUpstreams { - if oldUpstreams[i].Addr != newUpstreams[i].Addr { - return true - } - } - return false -} - // cleanupConnections walks all sessions and shuts down connections. // Additionally, sessions without connections are cleaned up from the // local worker's state. From 3792fec48b390f3f08c7e03ce6e0f9d43a8145a8 Mon Sep 17 00:00:00 2001 From: irenarindos Date: Thu, 9 Jun 2022 16:42:56 -0400 Subject: [PATCH 069/100] fixup! feat(worker): Map cluster ID in config to upstream address --- internal/daemon/worker/status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index 14b53e083..75b5aae79 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "github.com/hashicorp/go-secure-stdlib/strutil" "math/rand" "time" @@ -13,6 +12,7 @@ import ( pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/go-secure-stdlib/strutil" "google.golang.org/grpc/resolver" ) From b84001c07ddba300d8b9b997356f2fe437e5ee4f Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 10 Jun 2022 09:05:43 -0400 Subject: [PATCH 070/100] Add CLI for workers (#2164) --- api/workers/option.gen.go | 13 + api/workers/worker.gen.go | 21 ++ internal/api/genapi/input.go | 9 +- internal/cmd/commands.go | 38 +++ .../cmd/commands/accountscmd/accounts.gen.go | 5 +- .../authmethodscmd/authmethods.gen.go | 5 +- .../commands/authtokenscmd/authtokens.gen.go | 6 + .../credentiallibraries.gen.go | 5 +- .../credentialstores.gen.go | 5 +- .../hostcatalogscmd/hostcatalogs.gen.go | 5 +- internal/cmd/commands/hostscmd/hosts.gen.go | 5 +- .../cmd/commands/hostsetscmd/hostsets.gen.go | 5 +- .../managedgroupscmd/managedgroups.gen.go | 5 +- internal/cmd/commands/server/server.go | 12 +- .../server/worker_tags_reload_test.go | 12 +- .../cmd/commands/sessionscmd/sessions.gen.go | 6 + .../cmd/commands/targetscmd/targets.gen.go | 5 +- internal/cmd/commands/workerscmd/funcs.go | 215 ++++++++++++ .../commands/workerscmd/worker-led_funcs.go | 63 ++++ .../workerscmd/worker-led_workers.gen.go | 244 ++++++++++++++ .../cmd/commands/workerscmd/workers.gen.go | 314 ++++++++++++++++++ internal/cmd/gencli/input.go | 44 ++- internal/cmd/gencli/templates.go | 10 +- 23 files changed, 1019 insertions(+), 33 deletions(-) create mode 100644 internal/cmd/commands/workerscmd/funcs.go create mode 100644 internal/cmd/commands/workerscmd/worker-led_funcs.go create mode 100644 internal/cmd/commands/workerscmd/worker-led_workers.gen.go create mode 100644 internal/cmd/commands/workerscmd/workers.gen.go diff --git a/api/workers/option.gen.go b/api/workers/option.gen.go index 22be13b4b..29ba168a3 100644 --- a/api/workers/option.gen.go +++ b/api/workers/option.gen.go @@ -1,6 +1,7 @@ package workers import ( + "strconv" "strings" "github.com/hashicorp/boundary/api" @@ -21,6 +22,7 @@ type options struct { withAutomaticVersioning bool withSkipCurlOutput bool withFilter string + withRecursive bool } func getDefaultOptions() options { @@ -44,6 +46,9 @@ func getOpts(opt ...Option) (options, []api.Option) { if opts.withFilter != "" { opts.queryMap["filter"] = opts.withFilter } + if opts.withRecursive { + opts.queryMap["recursive"] = strconv.FormatBool(opts.withRecursive) + } return opts, apiOpts } @@ -74,6 +79,14 @@ func WithFilter(filter string) Option { } } +// WithRecursive tells the API to use recursion for listing operations on this +// resource +func WithRecursive(recurse bool) Option { + return func(o *options) { + o.withRecursive = true + } +} + func WithAddress(inAddress string) Option { return func(o *options) { o.postMap["address"] = inAddress diff --git a/api/workers/worker.gen.go b/api/workers/worker.gen.go index 7064228a3..f108a3191 100644 --- a/api/workers/worker.gen.go +++ b/api/workers/worker.gen.go @@ -3,6 +3,7 @@ package workers import ( "context" + "errors" "fmt" "net/url" "time" @@ -193,6 +194,26 @@ func (c *Client) Update(ctx context.Context, id string, version uint32, opt ...O opts, apiOpts := getOpts(opt...) + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, errors.New("zero version number passed into Update request and automatic versioning not specified") + } + existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...) + if existingErr != nil { + if api.AsServerError(existingErr) != nil { + return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) + } + return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingTarget == nil { + return nil, errors.New("nil resource response found when performing initial check-and-set read") + } + if existingTarget.Item == nil { + return nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Item.Version + } + opts.postMap["version"] = version req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("workers/%s", url.PathEscape(id)), opts.postMap, apiOpts...) diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index 46d90b06a..52032262d 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -738,6 +738,10 @@ var inputStructs = []*structInfo{ fieldFilter: []string{"private_key"}, recursiveListing: true, }, + { + inProto: &workers.WorkerProvidedConfiguration{}, + outFile: "workers/worker_provided_configuration.gen.go", + }, { inProto: &workers.Worker{}, outFile: "workers/worker.gen.go", @@ -769,9 +773,6 @@ var inputStructs = []*structInfo{ pluralResourceName: "workers", createResponseTypes: true, recursiveListing: true, - }, - { - inProto: &workers.WorkerProvidedConfiguration{}, - outFile: "workers/worker_provided_configuration.gen.go", + versionEnabled: true, }, } diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index eb54312c5..466a02752 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -25,6 +25,7 @@ import ( "github.com/hashicorp/boundary/internal/cmd/commands/targetscmd" "github.com/hashicorp/boundary/internal/cmd/commands/userscmd" "github.com/hashicorp/boundary/internal/cmd/commands/version" + "github.com/hashicorp/boundary/internal/cmd/commands/workerscmd" "github.com/mitchellh/cli" ) @@ -1028,5 +1029,42 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "remove-accounts", }, nil }, + + "workers create": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "workers create worker-led": func() (cli.Command, error) { + return &workerscmd.WorkerLedCommand{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "workers read": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + Func: "read", + }, nil + }, + "workers update": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + "workers delete": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + Func: "delete", + }, nil + }, + "workers list": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + Func: "list", + }, nil + }, } } diff --git a/internal/cmd/commands/accountscmd/accounts.gen.go b/internal/cmd/commands/accountscmd/accounts.gen.go index 3e1ee1768..c350ab2cd 100644 --- a/internal/cmd/commands/accountscmd/accounts.gen.go +++ b/internal/cmd/commands/accountscmd/accounts.gen.go @@ -117,7 +117,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/authmethodscmd/authmethods.gen.go b/internal/cmd/commands/authmethodscmd/authmethods.gen.go index 967da8645..6eec33d34 100644 --- a/internal/cmd/commands/authmethodscmd/authmethods.gen.go +++ b/internal/cmd/commands/authmethodscmd/authmethods.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/authtokenscmd/authtokens.gen.go b/internal/cmd/commands/authtokenscmd/authtokens.gen.go index 2cf42c934..e7454237a 100644 --- a/internal/cmd/commands/authtokenscmd/authtokens.gen.go +++ b/internal/cmd/commands/authtokenscmd/authtokens.gen.go @@ -115,6 +115,12 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp + case "create": + return cli.RunResultHelp + + case "update": + return cli.RunResultHelp + } c.plural = "auth token" diff --git a/internal/cmd/commands/credentiallibrariescmd/credentiallibraries.gen.go b/internal/cmd/commands/credentiallibrariescmd/credentiallibraries.gen.go index 2a8d809e0..e83a1c9d3 100644 --- a/internal/cmd/commands/credentiallibrariescmd/credentiallibraries.gen.go +++ b/internal/cmd/commands/credentiallibrariescmd/credentiallibraries.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/credentialstorescmd/credentialstores.gen.go b/internal/cmd/commands/credentialstorescmd/credentialstores.gen.go index 162d88311..54366424a 100644 --- a/internal/cmd/commands/credentialstorescmd/credentialstores.gen.go +++ b/internal/cmd/commands/credentialstorescmd/credentialstores.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/hostcatalogscmd/hostcatalogs.gen.go b/internal/cmd/commands/hostcatalogscmd/hostcatalogs.gen.go index 439956b52..2dbe07472 100644 --- a/internal/cmd/commands/hostcatalogscmd/hostcatalogs.gen.go +++ b/internal/cmd/commands/hostcatalogscmd/hostcatalogs.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/hostscmd/hosts.gen.go b/internal/cmd/commands/hostscmd/hosts.gen.go index 3212c337c..4316dba34 100644 --- a/internal/cmd/commands/hostscmd/hosts.gen.go +++ b/internal/cmd/commands/hostscmd/hosts.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/hostsetscmd/hostsets.gen.go b/internal/cmd/commands/hostsetscmd/hostsets.gen.go index fc4bd76c0..5df30d0be 100644 --- a/internal/cmd/commands/hostsetscmd/hostsets.gen.go +++ b/internal/cmd/commands/hostsetscmd/hostsets.gen.go @@ -117,7 +117,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/managedgroupscmd/managedgroups.gen.go b/internal/cmd/commands/managedgroupscmd/managedgroups.gen.go index 913dfcac0..0a2656650 100644 --- a/internal/cmd/commands/managedgroupscmd/managedgroups.gen.go +++ b/internal/cmd/commands/managedgroupscmd/managedgroups.gen.go @@ -115,7 +115,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index ef519e882..848d21e35 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -538,9 +538,15 @@ func (c *Command) ParseFlagsAndConfig(args []string) int { c.UI.Error("Controller has no name set. It must be the unique name of this instance.") return base.CommandUserError } - if c.Config.Worker != nil && c.Config.Worker.Name == "" { - c.UI.Error("Worker has no name set. It must be the unique name of this instance.") - return base.CommandUserError + if c.Config.Worker != nil { + if c.Config.Worker.Name == "" { + c.UI.Error("Worker has no name set. It must be the unique name of this instance.") + return base.CommandUserError + } + if c.Config.Worker.AuthStoragePath == "" { + c.UI.Error("No worker auth KMS specified and no worker auth storage path specified.") + return base.CommandUserError + } } return base.CommandSuccess diff --git a/internal/cmd/commands/server/worker_tags_reload_test.go b/internal/cmd/commands/server/worker_tags_reload_test.go index 5dce67d62..01114b811 100644 --- a/internal/cmd/commands/server/worker_tags_reload_test.go +++ b/internal/cmd/commands/server/worker_tags_reload_test.go @@ -14,6 +14,8 @@ import ( "crypto/rand" "encoding/base64" "fmt" + "os" + "path/filepath" "sync" "testing" "time" @@ -50,6 +52,7 @@ worker { tags { type = ["dev", "local"] } + auth_storage_path = "%s" } ` @@ -61,6 +64,7 @@ worker { tags { foo = ["bar", "baz"] } + auth_storage_path = "%s" } ` @@ -74,10 +78,14 @@ func TestServer_ReloadWorkerTags(t *testing.T) { testController := controller.NewTestController(t, controller.WithWorkerAuthKms(workerAuthWrapper), controller.WithRootKms(rootWrapper), controller.WithRecoveryKms(recoveryWrapper)) defer testController.Shutdown() + authStoragePath, err := os.MkdirTemp("", "") + require.NoError(err) + t.Cleanup(func() { os.RemoveAll(authStoragePath) }) + wg := &sync.WaitGroup{} cmd := testServerCommand(t, testServerCommandOpts{}) - cmd.presetConfig = atomic.NewString(fmt.Sprintf(workerBaseConfig+tag1Config, key, testController.ClusterAddrs()[0])) + cmd.presetConfig = atomic.NewString(fmt.Sprintf(workerBaseConfig+tag1Config, key, testController.ClusterAddrs()[0], filepath.Join(authStoragePath, "tag1"))) wg.Add(1) go func() { @@ -110,7 +118,7 @@ func TestServer_ReloadWorkerTags(t *testing.T) { time.Sleep(10 * time.Second) fetchWorkerTags("test", "type", []string{"dev", "local"}) - cmd.presetConfig.Store(fmt.Sprintf(workerBaseConfig+tag2Config, key, testController.ClusterAddrs()[0])) + cmd.presetConfig.Store(fmt.Sprintf(workerBaseConfig+tag2Config, key, testController.ClusterAddrs()[0], filepath.Join(authStoragePath, "tag2"))) cmd.SighupCh <- struct{}{} select { diff --git a/internal/cmd/commands/sessionscmd/sessions.gen.go b/internal/cmd/commands/sessionscmd/sessions.gen.go index 372fec9d6..550b4246b 100644 --- a/internal/cmd/commands/sessionscmd/sessions.gen.go +++ b/internal/cmd/commands/sessionscmd/sessions.gen.go @@ -110,6 +110,12 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp + case "create": + return cli.RunResultHelp + + case "update": + return cli.RunResultHelp + } c.plural = "session" diff --git a/internal/cmd/commands/targetscmd/targets.gen.go b/internal/cmd/commands/targetscmd/targets.gen.go index 10e00939d..c4ac75fe6 100644 --- a/internal/cmd/commands/targetscmd/targets.gen.go +++ b/internal/cmd/commands/targetscmd/targets.gen.go @@ -123,7 +123,10 @@ func (c *Command) Run(args []string) int { case "": return cli.RunResultHelp - case "create", "update": + case "create": + return cli.RunResultHelp + + case "update": return cli.RunResultHelp } diff --git a/internal/cmd/commands/workerscmd/funcs.go b/internal/cmd/commands/workerscmd/funcs.go new file mode 100644 index 000000000..1e959eb3a --- /dev/null +++ b/internal/cmd/commands/workerscmd/funcs.go @@ -0,0 +1,215 @@ +package workerscmd + +import ( + "fmt" + "time" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/workers" + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func (c *Command) printListTable(items []*workers.Worker) string { + if len(items) == 0 { + return "No workers found" + } + + var output []string + output = []string{ + "", + "Worker information:", + } + for i, item := range items { + if i > 0 { + output = append(output, "") + } + if item.Id != "" { + output = append(output, + fmt.Sprintf(" ID: %s", item.Id), + ) + } else { + output = append(output, + fmt.Sprintf(" ID: %s", "(not available)"), + ) + } + if c.FlagRecursive && item.ScopeId != "" { + output = append(output, + fmt.Sprintf(" Scope ID: %s", item.ScopeId), + ) + } + if item.Version > 0 { + output = append(output, + fmt.Sprintf(" Version: %d", item.Version), + ) + } + if item.Name != "" { + output = append(output, + fmt.Sprintf(" Name: %s", item.Name), + ) + } + if item.Description != "" { + output = append(output, + fmt.Sprintf(" Description: %s", item.Description), + ) + } + if item.CanonicalAddress != "" { + output = append(output, + fmt.Sprintf(" Canonical Address: %s", item.CanonicalAddress), + ) + } + if !item.LastStatusTime.IsZero() { + output = append(output, + fmt.Sprintf(" Last Status Time: %s", item.LastStatusTime.Format(time.RFC1123)), + ) + } + if true { + output = append(output, + fmt.Sprintf(" Active Connection Count: %d", item.ActiveConnectionCount), + ) + } + + if len(item.AuthorizedActions) > 0 { + output = append(output, + " Authorized Actions:", + base.WrapSlice(6, item.AuthorizedActions), + ) + } + } + + return base.WrapForHelpText(output) +} + +func printItemTable(result api.GenericResult) string { + item := result.GetItem().(*workers.Worker) + nonAttributeMap := map[string]interface{}{} + if item.Id != "" { + nonAttributeMap["ID"] = item.Id + } + if item.Version != 0 { + nonAttributeMap["Version"] = item.Version + } + if !item.CreatedTime.IsZero() { + nonAttributeMap["Created Time"] = item.CreatedTime.Local().Format(time.RFC1123) + } + if !item.UpdatedTime.IsZero() { + nonAttributeMap["Updated Time"] = item.UpdatedTime.Local().Format(time.RFC1123) + } + if item.Name != "" { + nonAttributeMap["Name"] = item.Name + } + if item.Description != "" { + nonAttributeMap["Description"] = item.Description + } + if !item.LastStatusTime.IsZero() { + nonAttributeMap["Last Status Time"] = item.LastStatusTime + } + nonAttributeMap["Active Connection Count"] = item.ActiveConnectionCount + + maxLength := base.MaxAttributesLength(nonAttributeMap, nil, nil) + + ret := []string{ + "", + "Worker information:", + base.WrapMap(2, maxLength+2, nonAttributeMap), + } + + if item.Scope != nil { + ret = append(ret, + "", + " Scope:", + base.ScopeInfoForOutput(item.Scope, maxLength), + ) + } + + var workerProvidedTags map[string][]string + var workerProvidedAddress string + if item.WorkerProvidedConfiguration != nil { + config := item.WorkerProvidedConfiguration + configMap := make(map[string]any) + if config.Address != "" { + configMap["Address"] = config.Address + workerProvidedAddress = config.Address + } + if config.Name != "" { + configMap["Name"] = config.Name + } + ret = append(ret, + "", + " Worker-Provided Configuration:", + base.WrapMap(4, maxLength, configMap), + ) + workerProvidedTags = config.Tags + } + + if len(item.Address) > 0 || len(item.CanonicalAddress) > 0 || len(workerProvidedAddress) > 0 { + ret = append(ret, + "", + " Address:", + ) + if len(item.Address) > 0 { + ret = append(ret, + " Item (via API):", + " "+item.Address, + ) + } + if len(workerProvidedAddress) > 0 { + ret = append(ret, + " Worker Configuration:", + " "+workerProvidedAddress, + ) + } + if len(item.CanonicalAddress) > 0 { + ret = append(ret, + " Canonical:", + " "+item.CanonicalAddress, + ) + } + } + + if len(item.Tags) > 0 || len(item.CanonicalTags) > 0 || len(workerProvidedTags) > 0 { + ret = append(ret, + "", + " Tags:", + ) + if len(item.Tags) > 0 { + tagMap := make(map[string]any, len(item.Tags)) + for k, v := range item.Tags { + tagMap[k] = v + } + ret = append(ret, + " Item (via API):", + base.WrapMap(6, 2, tagMap), + ) + } + if len(workerProvidedTags) > 0 { + tagMap := make(map[string]any, len(workerProvidedTags)) + for k, v := range workerProvidedTags { + tagMap[k] = v + } + ret = append(ret, + " Worker Configuration:", + base.WrapMap(6, 2, tagMap), + ) + } + if len(item.CanonicalTags) > 0 { + tagMap := make(map[string]any, len(item.CanonicalTags)) + for k, v := range item.CanonicalTags { + tagMap[k] = v + } + ret = append(ret, + " Canonical:", + base.WrapMap(6, 2, tagMap), + ) + } + } + + if len(item.AuthorizedActions) > 0 { + ret = append(ret, + "", + " Authorized Actions:", + base.WrapSlice(4, item.AuthorizedActions), + ) + } + + return base.WrapForHelpText(ret) +} diff --git a/internal/cmd/commands/workerscmd/worker-led_funcs.go b/internal/cmd/commands/workerscmd/worker-led_funcs.go new file mode 100644 index 000000000..5c07dda6a --- /dev/null +++ b/internal/cmd/commands/workerscmd/worker-led_funcs.go @@ -0,0 +1,63 @@ +package workerscmd + +import ( + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/workers" + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func init() { + extraWorkerLedActionsFlagsMapFunc = extraWorkerLedActionsFlagsMapFuncImpl + extraWorkerLedFlagsFunc = extraWorkerLedFlagsFuncImpl + executeExtraWorkerLedActions = executeExtraWorkerLedActionsImpl +} + +type extraWorkerLedCmdVars struct { + flagWorkerGeneratedAuthToken string +} + +func extraWorkerLedActionsFlagsMapFuncImpl() map[string][]string { + return map[string][]string{ + "create": {"worker-generated-auth-token"}, + } +} + +func (c *WorkerLedCommand) extraWorkerLedHelpFunc(helpMap map[string]func() string) string { + var helpStr string + switch c.Func { + case "create": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary workers create worker-led [options] [args]", + "", + " Create a worker using the worker-led approach by providing an auth token from the worker. Example:", + "", + ` $ boundary workers create worker-led -name us-east-1-1 -worker-generated-auth-token "`, + "", + "", + }) + } + return helpStr + c.Flags().Help() +} + +func extraWorkerLedFlagsFuncImpl(c *WorkerLedCommand, set *base.FlagSets, f *base.FlagSet) { + f = set.NewFlagSet("Worker Creation Options") + + for _, name := range flagsWorkerLedMap[c.Func] { + switch name { + case "worker-generated-auth-token": + f.StringVar(&base.StringVar{ + Name: "worker-generated-auth-token", + Target: &c.flagWorkerGeneratedAuthToken, + Usage: "The auth token provided by the worker to use to register it to Boundary", + }) + } + } +} + +func executeExtraWorkerLedActionsImpl(c *WorkerLedCommand, origResult api.GenericResult, origError error, workerClient *workers.Client, version uint32, opts []workers.Option) (api.GenericResult, error) { + switch c.Func { + case "create": + return workerClient.CreateWorkerLed(c.Context, c.flagWorkerGeneratedAuthToken, c.FlagScopeId, opts...) + } + return origResult, origError +} diff --git a/internal/cmd/commands/workerscmd/worker-led_workers.gen.go b/internal/cmd/commands/workerscmd/worker-led_workers.gen.go new file mode 100644 index 000000000..caa2c17e9 --- /dev/null +++ b/internal/cmd/commands/workerscmd/worker-led_workers.gen.go @@ -0,0 +1,244 @@ +// Code generated by "make cli"; DO NOT EDIT. +package workerscmd + +import ( + "errors" + "fmt" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/workers" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +func initWorkerLedFlags() { + flagsOnce.Do(func() { + extraFlags := extraWorkerLedActionsFlagsMapFunc() + for k, v := range extraFlags { + flagsWorkerLedMap[k] = append(flagsWorkerLedMap[k], v...) + } + }) +} + +var ( + _ cli.Command = (*WorkerLedCommand)(nil) + _ cli.CommandAutocomplete = (*WorkerLedCommand)(nil) +) + +type WorkerLedCommand struct { + *base.Command + + Func string + + plural string + + extraWorkerLedCmdVars +} + +func (c *WorkerLedCommand) AutocompleteArgs() complete.Predictor { + initWorkerLedFlags() + return complete.PredictAnything +} + +func (c *WorkerLedCommand) AutocompleteFlags() complete.Flags { + initWorkerLedFlags() + return c.Flags().Completions() +} + +func (c *WorkerLedCommand) Synopsis() string { + if extra := extraWorkerLedSynopsisFunc(c); extra != "" { + return extra + } + + synopsisStr := "worker" + + synopsisStr = fmt.Sprintf("%s %s", "worker-led-type", synopsisStr) + + return common.SynopsisFunc(c.Func, synopsisStr) +} + +func (c *WorkerLedCommand) Help() string { + initWorkerLedFlags() + + var helpStr string + helpMap := common.HelpMap("worker") + + switch c.Func { + + default: + + helpStr = c.extraWorkerLedHelpFunc(helpMap) + + } + + // Keep linter from complaining if we don't actually generate code using it + _ = helpMap + return helpStr +} + +var flagsWorkerLedMap = map[string][]string{ + + "create": {"scope-id", "name", "description"}, +} + +func (c *WorkerLedCommand) Flags() *base.FlagSets { + if len(flagsWorkerLedMap[c.Func]) == 0 { + return c.FlagSet(base.FlagSetNone) + } + + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + common.PopulateCommonFlags(c.Command, f, "worker-led-type worker", flagsWorkerLedMap, c.Func) + + extraWorkerLedFlagsFunc(c, set, f) + + return set +} + +func (c *WorkerLedCommand) Run(args []string) int { + initWorkerLedFlags() + + switch c.Func { + case "": + return cli.RunResultHelp + + case "update": + return cli.RunResultHelp + + } + + c.plural = "worker-led-type worker" + switch c.Func { + case "list": + c.plural = "worker-led-type workers" + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + + if strutil.StrListContains(flagsWorkerLedMap[c.Func], "id") && c.FlagId == "" { + c.PrintCliError(errors.New("ID is required but not passed in via -id")) + return base.CommandUserError + } + + var opts []workers.Option + + if strutil.StrListContains(flagsWorkerLedMap[c.Func], "scope-id") { + switch c.Func { + + case "create": + if c.FlagScopeId == "" { + c.PrintCliError(errors.New("Scope ID must be passed in via -scope-id or BOUNDARY_SCOPE_ID")) + return base.CommandUserError + } + + } + } + + client, err := c.Client() + if c.WrapperCleanupFunc != nil { + defer func() { + if err := c.WrapperCleanupFunc(); err != nil { + c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err)) + } + }() + } + if err != nil { + c.PrintCliError(fmt.Errorf("Error creating API client: %w", err)) + return base.CommandCliError + } + workersClient := workers.NewClient(client) + + switch c.FlagName { + case "": + case "null": + opts = append(opts, workers.DefaultName()) + default: + opts = append(opts, workers.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, workers.DefaultDescription()) + default: + opts = append(opts, workers.WithDescription(c.FlagDescription)) + } + + switch c.FlagRecursive { + case true: + opts = append(opts, workers.WithRecursive(true)) + } + + if c.FlagFilter != "" { + opts = append(opts, workers.WithFilter(c.FlagFilter)) + } + + var version uint32 + + if ok := extraWorkerLedFlagsHandlingFunc(c, f, &opts); !ok { + return base.CommandUserError + } + + var result api.GenericResult + + switch c.Func { + + } + + result, err = executeExtraWorkerLedActions(c, result, err, workersClient, version, opts) + + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil { + var opts []base.Option + + c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...) + return base.CommandApiError + } + c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error())) + return base.CommandCliError + } + + output, err := printCustomWorkerLedActionOutput(c) + if err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + if output { + return base.CommandSuccess + } + + switch c.Func { + + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printItemTable(result)) + + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + } + + return base.CommandSuccess +} + +var ( + extraWorkerLedActionsFlagsMapFunc = func() map[string][]string { return nil } + extraWorkerLedSynopsisFunc = func(*WorkerLedCommand) string { return "" } + extraWorkerLedFlagsFunc = func(*WorkerLedCommand, *base.FlagSets, *base.FlagSet) {} + extraWorkerLedFlagsHandlingFunc = func(*WorkerLedCommand, *base.FlagSets, *[]workers.Option) bool { return true } + executeExtraWorkerLedActions = func(_ *WorkerLedCommand, inResult api.GenericResult, inErr error, _ *workers.Client, _ uint32, _ []workers.Option) (api.GenericResult, error) { + return inResult, inErr + } + printCustomWorkerLedActionOutput = func(*WorkerLedCommand) (bool, error) { return false, nil } +) diff --git a/internal/cmd/commands/workerscmd/workers.gen.go b/internal/cmd/commands/workerscmd/workers.gen.go new file mode 100644 index 000000000..a75ad588c --- /dev/null +++ b/internal/cmd/commands/workerscmd/workers.gen.go @@ -0,0 +1,314 @@ +// Code generated by "make cli"; DO NOT EDIT. +package workerscmd + +import ( + "errors" + "fmt" + "sync" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/workers" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +func initFlags() { + flagsOnce.Do(func() { + extraFlags := extraActionsFlagsMapFunc() + for k, v := range extraFlags { + flagsMap[k] = append(flagsMap[k], v...) + } + }) +} + +var ( + _ cli.Command = (*Command)(nil) + _ cli.CommandAutocomplete = (*Command)(nil) +) + +type Command struct { + *base.Command + + Func string + + plural string +} + +func (c *Command) AutocompleteArgs() complete.Predictor { + initFlags() + return complete.PredictAnything +} + +func (c *Command) AutocompleteFlags() complete.Flags { + initFlags() + return c.Flags().Completions() +} + +func (c *Command) Synopsis() string { + if extra := extraSynopsisFunc(c); extra != "" { + return extra + } + + synopsisStr := "worker" + + return common.SynopsisFunc(c.Func, synopsisStr) +} + +func (c *Command) Help() string { + initFlags() + + var helpStr string + helpMap := common.HelpMap("worker") + + switch c.Func { + + case "read": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + case "update": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + case "delete": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + case "list": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + default: + + helpStr = helpMap["base"]() + + } + + // Keep linter from complaining if we don't actually generate code using it + _ = helpMap + return helpStr +} + +var flagsMap = map[string][]string{ + + "read": {"id"}, + + "update": {"id", "name", "description", "version"}, + + "delete": {"id"}, + + "list": {"scope-id", "filter", "recursive"}, +} + +func (c *Command) Flags() *base.FlagSets { + if len(flagsMap[c.Func]) == 0 { + return c.FlagSet(base.FlagSetNone) + } + + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + common.PopulateCommonFlags(c.Command, f, "worker", flagsMap, c.Func) + + extraFlagsFunc(c, set, f) + + return set +} + +func (c *Command) Run(args []string) int { + initFlags() + + switch c.Func { + case "": + return cli.RunResultHelp + + case "create": + return cli.RunResultHelp + + } + + c.plural = "worker" + switch c.Func { + case "list": + c.plural = "workers" + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + + if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" { + c.PrintCliError(errors.New("ID is required but not passed in via -id")) + return base.CommandUserError + } + + var opts []workers.Option + + if strutil.StrListContains(flagsMap[c.Func], "scope-id") { + switch c.Func { + + case "list": + if c.FlagScopeId == "" { + c.PrintCliError(errors.New("Scope ID must be passed in via -scope-id or BOUNDARY_SCOPE_ID")) + return base.CommandUserError + } + + } + } + + client, err := c.Client() + if c.WrapperCleanupFunc != nil { + defer func() { + if err := c.WrapperCleanupFunc(); err != nil { + c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err)) + } + }() + } + if err != nil { + c.PrintCliError(fmt.Errorf("Error creating API client: %w", err)) + return base.CommandCliError + } + workersClient := workers.NewClient(client) + + switch c.FlagName { + case "": + case "null": + opts = append(opts, workers.DefaultName()) + default: + opts = append(opts, workers.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, workers.DefaultDescription()) + default: + opts = append(opts, workers.WithDescription(c.FlagDescription)) + } + + switch c.FlagRecursive { + case true: + opts = append(opts, workers.WithRecursive(true)) + } + + if c.FlagFilter != "" { + opts = append(opts, workers.WithFilter(c.FlagFilter)) + } + + var version uint32 + + switch c.Func { + + case "update": + switch c.FlagVersion { + case 0: + opts = append(opts, workers.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + + } + + if ok := extraFlagsHandlingFunc(c, f, &opts); !ok { + return base.CommandUserError + } + + var result api.GenericResult + + var listResult api.GenericListResult + + switch c.Func { + + case "read": + result, err = workersClient.Read(c.Context, c.FlagId, opts...) + + case "update": + result, err = workersClient.Update(c.Context, c.FlagId, version, opts...) + + case "delete": + result, err = workersClient.Delete(c.Context, c.FlagId, opts...) + + case "list": + listResult, err = workersClient.List(c.Context, c.FlagScopeId, opts...) + + } + + result, err = executeExtraActions(c, result, err, workersClient, version, opts) + + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil { + var opts []base.Option + + c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...) + return base.CommandApiError + } + c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error())) + return base.CommandCliError + } + + output, err := printCustomActionOutput(c) + if err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + if output { + return base.CommandSuccess + } + + switch c.Func { + + case "delete": + switch base.Format(c.UI) { + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + + case "table": + c.UI.Output("The delete operation completed successfully.") + } + + return base.CommandSuccess + + case "list": + switch base.Format(c.UI) { + case "json": + if ok := c.PrintJsonItems(listResult); !ok { + return base.CommandCliError + } + + case "table": + listedItems := listResult.GetItems().([]*workers.Worker) + c.UI.Output(c.printListTable(listedItems)) + } + + return base.CommandSuccess + + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printItemTable(result)) + + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + } + + return base.CommandSuccess +} + +var ( + flagsOnce = new(sync.Once) + + extraActionsFlagsMapFunc = func() map[string][]string { return nil } + extraSynopsisFunc = func(*Command) string { return "" } + extraFlagsFunc = func(*Command, *base.FlagSets, *base.FlagSet) {} + extraFlagsHandlingFunc = func(*Command, *base.FlagSets, *[]workers.Option) bool { return true } + executeExtraActions = func(_ *Command, inResult api.GenericResult, inErr error, _ *workers.Client, _ uint32, _ []workers.Option) (api.GenericResult, error) { + return inResult, inErr + } + printCustomActionOutput = func(*Command) (bool, error) { return false, nil } +) diff --git a/internal/cmd/gencli/input.go b/internal/cmd/gencli/input.go index 0c57d3827..cc64b3d85 100644 --- a/internal/cmd/gencli/input.go +++ b/internal/cmd/gencli/input.go @@ -30,10 +30,6 @@ type cmdInfo struct { // output env var and print HasExampleCliOutput bool - // IsAbstractType triggers some behavior specialized for abstract types, - // e.g. those that have subcommands for create/update - IsAbstractType bool - // HasId controls whether to add ID emptiness checking. Note that some // commands that allow name/scope id or name/scope name handling may skip // this in favor of custom logic. @@ -79,6 +75,10 @@ type cmdInfo struct { // IsPluginType controls whether standard plugin flags are generated IsPluginType bool + + // SkipClientCallActions allows skipping creation of an actual client + // call for an action in favor of custom logic in extra actions + SkipClientCallActions []string } var inputStructs = map[string][]*cmdInfo{ @@ -89,7 +89,6 @@ var inputStructs = map[string][]*cmdInfo{ StdActions: []string{"read", "delete", "list"}, HasExtraCommandVars: true, HasExtraHelpFunc: true, - IsAbstractType: true, Container: "AuthMethod", HasId: true, HasName: true, @@ -130,7 +129,6 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.AuthMethod.String(), Pkg: "authmethods", StdActions: []string{"read", "delete", "list"}, - IsAbstractType: true, HasExtraHelpFunc: true, Container: "Scope", HasId: true, @@ -179,7 +177,6 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.CredentialStore.String(), Pkg: "credentialstores", StdActions: []string{"read", "delete", "list"}, - IsAbstractType: true, HasExtraHelpFunc: true, Container: "Scope", HasId: true, @@ -206,7 +203,6 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.CredentialLibrary.String(), Pkg: "credentiallibraries", StdActions: []string{"read", "delete", "list"}, - IsAbstractType: true, HasExtraHelpFunc: true, Container: "CredentialStore", HasId: true, @@ -246,7 +242,6 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.HostCatalog.String(), Pkg: "hostcatalogs", StdActions: []string{"read", "delete", "list"}, - IsAbstractType: true, HasExtraHelpFunc: true, Container: "Scope", HasId: true, @@ -290,7 +285,6 @@ var inputStructs = map[string][]*cmdInfo{ StdActions: []string{"read", "delete", "list"}, HasExtraCommandVars: true, HasExtraHelpFunc: true, - IsAbstractType: true, Container: "HostCatalog", HasId: true, HasName: true, @@ -332,7 +326,6 @@ var inputStructs = map[string][]*cmdInfo{ Pkg: "hosts", StdActions: []string{"read", "delete", "list"}, HasExtraHelpFunc: true, - IsAbstractType: true, Container: "HostCatalog", HasId: true, HasName: true, @@ -358,7 +351,6 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.ManagedGroup.String(), Pkg: "managedgroups", StdActions: []string{"read", "delete", "list"}, - IsAbstractType: true, Container: "AuthMethod", HasId: true, HasName: true, @@ -425,7 +417,6 @@ var inputStructs = map[string][]*cmdInfo{ HasExtraCommandVars: true, HasExtraHelpFunc: true, HasExampleCliOutput: true, - IsAbstractType: true, HasName: true, HasDescription: true, Container: "Scope", @@ -461,4 +452,31 @@ var inputStructs = map[string][]*cmdInfo{ VersionedActions: []string{"update", "add-accounts", "remove-accounts", "set-accounts"}, }, }, + "workers": { + { + ResourceType: resource.Worker.String(), + Pkg: "workers", + StdActions: []string{"read", "update", "delete", "list"}, + HasId: true, + Container: "Scope", + HasName: true, + HasDescription: true, + VersionedActions: []string{"update"}, + }, + { + ResourceType: resource.Worker.String(), + Pkg: "workers", + StdActions: []string{"create"}, + SubActionPrefix: "worker-led", + HasExtraCommandVars: true, + SkipNormalHelp: true, + HasExtraHelpFunc: true, + HasId: true, + HasName: true, + Container: "Scope", + HasDescription: true, + NeedsSubtypeInCreate: true, + SkipClientCallActions: []string{"create"}, + }, + }, } diff --git a/internal/cmd/gencli/templates.go b/internal/cmd/gencli/templates.go index 230e70a06..4cb5fd21e 100644 --- a/internal/cmd/gencli/templates.go +++ b/internal/cmd/gencli/templates.go @@ -229,8 +229,12 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Run(args []string) int { switch c.Func { case "": return cli.RunResultHelp - {{ if .IsAbstractType }} - case "create", "update": + {{ if (not (hasAction .StdActions "create" ) )}} + case "create": + return cli.RunResultHelp + {{ end }} + {{ if (not (hasAction .StdActions "update" ) )}} + case "update": return cli.RunResultHelp {{ end }} } @@ -403,9 +407,11 @@ func (c *{{ camelCase .SubActionPrefix }}Command) Run(args []string) int { switch c.Func { {{ range $i, $action := $input.StdActions }} {{ if eq $action "create" }} + {{ if ( not ( hasAction $input.SkipClientCallActions "create") ) }} case "create": result, err = {{ $input.Pkg }}Client.Create(c.Context, {{ if (and $input.SubActionPrefix $input.NeedsSubtypeInCreate) }}"{{ $input.SubActionPrefix }}",{{ end }} c.Flag{{ $input.Container }}Id, opts...) {{ end }} + {{ end }} {{ if eq $action "read" }} case "read": result, err = {{ $input.Pkg }}Client.Read(c.Context, c.FlagId, opts...) From 16d95262a6f757a56a7427e87ac3bbb0cd0a9c9a Mon Sep 17 00:00:00 2001 From: irenarindos Date: Mon, 6 Jun 2022 14:25:14 -0400 Subject: [PATCH 071/100] feature(worker): add workerKeyId to status --- .../daemon/cluster/handlers/worker_service.go | 34 +- .../handlers/worker_service_status_test.go | 196 ++++++++- internal/daemon/worker/status.go | 11 + internal/daemon/worker/worker.go | 18 +- internal/daemon/worker/worker_test.go | 12 +- .../99/02_worker_controller_tables.up.sql | 14 +- .../oss/postgres/99/04_views.up.sql | 2 +- .../servers/services/session_service.pb.go | 394 +++++++++--------- .../servers/services/v1/session_service.proto | 3 + .../proto/controller/servers/v1/servers.proto | 3 + .../storage/servers/store/v1/worker.proto | 4 + internal/servers/public_ids.go | 12 - internal/servers/repository_worker.go | 62 ++- internal/servers/repository_worker_test.go | 60 +++ internal/servers/servers.pb.go | 22 +- internal/servers/store/worker.pb.go | 39 +- internal/servers/testing.go | 4 +- internal/servers/worker.go | 3 + internal/servers/worker_test.go | 47 +++ 19 files changed, 679 insertions(+), 261 deletions(-) diff --git a/internal/daemon/cluster/handlers/worker_service.go b/internal/daemon/cluster/handlers/worker_service.go index ac7e03aee..d1badb5a3 100644 --- a/internal/daemon/cluster/handlers/worker_service.go +++ b/internal/daemon/cluster/handlers/worker_service.go @@ -60,9 +60,12 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques // terminate all sessions since we can't know if they were canceled? wStat := req.GetWorkerStatus() + if wStat == nil { + return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Worker sent nil status.") + } switch { - case wStat.GetName() == "": - return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Name is not set in the request but is required.") + case wStat.GetName() == "" && wStat.GetKeyId() == "": + return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Name and keyId are not set in the request; at least one is required.") case wStat.GetAddress() == "": return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Address is not set but is required.") } @@ -85,10 +88,13 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques }) } + wKeyId := wStat.GetKeyId() + wConf := servers.NewWorkerForStatus(scope.Global.String(), servers.WithName(wStat.GetName()), servers.WithAddress(wStat.GetAddress()), - servers.WithWorkerTags(workerTags...)) + servers.WithWorkerTags(workerTags...), + servers.WithKeyId(wKeyId)) opts := []servers.Option{servers.WithUpdateTags(req.GetUpdateTags())} if wStat.GetPublicId() != "" { opts = append(opts, servers.WithPublicId(wStat.GetPublicId())) @@ -221,10 +227,24 @@ func (ws *workerServiceServer) LookupSession(ctx context.Context, req *pbs.Looku event.WriteError(ctx, op, err, event.WithInfoMsg("error getting servers repo")) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error acquiring server repo when looking up session: %v", err) } - w, err := serversRepo.LookupWorkerByWorkerReportedName(ctx, req.GetServerId()) - if err != nil { - event.WriteError(ctx, op, err, event.WithInfoMsg("error looking up worker by name", "name", req.ServerId)) - return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error looking up tags for server: %v", err) + var w *servers.Worker + if req.WorkerKeyId != "" { + workerId, err := serversRepo.LookupWorkerIdByWorkerReportedKeyId(ctx, req.WorkerKeyId) + if err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error looking up worker by keyId", "keyId", req.WorkerKeyId)) + return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error looking up tags for server: %v", err) + } + w, err = serversRepo.LookupWorker(ctx, workerId) + if err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error looking up worker by workerId", "workerId", workerId)) + return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error looking up tags for server: %v", err) + } + } else { + w, err = serversRepo.LookupWorkerByWorkerReportedName(ctx, req.GetServerId()) + if err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error looking up worker by name", "name", req.ServerId)) + return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error looking up tags for server: %v", err) + } } // Build the map for filtering. tagMap := w.CanonicalTags() diff --git a/internal/daemon/cluster/handlers/worker_service_status_test.go b/internal/daemon/cluster/handlers/worker_service_status_test.go index d34924890..144a753b2 100644 --- a/internal/daemon/cluster/handlers/worker_service_status_test.go +++ b/internal/daemon/cluster/handlers/worker_service_status_test.go @@ -19,6 +19,11 @@ import ( "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/registration" + "github.com/hashicorp/nodeenrollment/rotation" + "github.com/hashicorp/nodeenrollment/storage/file" + "github.com/hashicorp/nodeenrollment/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" @@ -156,7 +161,7 @@ func TestStatus(t *testing.T) { }, }, { - name: "No Name", + name: "No Name or keyId", wantErr: true, req: &pbs.StatusRequest{ WorkerStatus: &servers.ServerWorkerStatus{ @@ -164,7 +169,7 @@ func TestStatus(t *testing.T) { Address: worker1.CanonicalAddress(), }, }, - wantErrMsg: status.Error(codes.InvalidArgument, "Name is not set in the request but is required.").Error(), + wantErrMsg: status.Error(codes.InvalidArgument, "Name and keyId are not set in the request; at least one is required.").Error(), }, { name: "No Address", @@ -531,3 +536,190 @@ func TestStatusDeadConnection(t *testing.T) { assert.Nil(t, states[0].EndTime) assert.Equal(t, session.StatusClosed, states[0].Status) } + +func TestStatusWorkerWithKeyId(t *testing.T) { + ctx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + serverRepo, _ := servers.NewRepository(rw, rw, kms) + serverRepo.UpsertController(ctx, &store.Controller{ + PrivateId: "test_controller1", + Address: "127.0.0.1", + }) + serversRepoFn := func() (*servers.Repository, error) { + return serverRepo, nil + } + sessionRepoFn := func() (*session.Repository, error) { + return session.NewRepository(rw, rw, kms) + } + connRepoFn := func() (*session.ConnectionRepository, error) { + return session.NewConnectionRepository(ctx, rw, rw, kms) + } + + repo, err := sessionRepoFn() + require.NoError(t, err) + connRepo, err := connRepoFn() + require.NoError(t, err) + + at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) + uId := at.GetIamUserId() + hc := static.TestCatalogs(t, conn, prj.GetPublicId(), 1)[0] + hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] + h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] + static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) + tar := tcp.TestTarget( + ctx, + t, conn, prj.GetPublicId(), "test", + target.WithHostSources([]string{hs.GetPublicId()}), + target.WithSessionConnectionLimit(-1), + ) + + worker1 := servers.TestWorker(t, conn, wrapper) + + rootStorage, err := servers.NewRepositoryStorage(ctx, rw, rw, kms) + require.NoError(t, err) + + _, err = rotation.RotateRootCertificates(ctx, rootStorage) + require.NoError(t, err) + + // Create struct to pass in with workerId that will be passed along to storage + state, err := servers.AttachWorkerIdToState(ctx, worker1.PublicId) + require.NoError(t, err) + + // This happens on the worker + fileStorage, err := file.New(ctx) + require.NoError(t, err) + nodeCreds, err := types.NewNodeCredentials(ctx, fileStorage) + require.NoError(t, err) + // Create request using worker id + fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(ctx) + require.NoError(t, err) + + // The AuthorizeNode request will result in a WorkerAuth record being stored under the workerId + nodeInfo, err := registration.AuthorizeNode(ctx, rootStorage, fetchReq, nodeenrollment.WithState(state)) + require.NoError(t, err) + + sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ + UserId: uId, + HostId: h.GetPublicId(), + TargetId: tar.GetPublicId(), + HostSetId: hs.GetPublicId(), + AuthTokenId: at.GetPublicId(), + ScopeId: prj.GetPublicId(), + Endpoint: "tcp://127.0.0.1:22", + ConnectionLimit: 10, + }) + tofu := session.TestTofu(t) + sess, _, err = repo.ActivateSession(ctx, sess.PublicId, sess.Version, tofu) + require.NoError(t, err) + require.NoError(t, err) + + s := handlers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connRepoFn, new(sync.Map), kms) + require.NotNil(t, s) + + connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PublicId) + require.NoError(t, err) + + cases := []struct { + name string + wantErr bool + wantErrMsg string + req *pbs.StatusRequest + want *pbs.StatusResponse + }{ + { + name: "Identify workerID based on keyId in status", + wantErr: false, + req: &pbs.StatusRequest{ + WorkerStatus: &servers.ServerWorkerStatus{ + Name: worker1.GetWorkerReportedName(), + Address: worker1.CanonicalAddress(), + KeyId: nodeInfo.Id, + }, + }, + want: &pbs.StatusResponse{ + CalculatedUpstreams: []*pbs.UpstreamServer{ + { + Type: pbs.UpstreamServer_TYPE_CONTROLLER, + Address: "127.0.0.1", + }, + }, + WorkerId: worker1.PublicId, + }, + }, + { + name: "Active keyId Worker", + wantErr: false, + req: &pbs.StatusRequest{ + WorkerStatus: &servers.ServerWorkerStatus{ + KeyId: nodeInfo.Id, + Name: worker1.GetWorkerReportedName(), + Address: worker1.CanonicalAddress(), + }, + Jobs: []*pbs.JobStatus{ + { + Job: &pbs.Job{ + Type: pbs.JOBTYPE_JOBTYPE_SESSION, + JobInfo: &pbs.Job_SessionInfo{ + SessionInfo: &pbs.SessionJobInfo{ + SessionId: sess.PublicId, + Status: pbs.SESSIONSTATUS_SESSIONSTATUS_ACTIVE, + Connections: []*pbs.Connection{ + { + ConnectionId: connection.PublicId, + Status: pbs.CONNECTIONSTATUS_CONNECTIONSTATUS_CONNECTED, + }, + }, + }, + }, + }, + }, + }, + }, + want: &pbs.StatusResponse{ + CalculatedUpstreams: []*pbs.UpstreamServer{ + { + Type: pbs.UpstreamServer_TYPE_CONTROLLER, + Address: "127.0.0.1", + }, + }, + WorkerId: worker1.PublicId, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + got, err := s.Status(ctx, tc.req) + if tc.wantErr { + require.Error(err) + assert.Equal(got, &pbs.StatusResponse{}) + assert.Equal(tc.wantErrMsg, err.Error()) + return + } + assert.Empty( + cmp.Diff( + tc.want, + got, + cmpopts.IgnoreUnexported( + pbs.StatusResponse{}, + servers.ServerWorkerStatus{}, + pbs.UpstreamServer{}, + pbs.JobChangeRequest{}, + pbs.Job{}, + pbs.Job_SessionInfo{}, + pbs.SessionJobInfo{}, + pbs.Connection{}, + ), + cmpopts.IgnoreFields(servers.ServerWorkerStatus{}, "Tags"), + ), + ) + }) + } +} diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index 8890ef111..4bb3ed1cd 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -145,12 +145,23 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { statusCtx, statusCancel := context.WithTimeout(cancelCtx, common.StatusTimeout) defer statusCancel() + var keyId string + if w.WorkerAuthCurrentKeyId != "" { + keyId = w.WorkerAuthCurrentKeyId + } + + if w.conf.RawConfig.Worker.Name == "" && keyId == "" { + event.WriteError(statusCtx, op, errors.New("worker name and keyId are both empty; one is needed to identify a worker"), + event.WithInfoMsg("error making status request to controller")) + } + result, err := client.Status(statusCtx, &pbs.StatusRequest{ Jobs: activeJobs, WorkerStatus: &servers.ServerWorkerStatus{ Name: w.conf.RawConfig.Worker.Name, Address: w.conf.RawConfig.Worker.PublicAddr, Tags: tags, + KeyId: keyId, }, UpdateTags: w.updateTags.Load(), }) diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 31b95380c..7c759d276 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -190,10 +190,6 @@ func (w *Worker) Start() error { controllerResolver := manual.NewBuilderWithScheme(scheme) w.controllerResolver.Store(controllerResolver) - if err := w.startListeners(); err != nil { - return fmt.Errorf("error starting worker listeners: %w", err) - } - if w.conf.WorkerAuthKms == nil { // In this section, we look for existing worker credentials. The two // variables below store whether to create new credentials and whether @@ -291,8 +287,17 @@ func (w *Worker) Start() error { return fmt.Errorf("error deriving worker auth key id: %w", err) } } + // Regardless, we want to load the currentKeyId + currentKeyId, err := nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) + if err != nil { + return fmt.Errorf("error deriving worker auth key id: %w", err) + } + w.WorkerAuthCurrentKeyId.Store(currentKeyId) } + if err := w.startListeners(); err != nil { + return fmt.Errorf("error starting worker listeners: %w", err) + } if err := w.StartControllerConnections(); err != nil { return fmt.Errorf("error making controller connections: %w", err) } @@ -499,8 +504,9 @@ func (w *Worker) getSessionTls(hello *tls.ClientHelloInfo) (*tls.Config, error) defer cancel() resp, err := conn.LookupSession(timeoutContext, &pbs.LookupSessionRequest{ - ServerId: w.conf.RawConfig.Worker.Name, - SessionId: sessionId, + ServerId: w.conf.RawConfig.Worker.Name, + SessionId: sessionId, + WorkerKeyId: w.WorkerAuthCurrentKeyId.Load(), }) if err != nil { return nil, fmt.Errorf("error validating session: %w", err) diff --git a/internal/daemon/worker/worker_test.go b/internal/daemon/worker/worker_test.go index fcd57d79a..017bd978f 100644 --- a/internal/daemon/worker/worker_test.go +++ b/internal/daemon/worker/worker_test.go @@ -200,7 +200,6 @@ func TestSetupWorkerAuthStorage(t *testing.T) { name string in func(t *testing.T, w *Worker) expKeyId string // If set, the existing key ID to expect - expCreation bool // Whether we should expect a new key ID expRegistrationRequest bool // Whether we should have seen a registration request generated expError string // Some other error }{ @@ -209,7 +208,6 @@ func TestSetupWorkerAuthStorage(t *testing.T) { in: func(t *testing.T, w *Worker) { // Do nothing; in this case it will have already been cleared }, - expCreation: true, expRegistrationRequest: true, }, { @@ -218,6 +216,7 @@ func TestSetupWorkerAuthStorage(t *testing.T) { // Store the authorized creds require.NoError(t, initNodeCreds.Store(ctx, w.WorkerAuthStorage)) }, + expKeyId: initKeyId, }, { name: "existing but not validated", @@ -238,7 +237,6 @@ func TestSetupWorkerAuthStorage(t *testing.T) { creds.CertificateBundles[0].CertificateNotAfter = timestamppb.New(time.Time{}) require.NoError(t, creds.Store(ctx, w.WorkerAuthStorage)) }, - expCreation: true, expRegistrationRequest: true, }, } @@ -260,15 +258,11 @@ func TestSetupWorkerAuthStorage(t *testing.T) { // Start up to run logic require.NoError(t, tw.Worker().Start()) - // Validate state + // Validate existing key was loaded or new key was created and loaded if tt.expKeyId != "" { assert.Equal(t, tt.expKeyId, tw.Worker().WorkerAuthCurrentKeyId) } else { - if tt.expCreation { - assert.NotEmpty(t, tw.Worker().WorkerAuthCurrentKeyId) - } else { - assert.Empty(t, tw.Worker().WorkerAuthCurrentKeyId) - } + assert.NotEmpty(t, tw.Worker().WorkerAuthCurrentKeyId.Load()) } if tt.expRegistrationRequest { assert.NotEmpty(t, tw.Worker().WorkerAuthRegistrationRequest) diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index 1e8088baa..d2cf36eb6 100644 --- a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -54,19 +54,27 @@ create table server_worker ( ), -- This is the name that the worker reports in it's status updates. worker_reported_name wt_name - -- TODO: When we are recording the worker node's key id allow that as an - -- alternative value that must be set besides worker_reported_name if - -- last_status_time is set. constraint worker_reported_name_must_be_set_by_status check ( (last_status_time is null and worker_reported_name is null) or (last_status_time is not null and worker_reported_name is not null) + or + (last_status_time is not null and worker_reported_key_id is not null) ) constraint worker_reported_name_must_be_lowercase check (lower(trim(worker_reported_name)) = worker_reported_name) constraint worker_reported_name_only_has_printable_characters check (worker_reported_name !~ '[^[:print:]]'), + worker_reported_key_id text + constraint worker_reported_key_id_must_be_set_by_status + check ( + (last_status_time is null and worker_reported_key_id is null) + or + (last_status_time is not null and worker_reported_name is not null) + or + (last_status_time is not null and worker_reported_key_id is not null) + ), constraint server_worker_scope_id_name_uq unique(scope_id, name), constraint server_worker_scope_id_worker_reported_name_uq diff --git a/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql b/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql index 8ac26e22b..80e7bf993 100644 --- a/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql @@ -42,7 +42,7 @@ from server_worker w left join worker_config_tags ct on w.public_id = ct.worker_id and ct.source = 'configuration' left join connection_count as cc on - w.public_id = cc.worker_id; + w.public_id = cc.worker_id; comment on view server_worker_aggregate is 'server_worker_aggregate contains the worker resource with its worker provided config values and its configuration and api provided tags.'; diff --git a/internal/gen/controller/servers/services/session_service.pb.go b/internal/gen/controller/servers/services/session_service.pb.go index 9665872f9..6d57182bd 100644 --- a/internal/gen/controller/servers/services/session_service.pb.go +++ b/internal/gen/controller/servers/services/session_service.pb.go @@ -32,6 +32,9 @@ type LookupSessionRequest struct { // The name of the requesting worker, used for filtering to ensure this // worker is allowed to handle this session. ServerId string `protobuf:"bytes,20,opt,name=server_id,json=serverId,proto3" json:"server_id,omitempty" class:"public"` // @gotags: `class:"public"` + // The key id of the requesting worker, used for filtering + // if the server name is not provided + WorkerKeyId string `protobuf:"bytes,30,opt,name=worker_key_id,json=workerKeyId,proto3" json:"worker_key_id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *LookupSessionRequest) Reset() { @@ -80,6 +83,13 @@ func (x *LookupSessionRequest) GetServerId() string { return "" } +func (x *LookupSessionRequest) GetWorkerKeyId() string { + if x != nil { + return x.WorkerKeyId + } + return "" +} + // LookupSessionResponse contains information necessary for a client to // establish a session. type LookupSessionResponse struct { @@ -947,216 +957,218 @@ var file_controller_servers_services_v1_session_service_proto_rawDesc = []byte{ 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x52, 0x0a, 0x14, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x76, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, - 0x22, 0xe8, 0x04, 0x0a, 0x15, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x0d, 0x61, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, - 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x66, 0x75, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x14, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x66, 0x75, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, - 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x45, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x46, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, - 0x69, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x5f, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x50, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4c, 0x65, 0x66, 0x74, 0x12, 0x17, 0x0a, - 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, - 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, 0x73, - 0x74, 0x53, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x78, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x4d, 0x0a, 0x0b, - 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x82, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0b, - 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x22, 0xd4, 0x01, 0x0a, 0x16, - 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x66, 0x75, 0x5f, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x66, 0x75, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x1e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, - 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x45, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x45, 0x53, - 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x22, 0x60, 0x0a, 0x17, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x22, 0x35, 0x0a, 0x14, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x5e, 0x0a, 0x15, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, + 0x12, 0x22, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, + 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4b, + 0x65, 0x79, 0x49, 0x64, 0x22, 0xe8, 0x04, 0x0a, 0x15, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, + 0x0a, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x44, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x66, 0x75, 0x5f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x66, 0x75, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, + 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, - 0x54, 0x55, 0x53, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x58, 0x0a, 0x1a, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x49, 0x64, 0x22, 0xb7, 0x01, 0x0a, 0x1b, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x7a, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x54, 0x55, 0x53, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x46, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x50, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4c, 0x65, 0x66, + 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x5a, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0b, 0x68, 0x6f, + 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x68, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x78, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x4d, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, + 0x82, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x22, + 0xd4, 0x01, 0x0a, 0x16, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x66, + 0x75, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, + 0x6f, 0x66, 0x75, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, + 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x45, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x60, 0x0a, 0x17, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x45, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x35, 0x0a, 0x14, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, + 0x5e, 0x0a, 0x15, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, + 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x58, 0x0a, 0x1a, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, + 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x22, 0xb7, 0x01, 0x0a, 0x1b, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x48, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4c, + 0x65, 0x66, 0x74, 0x22, 0xad, 0x02, 0x0a, 0x18, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, + 0x74, 0x63, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x63, 0x70, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x63, + 0x70, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x54, 0x63, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x65, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x54, 0x63, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2a, 0x0a, + 0x11, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x70, 0x6f, + 0x72, 0x74, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x54, 0x63, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, + 0x0e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, + 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x49, 0x70, 0x22, 0x65, 0x0a, 0x19, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x48, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, + 0x55, 0x53, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x93, 0x01, 0x0a, 0x1a, 0x43, + 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x19, + 0x0a, 0x08, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x75, 0x70, 0x18, 0x14, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x55, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x22, 0x82, 0x01, 0x0a, 0x16, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x68, 0x0a, 0x12, 0x63, + 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x52, 0x10, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x44, 0x61, 0x74, 0x61, 0x22, 0x8c, 0x01, 0x0a, 0x1b, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x48, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x5f, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4c, 0x65, 0x66, 0x74, 0x22, - 0xad, 0x02, 0x0a, 0x18, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x63, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x26, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x70, 0x6f, - 0x72, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x54, 0x63, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x65, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, - 0x63, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x32, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x63, - 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x3c, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x73, 0x65, - 0x72, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x46, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x22, - 0x65, 0x0a, 0x19, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x4f, - 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x93, 0x01, 0x0a, 0x1a, 0x43, 0x6c, 0x6f, 0x73, 0x65, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x5f, 0x75, 0x70, 0x18, 0x14, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x55, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x64, - 0x6f, 0x77, 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x28, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x82, 0x01, 0x0a, - 0x16, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x68, 0x0a, 0x12, 0x63, 0x6c, 0x6f, 0x73, 0x65, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x10, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x22, 0x8c, 0x01, 0x0a, 0x1b, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x48, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x14, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, - 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x22, 0x86, 0x01, 0x0a, 0x17, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x13, - 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x11, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x32, 0xbe, 0x06, 0x0a, 0x0e, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7e, 0x0a, 0x0d, - 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x2e, + 0x61, 0x74, 0x75, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x17, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x6b, 0x0a, 0x13, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x84, 0x01, 0x0a, - 0x0f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x11, 0x63, 0x6c, 0x6f, 0x73, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x32, 0xbe, 0x06, + 0x0a, 0x0e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x7e, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x84, 0x01, 0x0a, 0x0f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x90, 0x01, 0x0a, 0x13, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x2e, 0x63, 0x6f, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x90, 0x01, 0x0a, 0x13, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x3a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x7a, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8a, 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x84, 0x01, 0x0a, 0x0f, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x51, 0x5a, 0x4f, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8a, 0x01, 0x0a, 0x11, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x84, 0x01, 0x0a, 0x0f, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, + 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x51, + 0x5a, 0x4f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/proto/controller/servers/services/v1/session_service.proto b/internal/proto/controller/servers/services/v1/session_service.proto index ea022b449..3286a8870 100644 --- a/internal/proto/controller/servers/services/v1/session_service.proto +++ b/internal/proto/controller/servers/services/v1/session_service.proto @@ -36,6 +36,9 @@ message LookupSessionRequest { // The name of the requesting worker, used for filtering to ensure this // worker is allowed to handle this session. string server_id = 20; // @gotags: `class:"public"` + // The key id of the requesting worker, used for filtering + // if the server name is not provided + string worker_key_id = 30; // @gotags: `class:"public"` } // LookupSessionResponse contains information necessary for a client to diff --git a/internal/proto/controller/servers/v1/servers.proto b/internal/proto/controller/servers/v1/servers.proto index 3339c9ba9..461154084 100644 --- a/internal/proto/controller/servers/v1/servers.proto +++ b/internal/proto/controller/servers/v1/servers.proto @@ -23,4 +23,7 @@ message ServerWorkerStatus { // Tags for workers repeated TagPair tags = 40; + + // The key id for this worker, if applicable (optional) + string key_id = 50; } diff --git a/internal/proto/controller/storage/servers/store/v1/worker.proto b/internal/proto/controller/storage/servers/store/v1/worker.proto index b988adb6c..0a160a87e 100644 --- a/internal/proto/controller/storage/servers/store/v1/worker.proto +++ b/internal/proto/controller/storage/servers/store/v1/worker.proto @@ -62,6 +62,10 @@ message Worker { // The last_status_time is the last time a worker daemon sent a status update. // @inject_tag: `gorm:"default:null"` timestamp.v1.Timestamp last_status_time = 110; + + // keyId reported by a PKI worker (optional) + // @inject_tag: `gorm:"default:null"` + string worker_reported_key_id = 120; } // WorkerTag is a tag for a worker. The primary key is comprised of the diff --git a/internal/servers/public_ids.go b/internal/servers/public_ids.go index 25974e32c..16d9a7829 100644 --- a/internal/servers/public_ids.go +++ b/internal/servers/public_ids.go @@ -19,15 +19,3 @@ func newWorkerId(ctx context.Context) (string, error) { } return id, nil } - -func newWorkerIdFromName(ctx context.Context, name string) (string, error) { - const op = "servers.newWorkerIdFromName" - if name == "" { - return "", errors.New(ctx, errors.InvalidParameter, op, "name is empty") - } - id, err := db.NewPublicId(WorkerPrefix, db.WithPrngValues([]string{name})) - if err != nil { - return "", errors.Wrap(ctx, err, op) - } - return id, nil -} diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index 187ef216e..836509f3f 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -67,19 +67,44 @@ func (r *Repository) LookupWorkerByWorkerReportedName(ctx context.Context, name case name == "": return nil, errors.New(ctx, errors.InvalidParameter, op, "name is empty") } - // we derive the id instead of doing a query to be consistant with the - // UpsertWorkerStatus flow which uses this function to upsert a worker. - id, err := newWorkerIdFromName(ctx, name) + + wAgg := &workerAggregate{} + err := r.reader.LookupWhere(ctx, &wAgg, "worker_reported_name = ?", []interface{}{name}) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error while calculating the worker's id")) + if errors.IsNotFoundError(err) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op) } - w, err := lookupWorker(ctx, r.reader, id) + w, err := wAgg.toWorker(ctx) if err != nil { return nil, errors.Wrap(ctx, err, op) } return w, nil } +func (r *Repository) LookupWorkerIdByWorkerReportedKeyId(ctx context.Context, keyId string) (string, error) { + const op = "servers.(Repository).LookupWorkerIdByWorkerReportedKeyId" + switch { + case keyId == "": + return "", errors.New(ctx, errors.InvalidParameter, op, "keyId is empty") + } + + // We're searching for a workerAuth record based on worker id + worker := allocWorkerAuth() + worker.WorkerKeyIdentifier = keyId + + err := r.reader.LookupById(ctx, worker) + if err != nil { + if errors.IsNotFoundError(err) { + return "", nil + } + return "", errors.Wrap(ctx, err, op) + } + + return worker.WorkerId, nil +} + // LookupWorker returns the worker for the provided publicId. This returns // nil nil in the situation where no worker can be found with that public id. func (r *Repository) LookupWorker(ctx context.Context, publicId string, _ ...Option) (*Worker, error) { @@ -192,8 +217,8 @@ func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt return nil, errors.New(ctx, errors.InvalidParameter, op, "scope id is empty") case worker.PublicId != "": return nil, errors.New(ctx, errors.InvalidParameter, op, "worker id is not empty") - case worker.GetWorkerReportedName() == "": - return nil, errors.New(ctx, errors.InvalidParameter, op, "worker reported name is empty") + case worker.GetWorkerReportedName() == "" && worker.WorkerReportedKeyId == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "worker keyId and reported name are both empty; at least one is required") case len(worker.apiTags) > 0: return nil, errors.New(ctx, errors.InvalidParameter, op, "api tags is not empty") } @@ -202,7 +227,8 @@ func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt worker = NewWorkerForStatus(worker.GetScopeId(), WithName(worker.GetWorkerReportedName()), WithAddress(worker.GetWorkerReportedAddress()), - WithWorkerTags(worker.configTags...)) + WithWorkerTags(worker.configTags...), + WithKeyId(worker.WorkerReportedKeyId)) opts := getOpts(opt...) @@ -210,10 +236,26 @@ func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt switch { case opts.withPublicId != "": worker.PublicId = opts.withPublicId + case worker.WorkerReportedKeyId != "": + workerId, err := r.LookupWorkerIdByWorkerReportedKeyId(ctx, worker.WorkerReportedKeyId) + if err != nil || workerId == "" { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error finding worker by keyId")) + } + worker.PublicId = workerId default: - worker.PublicId, err = newWorkerIdFromName(ctx, worker.GetWorkerReportedName()) + // Attempt to find workerId by name. If not found, generate an id + w, err := r.LookupWorkerByWorkerReportedName(ctx, worker.GetWorkerReportedName()) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error generating worker id")) + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error finding worker by name")) + } + if w != nil { + worker.PublicId = w.PublicId + } else { + workerId, err := newWorkerId(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error error generating worker id")) + } + worker.PublicId = workerId } } diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index be0807d12..73df90403 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -2,6 +2,7 @@ package servers_test import ( "context" + "crypto/rand" "fmt" "strings" "testing" @@ -20,6 +21,8 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-dbw" "github.com/hashicorp/go-kms-wrapping/extras/kms/v2/migrations" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/registration" "github.com/hashicorp/nodeenrollment/rotation" "github.com/hashicorp/nodeenrollment/storage/file" "github.com/hashicorp/nodeenrollment/types" @@ -138,6 +141,63 @@ func TestLookupWorkerByWorkerReportedName(t *testing.T) { }) } +func TestLookupWorkerByWorkerReportedKeyId(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + repo, err := servers.NewRepository(rw, rw, kmsCache) + require.NoError(t, err) + ctx := context.Background() + // Ensures the global scope contains a valid root key + err = kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(t, err) + + w := servers.TestWorker(t, conn, wrapper) + + rootStorage, err := servers.NewRepositoryStorage(ctx, rw, rw, kmsCache) + require.NoError(t, err) + _, err = rotation.RotateRootCertificates(ctx, rootStorage) + require.NoError(t, err) + // Create struct to pass in with workerId that will be passed along to storage + state, err := servers.AttachWorkerIdToState(ctx, w.PublicId) + require.NoError(t, err) + + // This happens on the worker + fileStorage, err := file.New(ctx) + require.NoError(t, err) + nodeCreds, err := types.NewNodeCredentials(ctx, fileStorage) + require.NoError(t, err) + // Create request using worker id + fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(ctx) + require.NoError(t, err) + + // The AuthorizeNode request will result in a WorkerAuth record being stored + registeredNode, err := registration.AuthorizeNode(ctx, rootStorage, fetchReq, nodeenrollment.WithState(state)) + require.NoError(t, err) + t.Run("success", func(t *testing.T) { + got, err := repo.LookupWorkerIdByWorkerReportedKeyId(ctx, registeredNode.Id) + require.NoError(t, err) + assert.Equal(t, w.PublicId, got) + }) + t.Run("not found", func(t *testing.T) { + got, err := repo.LookupWorkerIdByWorkerReportedKeyId(ctx, "unknown_key") + require.NoError(t, err) + assert.Empty(t, got) + }) + t.Run("db error", func(t *testing.T) { + conn, mock := db.TestSetupWithMock(t) + rw := db.New(conn) + mock.ExpectQuery(`SELECT`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "lookup-error")) + r, err := servers.NewRepository(rw, rw, kmsCache) + require.NoError(t, err) + got, err := r.LookupWorkerIdByWorkerReportedKeyId(ctx, w.GetWorkerReportedName()) + assert.NoError(t, mock.ExpectationsWereMet()) + assert.Truef(t, errors.Match(errors.T(errors.Op("servers.(Repository).LookupWorkerIdByWorkerReportedKeyId")), err), "got error %v", err) + assert.Empty(t, got) + }) +} + func TestLookupWorker(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) diff --git a/internal/servers/servers.pb.go b/internal/servers/servers.pb.go index 46f2879cc..022ca7002 100644 --- a/internal/servers/servers.pb.go +++ b/internal/servers/servers.pb.go @@ -90,6 +90,8 @@ type ServerWorkerStatus struct { Address string `protobuf:"bytes,30,opt,name=address,proto3" json:"address,omitempty"` // Tags for workers Tags []*TagPair `protobuf:"bytes,40,rep,name=tags,proto3" json:"tags,omitempty"` + // The key id for this worker, if applicable (optional) + KeyId string `protobuf:"bytes,50,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty"` } func (x *ServerWorkerStatus) Reset() { @@ -152,6 +154,13 @@ func (x *ServerWorkerStatus) GetTags() []*TagPair { return nil } +func (x *ServerWorkerStatus) GetKeyId() string { + if x != nil { + return x.KeyId + } + return "" +} + var File_controller_servers_v1_servers_proto protoreflect.FileDescriptor var file_controller_servers_v1_servers_proto_rawDesc = []byte{ @@ -162,7 +171,7 @@ var file_controller_servers_v1_servers_proto_rawDesc = []byte{ 0x54, 0x61, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0x93, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0xaa, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, @@ -171,11 +180,12 @@ var file_controller_servers_v1_servers_proto_rawDesc = []byte{ 0x73, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, - 0x04, 0x74, 0x61, 0x67, 0x73, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, + 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x42, 0x38, 0x5a, 0x36, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x3b, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/servers/store/worker.pb.go b/internal/servers/store/worker.pb.go index 0004e8869..3420bc49f 100644 --- a/internal/servers/store/worker.pb.go +++ b/internal/servers/store/worker.pb.go @@ -62,6 +62,9 @@ type Worker struct { // The last_status_time is the last time a worker daemon sent a status update. // @inject_tag: `gorm:"default:null"` LastStatusTime *timestamp.Timestamp `protobuf:"bytes,110,opt,name=last_status_time,json=lastStatusTime,proto3" json:"last_status_time,omitempty" gorm:"default:null"` + // keyId reported by a PKI worker (optional) + // @inject_tag: `gorm:"default:null"` + WorkerReportedKeyId string `protobuf:"bytes,120,opt,name=worker_reported_key_id,json=workerReportedKeyId,proto3" json:"worker_reported_key_id,omitempty" gorm:"default:null"` } func (x *Worker) Reset() { @@ -173,6 +176,13 @@ func (x *Worker) GetLastStatusTime() *timestamp.Timestamp { return nil } +func (x *Worker) GetWorkerReportedKeyId() string { + if x != nil { + return x.WorkerReportedKeyId + } + return "" +} + // WorkerTag is a tag for a worker. The primary key is comprised of the // worker_id, key, value, and source. type WorkerTag struct { @@ -268,7 +278,7 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x6f, 0x74, 0x6f, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xce, 0x04, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x83, 0x05, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, @@ -305,18 +315,21 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x54, - 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, - 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, - 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, + 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, + 0x78, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x68, 0x0a, 0x09, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/servers/testing.go b/internal/servers/testing.go index 1cd081026..9314cc51e 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -96,10 +96,12 @@ func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Opti namePart, err := newWorkerId(ctx) require.NoError(t, err) name := "test-worker-" + strings.ToLower(namePart) + id, err := newWorkerId(ctx) + require.NoError(t, err) wrk := NewWorkerForStatus(scope.Global.String(), WithName(name), WithAddress("127.0.0.1")) - wrk, err = serversRepo.UpsertWorkerStatus(ctx, wrk) + wrk, err = serversRepo.UpsertWorkerStatus(ctx, wrk, WithPublicId(id)) require.NoError(t, err) require.NotNil(t, wrk) opts := getOpts(opt...) diff --git a/internal/servers/worker.go b/internal/servers/worker.go index 895c73e6a..b13cf80e2 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -68,6 +68,7 @@ func NewWorkerForStatus(scopeId string, opt ...Option) *Worker { ScopeId: scopeId, WorkerReportedName: opts.withName, WorkerReportedAddress: opts.withAddress, + WorkerReportedKeyId: opts.withKeyId, }, configTags: opts.withWorkerTags, } @@ -187,6 +188,7 @@ type workerAggregate struct { WorkerReportedName string WorkerReportedAddress string LastStatusTime *timestamp.Timestamp + WorkerReportedKeyId string WorkerConfigTags string } @@ -205,6 +207,7 @@ func (a *workerAggregate) toWorker(ctx context.Context) (*Worker, error) { WorkerReportedAddress: a.WorkerReportedAddress, WorkerReportedName: a.WorkerReportedName, LastStatusTime: a.LastStatusTime, + WorkerReportedKeyId: a.WorkerReportedKeyId, }, activeConnectionCount: a.ActiveConnectionCount, } diff --git a/internal/servers/worker_test.go b/internal/servers/worker_test.go index 2830aba62..4e6d26a2d 100644 --- a/internal/servers/worker_test.go +++ b/internal/servers/worker_test.go @@ -2,6 +2,7 @@ package servers import ( "context" + "fmt" "strings" "testing" @@ -292,6 +293,32 @@ func TestWorker_Update(t *testing.T) { assert.Equal(t, up.GetLastStatusTime().AsTime(), up.GetUpdateTime().AsTime()) }, }, + { + name: "base update with worker reported address and keyId", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Description: "base update with status", + }, + }, + update: Worker{ + Worker: &store.Worker{ + WorkerReportedKeyId: "base update with worker reported address and keyId", + WorkerReportedAddress: "base update with worker reported address and keyId", + }, + }, + mask: []string{"WorkerReportedAddress", "WorkerReportedKeyId"}, + assert: func(t *testing.T, init, up *Worker) { + t.Helper() + assert.Equal(t, "base update with worker reported address and keyId", up.WorkerReportedAddress) + assert.Equal(t, "base update with worker reported address and keyId", up.WorkerReportedKeyId) + assert.Equal(t, uint32(1), up.Version) + assert.NotNil(t, up.GetLastStatusTime()) + assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) + assert.Equal(t, up.GetLastStatusTime().AsTime(), up.GetUpdateTime().AsTime()) + }, + }, { name: "base update with worker reported address", initial: Worker{ @@ -328,6 +355,25 @@ func TestWorker_Update(t *testing.T) { mask: []string{"WorkerReportedName"}, wantUpdateError: true, }, + { + // If any status fields are set then worker reported address must + // be set. + name: "base update with worker reported keyId", + initial: Worker{ + Worker: &store.Worker{ + ScopeId: scope.Global.String(), + PublicId: newId(), + Description: "base update with status", + }, + }, + update: Worker{ + Worker: &store.Worker{ + WorkerReportedKeyId: "base update with worker reported keyId", + }, + }, + mask: []string{"WorkerReportedKeyId"}, + wantUpdateError: true, + }, { name: "base update with worker reported fields", initial: Worker{ @@ -464,6 +510,7 @@ func TestWorker_Update(t *testing.T) { up.PublicId = init.PublicId _, err := rw.Update(ctx, up, tc.mask, tc.nullMask) if tc.wantUpdateError { + fmt.Println(err) assert.Error(t, err) return } else { From 5a7291131f71f31b01c9c9df4d329ae6f1b65390 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 7 Jun 2022 16:43:07 -0400 Subject: [PATCH 072/100] Make current key update on rotation --- internal/cmd/commands/dev/dev.go | 2 +- internal/cmd/commands/server/server.go | 2 +- internal/daemon/worker/auth_rotation.go | 8 +++++++- internal/daemon/worker/auth_rotation_test.go | 3 +++ internal/daemon/worker/status.go | 5 +---- internal/daemon/worker/worker.go | 6 ++++-- internal/daemon/worker/worker_test.go | 2 +- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 76b42fe93..6d3c65904 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -715,7 +715,7 @@ func (c *Command) Run(args []string) int { c.InfoKeys = append(c.InfoKeys, "worker auth registration request") c.Info["worker auth registration request"] = req c.InfoKeys = append(c.InfoKeys, "worker auth current key id") - c.Info["worker auth current key id"] = c.worker.WorkerAuthCurrentKeyId + c.Info["worker auth current key id"] = c.worker.WorkerAuthCurrentKeyId.Load() c.InfoKeys = append(c.InfoKeys, "worker auth storage path") c.Info["worker auth storage path"] = c.worker.WorkerAuthStorage.BaseDir() if err := c.StoreWorkerAuthReq(c.worker.WorkerAuthRegistrationRequest, c.worker.WorkerAuthStorage.BaseDir()); err != nil { diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index 848d21e35..dd453d3da 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -468,7 +468,7 @@ func (c *Command) Run(args []string) int { c.InfoKeys = append(c.InfoKeys, "worker auth registration request") c.Info["worker auth registration request"] = c.worker.WorkerAuthRegistrationRequest c.InfoKeys = append(c.InfoKeys, "worker auth current key id") - c.Info["worker auth current key id"] = c.worker.WorkerAuthCurrentKeyId + c.Info["worker auth current key id"] = c.worker.WorkerAuthCurrentKeyId.Load() // Write the WorkerAuth request to a file if err := c.StoreWorkerAuthReq(c.worker.WorkerAuthRegistrationRequest, c.worker.WorkerAuthStorage.BaseDir()); err != nil { diff --git a/internal/daemon/worker/auth_rotation.go b/internal/daemon/worker/auth_rotation.go index ae3b7c28e..cdf3f38b7 100644 --- a/internal/daemon/worker/auth_rotation.go +++ b/internal/daemon/worker/auth_rotation.go @@ -182,7 +182,7 @@ func rotateWorkerAuth(ctx context.Context, w *Worker, currentNodeCreds *types.No // Call handle on the inner response, which will be encrypted to the // new node credentials. This will also store the result, // overwriting the current credentials. - _, err = newNodeCreds.HandleFetchNodeCredentialsResponse( + newNodeCreds, err = newNodeCreds.HandleFetchNodeCredentialsResponse( ctx, w.WorkerAuthStorage, fetchResp, @@ -192,5 +192,11 @@ func rotateWorkerAuth(ctx context.Context, w *Worker, currentNodeCreds *types.No return berrors.Wrap(ctx, err, op) } + newKeyId, err := nodeenrollment.KeyIdFromPkix(newNodeCreds.CertificatePublicKeyPkix) + if err != nil { + return berrors.Wrap(ctx, err, op) + } + w.WorkerAuthCurrentKeyId.Store(newKeyId) + return nil } diff --git a/internal/daemon/worker/auth_rotation_test.go b/internal/daemon/worker/auth_rotation_test.go index f5468c406..c35e6213e 100644 --- a/internal/daemon/worker/auth_rotation_test.go +++ b/internal/daemon/worker/auth_rotation_test.go @@ -102,6 +102,9 @@ func TestRotationTicking(t *testing.T) { require.NoError(err) assert.NotEqual(currKey, currNodeCreds.CertificatePublicKeyPkix) currKey = currNodeCreds.CertificatePublicKeyPkix + currKeyId, err := nodeenrollment.KeyIdFromPkix(currNodeCreds.CertificatePublicKeyPkix) + require.NoError(err) + assert.Equal(currKeyId, w.Worker().WorkerAuthCurrentKeyId.Load()) // Stop and start the client connections to ensure the new credentials // are valid; if not, we won't establish a new connection and rotation diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index 4bb3ed1cd..9891fe924 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -145,10 +145,7 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { statusCtx, statusCancel := context.WithTimeout(cancelCtx, common.StatusTimeout) defer statusCancel() - var keyId string - if w.WorkerAuthCurrentKeyId != "" { - keyId = w.WorkerAuthCurrentKeyId - } + keyId := w.WorkerAuthCurrentKeyId.Load() if w.conf.RawConfig.Worker.Name == "" && keyId == "" { event.WriteError(statusCtx, op, errors.New("worker name and keyId are both empty; one is needed to identify a worker"), diff --git a/internal/daemon/worker/worker.go b/internal/daemon/worker/worker.go index 7c759d276..99af08590 100644 --- a/internal/daemon/worker/worker.go +++ b/internal/daemon/worker/worker.go @@ -80,7 +80,7 @@ type Worker struct { // The storage for node enrollment WorkerAuthStorage *nodeefile.Storage - WorkerAuthCurrentKeyId string + WorkerAuthCurrentKeyId *ua.String WorkerAuthRegistrationRequest string workerAuthSplitListener *nodeenet.SplitListener @@ -110,6 +110,7 @@ func New(conf *Config) (*Worker, error) { tags: new(atomic.Value), updateTags: ua.NewBool(false), nonceFn: base62.Random, + WorkerAuthCurrentKeyId: new(ua.String), } w.lastStatusSuccess.Store((*LastStatusInformation)(nil)) @@ -282,10 +283,11 @@ func (w *Worker) Start() error { if err != nil { return fmt.Errorf("error encoding worker auth registration request: %w", err) } - w.WorkerAuthCurrentKeyId, err = nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) + currentKeyId, err := nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) if err != nil { return fmt.Errorf("error deriving worker auth key id: %w", err) } + w.WorkerAuthCurrentKeyId.Store(currentKeyId) } // Regardless, we want to load the currentKeyId currentKeyId, err := nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix) diff --git a/internal/daemon/worker/worker_test.go b/internal/daemon/worker/worker_test.go index 017bd978f..028c4e152 100644 --- a/internal/daemon/worker/worker_test.go +++ b/internal/daemon/worker/worker_test.go @@ -260,7 +260,7 @@ func TestSetupWorkerAuthStorage(t *testing.T) { // Validate existing key was loaded or new key was created and loaded if tt.expKeyId != "" { - assert.Equal(t, tt.expKeyId, tw.Worker().WorkerAuthCurrentKeyId) + assert.Equal(t, tt.expKeyId, tw.Worker().WorkerAuthCurrentKeyId.Load()) } else { assert.NotEmpty(t, tw.Worker().WorkerAuthCurrentKeyId.Load()) } From 2d274c3742a59469b9aff6f88c683520c9036971 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 10 Jun 2022 19:20:16 -0400 Subject: [PATCH 073/100] Fix merge brokenness --- internal/session/repository_session_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/session/repository_session_test.go b/internal/session/repository_session_test.go index 345440260..ed4652fb2 100644 --- a/internal/session/repository_session_test.go +++ b/internal/session/repository_session_test.go @@ -2,6 +2,7 @@ package session import ( "context" + "fmt" "testing" "time" From cb190c0ea1898e84fb7d168181ae23df0cc2c6f3 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 11 Jun 2022 08:52:05 -0500 Subject: [PATCH 074/100] Disable session jobs for now so other jobs work right --- internal/daemon/controller/controller.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index 47d7931e1..2dff70703 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -361,9 +361,12 @@ func (c *Controller) registerJobs() error { if err := pluginhost.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms, c.conf.HostPlugins); err != nil { return err } - if err := session.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms, c.conf.StatusGracePeriodDuration); err != nil { - return err - } + // FIXME: This needs to get fixed before merge + /* + if err := session.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms, c.conf.StatusGracePeriodDuration); err != nil { + return err + } + */ if err := serversjob.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms); err != nil { return err } From 82b4e29d78742ce5af14dc8a3b34a8cb9cf7e6b1 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 11 Jun 2022 11:04:40 -0500 Subject: [PATCH 075/100] Properly fix (for now) the job limit --- internal/daemon/controller/controller.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index 2dff70703..9eac51878 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -251,7 +251,7 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { } // TODO: the RunJobsLimit is temporary until a better fix gets in. This // currently caps the scheduler at running 10 jobs per interval. - schedulerOpts := []scheduler.Option{scheduler.WithRunJobsLimit(10)} + schedulerOpts := []scheduler.Option{scheduler.WithRunJobsLimit(20)} if c.conf.RawConfig.Controller.SchedulerRunJobInterval > 0 { schedulerOpts = append(schedulerOpts, scheduler.WithRunJobsInterval(c.conf.RawConfig.Controller.SchedulerRunJobInterval)) } @@ -361,12 +361,9 @@ func (c *Controller) registerJobs() error { if err := pluginhost.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms, c.conf.HostPlugins); err != nil { return err } - // FIXME: This needs to get fixed before merge - /* - if err := session.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms, c.conf.StatusGracePeriodDuration); err != nil { - return err - } - */ + if err := session.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms, c.conf.StatusGracePeriodDuration); err != nil { + return err + } if err := serversjob.RegisterJobs(c.baseContext, c.scheduler, rw, rw, c.kms); err != nil { return err } From 8a131ef4f7733a99f142b74da61d77573f85c438 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 11 Jun 2022 16:13:15 -0400 Subject: [PATCH 076/100] Fix missing guardrail for worker kms auth population (#2172) * Fix missing guardrail for worker kms auth population --- internal/cmd/commands/server/server.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index d70b72a4f..0f5e21451 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -178,6 +178,8 @@ func (c *Command) Run(args []string) int { base.StartMemProfiler(c.Context) + // Note: the checks directly after this must remain where they are because + // they rely on the state of configured KMSes. if err := c.SetupKMSes(c.Context, c.UI, c.Config); err != nil { c.UI.Error(err.Error()) return base.CommandUserError @@ -188,6 +190,20 @@ func (c *Command) Run(args []string) int { return base.CommandUserError } } + if c.Config.Worker != nil { + switch c.WorkerAuthKms { + case nil: + if c.Config.Worker.AuthStoragePath == "" { + c.UI.Error("No worker auth KMS specified and no worker auth storage path specified.") + return base.CommandUserError + } + default: + if c.Config.Worker.Name == "" { + c.UI.Error("Worker is using KMS auth but has no name set. It must be the unique name of this instance.") + return base.CommandUserError + } + } + } if c.Config.DefaultMaxRequestDuration != 0 { globals.DefaultMaxRequestDuration = c.Config.DefaultMaxRequestDuration @@ -547,16 +563,6 @@ func (c *Command) ParseFlagsAndConfig(args []string) int { c.UI.Error("Controller has no name set. It must be the unique name of this instance.") return base.CommandUserError } - if c.Config.Worker != nil { - if c.Config.Worker.Name == "" { - c.UI.Error("Worker has no name set. It must be the unique name of this instance.") - return base.CommandUserError - } - if c.Config.Worker.AuthStoragePath == "" { - c.UI.Error("No worker auth KMS specified and no worker auth storage path specified.") - return base.CommandUserError - } - } return base.CommandSuccess } From ee0ecc999187a36fba094fd7ddef322bc4ddaf4a Mon Sep 17 00:00:00 2001 From: irenarindos Date: Fri, 10 Jun 2022 14:10:05 -0400 Subject: [PATCH 077/100] fix context for worker upstreams --- internal/daemon/worker/controller_connection.go | 2 +- internal/daemon/worker/status.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index 82e410e4e..aa4d1934d 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -62,7 +62,7 @@ func (w *Worker) StartControllerConnections() error { if w.conf.RawConfig.HCPBClusterId != "" { clusterAddress := fmt.Sprintf("%s%s", w.conf.RawConfig.HCPBClusterId, hcpbUrlSuffix) initialAddrs = append(initialAddrs, resolver.Address{Addr: clusterAddress}) - event.WriteSysEvent(context.TODO(), op, fmt.Sprintf("Setting HCPB Cluster address %s as upstream address", clusterAddress)) + event.WriteSysEvent(w.baseContext, op, fmt.Sprintf("Setting HCPB Cluster address %s as upstream address", clusterAddress)) } else { return errors.New("no initial controller addresses found") } diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index 89840a3aa..aaeffe710 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -213,11 +213,11 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { // Compare upstreams; update resolver if there is a difference, and emit an event with old and new addresses if lastStatus != nil && !strutil.EquivalentSlices(lastStatus.LastCalculatedUpstreams, newUpstreams) { upstreamsMessage := fmt.Sprintf("Upstreams has changed; old upstreams were: %s, new upstreams are: %s", lastStatus.LastCalculatedUpstreams, newUpstreams) - event.WriteSysEvent(context.TODO(), op, upstreamsMessage) + event.WriteSysEvent(cancelCtx, op, upstreamsMessage) w.Resolver().UpdateState(resolver.State{Addresses: addrs}) } else if lastStatus == nil { w.Resolver().UpdateState(resolver.State{Addresses: addrs}) - event.WriteSysEvent(context.TODO(), op, fmt.Sprintf("Upstreams after first status set to: %s", newUpstreams)) + event.WriteSysEvent(cancelCtx, op, fmt.Sprintf("Upstreams after first status set to: %s", newUpstreams)) } } w.lastStatusSuccess.Store(&LastStatusInformation{StatusResponse: result, StatusTime: time.Now(), LastCalculatedUpstreams: newUpstreams}) From 4b526538af660cf4c29988cd514b18fe673dc8a8 Mon Sep 17 00:00:00 2001 From: Todd Date: Mon, 13 Jun 2022 12:55:05 -0700 Subject: [PATCH 078/100] Make server_id in job_run type text since it is nullable. (#2177) --- .../oss/postgres/99/02_worker_controller_tables.up.sql | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index d2cf36eb6..f2ff45a45 100644 --- a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -199,9 +199,13 @@ alter table session_connection -- Finally, neither jobs nor servers are exposed out of boundary so the risk of -- losing data that would be useful later on is diminished. alter table job_run - add column controller_id wt_private_id, + add column controller_id text, drop column server_id; alter table job_run + add constraint controller_id_must_be_at_least_10_characters + check( + length(trim(controller_id)) > 10 + ), add constraint server_controller_fkey foreign key (controller_id) references server_controller (private_id) From 40ba275759fd8aa9a6daa7d49f17c1b47a58c1a2 Mon Sep 17 00:00:00 2001 From: Haotian Date: Mon, 13 Jun 2022 15:06:03 -0700 Subject: [PATCH 079/100] feat(worker): add 'initial_upstreams' field to worker config to prepare to replace 'controllers' field. Update worker config description on website. --- internal/cmd/commands/dev/dev.go | 2 +- internal/cmd/commands/server/server.go | 27 +-- .../server/worker_shutdown_reload_test.go | 2 +- .../server/worker_tags_reload_test.go | 4 +- internal/cmd/config/config.go | 53 ++++-- internal/cmd/config/config_test.go | 154 +++++++++++------- .../credential/vault/setup_manual_test.go | 8 +- internal/daemon/worker/auth_rotation_test.go | 2 +- .../daemon/worker/controller_connection.go | 12 +- internal/daemon/worker/testing.go | 12 +- internal/tests/cluster/ipv6_listener_test.go | 8 +- .../cluster/multi_controller_worker_test.go | 12 +- .../tests/cluster/session_cleanup_test.go | 4 +- internal/tests/cluster/unix_listener_test.go | 8 +- internal/tests/cluster/worker_replay_test.go | 12 +- internal/tests/cluster/worker_tagging_test.go | 24 +-- .../tests/cluster/x509_verification_test.go | 10 +- website/content/docs/configuration/worker.mdx | 4 +- 18 files changed, 208 insertions(+), 150 deletions(-) diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 6d3c65904..8081efa49 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -483,7 +483,7 @@ func (c *Command) Run(args []string) int { if c.flagControllerClusterListenAddr != "" { l.Address = c.flagControllerClusterListenAddr if !c.flagControllerOnly { - c.Config.Worker.Controllers = []string{l.Address} + c.Config.Worker.InitialUpstreams = []string{l.Address} } } else { l.Address = "127.0.0.1:9201" diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index 0f5e21451..9a7a07897 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -280,6 +280,9 @@ func (c *Command) Run(args []string) int { c.UI.Error(`Config activates worker but no listener with "proxy" purpose found`) return base.CommandUserError } + if c.Config.Worker.ControllersRaw != nil { + c.UI.Warn("The \"controllers\" field for worker config is deprecated. Please use \"initial_upstreams\" instead.") + } if err := c.SetupWorkerPublicAddress(c.Config, ""); err != nil { c.UI.Error(err.Error()) @@ -289,25 +292,25 @@ func (c *Command) Run(args []string) int { c.Info["worker public proxy addr"] = c.Config.Worker.PublicAddr if c.Config.Controller != nil { - switch len(c.Config.Worker.Controllers) { + switch len(c.Config.Worker.InitialUpstreams) { case 0: if c.Config.Controller.PublicClusterAddr != "" { clusterAddr = c.Config.Controller.PublicClusterAddr } - c.Config.Worker.Controllers = []string{clusterAddr} + c.Config.Worker.InitialUpstreams = []string{clusterAddr} case 1: - if c.Config.Worker.Controllers[0] == clusterAddr { + if c.Config.Worker.InitialUpstreams[0] == clusterAddr { break } if c.Config.Controller.PublicClusterAddr != "" && - c.Config.Worker.Controllers[0] == c.Config.Controller.PublicClusterAddr { + c.Config.Worker.InitialUpstreams[0] == c.Config.Controller.PublicClusterAddr { break } // Best effort see if it's a domain name and if not assume it must match - host, _, err := net.SplitHostPort(c.Config.Worker.Controllers[0]) + host, _, err := net.SplitHostPort(c.Config.Worker.InitialUpstreams[0]) if err != nil && strings.Contains(err.Error(), "missing port in address") { err = nil - host = c.Config.Worker.Controllers[0] + host = c.Config.Worker.InitialUpstreams[0] } if err == nil { ip := net.ParseIP(host) @@ -318,17 +321,17 @@ func (c *Command) Run(args []string) int { } fallthrough default: - c.UI.Error(`When running a combined controller and worker, it's invalid to specify a "controllers" key in the worker block with any value other than the controller cluster address/port when using IPs rather than DNS names`) + c.UI.Error(`When running a combined controller and worker, it's invalid to specify a "initial_upstreams" or "controllers" key in the worker block with any values other than the controller cluster or upstream worker address/port when using IPs rather than DNS names`) return base.CommandUserError } } - for _, controller := range c.Config.Worker.Controllers { - host, _, err := net.SplitHostPort(controller) + for _, upstream := range c.Config.Worker.InitialUpstreams { + host, _, err := net.SplitHostPort(upstream) if err != nil { if strings.Contains(err.Error(), "missing port in address") { - host = controller + host = upstream } else { - c.UI.Error(fmt.Errorf("Invalid controller address %q: %w", controller, err).Error()) + c.UI.Error(fmt.Errorf("Invalid worker upstream address %q: %w", upstream, err).Error()) return base.CommandUserError } } @@ -342,7 +345,7 @@ func (c *Command) Run(args []string) int { errMsg = "a multicast" } if errMsg != "" { - c.UI.Error(fmt.Sprintf("Controller address %q is invalid: cannot be %s address", controller, errMsg)) + c.UI.Error(fmt.Sprintf("Worker upstream address %q is invalid: cannot be %s address", upstream, errMsg)) return base.CommandUserError } } diff --git a/internal/cmd/commands/server/worker_shutdown_reload_test.go b/internal/cmd/commands/server/worker_shutdown_reload_test.go index 7876e870e..546840464 100644 --- a/internal/cmd/commands/server/worker_shutdown_reload_test.go +++ b/internal/cmd/commands/server/worker_shutdown_reload_test.go @@ -31,7 +31,7 @@ disable_mlock = true worker { name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["%s"] + initial_upstreams = ["%s"] } kms "aead" { diff --git a/internal/cmd/commands/server/worker_tags_reload_test.go b/internal/cmd/commands/server/worker_tags_reload_test.go index 01114b811..74bc29f01 100644 --- a/internal/cmd/commands/server/worker_tags_reload_test.go +++ b/internal/cmd/commands/server/worker_tags_reload_test.go @@ -48,7 +48,7 @@ const tag1Config = ` worker { name = "test" description = "A default worker created in dev mode" - controllers = ["%s"] + initial_upstreams = ["%s"] tags { type = ["dev", "local"] } @@ -60,7 +60,7 @@ const tag2Config = ` worker { name = "test" description = "A default worker created in dev mode" - controllers = ["%s"] + initial_upstreams = ["%s"] tags { foo = ["bar", "baz"] } diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index 7e054b082..95c048d49 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -96,7 +96,7 @@ listener "tcp" { worker { name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] tags { type = ["dev", "local"] } @@ -181,8 +181,12 @@ type Worker struct { // We use a raw interface here so that we can take in a string // value pointing to an env var or file. We then resolve that - // and get the actual controller addresses. - Controllers []string `hcl:"-"` + // and get the actual upstream controller or worker addresses. + InitialUpstreams []string `hcl:"-"` + InitialUpstreamsRaw any `hcl:"initial_upstreams"` + + // The ControllersRaw field is deprecated and users should use InitialUpstreamsRaw instead. + // TODO: remove this field when support is discontinued. ControllersRaw interface{} `hcl:"controllers"` // We use a raw interface for parsing so that people can use JSON-like @@ -525,9 +529,9 @@ func Parse(d string) (*Config, error) { } } - result.Worker.Controllers, err = parseWorkerControllers(result) + result.Worker.InitialUpstreams, err = parseWorkerUpstreams(result) if err != nil { - return nil, fmt.Errorf("Failed to parse worker controllers: %w", err) + return nil, fmt.Errorf("Failed to parse worker upstreams: %w", err) } } @@ -587,35 +591,52 @@ func Parse(d string) (*Config, error) { return result, nil } -func parseWorkerControllers(c *Config) ([]string, error) { +// supportControllersRawConfig returns either initialUpstreamsRaw or controllersRaw depending on which is populated. Errors when both fields are populated. +// +func supportControllersRawConfig(initialUpstreamsRaw, controllersRaw any) (any, error) { + switch { + case initialUpstreamsRaw == nil && controllersRaw != nil: + return controllersRaw, nil + case initialUpstreamsRaw != nil && controllersRaw != nil: + return nil, fmt.Errorf("both initial_upstreams and controllers fields are populated") + } + return initialUpstreamsRaw, nil +} + +func parseWorkerUpstreams(c *Config) ([]string, error) { if c == nil || c.Worker == nil { return nil, fmt.Errorf("config or worker field is nil") } - if c.Worker.ControllersRaw == nil { + if c.Worker.InitialUpstreamsRaw == nil && c.Worker.ControllersRaw == nil { + // return nil here so that other address sources can be provided outside of config return nil, nil } + rawUpstreams, err := supportControllersRawConfig(c.Worker.InitialUpstreamsRaw, c.Worker.ControllersRaw) + if err != nil { + return nil, err + } - switch t := c.Worker.ControllersRaw.(type) { + switch t := rawUpstreams.(type) { case []interface{}: // An array was configured directly in Boundary's HCL Config file. - var controllers []string - err := mapstructure.WeakDecode(c.Worker.ControllersRaw, &controllers) + var upstreams []string + err := mapstructure.WeakDecode(rawUpstreams, &upstreams) if err != nil { - return nil, fmt.Errorf("failed to decode worker controllers block into config field: %w", err) + return nil, fmt.Errorf("failed to decode worker initial_upstreams block into config field: %w", err) } - return controllers, nil + return upstreams, nil case string: - controllersStr, err := parseutil.ParsePath(t) + upstreamsStr, err := parseutil.ParsePath(t) if err != nil { return nil, fmt.Errorf("bad env var or file pointer: %w", err) } - var addrs []string - err = json.Unmarshal([]byte(controllersStr), &addrs) + var upstreams []string + err = json.Unmarshal([]byte(upstreamsStr), &upstreams) if err != nil { return nil, fmt.Errorf("failed to unmarshal env/file contents: %w", err) } - return addrs, nil + return upstreams, nil default: typ := reflect.TypeOf(t) diff --git a/internal/cmd/config/config_test.go b/internal/cmd/config/config_test.go index 44bbc5e34..6afefae26 100644 --- a/internal/cmd/config/config_test.go +++ b/internal/cmd/config/config_test.go @@ -190,10 +190,10 @@ func TestDevWorker(t *testing.T) { }, }, Worker: &Worker{ - Name: "w_1234567890", - Description: "A default worker created in dev mode", - Controllers: []string{"127.0.0.1"}, - ControllersRaw: []interface{}{"127.0.0.1"}, + Name: "w_1234567890", + Description: "A default worker created in dev mode", + InitialUpstreams: []string{"127.0.0.1"}, + InitialUpstreamsRaw: []interface{}{"127.0.0.1"}, Tags: map[string][]string{ "type": {"dev", "local"}, }, @@ -213,7 +213,7 @@ func TestDevWorker(t *testing.T) { worker { name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] tags = ["type=dev", "type=local"] } ` @@ -233,7 +233,7 @@ func TestDevWorker(t *testing.T) { worker { name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] tags { type = "local" } @@ -258,7 +258,7 @@ func TestDevWorker(t *testing.T) { worker { name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] tags = ["tyPe=dev", "type=local"] } ` @@ -275,7 +275,7 @@ func TestDevWorker(t *testing.T) { worker { name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] tags = ["type=dev", "type=loCal"] } ` @@ -292,7 +292,7 @@ func TestDevWorker(t *testing.T) { worker { name = "dev-work\u0000er" description = "A default worker created in dev mode" - controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] tags = ["type=dev", "type=local"] } ` @@ -376,10 +376,10 @@ func TestDevCombined(t *testing.T) { }, DevController: true, Worker: &Worker{ - Name: "w_1234567890", - Description: "A default worker created in dev mode", - Controllers: []string{"127.0.0.1"}, - ControllersRaw: []interface{}{"127.0.0.1"}, + Name: "w_1234567890", + Description: "A default worker created in dev mode", + InitialUpstreams: []string{"127.0.0.1"}, + InitialUpstreamsRaw: []interface{}{"127.0.0.1"}, Tags: map[string][]string{ "type": {"dev", "local"}, }, @@ -419,7 +419,7 @@ func TestDevWorkerCredentialStorageDir(t *testing.T) { worker { name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] tags { type = ["dev", "local"] } @@ -438,7 +438,7 @@ func TestDevWorkerCredentialStorageDir(t *testing.T) { worker { name = "w_1234567890" description = "A default worker created in dev mode" - controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] tags { type = ["dev", "local"] } @@ -1039,118 +1039,152 @@ func TestController_EventingConfig(t *testing.T) { } } -func TestWorkerControllers(t *testing.T) { +func TestWorkerUpstreams(t *testing.T) { tests := []struct { - name string - in string - stateFn func(t *testing.T) - expWorkerControllers []string - expErr bool - expErrIs error - expErrStr string + name string + in string + stateFn func(t *testing.T) + expWorkerUpstreams []string + expErr bool + expErrIs error + expErrStr string }{ { - name: "No Controllers", + name: "No Upstreams", in: ` worker { name = "test" } `, - expWorkerControllers: nil, - expErr: false, + expWorkerUpstreams: nil, + expErr: false, }, { - name: "One Controller", + name: "One Upstream", in: ` worker { name = "test" - controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] } `, - expWorkerControllers: []string{"127.0.0.1"}, - expErr: false, + expWorkerUpstreams: []string{"127.0.0.1"}, + expErr: false, }, { - name: "Multiple controllers", + name: "Multiple Upstreams", in: ` worker { name = "test" - controllers = ["127.0.0.1", "127.0.0.2", "127.0.0.3"] + initial_upstreams = ["127.0.0.1", "127.0.0.2", "127.0.0.3"] } `, - expWorkerControllers: []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, - expErr: false, + expWorkerUpstreams: []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, + expErr: false, }, { name: "Using env var", in: ` worker { name = "test" - controllers = "env://BOUNDARY_WORKER_CONTROLLERS" + initial_upstreams = "env://BOUNDARY_WORKER_UPSTREAMS" } `, - stateFn: func(t *testing.T) { t.Setenv("BOUNDARY_WORKER_CONTROLLERS", `["127.0.0.1", "127.0.0.2", "127.0.0.3"]`) }, - expWorkerControllers: []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, - expErr: false, + stateFn: func(t *testing.T) { t.Setenv("BOUNDARY_WORKER_UPSTREAMS", `["127.0.0.1", "127.0.0.2", "127.0.0.3"]`) }, + expWorkerUpstreams: []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, + expErr: false, }, { name: "Using env var - invalid input 1", in: ` worker { name = "test" - controllers = "env://BOUNDARY_WORKER_CONTROLLERS" + initial_upstreams = "env://BOUNDARY_WORKER_UPSTREAMS" } `, stateFn: func(t *testing.T) { - controllers := ` + upstreams := ` worker { - controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] } ` - t.Setenv("BOUNDARY_WORKER_CONTROLLERS", controllers) + t.Setenv("BOUNDARY_WORKER_UPSTREAMS", upstreams) }, - expWorkerControllers: nil, - expErr: true, - expErrStr: "Failed to parse worker controllers: failed to unmarshal env/file contents: invalid character 'w' looking for beginning of value", + expWorkerUpstreams: nil, + expErr: true, + expErrStr: "Failed to parse worker upstreams: failed to unmarshal env/file contents: invalid character 'w' looking for beginning of value", }, { name: "Using env var - invalid input 2", in: ` worker { name = "test" - controllers = "env://BOUNDARY_WORKER_CONTROLLERS" + initial_upstreams = "env://BOUNDARY_WORKER_UPSTREAMS" } `, - stateFn: func(t *testing.T) { t.Setenv("BOUNDARY_WORKER_CONTROLLERS", `controllers = ["127.0.0.1"]`) }, - expWorkerControllers: nil, - expErr: true, - expErrStr: "Failed to parse worker controllers: failed to unmarshal env/file contents: invalid character 'c' looking for beginning of value", + stateFn: func(t *testing.T) { t.Setenv("BOUNDARY_WORKER_UPSTREAMS", `initial_upstreams = ["127.0.0.1"]`) }, + expWorkerUpstreams: nil, + expErr: true, + expErrStr: "Failed to parse worker upstreams: failed to unmarshal env/file contents: invalid character 'i' looking for beginning of value", }, { name: "Unsupported object", in: ` worker { name = "test" - controllers = { + initial_upstreams = { ip = "127.0.0.1" ip = "127.0.0.2" } } `, - expWorkerControllers: nil, - expErr: true, - expErrStr: "Failed to parse worker controllers: unexpected type \"[]map[string]interface {}\"", + expWorkerUpstreams: nil, + expErr: true, + expErrStr: "Failed to parse worker upstreams: unexpected type \"[]map[string]interface {}\"", }, { - name: "worker controllers set to invalid url", + name: "Worker initial_upstreams set to invalid url", in: ` worker { name = "test" - controllers = "env://\x00" + initial_upstreams = "env://\x00" }`, - expWorkerControllers: nil, - expErr: true, - expErrIs: parseutil.ErrNotAUrl, + expWorkerUpstreams: nil, + expErr: true, + expErrIs: parseutil.ErrNotAUrl, + }, + { + name: "Worker using deprecated controllers field", + in: ` + worker { + name = "test" + controllers = ["127.0.0.1", "127.0.0.2", "127.0.0.3"] + }`, + expWorkerUpstreams: []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, + expErr: false, + }, + { + name: "Different values in controllers and initial_upstreams field", + in: ` + worker { + name = "test" + controllers = ["127.0.0.1", "127.0.0.2", "127.0.0.3"] + initial_upstreams = ["127.0.0.1"] + }`, + expWorkerUpstreams: nil, + expErr: true, + expErrStr: "Failed to parse worker upstreams: both initial_upstreams and controllers fields are populated", + }, + { + name: "Identical values in controllers and initial_upstreams field", + in: ` + worker { + name = "test" + controllers = ["127.0.0.1"] + initial_upstreams = ["127.0.0.1"] + }`, + expWorkerUpstreams: nil, + expErr: true, + expErrStr: "Failed to parse worker upstreams: both initial_upstreams and controllers fields are populated", }, } @@ -1174,7 +1208,7 @@ func TestWorkerControllers(t *testing.T) { require.NoError(t, err) require.NotNil(t, c) require.NotNil(t, c.Worker) - require.EqualValues(t, tt.expWorkerControllers, c.Worker.Controllers) + require.EqualValues(t, tt.expWorkerUpstreams, c.Worker.InitialUpstreams) }) } } diff --git a/internal/credential/vault/setup_manual_test.go b/internal/credential/vault/setup_manual_test.go index cbd01a81f..23ed9c902 100644 --- a/internal/credential/vault/setup_manual_test.go +++ b/internal/credential/vault/setup_manual_test.go @@ -91,10 +91,10 @@ func TestSetupSleepyDevEnvironment(t *testing.T) { "foo": {"bar"}, } w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - Config: conf, - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), - Logger: logger.Named("w1"), + Config: conf, + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), + Logger: logger.Named("w1"), }) defer w1.Shutdown() addr := c1.ApiAddrs()[0] diff --git a/internal/daemon/worker/auth_rotation_test.go b/internal/daemon/worker/auth_rotation_test.go index c35e6213e..4e306d66c 100644 --- a/internal/daemon/worker/auth_rotation_test.go +++ b/internal/daemon/worker/auth_rotation_test.go @@ -42,7 +42,7 @@ func TestRotationTicking(t *testing.T) { const rotationPeriod = 20 * time.Second w := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - InitialControllers: c.ClusterAddrs(), + InitialUpstreams: c.ClusterAddrs(), Logger: logger.Named("worker"), AuthRotationPeriod: rotationPeriod, }) diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index aa4d1934d..a7d77bbf9 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -41,8 +41,8 @@ const hcpbUrlSuffix = ".proxy.boundary.hashicorp.cloud" // connection client creation func (w *Worker) StartControllerConnections() error { const op = "worker.(Worker).StartControllerConnections" - initialAddrs := make([]resolver.Address, 0, len(w.conf.RawConfig.Worker.Controllers)) - for _, addr := range w.conf.RawConfig.Worker.Controllers { + initialAddrs := make([]resolver.Address, 0, len(w.conf.RawConfig.Worker.InitialUpstreams)) + for _, addr := range w.conf.RawConfig.Worker.InitialUpstreams { switch { case strings.HasPrefix(addr, "/"): initialAddrs = append(initialAddrs, resolver.Address{Addr: addr}) @@ -52,7 +52,7 @@ func (w *Worker) StartControllerConnections() error { host, port, err = net.SplitHostPort(net.JoinHostPort(addr, "9201")) } if err != nil { - return fmt.Errorf("error parsing controller address: %w", err) + return fmt.Errorf("error parsing upstream address: %w", err) } initialAddrs = append(initialAddrs, resolver.Address{Addr: net.JoinHostPort(host, port)}) } @@ -64,7 +64,7 @@ func (w *Worker) StartControllerConnections() error { initialAddrs = append(initialAddrs, resolver.Address{Addr: clusterAddress}) event.WriteSysEvent(w.baseContext, op, fmt.Sprintf("Setting HCPB Cluster address %s as upstream address", clusterAddress)) } else { - return errors.New("no initial controller addresses found") + return errors.New("no initial upstream addresses found") } } @@ -72,7 +72,7 @@ func (w *Worker) StartControllerConnections() error { Addresses: initialAddrs, }) if err := w.createClientConn(initialAddrs[0].Addr); err != nil { - return fmt.Errorf("error making client connection to controller: %w", err) + return fmt.Errorf("error making client connection to upstream address: %w", err) } return nil @@ -135,7 +135,7 @@ func (w *Worker) v1KmsAuthDialFn(ctx context.Context, addr string) (net.Conn, er nonTlsConn, err = dialer.DialContext(ctx, "tcp", addr) } if err != nil { - return nil, fmt.Errorf("unable to dial to controller: %w", err) + return nil, fmt.Errorf("unable to dial to upstream: %w", err) } tlsConn := tls.Client(nonTlsConn, tlsConf) written, err := tlsConn.Write([]byte(authInfo.ConnectionNonce)) diff --git a/internal/daemon/worker/testing.go b/internal/daemon/worker/testing.go index d2711b7d9..865352b92 100644 --- a/internal/daemon/worker/testing.go +++ b/internal/daemon/worker/testing.go @@ -53,7 +53,7 @@ func (tw *TestWorker) Name() string { return tw.name } -func (tw *TestWorker) ControllerAddrs() []string { +func (tw *TestWorker) UpstreamAddrs() []string { var addrs []string lastStatus := tw.w.LastStatusSuccess() for _, v := range lastStatus.GetCalculatedUpstreams() { @@ -165,8 +165,8 @@ type TestWorkerOpts struct { // Config; if not provided a dev one will be created Config *config.Config - // Sets initial controller addresses - InitialControllers []string + // Sets initial upstream addresses + InitialUpstreams []string // If true, the worker will not be started DisableAutoStart bool @@ -233,8 +233,8 @@ func NewTestWorker(t testing.TB, opts *TestWorkerOpts) *TestWorker { opts.Config.Worker.Name = opts.Name } - if len(opts.InitialControllers) > 0 { - opts.Config.Worker.Controllers = opts.InitialControllers + if len(opts.InitialUpstreams) > 0 { + opts.Config.Worker.InitialUpstreams = opts.InitialUpstreams } // Start a logger @@ -325,7 +325,7 @@ func (tw *TestWorker) AddClusterWorkerMember(t testing.TB, opts *TestWorkerOpts) WorkerAuthKms: tw.w.conf.WorkerAuthKms, WorkerAuthStorageKms: tw.w.conf.WorkerAuthStorageKms, Name: opts.Name, - InitialControllers: tw.ControllerAddrs(), + InitialUpstreams: tw.UpstreamAddrs(), Logger: tw.w.conf.Logger, StatusGracePeriodDuration: opts.StatusGracePeriodDuration, } diff --git a/internal/tests/cluster/ipv6_listener_test.go b/internal/tests/cluster/ipv6_listener_test.go index 9de82f5d6..3cca5b714 100644 --- a/internal/tests/cluster/ipv6_listener_test.go +++ b/internal/tests/cluster/ipv6_listener_test.go @@ -45,10 +45,10 @@ func TestIPv6Listener(t *testing.T) { require.NoError(err) w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - Config: wconf, - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), - Logger: logger.Named("w1"), + Config: wconf, + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), + Logger: logger.Named("w1"), }) defer w1.Shutdown() diff --git a/internal/tests/cluster/multi_controller_worker_test.go b/internal/tests/cluster/multi_controller_worker_test.go index 98b040d63..df6c023b9 100644 --- a/internal/tests/cluster/multi_controller_worker_test.go +++ b/internal/tests/cluster/multi_controller_worker_test.go @@ -35,9 +35,9 @@ func TestMultiControllerMultiWorkerConnections(t *testing.T) { expectWorkers(t, c2) w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), - Logger: logger.Named("w1"), + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), + Logger: logger.Named("w1"), }) defer w1.Shutdown() @@ -60,9 +60,9 @@ func TestMultiControllerMultiWorkerConnections(t *testing.T) { expectWorkers(t, c2, w2) w1 = worker.NewTestWorker(t, &worker.TestWorkerOpts{ - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), - Logger: logger.Named("w1"), + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), + Logger: logger.Named("w1"), }) defer w1.Shutdown() diff --git a/internal/tests/cluster/session_cleanup_test.go b/internal/tests/cluster/session_cleanup_test.go index 150f474ce..ad6739430 100644 --- a/internal/tests/cluster/session_cleanup_test.go +++ b/internal/tests/cluster/session_cleanup_test.go @@ -109,7 +109,7 @@ func testWorkerSessionCleanupSingle(burdenCase timeoutBurdenType) func(t *testin w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: []string{proxy.ListenerAddr()}, + InitialUpstreams: []string{proxy.ListenerAddr()}, Logger: logger.Named("w1"), StatusGracePeriodDuration: workerGracePeriod(burdenCase), }) @@ -269,7 +269,7 @@ func testWorkerSessionCleanupMulti(burdenCase timeoutBurdenType) func(t *testing // ************ w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: []string{p1.ListenerAddr(), p2.ListenerAddr()}, + InitialUpstreams: []string{p1.ListenerAddr(), p2.ListenerAddr()}, Logger: logger.Named("w1"), StatusGracePeriodDuration: workerGracePeriod(burdenCase), }) diff --git a/internal/tests/cluster/unix_listener_test.go b/internal/tests/cluster/unix_listener_test.go index 9fa104bf4..97a0b9f80 100644 --- a/internal/tests/cluster/unix_listener_test.go +++ b/internal/tests/cluster/unix_listener_test.go @@ -58,10 +58,10 @@ func TestUnixListener(t *testing.T) { require.NoError(err) w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - Config: wconf, - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), - Logger: logger.Named("w1"), + Config: wconf, + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), + Logger: logger.Named("w1"), }) defer w1.Shutdown() diff --git a/internal/tests/cluster/worker_replay_test.go b/internal/tests/cluster/worker_replay_test.go index ff2458e80..a5a1fd2c6 100644 --- a/internal/tests/cluster/worker_replay_test.go +++ b/internal/tests/cluster/worker_replay_test.go @@ -37,9 +37,9 @@ func TestWorkerReplay(t *testing.T) { conf.Eventing = &ec.EventerConfig require.NoError(t, err) w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - Config: conf, - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), + Config: conf, + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), NonceFn: func(length int) (string, error) { return "test_noncetest_nonce", nil }, @@ -63,9 +63,9 @@ func TestWorkerReplay(t *testing.T) { // Now, start up again with the same nonce w1 = worker.NewTestWorker(t, &worker.TestWorkerOpts{ - Config: conf, - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), + Config: conf, + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), NonceFn: func(length int) (string, error) { return "test_noncetest_nonce", nil }, diff --git a/internal/tests/cluster/worker_tagging_test.go b/internal/tests/cluster/worker_tagging_test.go index 7c6132810..540f1d2f0 100644 --- a/internal/tests/cluster/worker_tagging_test.go +++ b/internal/tests/cluster/worker_tagging_test.go @@ -53,10 +53,10 @@ func TestWorkerTagging(t *testing.T) { "foo": {"bar"}, } w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - Config: conf, - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), - Logger: logger.Named("w1"), + Config: conf, + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), + Logger: logger.Named("w1"), }) defer w1.Shutdown() w1Addr := w1.ProxyAddrs()[0] @@ -70,10 +70,10 @@ func TestWorkerTagging(t *testing.T) { "az": {"one", "two", "three"}, } w2 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - Config: conf, - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), - Logger: logger.Named("w2"), + Config: conf, + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), + Logger: logger.Named("w2"), }) defer w2.Shutdown() w2Addr := w2.ProxyAddrs()[0] @@ -87,10 +87,10 @@ func TestWorkerTagging(t *testing.T) { "az": {"one", "three"}, } w3 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - Config: conf, - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), - Logger: logger.Named("w3"), + Config: conf, + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), + Logger: logger.Named("w3"), }) defer w3.Shutdown() w3Addr := w3.ProxyAddrs()[0] diff --git a/internal/tests/cluster/x509_verification_test.go b/internal/tests/cluster/x509_verification_test.go index bf6aeca78..2aa142e52 100644 --- a/internal/tests/cluster/x509_verification_test.go +++ b/internal/tests/cluster/x509_verification_test.go @@ -49,9 +49,9 @@ func TestCustomX509Verification_Client(t *testing.T) { conf.Eventing = &ec.EventerConfig req.NoError(err) w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), - Logger: logger.Named("w1"), + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), + Logger: logger.Named("w1"), }) t.Cleanup(w1.Shutdown) @@ -188,8 +188,8 @@ func testCustomX509Verification_Server(ec event.TestConfig, certPool *x509.CertP conf.Eventing = &ec.EventerConfig req.NoError(err) w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{ - WorkerAuthKms: c1.Config().WorkerAuthKms, - InitialControllers: c1.ClusterAddrs(), + WorkerAuthKms: c1.Config().WorkerAuthKms, + InitialUpstreams: c1.ClusterAddrs(), }) w1.Worker().TestOverrideX509VerifyCertPool = certPool w1.Worker().TestOverrideX509VerifyDnsName = dnsName diff --git a/website/content/docs/configuration/worker.mdx b/website/content/docs/configuration/worker.mdx index 72e1be5a7..7a4a62c5c 100644 --- a/website/content/docs/configuration/worker.mdx +++ b/website/content/docs/configuration/worker.mdx @@ -36,8 +36,8 @@ worker { an env var (env://) from which the address will be read; or a [go-sockaddr template](https://godoc.org/github.com/hashicorp/go-sockaddr/template). -- `controllers` - A list of hosts/IP addresses and optionally ports for reaching - controllers. The port will default to :9201 if not specified. This value can be +- `initial_upstreams` - A list of hosts/IP addresses and optionally ports for reaching + controllers or workers. The port will default to :9201 if not specified. This value can be a direct access string array with the addresses, or it can refer to a file on disk (file://) from which the addresses will be read, or an env var (env://) from which the addresses will be read. When using env or file, their contents From 7c139d28b291deb38a3ef72ffae61e0651118a3d Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 13 Jun 2022 19:02:11 -0400 Subject: [PATCH 080/100] Have hclog payloads use `json` tags (#2171) * Have hclog payloads use `json` tags Previously a global setting for the `structs` lib was causing the `json` tag to be used, but this could have side effects from other packages, or could be affected by other packages' behavior. This behavior was removed earlier but it means this format call no longer uses `json` tags; this behavior reverts it. It also normalizes using mapstructure in a few other places. * Update some tests * Fix incorrect op const * Fix more tests * refactor (events): tell filter to use json tags (fix related tests) Explicitly tell the filter to use the "json" tags so we don't have to re-tag everything with a mapstructure tag via bexpr.WithTagName("json") Update docs Co-authored-by: Jim --- CHANGELOG.md | 2 + .../event/cloudevents_formatter_node.go | 4 +- .../event/cloudevents_formatter_node_test.go | 2 +- internal/observability/event/context_test.go | 8 ++-- .../event/hclog_formatter_node.go | 6 ++- .../event/hclog_formatter_node_test.go | 42 +++++++++---------- internal/servers/repository_workerauth.go | 7 ++-- .../servers/repository_workerauth_test.go | 10 +++-- internal/servers/worker.go | 7 ++-- .../docs/concepts/filtering/events.mdx | 8 ++-- 10 files changed, 52 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd660f6bc..f46ea7584 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. sessions after 1 hour. See Deprecations/Changes for some additional details. [PR](https://github.com/hashicorp/boundary/pull/2160). +* event filtering: Change event filters to use lowercase and snake case for data + elements like the rest of Boundary filters do. ### Deprecations/Changes diff --git a/internal/observability/event/cloudevents_formatter_node.go b/internal/observability/event/cloudevents_formatter_node.go index 3dee6dc8f..d457703f2 100644 --- a/internal/observability/event/cloudevents_formatter_node.go +++ b/internal/observability/event/cloudevents_formatter_node.go @@ -143,7 +143,9 @@ func newFilter(f string) (*filter, error) { if f == "" { return nil, fmt.Errorf("%s: missing filter: %w", op, ErrInvalidParameter) } - e, err := bexpr.CreateEvaluator(f, bexpr.WithHookFn(filterpkg.WellKnownTypeFilterHook)) + // explicitly tell the filter to use the "json" tags so we don't have to + // re-tag everything with a mapstructure tag via bexpr.WithTagName("json") + e, err := bexpr.CreateEvaluator(f, bexpr.WithHookFn(filterpkg.WellKnownTypeFilterHook), bexpr.WithTagName("json")) if err != nil { return nil, fmt.Errorf("%s: %w", op, err) } diff --git a/internal/observability/event/cloudevents_formatter_node_test.go b/internal/observability/event/cloudevents_formatter_node_test.go index 6ad44f316..2aee2598d 100644 --- a/internal/observability/event/cloudevents_formatter_node_test.go +++ b/internal/observability/event/cloudevents_formatter_node_test.go @@ -154,7 +154,7 @@ func TestNode_Process(t *testing.T) { testNode, err := newCloudEventsFormatterFilter(testUrl, cloudevents.FormatJSON, WithSchema(testUrl)) require.NoError(t, err) - f, err := newFilter(`Data == "match-filter"`) + f, err := newFilter(`data == "match-filter"`) require.NoError(t, err) tests := []struct { diff --git a/internal/observability/event/context_test.go b/internal/observability/event/context_test.go index c145c6863..69990ea29 100644 --- a/internal/observability/event/context_test.go +++ b/internal/observability/event/context_test.go @@ -517,25 +517,25 @@ func Test_Filtering(t *testing.T) { }{ { name: "allowed", - allow: []string{`"/Data/list" contains "1"`}, + allow: []string{`"/data/list" contains "1"`}, hdr: []interface{}{"list", []string{"1", "2"}}, found: true, }, { name: "not-allowed", - allow: []string{`"/Data/list" contains "22"`}, + allow: []string{`"/data/list" contains "22"`}, hdr: []interface{}{"list", []string{"1", "2"}}, found: false, }, { name: "deny", - deny: []string{`"/Data/list" contains "1"`}, + deny: []string{`"/data/list" contains "1"`}, hdr: []interface{}{"list", []string{"1", "2"}}, found: false, }, { name: "not-deny", - deny: []string{`"/Data/list" contains "22"`}, + deny: []string{`"/data/list" contains "22"`}, hdr: []interface{}{"list", []string{"1", "2"}}, found: true, }, diff --git a/internal/observability/event/hclog_formatter_node.go b/internal/observability/event/hclog_formatter_node.go index 7cb36562a..480ba0a6a 100644 --- a/internal/observability/event/hclog_formatter_node.go +++ b/internal/observability/event/hclog_formatter_node.go @@ -36,7 +36,7 @@ type hclogFormatterFilter struct { } func newHclogFormatterFilter(jsonFormat bool, opt ...Option) (*hclogFormatterFilter, error) { - const op = "event.NewHclogFormatter" + const op = "event.newHclogFormatterFilter" opts := getOpts(opt...) var s signer n := hclogFormatterFilter{ @@ -147,7 +147,9 @@ func (f *hclogFormatterFilter) Process(ctx context.Context, e *eventlogger.Event var m map[string]interface{} switch string(e.Type) { case string(ErrorType), string(AuditType), string(SystemType): - m = structs.Map(e.Payload) + s := structs.New(e.Payload) + s.TagName = "json" + m = s.Map() case string(ObservationType): m = e.Payload.(map[string]interface{}) default: diff --git a/internal/observability/event/hclog_formatter_node_test.go b/internal/observability/event/hclog_formatter_node_test.go index d8d717fbd..ab9ba3104 100644 --- a/internal/observability/event/hclog_formatter_node_test.go +++ b/internal/observability/event/hclog_formatter_node_test.go @@ -15,7 +15,7 @@ func TestHclogFormatter_Process(t *testing.T) { t.Parallel() ctx := context.Background() - f, e := newFilter(`Op == "match-filter"`) + f, e := newFilter(`op == "match-filter"`) require.NoError(t, e) testPredicate := newPredicate([]*filter{f}, nil) @@ -60,10 +60,9 @@ func TestHclogFormatter_Process(t *testing.T) { }, want: []string{ "[INFO] system event:", - "Data:msg=hello", - "Id=1", - "Version=v0.1", - "Op=text", + "data:msg=hello", + "version=v0.1", + "op=text", }, }, { @@ -122,10 +121,10 @@ func TestHclogFormatter_Process(t *testing.T) { }, want: []string{ "[ERROR] error event:", - "Error=\"invalid parameter\"", - "Id=1", - "Version=v0.1", - "Op=text", + "error=\"invalid parameter\"", + "id=1", + "version=v0.1", + "op=text", }, }, { @@ -144,10 +143,10 @@ func TestHclogFormatter_Process(t *testing.T) { }, want: []string{ "{\"@level\":\"error\",\"@message\":\"error event\"", - "\"Error\":\"invalid parameter\"", - "\"Id\":\"1\"", - "\"Version\":\"v0.1\"", - "\"Op\":\"text\"", + "\"error\":\"invalid parameter\"", + "\"id\":\"1\"", + "\"version\":\"v0.1\"", + "\"op\":\"text\"", }, }, { @@ -167,11 +166,11 @@ func TestHclogFormatter_Process(t *testing.T) { }, want: []string{ "[ERROR] error event:", - "Error=\"invalid parameter\"", - "Id=1", - "Version=v0.1", - "Info:name=alice", - "Op=text", + "error=\"invalid parameter\"", + "id=1", + "version=v0.1", + "info:name=alice", + "op=text", }, }, { @@ -193,10 +192,9 @@ func TestHclogFormatter_Process(t *testing.T) { }, want: []string{ "[INFO] system event:", - "Data:msg=hello", - "Id=1", - "Version=v0.1", - "Op=match-filter", + "data:msg=hello", + "version=v0.1", + "op=match-filter", }, }, { diff --git a/internal/servers/repository_workerauth.go b/internal/servers/repository_workerauth.go index cdcd34b80..9c94b4bb0 100644 --- a/internal/servers/repository_workerauth.go +++ b/internal/servers/repository_workerauth.go @@ -31,7 +31,7 @@ var ( ) type rootCertificatesVersion struct { - Version uint32 `mapstructure:"version" structs:"version"` + Version uint32 `mapstructure:"version"` } // WorkerAuthRepositoryStorage is the Worker Auth database repository @@ -398,8 +398,9 @@ func (r *WorkerAuthRepositoryStorage) loadNodeInformation(ctx context.Context, n } workerIdInfo := workerAuthWorkerId{WorkerId: authorizedWorker.GetWorkerId()} - workerIdMap := structs.Map(workerIdInfo) - state, err := structpb.NewStruct(workerIdMap) + s := structs.New(workerIdInfo) + s.TagName = "mapstructure" + state, err := structpb.NewStruct(s.Map()) if err != nil { return errors.Wrap(ctx, err, op) } diff --git a/internal/servers/repository_workerauth_test.go b/internal/servers/repository_workerauth_test.go index 94aa141bc..95b85853d 100644 --- a/internal/servers/repository_workerauth_test.go +++ b/internal/servers/repository_workerauth_test.go @@ -106,8 +106,9 @@ func TestStoreCertAuthorityVersioning(t *testing.T) { // Store CA with old version, expect error badVersion := &rootCertificatesVersion{Version: uint32(1)} - badStateOpt := structs.Map(badVersion) - badState, err := structpb.NewStruct(badStateOpt) + s := structs.New(badVersion) + s.TagName = "mapstructure" + badState, err := structpb.NewStruct(s.Map()) require.NoError(err) newRoots2 := &types.RootCertificates{ Id: ca_id, @@ -121,8 +122,9 @@ func TestStoreCertAuthorityVersioning(t *testing.T) { // Remove CA, expect updated version cAuthRemove := &types.RootCertificates{Id: ca_id} removeVersion := &rootCertificatesVersion{Version: uint32(2)} - removeOpt := structs.Map(removeVersion) - removeState, err := structpb.NewStruct(removeOpt) + s = structs.New(removeVersion) + s.TagName = "mapstructure" + removeState, err := structpb.NewStruct(s.Map()) require.NoError(err) cAuthRemove.State = removeState err = workerAuthRepo.Remove(ctx, cAuthRemove) diff --git a/internal/servers/worker.go b/internal/servers/worker.go index b13cf80e2..a80383508 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -13,7 +13,7 @@ import ( ) type workerAuthWorkerId struct { - WorkerId string `mapstructure:"worker_id" structs:"worker_id"` + WorkerId string `mapstructure:"worker_id"` } // AttachWorkerIdToState accepts a workerId and creates a struct for use with the Nodeenrollment lib @@ -26,8 +26,9 @@ func AttachWorkerIdToState(ctx context.Context, workerId string) (*structpb.Stru } workerMap := &workerAuthWorkerId{WorkerId: workerId} - stateOpt := structs.Map(workerMap) - return structpb.NewStruct(stateOpt) + 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 diff --git a/website/content/docs/concepts/filtering/events.mdx b/website/content/docs/concepts/filtering/events.mdx index f2397d780..a2e1beb23 100644 --- a/website/content/docs/concepts/filtering/events.mdx +++ b/website/content/docs/concepts/filtering/events.mdx @@ -75,8 +75,8 @@ sink "stderr" = { event_types = ["*"] format = "cloudevents-text" allow_filters = [ - '"/Data/request_info/Path" contains ":authenticate"' - '"/Data/Op" contains ".createClientConn"' + '"/data/request_info/path" contains ":authenticate"' + '"/data/op" contains ".createClientConn"' ] # note: deny_filters are also supported. } @@ -85,8 +85,8 @@ sink "stderr" = { When running `boundary dev` the example allow filter can be given via: ```bash boundary dev \ - -event-allow-filter '"/Data/request_info/Path" contains ":authenticate"' \ - -event-allow-filter '"/Data/Op" contains ".createClientConn"' + -event-allow-filter '"/data/request_info/path" contains ":authenticate"' \ + -event-allow-filter '"/data/op" contains ".createClientConn"' ``` ~> Double quotes are part of the filter syntax; when using the CLI, it is likely easier to surround the filter with single quotes than to deal with escaping From 9dde83aea760cf3b743cc22f7ed6561cb249eca6 Mon Sep 17 00:00:00 2001 From: Jim Date: Tue, 14 Jun 2022 12:09:02 -0400 Subject: [PATCH 081/100] feature (byow): classify status msg data (#2181) Classifying the data allows it to appropriately included in audit events --- Makefile | 2 ++ .../services/server_coordination_service.pb.go | 8 ++++---- .../services/v1/server_coordination_service.proto | 8 ++++---- internal/proto/controller/servers/v1/servers.proto | 12 ++++++------ internal/servers/servers.pb.go | 12 ++++++------ 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index cbab664db..52a9712b5 100644 --- a/Makefile +++ b/Makefile @@ -196,6 +196,8 @@ protobuild: @protoc-go-inject-tag -input=./internal/gen/controller/api/services/user_service.pb.go @protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/workers/worker.pb.go @protoc-go-inject-tag -input=./internal/gen/controller/api/services/worker_service.pb.go + @protoc-go-inject-tag -input=./internal/gen/controller/servers/services/server_coordination_service.pb.go + # these protos, services and openapi artifacts are purely for testing purposes @protoc-go-inject-tag -input=./internal/gen/testing/event/event.pb.go diff --git a/internal/gen/controller/servers/services/server_coordination_service.pb.go b/internal/gen/controller/servers/services/server_coordination_service.pb.go index 8fc3654e2..9f04a6d8a 100644 --- a/internal/gen/controller/servers/services/server_coordination_service.pb.go +++ b/internal/gen/controller/servers/services/server_coordination_service.pb.go @@ -276,7 +276,7 @@ type Connection struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` + ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" class:"public"` // @gotags: `class:"public"` Status CONNECTIONSTATUS `protobuf:"varint,2,opt,name=status,proto3,enum=controller.servers.services.v1.CONNECTIONSTATUS" json:"status,omitempty"` } @@ -331,7 +331,7 @@ type SessionJobInfo struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty" class:"public"` // @gotags: `class:"public"` Status SESSIONSTATUS `protobuf:"varint,2,opt,name=status,proto3,enum=controller.servers.services.v1.SESSIONSTATUS" json:"status,omitempty"` Connections []*Connection `protobuf:"bytes,3,rep,name=connections,proto3" json:"connections,omitempty"` } @@ -521,7 +521,7 @@ type UpstreamServer struct { // type specifies between controller or worker. Type UpstreamServer_TYPE `protobuf:"varint,10,opt,name=type,proto3,enum=controller.servers.services.v1.UpstreamServer_TYPE" json:"type,omitempty"` // Address of the daemon - Address string `protobuf:"bytes,20,opt,name=address,proto3" json:"address,omitempty"` + Address string `protobuf:"bytes,20,opt,name=address,proto3" json:"address,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *UpstreamServer) Reset() { @@ -712,7 +712,7 @@ type StatusResponse struct { CalculatedUpstreams []*UpstreamServer `protobuf:"bytes,30,rep,name=calculated_upstreams,json=calculatedUpstreams,proto3" json:"calculated_upstreams,omitempty"` // The ID of the worker which made the request. The worker can send this value in subsequent requests so the // controller does not need to do a database lookup for the id using the name field. - WorkerId string `protobuf:"bytes,40,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty"` + WorkerId string `protobuf:"bytes,40,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *StatusResponse) Reset() { diff --git a/internal/proto/controller/servers/services/v1/server_coordination_service.proto b/internal/proto/controller/servers/services/v1/server_coordination_service.proto index 6f47f3918..ca43cb101 100644 --- a/internal/proto/controller/servers/services/v1/server_coordination_service.proto +++ b/internal/proto/controller/servers/services/v1/server_coordination_service.proto @@ -21,7 +21,7 @@ enum CONNECTIONSTATUS { } message Connection { - string connection_id = 1; + string connection_id = 1; // @gotags: `class:"public"` CONNECTIONSTATUS status = 2; } @@ -34,7 +34,7 @@ enum SESSIONSTATUS { } message SessionJobInfo { - string session_id = 1; + string session_id = 1; // @gotags: `class:"public"` SESSIONSTATUS status = 2; repeated Connection connections = 3; } @@ -69,7 +69,7 @@ message UpstreamServer { TYPE type = 10; // Address of the daemon - string address = 20; + string address = 20; // @gotags: `class:"public"` } message StatusRequest { @@ -120,5 +120,5 @@ message StatusResponse { // The ID of the worker which made the request. The worker can send this value in subsequent requests so the // controller does not need to do a database lookup for the id using the name field. - string worker_id = 40; + string worker_id = 40; // @gotags: `class:"public"` } diff --git a/internal/proto/controller/servers/v1/servers.proto b/internal/proto/controller/servers/v1/servers.proto index 461154084..33b06d696 100644 --- a/internal/proto/controller/servers/v1/servers.proto +++ b/internal/proto/controller/servers/v1/servers.proto @@ -6,24 +6,24 @@ option go_package = "github.com/hashicorp/boundary/internal/servers;servers"; // TagPair matches a key to a value. message TagPair { - string key = 1; - string value = 2; + string key = 1; // @gotags: `class:"public"` + string value = 2; // @gotags: `class:"public"` } // ServerWorkerStatus is the new message used in place of Server to relay status request info. message ServerWorkerStatus { // Id of the worker. - string public_id = 10; + string public_id = 10; // @gotags: `class:"public"` // Name of the resource (optional) - string name = 20; + string name = 20; // @gotags: `class:"public"` // Address for the worker. This must be set. - string address = 30; + string address = 30; // @gotags: `class:"public"` // Tags for workers repeated TagPair tags = 40; // The key id for this worker, if applicable (optional) - string key_id = 50; + string key_id = 50; // @gotags: `class:"public"` } diff --git a/internal/servers/servers.pb.go b/internal/servers/servers.pb.go index 022ca7002..842ba9bd4 100644 --- a/internal/servers/servers.pb.go +++ b/internal/servers/servers.pb.go @@ -26,8 +26,8 @@ type TagPair struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty" class:"public"` // @gotags: `class:"public"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *TagPair) Reset() { @@ -83,15 +83,15 @@ type ServerWorkerStatus struct { unknownFields protoimpl.UnknownFields // Id of the worker. - PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty"` + PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" class:"public"` // @gotags: `class:"public"` // Name of the resource (optional) - Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty" class:"public"` // @gotags: `class:"public"` // Address for the worker. This must be set. - Address string `protobuf:"bytes,30,opt,name=address,proto3" json:"address,omitempty"` + Address string `protobuf:"bytes,30,opt,name=address,proto3" json:"address,omitempty" class:"public"` // @gotags: `class:"public"` // Tags for workers Tags []*TagPair `protobuf:"bytes,40,rep,name=tags,proto3" json:"tags,omitempty"` // The key id for this worker, if applicable (optional) - KeyId string `protobuf:"bytes,50,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty"` + KeyId string `protobuf:"bytes,50,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *ServerWorkerStatus) Reset() { From 8fd663af472bd512bbc61244598fe9d7785bb6ec Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 14 Jun 2022 13:27:55 -0400 Subject: [PATCH 082/100] Send more info on KMS connect (#2182) --- internal/cmd/base/listener.go | 1 + internal/daemon/controller/intercepting_listener.go | 2 +- internal/daemon/worker/controller_connection.go | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/cmd/base/listener.go b/internal/cmd/base/listener.go index ef4d82eb5..df148b6f1 100644 --- a/internal/cmd/base/listener.go +++ b/internal/cmd/base/listener.go @@ -36,6 +36,7 @@ type WorkerAuthInfo struct { Name string `json:"name"` Description string `json:"description"` ConnectionNonce string `json:"connection_nonce"` + ProxyAddress string `json:"proxy_address"` } // Factory is the factory function to create a listener. diff --git a/internal/daemon/controller/intercepting_listener.go b/internal/daemon/controller/intercepting_listener.go index 6a95f18ae..b427fdd4d 100644 --- a/internal/daemon/controller/intercepting_listener.go +++ b/internal/daemon/controller/intercepting_listener.go @@ -113,7 +113,7 @@ func (m *interceptingListener) Accept() (net.Conn, error) { } workerInfo := workerInfoRaw.(*workerAuthEntry) workerInfo.conn = tlsConn - event.WriteSysEvent(ctx, op, "worker successfully authed", "name", workerInfo.Name) + event.WriteSysEvent(ctx, op, "worker successfully authed", "name", workerInfo.Name, "description", workerInfo.Description, "proxy_address", workerInfo.ProxyAddress) return tlsConn, nil default: diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index a7d77bbf9..9f51475d4 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -208,8 +208,9 @@ func (w *Worker) createClientConn(addr string) error { func (w *Worker) workerAuthTLSConfig() (*tls.Config, *base.WorkerAuthInfo, error) { var err error info := &base.WorkerAuthInfo{ - Name: w.conf.RawConfig.Worker.Name, - Description: w.conf.RawConfig.Worker.Description, + Name: w.conf.RawConfig.Worker.Name, + Description: w.conf.RawConfig.Worker.Description, + ProxyAddress: w.conf.RawConfig.Worker.PublicAddr, } info.ConnectionNonce, err = w.nonceFn(20) From cca044d8cb37715a9929a909f3132f365a8c5593 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 14 Jun 2022 13:19:50 -0500 Subject: [PATCH 083/100] Update nodeenrollment to v0.1.0 --- go.mod | 8 ++++---- go.sum | 15 ++++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index b3164431e..99b0ebca1 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/golang-migrate/migrate/v4 v4.15.1 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe github.com/golang/protobuf v1.5.2 - github.com/google/go-cmp v0.5.7 + github.com/google/go-cmp v0.5.8 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0 github.com/hashicorp/boundary/api v0.0.24 @@ -69,7 +69,7 @@ require ( github.com/posener/complete v1.2.3 github.com/prometheus/client_golang v1.12.1 github.com/ryanuber/go-glob v1.0.0 - github.com/stretchr/testify v1.7.1 + github.com/stretchr/testify v1.7.2 github.com/zalando/go-keyring v0.2.1 go.uber.org/atomic v1.9.0 golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 @@ -92,7 +92,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this i require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b - github.com/hashicorp/nodeenrollment v0.0.0-20220609044242-598c08a89c45 + github.com/hashicorp/nodeenrollment v0.1.0 ) require ( @@ -191,7 +191,7 @@ require ( google.golang.org/appengine v1.6.7 // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/sqlite v1.2.4 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 61ee52032..ec430c6cb 100644 --- a/go.sum +++ b/go.sum @@ -582,8 +582,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v35 v35.2.0/go.mod h1:s0515YVTI+IMrDoy9Y4pHt9ShGpzHvHO8rZ7L7acgvs= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -740,8 +740,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nodeenrollment v0.0.0-20220609044242-598c08a89c45 h1:vnRJfKOMBoan6+PFw5P5CItnl3Z6lJJI1JaBM1tlLCo= -github.com/hashicorp/nodeenrollment v0.0.0-20220609044242-598c08a89c45/go.mod h1:8l3RSuZJAibZKnqX6tvLaeoFzPhDf4oCRVsyFjU5Soc= +github.com/hashicorp/nodeenrollment v0.1.0 h1:mhMVbrHM6NTDcpte/mOilP3Fd/HrcctshGs4/82EO6U= +github.com/hashicorp/nodeenrollment v0.1.0/go.mod h1:LIPKi+g0g/vl3xhpbzugCalHSxX1PMeqnatkAsxRgyM= github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= @@ -1234,8 +1234,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -1913,8 +1913,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= gorm.io/driver/postgres v1.2.2 h1:Ka9W6feOU+rPM9m007eYLMD4QoZuYGBnQ3Jp0faGSwg= gorm.io/driver/postgres v1.2.2/go.mod h1:Ik3tK+a3FMp8ORZl29v4b3M0RsgXsaeMXh9s9eVMXco= From 9997f1a7bdf53266eaa400bf0f4badfe45dc3a95 Mon Sep 17 00:00:00 2001 From: Jim Date: Tue, 14 Jun 2022 17:51:56 -0400 Subject: [PATCH 084/100] feature (workers): required schema changes (#2185) * feature (workers): required schema changes Changes required for consolidation of worker name, description and address fields * fixup! feature (workers): required schema changes * fixup! feature (workers): required schema changes * fixup! feature (workers): required schema changes * Fixing up address constraint. * last_status_time = now() on insert only if the worker is type = 'kms' * update last_status_time for pki and kms based on different columns being updated. * tweaking the constraints on last_status_time and address. Co-authored-by: Todd --- .../99/02_worker_controller_tables.up.sql | 173 +++++++++++------- .../oss/postgres/99/04_views.up.sql | 2 - 2 files changed, 110 insertions(+), 65 deletions(-) diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index f2ff45a45..eeb1cfed7 100644 --- a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -27,6 +27,26 @@ create trigger controller_update_time_column before update on server_controller -- Worker table takes the place of the server table. -- instead of the private_id we use a wt_public_id field named public_id since -- workers will now be exposed as resources in boundary. + +create table server_worker_type_enm ( + name text primary key + constraint only_predefined_types_allowed + check ( + name in ( + 'pki', + 'kms' + ) + ) +); +comment on table server_worker_type_enm is + 'server_worker_type_enm is an enumeration table for worker types. ' + 'It contains rows for representing the pki and kms types.'; + +insert into server_worker_type_enm (name) +values + ('pki'), + ('kms'); + create table server_worker ( public_id wt_public_id primary key, scope_id wt_scope_id not null @@ -34,57 +54,44 @@ create table server_worker ( on delete cascade on update cascade, description wt_description, - name wt_name unique, - -- The address can be null since it is an optional value from the API. - address wt_network_address, + name wt_name -- server_worker_scope_id_name_uq defines an appropriate uniqueness constraint for name + constraint worker_name_must_be_set_by_status + check ( + type != 'kms' or name is not null + ) + constraint kms_name_must_be_lowercase + check (lower(trim(name)) = name and type = 'kms') + constraint kms_name_only_has_printable_characters + check (name !~ '[^[:print:]]' and type = 'kms'), + address wt_network_address + constraint address_must_be_set_by_status + check ( + -- address can be null only no status update has been received yet. + (last_status_time is not null and address is not null) + or + (last_status_time is null and address is null) + ), create_time wt_timestamp, update_time wt_timestamp, version wt_version, - last_status_time timestamp with time zone, - constraint last_status_time_not_before_create_time - check (last_status_time >= create_time), - -- This is the calculated address that the worker reports it is reachable on. - -- This must be set if the worker has ever received a status update. - worker_reported_address wt_network_address - constraint worker_reported_address_must_be_set_by_status - check ( - (last_status_time is null and worker_reported_address is null) - or - (last_status_time is not null and worker_reported_address is not null) - ), - -- This is the name that the worker reports in it's status updates. - worker_reported_name wt_name - constraint worker_reported_name_must_be_set_by_status - check ( - (last_status_time is null and worker_reported_name is null) - or - (last_status_time is not null and worker_reported_name is not null) - or - (last_status_time is not null and worker_reported_key_id is not null) - ) - constraint worker_reported_name_must_be_lowercase - check (lower(trim(worker_reported_name)) = worker_reported_name) - constraint worker_reported_name_only_has_printable_characters - check (worker_reported_name !~ '[^[:print:]]'), - worker_reported_key_id text - constraint worker_reported_key_id_must_be_set_by_status - check ( - (last_status_time is null and worker_reported_key_id is null) - or - (last_status_time is not null and worker_reported_name is not null) - or - (last_status_time is not null and worker_reported_key_id is not null) - ), + type text not null + constraint server_worker_type_enm_fkey + references server_worker_type_enm (name) + on delete restrict + on update cascade, + last_status_time timestamp with time zone + constraint last_status_time_not_before_create_time + check (last_status_time >= create_time) + constraint last_status_time_always_set_for_kms + check (type != 'kms' or last_status_time is not null), constraint server_worker_scope_id_name_uq - unique(scope_id, name), - constraint server_worker_scope_id_worker_reported_name_uq - unique(scope_id, worker_reported_name) + unique(scope_id, name) ); comment on table server_worker is 'server_worker is a table where each row represents a Boundary worker.'; create trigger immutable_columns before update on server_worker - for each row execute procedure immutable_columns('public_id', 'scope_id', 'create_time'); + for each row execute procedure immutable_columns('public_id', 'scope_id', 'type', 'create_time'); create trigger default_create_time_column before insert on server_worker for each row execute procedure default_create_time(); @@ -95,38 +102,78 @@ create trigger worker_insert_time_column before insert on server_worker create trigger worker_update_time_column before update on server_worker for each row execute procedure update_time_column(); -create trigger update_version_column after update of version, description, name, address on server_worker +-- fixme: we should only update the version column when type = 'pki', but that +-- can be deferred +create trigger update_version_column after update of version, description, name on server_worker for each row execute procedure update_version_column(); -create function update_server_worker_update_last_status_time_column() +create function update_kms_server_worker_update_last_status_time_column() returns trigger as $$ begin - new.last_status_time = now(); - return new; -end; -$$ language plpgsql; -comment on function update_server_worker_update_last_status_time_column is - 'function used to update the last_status_time column in server_worker to now'; - -create trigger update_server_worker_last_status_time_column before update of worker_reported_address, worker_reported_name on server_worker - for each row execute procedure update_server_worker_update_last_status_time_column(); - -create function insert_server_worker_update_last_status_time_column() - returns trigger -as $$ -begin - if new.worker_reported_address is not null or new.worker_reported_name is not null then + if new.type = 'kms' then new.last_status_time = now(); + return new; end if; - return new; end; $$ language plpgsql; -comment on function insert_server_worker_update_last_status_time_column is - 'function used to update the last_status_time column in server_worker to now'; +comment on function update_kms_server_worker_update_last_status_time_column is + 'function used to update the last_status_time column in server_worker with type kms to now'; + +create trigger update_kms_server_worker_last_status_time_column before update of address, name, description on server_worker + for each row execute procedure update_kms_server_worker_update_last_status_time_column(); + +create function update_pki_server_worker_update_last_status_time_column() + returns trigger +as $$ +begin + if new.type = 'pki' then + new.last_status_time = now(); + return new; + end if; +end; +$$ language plpgsql; +comment on function update_pki_server_worker_update_last_status_time_column is + 'function used to update the last_status_time column in server_worker with type pki to now'; + +create trigger update_pki_server_worker_last_status_time_column before update of address on server_worker + for each row execute procedure update_pki_server_worker_update_last_status_time_column(); + +create function insert_kms_server_worker_update_last_status_time_column() + returns trigger +as $$ +begin + if new.type = 'kms' then + new.last_status_time = now(); + return new; + end if; +end; +$$ language plpgsql; +comment on function insert_kms_server_worker_update_last_status_time_column is + 'function used to update the last_status_time column in server_worker with type kms to now'; create trigger insert_server_worker_last_update_time_column before insert on server_worker - for each row execute procedure insert_server_worker_update_last_status_time_column(); + for each row execute procedure insert_kms_server_worker_update_last_status_time_column(); + +create function insert_server_worker_type_column() + returns trigger +as $$ +begin + perform from worker_auth_authorized where worker_id = new.public_id; + case + when new.type = 'pki' and not found then + raise exception 'invalid type: pki worker is not authorized in worker_auth_authorized table'; + when new.type = 'kms' and found then + raise exception 'invalid type: kms worker cannot authorized in worker_auth_authorized table'; + end case; + return new; +end; +$$ language plpgsql; +comment on function insert_server_worker_type_column is + 'function used to constraint the type column in server_worker'; + +create trigger insert_server_worker_type_column before insert on server_worker + for each row execute procedure insert_server_worker_type_column(); -- Create table worker tag create table server_worker_tag_enm ( diff --git a/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql b/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql index 80e7bf993..a323a08fc 100644 --- a/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql @@ -29,8 +29,6 @@ select w.create_time, w.update_time, w.version, - w.worker_reported_name, - w.worker_reported_address, w.last_status_time, cc.count as active_connection_count, -- keys and tags can be any lowercase printable character so use uppercase characters as delimitors. From 8335deb8b64cea4702a1e113d77bbb718115b6dc Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 14 Jun 2022 17:54:35 -0400 Subject: [PATCH 085/100] Update protos, generated code, and service handlers (#2188) * Update protos, generated code, and service handlers * Address review feedback --- api/workers/option.gen.go | 12 - api/workers/worker.gen.go | 33 +- .../worker_provided_configuration.gen.go | 8 - globals/fields.go | 1 + internal/api/genapi/input.go | 4 - internal/api/genapi/parse.go | 2 +- internal/cmd/commands/workerscmd/funcs.go | 78 +--- .../handlers/workers/worker_service.go | 70 ++-- internal/gen/controller.swagger.json | 71 +--- .../api/resources/workers/v1/worker.proto | 56 +-- .../storage/servers/store/v1/worker.proto | 20 +- internal/servers/store/worker.pb.go | 109 ++---- .../api/resources/workers/worker.pb.go | 359 ++++++------------ 13 files changed, 258 insertions(+), 565 deletions(-) delete mode 100644 api/workers/worker_provided_configuration.gen.go diff --git a/api/workers/option.gen.go b/api/workers/option.gen.go index 29ba168a3..ca1fb0aa4 100644 --- a/api/workers/option.gen.go +++ b/api/workers/option.gen.go @@ -87,18 +87,6 @@ func WithRecursive(recurse bool) Option { } } -func WithAddress(inAddress string) Option { - return func(o *options) { - o.postMap["address"] = inAddress - } -} - -func DefaultAddress() Option { - return func(o *options) { - o.postMap["address"] = nil - } -} - func WithDescription(inDescription string) Option { return func(o *options) { o.postMap["description"] = inDescription diff --git a/api/workers/worker.gen.go b/api/workers/worker.gen.go index f108a3191..1a021c000 100644 --- a/api/workers/worker.gen.go +++ b/api/workers/worker.gen.go @@ -13,23 +13,22 @@ import ( ) type Worker struct { - Id string `json:"id,omitempty"` - ScopeId string `json:"scope_id,omitempty"` - Scope *scopes.ScopeInfo `json:"scope,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - CreatedTime time.Time `json:"created_time,omitempty"` - UpdatedTime time.Time `json:"updated_time,omitempty"` - Version uint32 `json:"version,omitempty"` - Address string `json:"address,omitempty"` - CanonicalAddress string `json:"canonical_address,omitempty"` - Tags map[string][]string `json:"tags,omitempty"` - CanonicalTags map[string][]string `json:"canonical_tags,omitempty"` - LastStatusTime time.Time `json:"last_status_time,omitempty"` - WorkerProvidedConfiguration *WorkerProvidedConfiguration `json:"worker_provided_configuration,omitempty"` - WorkerGeneratedAuthToken string `json:"worker_generated_auth_token,omitempty"` - ActiveConnectionCount uint32 `json:"active_connection_count,omitempty"` - AuthorizedActions []string `json:"authorized_actions,omitempty"` + Id string `json:"id,omitempty"` + ScopeId string `json:"scope_id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + CreatedTime time.Time `json:"created_time,omitempty"` + UpdatedTime time.Time `json:"updated_time,omitempty"` + Version uint32 `json:"version,omitempty"` + Address string `json:"address,omitempty"` + CanonicalTags map[string][]string `json:"canonical_tags,omitempty"` + ConfigTags map[string][]string `json:"config_tags,omitempty"` + LastStatusTime time.Time `json:"last_status_time,omitempty"` + WorkerGeneratedAuthToken string `json:"worker_generated_auth_token,omitempty"` + ActiveConnectionCount uint32 `json:"active_connection_count,omitempty"` + Type string `json:"type,omitempty"` + AuthorizedActions []string `json:"authorized_actions,omitempty"` response *api.Response } diff --git a/api/workers/worker_provided_configuration.gen.go b/api/workers/worker_provided_configuration.gen.go deleted file mode 100644 index 4a276211a..000000000 --- a/api/workers/worker_provided_configuration.gen.go +++ /dev/null @@ -1,8 +0,0 @@ -// Code generated by "make api"; DO NOT EDIT. -package workers - -type WorkerProvidedConfiguration struct { - Address string `json:"address,omitempty"` - Name string `json:"name,omitempty"` - Tags map[string][]string `json:"tags,omitempty"` -} diff --git a/globals/fields.go b/globals/fields.go index 84c23f810..ebe436c7b 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -78,6 +78,7 @@ const ( CanonicalAddressField = "canonical_address" TagsField = "tags" CanonicalTagsField = "canonical_tags" + ConfigTagsField = "config_tags" ConfigurationField = "configuration" WorkerGeneratedAuthTokenField = "worker_generated_auth_token" WorkerProvidedConfigurationField = "worker_provided_configuration" diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index bd2179a61..f2a0286fe 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -746,10 +746,6 @@ var inputStructs = []*structInfo{ fieldFilter: []string{"private_key"}, recursiveListing: true, }, - { - inProto: &workers.WorkerProvidedConfiguration{}, - outFile: "workers/worker_provided_configuration.gen.go", - }, { inProto: &workers.Worker{}, outFile: "workers/worker.gen.go", diff --git a/internal/api/genapi/parse.go b/internal/api/genapi/parse.go index 564b48c2e..9062e3aa8 100644 --- a/internal/api/genapi/parse.go +++ b/internal/api/genapi/parse.go @@ -70,7 +70,7 @@ func parsePBs() { name = fmt.Sprintf("%s.%s", pkg, name) } switch name { - case "v1.AuthorizedCollectionActionsEntry", "v1.CanonicalTagsEntry", "v1.TagsEntry": + case "v1.AuthorizedCollectionActionsEntry", "v1.CanonicalTagsEntry", "v1.TagsEntry", "v1.ConfigTagsEntry": fi.FieldType = "map[string][]string" default: fi.FieldType = sliceText + ptr + name diff --git a/internal/cmd/commands/workerscmd/funcs.go b/internal/cmd/commands/workerscmd/funcs.go index 1e959eb3a..e479b4a38 100644 --- a/internal/cmd/commands/workerscmd/funcs.go +++ b/internal/cmd/commands/workerscmd/funcs.go @@ -37,6 +37,11 @@ func (c *Command) printListTable(items []*workers.Worker) string { fmt.Sprintf(" Scope ID: %s", item.ScopeId), ) } + if item.Type != "" { + output = append(output, + fmt.Sprintf(" Type: %s", item.Type), + ) + } if item.Version > 0 { output = append(output, fmt.Sprintf(" Version: %d", item.Version), @@ -52,9 +57,9 @@ func (c *Command) printListTable(items []*workers.Worker) string { fmt.Sprintf(" Description: %s", item.Description), ) } - if item.CanonicalAddress != "" { + if item.Address != "" { output = append(output, - fmt.Sprintf(" Canonical Address: %s", item.CanonicalAddress), + fmt.Sprintf(" Address: %s", item.Address), ) } if !item.LastStatusTime.IsZero() { @@ -100,6 +105,12 @@ func printItemTable(result api.GenericResult) string { if item.Description != "" { nonAttributeMap["Description"] = item.Description } + if item.Type != "" { + nonAttributeMap["Type"] = item.Type + } + if item.Address != "" { + nonAttributeMap["Address"] = item.Address + } if !item.LastStatusTime.IsZero() { nonAttributeMap["Last Status Time"] = item.LastStatusTime } @@ -121,69 +132,14 @@ func printItemTable(result api.GenericResult) string { ) } - var workerProvidedTags map[string][]string - var workerProvidedAddress string - if item.WorkerProvidedConfiguration != nil { - config := item.WorkerProvidedConfiguration - configMap := make(map[string]any) - if config.Address != "" { - configMap["Address"] = config.Address - workerProvidedAddress = config.Address - } - if config.Name != "" { - configMap["Name"] = config.Name - } - ret = append(ret, - "", - " Worker-Provided Configuration:", - base.WrapMap(4, maxLength, configMap), - ) - workerProvidedTags = config.Tags - } - - if len(item.Address) > 0 || len(item.CanonicalAddress) > 0 || len(workerProvidedAddress) > 0 { - ret = append(ret, - "", - " Address:", - ) - if len(item.Address) > 0 { - ret = append(ret, - " Item (via API):", - " "+item.Address, - ) - } - if len(workerProvidedAddress) > 0 { - ret = append(ret, - " Worker Configuration:", - " "+workerProvidedAddress, - ) - } - if len(item.CanonicalAddress) > 0 { - ret = append(ret, - " Canonical:", - " "+item.CanonicalAddress, - ) - } - } - - if len(item.Tags) > 0 || len(item.CanonicalTags) > 0 || len(workerProvidedTags) > 0 { + if len(item.CanonicalTags) > 0 || len(item.ConfigTags) > 0 { ret = append(ret, "", " Tags:", ) - if len(item.Tags) > 0 { - tagMap := make(map[string]any, len(item.Tags)) - for k, v := range item.Tags { - tagMap[k] = v - } - ret = append(ret, - " Item (via API):", - base.WrapMap(6, 2, tagMap), - ) - } - if len(workerProvidedTags) > 0 { - tagMap := make(map[string]any, len(workerProvidedTags)) - for k, v := range workerProvidedTags { + if len(item.ConfigTags) > 0 { + tagMap := make(map[string]any, len(item.ConfigTags)) + for k, v := range item.ConfigTags { tagMap[k] = v } ret = append(ret, diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index b19540974..601e84a7f 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" + "github.com/hashicorp/go-secure-stdlib/strutil" "github.com/hashicorp/nodeenrollment/types" "github.com/mr-tron/base58" "google.golang.org/grpc/codes" @@ -27,6 +28,11 @@ import ( "google.golang.org/protobuf/types/known/wrapperspb" ) +const ( + PkiWorkerType = "pki" + KmsWorkerType = "kms" +) + var ( maskManager handlers.MaskManager @@ -330,7 +336,6 @@ func (s Service) createInRepo(ctx context.Context, worker *pb.Worker, creds *typ worker.GetScopeId(), servers.WithName(worker.GetName().GetValue()), servers.WithDescription(worker.GetDescription().GetValue()), - servers.WithAddress(worker.GetAddress().GetValue()), ) retWorker, err := repo.CreateWorker(ctx, newWorker, servers.WithFetchNodeCredentialsRequest(creds)) if err != nil { @@ -364,9 +369,6 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st if name := item.GetName(); name != nil { opts = append(opts, servers.WithName(name.GetValue())) } - if addr := item.GetAddress(); addr != nil { - opts = append(opts, servers.WithAddress(addr.GetValue())) - } w := servers.NewWorker(scopeId, opts...) w.PublicId = id dbMask := maskManager.Translate(mask) @@ -452,12 +454,13 @@ func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (* } if outputFields.Has(globals.AuthorizedActionsField) { out.AuthorizedActions = opts.WithAuthorizedActions + if in.Type == KmsWorkerType { + // KMS workers cannot be updated through the API + out.AuthorizedActions = strutil.StrListDelete(action.Update.String()) + } } if outputFields.Has(globals.AddressField) && in.GetAddress() != "" { - out.Address = wrapperspb.String(in.GetAddress()) - } - if outputFields.Has(globals.CanonicalAddressField) { - out.CanonicalAddress = in.CanonicalAddress() + out.Address = in.GetAddress() } if outputFields.Has(globals.LastStatusTimeField) { out.LastStatusTime = in.GetLastStatusTime().GetTimestamp() @@ -465,6 +468,13 @@ func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (* if outputFields.Has(globals.ActiveConnectionCountField) { out.ActiveConnectionCount = in.ActiveConnectionCount() } + if outputFields.Has(globals.ConfigTagsField) && len(in.GetConfigTags()) > 0 { + var err error + out.ConfigTags, err = tagsToMapProto(in.GetConfigTags()) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing config tags proto")) + } + } if outputFields.Has(globals.CanonicalTagsField) && len(in.CanonicalTags()) > 0 { var err error out.CanonicalTags, err = tagsToMapProto(in.CanonicalTags()) @@ -472,31 +482,6 @@ func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (* return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing canonical tags proto")) } } - if outputFields.Has(globals.TagsField) && len(in.GetApiTags()) > 0 { - var err error - out.Tags, err = tagsToMapProto(in.GetApiTags()) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing api tags proto")) - } - } - if outputFields.Has(globals.ConfigurationField) { - if in.GetWorkerReportedAddress() != "" || - in.GetWorkerReportedName() != "" || - len(in.GetConfigTags()) > 0 { - out.WorkerProvidedConfiguration = &pb.WorkerProvidedConfiguration{} - } - if len(in.GetConfigTags()) > 0 { - var err error - out.GetWorkerProvidedConfiguration().Tags, err = tagsToMapProto(in.GetConfigTags()) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing config tags proto")) - } - } - if out.WorkerProvidedConfiguration != nil { - out.GetWorkerProvidedConfiguration().Address = in.GetWorkerReportedAddress() - out.GetWorkerProvidedConfiguration().Name = in.GetWorkerReportedName() - } - } return &out, nil } @@ -551,17 +536,14 @@ func validateDeleteRequest(req *pbs.DeleteWorkerRequest) error { func validateUpdateRequest(req *pbs.UpdateWorkerRequest) error { return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string { badFields := map[string]string{} - if req.GetItem().GetWorkerProvidedConfiguration() != nil { - badFields[globals.ConfigurationField] = "This is a read only field." - } - if req.GetItem().GetCanonicalAddress() != "" { - badFields[globals.CanonicalAddressField] = "This is a read only field." + if req.GetItem().GetAddress() != "" { + badFields[globals.AddressField] = "This is a read only field." } if req.GetItem().GetCanonicalTags() != nil { badFields[globals.CanonicalAddressField] = "This is a read only field." } - if req.GetItem().GetTags() != nil { - badFields[globals.TagsField] = "These values cannot be updated in this type of request." + if req.GetItem().GetConfigTags() != nil { + badFields[globals.TagsField] = "This is a read only field." } return badFields }, servers.WorkerPrefix) @@ -583,21 +565,15 @@ func validateCreateRequest(req *pbs.CreateWorkerLedRequest) error { if req.GetItem().WorkerGeneratedAuthToken == nil { badFields[globals.WorkerGeneratedAuthTokenField] = cannotBeEmptyMsg } - if req.GetItem().CanonicalAddress != "" { + if req.GetItem().Address != "" { badFields[globals.CanonicalAddressField] = readOnlyFieldMsg } - if req.GetItem().Tags != nil { - badFields[globals.TagsField] = readOnlyFieldMsg - } if req.GetItem().CanonicalTags != nil { badFields[globals.CanonicalTagsField] = readOnlyFieldMsg } if req.GetItem().LastStatusTime != nil { badFields[globals.LastStatusTimeField] = readOnlyFieldMsg } - if req.GetItem().WorkerProvidedConfiguration != nil { - badFields[globals.WorkerProvidedConfigurationField] = readOnlyFieldMsg - } if req.GetItem().AuthorizedActions != nil { badFields[globals.AuthorizedActionsField] = readOnlyFieldMsg } diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 48efff41a..33c1a15d5 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -5524,11 +5524,11 @@ }, "name": { "type": "string", - "description": "Optional name for identification purposes." + "description": "Optional name for identification purposes. Can only be set through the API\nfor `pki`-type workers; read-only for `kms`-type workers." }, "description": { "type": "string", - "description": "Optional user-set description for identification purposes." + "description": "Optional user-set description for identification purposes. Can only be set\nthrough the API for `pki`-type workers; read-only for `kms`-type workers." }, "created_time": { "type": "string", @@ -5549,22 +5549,7 @@ }, "address": { "type": "string", - "description": "The address that this worker is reachable at." - }, - "canonical_address": { - "type": "string", - "description": "Output only. This is the address used by authorized session. The value\nis the same as the address field if set. Otherwise it uses the address the\nworker reports.", - "readOnly": true - }, - "tags": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "object" - } - }, - "description": "Output only. The tags attached to this worker.", + "description": "Output only. The address that this worker is reachable at, as sourced from\nthe worker's configuration file.", "readOnly": true }, "canonical_tags": { @@ -5575,7 +5560,18 @@ "type": "object" } }, - "description": "Output only. The deduplicated union of the tags reported by the worker and\nthe tags added through the user facing api. This is used when applying\nworker filters.", + "description": "Output only. The deduplicated union of the tags reported by the worker from\nits configuration and any tags added through other means. This is used\nwhen applying worker filters.", + "readOnly": true + }, + "config_tags": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "object" + } + }, + "description": "Output only. The tags set in the worker's configuration file.", "readOnly": true }, "last_status_time": { @@ -5584,11 +5580,6 @@ "description": "Output only. The time this worker daemon last reported its status.", "readOnly": true }, - "worker_provided_configuration": { - "$ref": "#/definitions/controller.api.resources.workers.v1.WorkerProvidedConfiguration", - "description": "Output only. The set of information the worker daemon has reported about\nitself.", - "readOnly": true - }, "worker_generated_auth_token": { "type": "string", "title": "worker_generated_auth_token is input only. Supports the worker led node\nenrollment flow where this credentials token is produced by a worker. This\ntoken is a base58 encoded types.FetchNodeCredentialsRequest from\nhttps://github.com/hashicorp/nodeenrollment" @@ -5599,6 +5590,11 @@ "description": "Output only. The number of connections that this worker is currently handling.", "readOnly": true }, + "type": { + "type": "string", + "description": "Output only. The type of the worker, denoted by how it authenticates: `pki`\nor `kms`.", + "readOnly": true + }, "authorized_actions": { "type": "array", "items": { @@ -5610,33 +5606,6 @@ }, "title": "Worker contains all fields related to a Worker resource" }, - "controller.api.resources.workers.v1.WorkerProvidedConfiguration": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Output only. The address the worker daemon reports itself as reachable at.", - "readOnly": true - }, - "name": { - "type": "string", - "description": "Output only. The name the worker daemon reports itself having.", - "readOnly": true - }, - "tags": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "object" - } - }, - "description": "Output only. The tags reporteed by the worker daemon.", - "readOnly": true - } - }, - "description": "WorkerProvidedConfiguration contains configuration values reported by the worker daemon." - }, "controller.api.services.v1.AddGroupMembersResponse": { "type": "object", "properties": { diff --git a/internal/proto/controller/api/resources/workers/v1/worker.proto b/internal/proto/controller/api/resources/workers/v1/worker.proto index f54a7d955..951fa6c75 100644 --- a/internal/proto/controller/api/resources/workers/v1/worker.proto +++ b/internal/proto/controller/api/resources/workers/v1/worker.proto @@ -10,18 +10,6 @@ import "google/protobuf/wrappers.proto"; option go_package = "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers;workers"; -// WorkerProvidedConfiguration contains configuration values reported by the worker daemon. -message WorkerProvidedConfiguration { - // Output only. The address the worker daemon reports itself as reachable at. - string address = 10; - - // Output only. The name the worker daemon reports itself having. - string name = 20; - - // Output only. The tags reporteed by the worker daemon. - map tags = 40; -} - // Worker contains all fields related to a Worker resource message Worker { // Output only. The ID of the User. @@ -33,7 +21,8 @@ message Worker { // Output only. Scope information for this resource. resources.scopes.v1.ScopeInfo scope = 30; - // Optional name for identification purposes. + // Optional name for identification purposes. Can only be set through the API + // for `pki`-type workers; read-only for `kms`-type workers. google.protobuf.StringValue name = 40 [ (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = { @@ -42,7 +31,8 @@ message Worker { } ]; // @gotags: `class:"sensitive"` - // Optional user-set description for identification purposes. + // Optional user-set description for identification purposes. Can only be set + // through the API for `pki`-type workers; read-only for `kms`-type workers. google.protobuf.StringValue description = 50 [ (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = { @@ -61,34 +51,20 @@ message Worker { // The mutation will fail if the version does not match the latest known good version. uint32 version = 80; // @gotags: `class:"public"` - // The address that this worker is reachable at. - google.protobuf.StringValue address = 90 [ - (custom_options.v1.generate_sdk_option) = true, - (custom_options.v1.mask_mapping) = { - this: "address" - that: "address" - } - ]; // @gotags: `class:"public"` + // Output only. The address that this worker is reachable at, as sourced from + // the worker's configuration file. + string address = 90; // @gotags: `class:"public"` - // Output only. This is the address used by authorized session. The value - // is the same as the address field if set. Otherwise it uses the address the - // worker reports. - string canonical_address = 100 [json_name = "canonical_address"]; // @gotags: `class:"public"` - - // Output only. The tags attached to this worker. - map tags = 110; // @gotags: `class:"public"` - - // Output only. The deduplicated union of the tags reported by the worker and - // the tags added through the user facing api. This is used when applying - // worker filters. + // Output only. The deduplicated union of the tags reported by the worker from + // its configuration and any tags added through other means. This is used + // when applying worker filters. map canonical_tags = 120 [json_name = "canonical_tags"]; // @gotags: `class:"public"` - // Output only. The time this worker daemon last reported its status. - google.protobuf.Timestamp last_status_time = 130 [json_name = "last_status_time"]; // @gotags: `class:"public"` + // Output only. The tags set in the worker's configuration file. + map config_tags = 130 [json_name = "config_tags"]; // @gotags: `class:"public"` - // Output only. The set of information the worker daemon has reported about - // itself. - WorkerProvidedConfiguration worker_provided_configuration = 140 [json_name = "worker_provided_configuration"]; + // Output only. The time this worker daemon last reported its status. + google.protobuf.Timestamp last_status_time = 140 [json_name = "last_status_time"]; // @gotags: `class:"public"` // worker_generated_auth_token is input only. Supports the worker led node // enrollment flow where this credentials token is produced by a worker. This @@ -99,6 +75,10 @@ message Worker { // Output only. The number of connections that this worker is currently handling. uint32 active_connection_count = 160 [json_name = "active_connection_count"]; // @gotags: `class:"public"` + // Output only. The type of the worker, denoted by how it authenticates: `pki` + // or `kms`. + string type = 170; + // Output only. The available actions on this resource for the requester. repeated string authorized_actions = 300 [json_name = "authorized_actions"]; // @gotags: `class:"public"` } diff --git a/internal/proto/controller/storage/servers/store/v1/worker.proto b/internal/proto/controller/storage/servers/store/v1/worker.proto index 0a160a87e..20c83d8a3 100644 --- a/internal/proto/controller/storage/servers/store/v1/worker.proto +++ b/internal/proto/controller/storage/servers/store/v1/worker.proto @@ -29,10 +29,7 @@ message Worker { // Address for the worker. This is optional. // @inject_tag: `gorm:"default:null"` - string address = 40 [(custom_options.v1.mask_mapping) = { - this: "address" - that: "address" - }]; + string address = 40; // The create_time is set by the database. // @inject_tag: `gorm:"default:current_timestamp"` @@ -50,22 +47,13 @@ message Worker { // @inject_tag: `gorm:"default:null"` uint32 version = 80; - // Name of the resource (optional) - // @inject_tag: `gorm:"default:null"` - string worker_reported_name = 90; - - // Address for the worker. This must be set when the worker has received - // at least 1 status update fromt he worker daemon. - // @inject_tag: `gorm:"default:null"` - string worker_reported_address = 100; - // The last_status_time is the last time a worker daemon sent a status update. // @inject_tag: `gorm:"default:null"` timestamp.v1.Timestamp last_status_time = 110; - // keyId reported by a PKI worker (optional) - // @inject_tag: `gorm:"default:null"` - string worker_reported_key_id = 120; + // The type of the worker, denoted by how it authenticates: pki or kms. + // @inject_tag: `gorm:"not_null"` + string type = 130; } // WorkerTag is a tag for a worker. The primary key is comprised of the diff --git a/internal/servers/store/worker.pb.go b/internal/servers/store/worker.pb.go index 3420bc49f..9ab200250 100644 --- a/internal/servers/store/worker.pb.go +++ b/internal/servers/store/worker.pb.go @@ -52,19 +52,12 @@ type Worker struct { // version allows optimistic locking of the resource. // @inject_tag: `gorm:"default:null"` Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` - // Name of the resource (optional) - // @inject_tag: `gorm:"default:null"` - WorkerReportedName string `protobuf:"bytes,90,opt,name=worker_reported_name,json=workerReportedName,proto3" json:"worker_reported_name,omitempty" gorm:"default:null"` - // Address for the worker. This must be set when the worker has received - // at least 1 status update fromt he worker daemon. - // @inject_tag: `gorm:"default:null"` - WorkerReportedAddress string `protobuf:"bytes,100,opt,name=worker_reported_address,json=workerReportedAddress,proto3" json:"worker_reported_address,omitempty" gorm:"default:null"` // The last_status_time is the last time a worker daemon sent a status update. // @inject_tag: `gorm:"default:null"` LastStatusTime *timestamp.Timestamp `protobuf:"bytes,110,opt,name=last_status_time,json=lastStatusTime,proto3" json:"last_status_time,omitempty" gorm:"default:null"` - // keyId reported by a PKI worker (optional) - // @inject_tag: `gorm:"default:null"` - WorkerReportedKeyId string `protobuf:"bytes,120,opt,name=worker_reported_key_id,json=workerReportedKeyId,proto3" json:"worker_reported_key_id,omitempty" gorm:"default:null"` + // The type of the worker, denoted by how it authenticates: pki or kms. + // @inject_tag: `gorm:"not_null"` + Type string `protobuf:"bytes,130,opt,name=type,proto3" json:"type,omitempty" gorm:"not_null"` } func (x *Worker) Reset() { @@ -155,20 +148,6 @@ func (x *Worker) GetVersion() uint32 { return 0 } -func (x *Worker) GetWorkerReportedName() string { - if x != nil { - return x.WorkerReportedName - } - return "" -} - -func (x *Worker) GetWorkerReportedAddress() string { - if x != nil { - return x.WorkerReportedAddress - } - return "" -} - func (x *Worker) GetLastStatusTime() *timestamp.Timestamp { if x != nil { return x.LastStatusTime @@ -176,9 +155,9 @@ func (x *Worker) GetLastStatusTime() *timestamp.Timestamp { return nil } -func (x *Worker) GetWorkerReportedKeyId() string { +func (x *Worker) GetType() string { if x != nil { - return x.WorkerReportedKeyId + return x.Type } return "" } @@ -278,7 +257,7 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x6f, 0x74, 0x6f, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x83, 0x05, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe1, 0x03, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, @@ -287,49 +266,39 @@ var file_controller_storage_servers_store_v1_worker_proto_rawDesc = []byte{ 0x6e, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x16, 0xc2, 0xdd, 0x29, 0x12, 0x0a, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, - 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, - 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x5a, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x54, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, - 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, - 0x78, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x68, 0x0a, 0x09, 0x57, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x4b, + 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x32, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x54, 0x0a, + 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x82, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x68, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1e, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/pbs/controller/api/resources/workers/worker.pb.go b/sdk/pbs/controller/api/resources/workers/worker.pb.go index 57e01edab..ed7e0dc91 100644 --- a/sdk/pbs/controller/api/resources/workers/worker.pb.go +++ b/sdk/pbs/controller/api/resources/workers/worker.pb.go @@ -25,73 +25,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// WorkerProvidedConfiguration contains configuration values reported by the worker daemon. -type WorkerProvidedConfiguration struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Output only. The address the worker daemon reports itself as reachable at. - Address string `protobuf:"bytes,10,opt,name=address,proto3" json:"address,omitempty"` - // Output only. The name the worker daemon reports itself having. - Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty"` - // Output only. The tags reporteed by the worker daemon. - Tags map[string]*structpb.ListValue `protobuf:"bytes,40,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (x *WorkerProvidedConfiguration) Reset() { - *x = WorkerProvidedConfiguration{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WorkerProvidedConfiguration) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WorkerProvidedConfiguration) ProtoMessage() {} - -func (x *WorkerProvidedConfiguration) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WorkerProvidedConfiguration.ProtoReflect.Descriptor instead. -func (*WorkerProvidedConfiguration) Descriptor() ([]byte, []int) { - return file_controller_api_resources_workers_v1_worker_proto_rawDescGZIP(), []int{0} -} - -func (x *WorkerProvidedConfiguration) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -func (x *WorkerProvidedConfiguration) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *WorkerProvidedConfiguration) GetTags() map[string]*structpb.ListValue { - if x != nil { - return x.Tags - } - return nil -} - // Worker contains all fields related to a Worker resource type Worker struct { state protoimpl.MessageState @@ -104,9 +37,11 @@ type Worker struct { ScopeId string `protobuf:"bytes,20,opt,name=scope_id,proto3" json:"scope_id,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. Scope information for this resource. Scope *scopes.ScopeInfo `protobuf:"bytes,30,opt,name=scope,proto3" json:"scope,omitempty"` - // Optional name for identification purposes. + // Optional name for identification purposes. Can only be set through the API + // for `pki`-type workers; read-only for `kms`-type workers. Name *wrapperspb.StringValue `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty" class:"sensitive"` // @gotags: `class:"sensitive"` - // Optional user-set description for identification purposes. + // Optional user-set description for identification purposes. Can only be set + // through the API for `pki`-type workers; read-only for `kms`-type workers. Description *wrapperspb.StringValue `protobuf:"bytes,50,opt,name=description,proto3" json:"description,omitempty" class:"sensitive"` // @gotags: `class:"sensitive"` // Output only. The time this resource was created. CreatedTime *timestamppb.Timestamp `protobuf:"bytes,60,opt,name=created_time,proto3" json:"created_time,omitempty" class:"public"` // @gotags: `class:"public"` @@ -115,23 +50,17 @@ type Worker struct { // Version is used in mutation requests, after the initial creation, to ensure this resource has not changed. // The mutation will fail if the version does not match the latest known good version. Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"` - // The address that this worker is reachable at. - Address *wrapperspb.StringValue `protobuf:"bytes,90,opt,name=address,proto3" json:"address,omitempty" class:"public"` // @gotags: `class:"public"` - // Output only. This is the address used by authorized session. The value - // is the same as the address field if set. Otherwise it uses the address the - // worker reports. - CanonicalAddress string `protobuf:"bytes,100,opt,name=canonical_address,proto3" json:"canonical_address,omitempty" class:"public"` // @gotags: `class:"public"` - // Output only. The tags attached to this worker. - Tags map[string]*structpb.ListValue `protobuf:"bytes,110,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" class:"public"` // @gotags: `class:"public"` - // Output only. The deduplicated union of the tags reported by the worker and - // the tags added through the user facing api. This is used when applying - // worker filters. + // Output only. The address that this worker is reachable at, as sourced from + // the worker's configuration file. + Address string `protobuf:"bytes,90,opt,name=address,proto3" json:"address,omitempty" class:"public"` // @gotags: `class:"public"` + // Output only. The deduplicated union of the tags reported by the worker from + // its configuration and any tags added through other means. This is used + // when applying worker filters. CanonicalTags map[string]*structpb.ListValue `protobuf:"bytes,120,rep,name=canonical_tags,proto3" json:"canonical_tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" class:"public"` // @gotags: `class:"public"` + // Output only. The tags set in the worker's configuration file. + ConfigTags map[string]*structpb.ListValue `protobuf:"bytes,130,rep,name=config_tags,proto3" json:"config_tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3" class:"public"` // @gotags: `class:"public"` // Output only. The time this worker daemon last reported its status. - LastStatusTime *timestamppb.Timestamp `protobuf:"bytes,130,opt,name=last_status_time,proto3" json:"last_status_time,omitempty" class:"public"` // @gotags: `class:"public"` - // Output only. The set of information the worker daemon has reported about - // itself. - WorkerProvidedConfiguration *WorkerProvidedConfiguration `protobuf:"bytes,140,opt,name=worker_provided_configuration,proto3" json:"worker_provided_configuration,omitempty"` + LastStatusTime *timestamppb.Timestamp `protobuf:"bytes,140,opt,name=last_status_time,proto3" json:"last_status_time,omitempty" class:"public"` // @gotags: `class:"public"` // worker_generated_auth_token is input only. Supports the worker led node // enrollment flow where this credentials token is produced by a worker. This // token is a base58 encoded types.FetchNodeCredentialsRequest from @@ -139,6 +68,9 @@ type Worker struct { WorkerGeneratedAuthToken *wrapperspb.StringValue `protobuf:"bytes,150,opt,name=worker_generated_auth_token,proto3" json:"worker_generated_auth_token,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The number of connections that this worker is currently handling. ActiveConnectionCount uint32 `protobuf:"varint,160,opt,name=active_connection_count,proto3" json:"active_connection_count,omitempty" class:"public"` // @gotags: `class:"public"` + // Output only. The type of the worker, denoted by how it authenticates: `pki` + // or `kms`. + Type string `protobuf:"bytes,170,opt,name=type,proto3" json:"type,omitempty"` // Output only. The available actions on this resource for the requester. AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty" class:"public"` // @gotags: `class:"public"` } @@ -146,7 +78,7 @@ type Worker struct { func (x *Worker) Reset() { *x = Worker{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[1] + mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -159,7 +91,7 @@ func (x *Worker) String() string { func (*Worker) ProtoMessage() {} func (x *Worker) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[1] + mi := &file_controller_api_resources_workers_v1_worker_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -172,7 +104,7 @@ func (x *Worker) ProtoReflect() protoreflect.Message { // Deprecated: Use Worker.ProtoReflect.Descriptor instead. func (*Worker) Descriptor() ([]byte, []int) { - return file_controller_api_resources_workers_v1_worker_proto_rawDescGZIP(), []int{1} + return file_controller_api_resources_workers_v1_worker_proto_rawDescGZIP(), []int{0} } func (x *Worker) GetId() string { @@ -231,27 +163,13 @@ func (x *Worker) GetVersion() uint32 { return 0 } -func (x *Worker) GetAddress() *wrapperspb.StringValue { +func (x *Worker) GetAddress() string { if x != nil { return x.Address } - return nil -} - -func (x *Worker) GetCanonicalAddress() string { - if x != nil { - return x.CanonicalAddress - } return "" } -func (x *Worker) GetTags() map[string]*structpb.ListValue { - if x != nil { - return x.Tags - } - return nil -} - func (x *Worker) GetCanonicalTags() map[string]*structpb.ListValue { if x != nil { return x.CanonicalTags @@ -259,16 +177,16 @@ func (x *Worker) GetCanonicalTags() map[string]*structpb.ListValue { return nil } -func (x *Worker) GetLastStatusTime() *timestamppb.Timestamp { +func (x *Worker) GetConfigTags() map[string]*structpb.ListValue { if x != nil { - return x.LastStatusTime + return x.ConfigTags } return nil } -func (x *Worker) GetWorkerProvidedConfiguration() *WorkerProvidedConfiguration { +func (x *Worker) GetLastStatusTime() *timestamppb.Timestamp { if x != nil { - return x.WorkerProvidedConfiguration + return x.LastStatusTime } return nil } @@ -287,6 +205,13 @@ func (x *Worker) GetActiveConnectionCount() uint32 { return 0 } +func (x *Worker) GetType() string { + if x != nil { + return x.Type + } + return "" +} + func (x *Worker) GetAuthorizedActions() []string { if x != nil { return x.AuthorizedActions @@ -314,83 +239,53 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x80, 0x02, 0x0a, 0x1b, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x5e, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4a, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, - 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc7, 0x0a, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, - 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, - 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, - 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, - 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, - 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, - 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x1a, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x12, - 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x63, - 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, - 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x04, 0x74, 0x61, 0x67, - 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, - 0x74, 0x61, 0x67, 0x73, 0x12, 0x66, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, - 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x63, + 0x74, 0x6f, 0x22, 0x85, 0x09, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, + 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, + 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, + 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x46, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, 0xda, 0x29, 0x01, + 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, + 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x66, + 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, + 0x18, 0x78, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, + 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x12, 0x5e, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x82, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, - 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x63, 0x61, - 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x10, - 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x87, 0x01, 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x5f, 0x0a, 0x1b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x96, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, @@ -400,27 +295,29 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x12, 0x39, 0x0a, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xa0, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x12, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x13, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0xaa, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x09, - 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, + 0x73, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, - 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, - 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x59, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, + 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -435,39 +332,33 @@ func file_controller_api_resources_workers_v1_worker_proto_rawDescGZIP() []byte return file_controller_api_resources_workers_v1_worker_proto_rawDescData } -var file_controller_api_resources_workers_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_controller_api_resources_workers_v1_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_controller_api_resources_workers_v1_worker_proto_goTypes = []interface{}{ - (*WorkerProvidedConfiguration)(nil), // 0: controller.api.resources.workers.v1.WorkerProvidedConfiguration - (*Worker)(nil), // 1: controller.api.resources.workers.v1.Worker - nil, // 2: controller.api.resources.workers.v1.WorkerProvidedConfiguration.TagsEntry - nil, // 3: controller.api.resources.workers.v1.Worker.TagsEntry - nil, // 4: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry - (*scopes.ScopeInfo)(nil), // 5: controller.api.resources.scopes.v1.ScopeInfo - (*wrapperspb.StringValue)(nil), // 6: google.protobuf.StringValue - (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp - (*structpb.ListValue)(nil), // 8: google.protobuf.ListValue + (*Worker)(nil), // 0: controller.api.resources.workers.v1.Worker + nil, // 1: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry + nil, // 2: controller.api.resources.workers.v1.Worker.ConfigTagsEntry + (*scopes.ScopeInfo)(nil), // 3: controller.api.resources.scopes.v1.ScopeInfo + (*wrapperspb.StringValue)(nil), // 4: google.protobuf.StringValue + (*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp + (*structpb.ListValue)(nil), // 6: google.protobuf.ListValue } var file_controller_api_resources_workers_v1_worker_proto_depIdxs = []int32{ - 2, // 0: controller.api.resources.workers.v1.WorkerProvidedConfiguration.tags:type_name -> controller.api.resources.workers.v1.WorkerProvidedConfiguration.TagsEntry - 5, // 1: controller.api.resources.workers.v1.Worker.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 6, // 2: controller.api.resources.workers.v1.Worker.name:type_name -> google.protobuf.StringValue - 6, // 3: controller.api.resources.workers.v1.Worker.description:type_name -> google.protobuf.StringValue - 7, // 4: controller.api.resources.workers.v1.Worker.created_time:type_name -> google.protobuf.Timestamp - 7, // 5: controller.api.resources.workers.v1.Worker.updated_time:type_name -> google.protobuf.Timestamp - 6, // 6: controller.api.resources.workers.v1.Worker.address:type_name -> google.protobuf.StringValue - 3, // 7: controller.api.resources.workers.v1.Worker.tags:type_name -> controller.api.resources.workers.v1.Worker.TagsEntry - 4, // 8: controller.api.resources.workers.v1.Worker.canonical_tags:type_name -> controller.api.resources.workers.v1.Worker.CanonicalTagsEntry - 7, // 9: controller.api.resources.workers.v1.Worker.last_status_time:type_name -> google.protobuf.Timestamp - 0, // 10: controller.api.resources.workers.v1.Worker.worker_provided_configuration:type_name -> controller.api.resources.workers.v1.WorkerProvidedConfiguration - 6, // 11: controller.api.resources.workers.v1.Worker.worker_generated_auth_token:type_name -> google.protobuf.StringValue - 8, // 12: controller.api.resources.workers.v1.WorkerProvidedConfiguration.TagsEntry.value:type_name -> google.protobuf.ListValue - 8, // 13: controller.api.resources.workers.v1.Worker.TagsEntry.value:type_name -> google.protobuf.ListValue - 8, // 14: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry.value:type_name -> google.protobuf.ListValue - 15, // [15:15] is the sub-list for method output_type - 15, // [15:15] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name + 3, // 0: controller.api.resources.workers.v1.Worker.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 4, // 1: controller.api.resources.workers.v1.Worker.name:type_name -> google.protobuf.StringValue + 4, // 2: controller.api.resources.workers.v1.Worker.description:type_name -> google.protobuf.StringValue + 5, // 3: controller.api.resources.workers.v1.Worker.created_time:type_name -> google.protobuf.Timestamp + 5, // 4: controller.api.resources.workers.v1.Worker.updated_time:type_name -> google.protobuf.Timestamp + 1, // 5: controller.api.resources.workers.v1.Worker.canonical_tags:type_name -> controller.api.resources.workers.v1.Worker.CanonicalTagsEntry + 2, // 6: controller.api.resources.workers.v1.Worker.config_tags:type_name -> controller.api.resources.workers.v1.Worker.ConfigTagsEntry + 5, // 7: controller.api.resources.workers.v1.Worker.last_status_time:type_name -> google.protobuf.Timestamp + 4, // 8: controller.api.resources.workers.v1.Worker.worker_generated_auth_token:type_name -> google.protobuf.StringValue + 6, // 9: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry.value:type_name -> google.protobuf.ListValue + 6, // 10: controller.api.resources.workers.v1.Worker.ConfigTagsEntry.value:type_name -> google.protobuf.ListValue + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_controller_api_resources_workers_v1_worker_proto_init() } @@ -477,18 +368,6 @@ func file_controller_api_resources_workers_v1_worker_proto_init() { } if !protoimpl.UnsafeEnabled { file_controller_api_resources_workers_v1_worker_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkerProvidedConfiguration); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_resources_workers_v1_worker_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Worker); i { case 0: return &v.state @@ -507,7 +386,7 @@ func file_controller_api_resources_workers_v1_worker_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_api_resources_workers_v1_worker_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, From 577d8418cda7dd179b7445c660c24395ff1a4f61 Mon Sep 17 00:00:00 2001 From: Todd Date: Tue, 14 Jun 2022 15:52:35 -0700 Subject: [PATCH 086/100] Make name column immutable for kms workers. (#2190) errcode 23601 is the error code used by the immutable_column() function. --- .../99/02_worker_controller_tables.up.sql | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index eeb1cfed7..157640250 100644 --- a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -107,6 +107,25 @@ create trigger worker_update_time_column before update on server_worker create trigger update_version_column after update of version, description, name on server_worker for each row execute procedure update_version_column(); +create function immutable_kms_name() + returns trigger +as $$ +begin + if old.type = 'kms' and new.name is distinct from old.name then + raise exception 'immutable column for kms worker: server_worker.name' using + errcode = '23601', + schema = tg_table_schema, + table = tg_table_name, + column = 'name'; + end if; +end; +$$ language plpgsql; +comment on function immutable_kms_name is + 'function used in before update triggers to make name column immutable for kms workers'; + +create trigger immutable_kms_name before update on server_worker + for each row execute procedure immutable_kms_name(); + create function update_kms_server_worker_update_last_status_time_column() returns trigger as $$ From 06d0f99536cf6779ee242da9f2ab5a6e35a29634 Mon Sep 17 00:00:00 2001 From: Irena Rindos Date: Tue, 14 Jun 2022 20:13:02 -0400 Subject: [PATCH 087/100] bug(workersAPI): do not allow nonprintable or uppercase name or desc (#2186) * Send more info on KMS connect (#2182) * Update nodeenrollment to v0.1.0 * bug(workersAPI): do not allow nonprintable or uppercase name or desc * Put checks on create too * Apply printable check to description in DB * chore (worker): address PR feedback Co-authored-by: Jeff Mitchell Co-authored-by: Jim --- .../handlers/workers/worker_service.go | 23 +++++++++++ .../handlers/workers/worker_service_test.go | 39 +++++++++++++++++++ .../99/02_worker_controller_tables.up.sql | 12 +++--- 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index 601e84a7f..52ea12c3b 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -3,6 +3,7 @@ package workers import ( "context" "fmt" + "strings" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/daemon/controller/auth" @@ -545,6 +546,17 @@ func validateUpdateRequest(req *pbs.UpdateWorkerRequest) error { if req.GetItem().GetConfigTags() != nil { badFields[globals.TagsField] = "This is a read only field." } + nameString := req.GetItem().GetName().String() + if !strutil.Printable(nameString) { + badFields[globals.NameField] = "Contains non-printable characters" + } + if strings.ToLower(nameString) != nameString { + badFields[globals.NameField] = "Must be all lowercase." + } + descriptionString := req.GetItem().GetDescription().String() + if !strutil.Printable(descriptionString) { + badFields[globals.DescriptionField] = "Contains non-printable characters." + } return badFields }, servers.WorkerPrefix) } @@ -577,6 +589,17 @@ func validateCreateRequest(req *pbs.CreateWorkerLedRequest) error { if req.GetItem().AuthorizedActions != nil { badFields[globals.AuthorizedActionsField] = readOnlyFieldMsg } + nameString := req.GetItem().GetName().String() + if !strutil.Printable(nameString) { + badFields[globals.NameField] = "Contains non-printable characters." + } + if strings.ToLower(nameString) != nameString { + badFields[globals.NameField] = "Must be all lowercase" + } + descriptionString := req.GetItem().GetDescription().String() + if !strutil.Printable(descriptionString) { + badFields[globals.DescriptionField] = "Contains non-printable characters." + } return badFields }) } diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index 20ef70d9c..a09c0c530 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -708,6 +708,45 @@ func TestUpdate(t *testing.T) { res: nil, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, + { + name: "Invalid name- uppercase", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name", "description", "address"}, + }, + Item: &pb.Worker{ + Name: wrapperspb.String("BADNAME"), + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Invalid name- non-printable", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name", "description", "address"}, + }, + Item: &pb.Worker{ + Name: wrapperspb.String("\x00"), + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Invalid description- nonprintable", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name", "description", "address"}, + }, + Item: &pb.Worker{ + Description: wrapperspb.String("\x00"), + }, + }, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index 157640250..c0ebbfea6 100644 --- a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -53,16 +53,18 @@ create table server_worker ( references iam_scope_global(scope_id) on delete cascade on update cascade, - description wt_description, + description wt_description + constraint description_only_has_printable_characters + check (description is null or description !~ '[^[:print:]]'), name wt_name -- server_worker_scope_id_name_uq defines an appropriate uniqueness constraint for name constraint worker_name_must_be_set_by_status check ( type != 'kms' or name is not null ) - constraint kms_name_must_be_lowercase - check (lower(trim(name)) = name and type = 'kms') - constraint kms_name_only_has_printable_characters - check (name !~ '[^[:print:]]' and type = 'kms'), + constraint name_must_be_lowercase + check (name is null or lower(trim(name)) = name) + constraint name_only_has_printable_characters + check (name is null or name !~ '[^[:print:]]'), address wt_network_address constraint address_must_be_set_by_status check ( From 80752e9a243debea419832cc64bde81442caa98f Mon Sep 17 00:00:00 2001 From: irenarindos Date: Wed, 15 Jun 2022 10:18:40 -0400 Subject: [PATCH 088/100] cli(workers): Add extra help func --- internal/cmd/commands.go | 5 +++++ internal/cmd/commands/workerscmd/funcs.go | 21 +++++++++++++++++++ .../cmd/commands/workerscmd/workers.gen.go | 2 +- internal/cmd/common/help.go | 1 + internal/cmd/gencli/input.go | 1 + 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index 466a02752..e465c37d0 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -1030,6 +1030,11 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { }, nil }, + "workers": func() (cli.Command, error) { + return &workerscmd.Command{ + Command: base.NewCommand(ui), + }, nil + }, "workers create": func() (cli.Command, error) { return &workerscmd.Command{ Command: base.NewCommand(ui), diff --git a/internal/cmd/commands/workerscmd/funcs.go b/internal/cmd/commands/workerscmd/funcs.go index 1e959eb3a..693c1d9f3 100644 --- a/internal/cmd/commands/workerscmd/funcs.go +++ b/internal/cmd/commands/workerscmd/funcs.go @@ -9,6 +9,27 @@ import ( "github.com/hashicorp/boundary/internal/cmd/base" ) +func (c *Command) extraHelpFunc(helpMap map[string]func() string) string { + var helpStr string + switch c.Func { + case "": + return base.WrapForHelpText([]string{ + "Usage: boundary workers [sub command] [options] [args]", + "", + " This command allows operations on Boundary worker resources. Example:", + "", + " Read a worker:", + "", + ` $ boundary workers read -id w_1234567890`, + "", + " Please see the workers subcommand help for detailed usage information.", + }) + default: + helpStr = helpMap[c.Func]() + } + return helpStr + c.Flags().Help() +} + func (c *Command) printListTable(items []*workers.Worker) string { if len(items) == 0 { return "No workers found" diff --git a/internal/cmd/commands/workerscmd/workers.gen.go b/internal/cmd/commands/workerscmd/workers.gen.go index a75ad588c..d9434dae6 100644 --- a/internal/cmd/commands/workerscmd/workers.gen.go +++ b/internal/cmd/commands/workerscmd/workers.gen.go @@ -79,7 +79,7 @@ func (c *Command) Help() string { default: - helpStr = helpMap["base"]() + helpStr = c.extraHelpFunc(helpMap) } diff --git a/internal/cmd/common/help.go b/internal/cmd/common/help.go index cea87b356..20f6eb026 100644 --- a/internal/cmd/common/help.go +++ b/internal/cmd/common/help.go @@ -38,6 +38,7 @@ func HelpMap(resType string) map[string]func() string { resource.Host.String(): "h", resource.Session.String(): "s", resource.Target.String(): "t", + resource.Worker.String(): "w", } return map[string]func() string{ "base": func() string { diff --git a/internal/cmd/gencli/input.go b/internal/cmd/gencli/input.go index 062a16178..e6960b088 100644 --- a/internal/cmd/gencli/input.go +++ b/internal/cmd/gencli/input.go @@ -458,6 +458,7 @@ var inputStructs = map[string][]*cmdInfo{ ResourceType: resource.Worker.String(), Pkg: "workers", StdActions: []string{"read", "update", "delete", "list"}, + HasExtraHelpFunc: true, HasId: true, Container: "Scope", HasName: true, From beecbbb8a2660fd9cdfb8cf014f354b9752a4055 Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 15 Jun 2022 08:27:08 -0700 Subject: [PATCH 089/100] Upsert creates a new KMS and Updates PKI workers (#2187) --- .../server/worker_tags_reload_test.go | 2 +- .../daemon/cluster/handlers/worker_service.go | 26 +- .../handlers/worker_service_status_test.go | 8 +- .../handlers/targets/target_service.go | 9 +- .../targets/tcp/target_service_test.go | 4 +- .../handlers/workers/worker_service.go | 2 +- .../handlers/workers/worker_service_test.go | 10 +- .../99/02_worker_controller_tables.up.sql | 27 +- .../99/03_worker_authentication.up.sql | 20 +- .../oss/postgres/99/04_views.up.sql | 2 +- internal/servers/public_ids.go | 12 + internal/servers/repository_worker.go | 143 ++-- internal/servers/repository_worker_test.go | 225 +++--- .../servers/repository_workerauth_test.go | 4 +- internal/servers/testing.go | 66 +- internal/servers/testing_test.go | 27 +- internal/servers/worker.go | 81 ++- internal/servers/worker_tags_test.go | 2 +- internal/servers/worker_test.go | 662 ++++++++---------- internal/servers/workerauth_store_test.go | 4 +- internal/session/job_session_cleanup_test.go | 27 +- .../session/repository_connection_test.go | 4 +- .../service_authorize_connection_test.go | 2 +- .../service_worker_status_report_test.go | 14 +- internal/session/testing_test.go | 2 +- 25 files changed, 676 insertions(+), 709 deletions(-) diff --git a/internal/cmd/commands/server/worker_tags_reload_test.go b/internal/cmd/commands/server/worker_tags_reload_test.go index 74bc29f01..0fbd9a13f 100644 --- a/internal/cmd/commands/server/worker_tags_reload_test.go +++ b/internal/cmd/commands/server/worker_tags_reload_test.go @@ -106,7 +106,7 @@ func TestServer_ReloadWorkerTags(t *testing.T) { t.Helper() serversRepo, err := testController.Controller().ServersRepoFn() require.NoError(err) - w, err := serversRepo.LookupWorkerByWorkerReportedName(testController.Context(), name) + w, err := serversRepo.LookupWorkerByName(testController.Context(), name) require.NoError(err) require.NotNil(w) v, ok := w.CanonicalTags()[key] diff --git a/internal/daemon/cluster/handlers/worker_service.go b/internal/daemon/cluster/handlers/worker_service.go index d1badb5a3..3b2810754 100644 --- a/internal/daemon/cluster/handlers/worker_service.go +++ b/internal/daemon/cluster/handlers/worker_service.go @@ -65,7 +65,9 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques } switch { case wStat.GetName() == "" && wStat.GetKeyId() == "": - return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Name and keyId are not set in the request; at least one is required.") + return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Name and keyId are not set in the request; one is required.") + case wStat.GetName() != "" && wStat.GetKeyId() != "": + return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Name and keyId are both set in the request; only one is allowed.") case wStat.GetAddress() == "": return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Address is not set but is required.") } @@ -88,17 +90,17 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques }) } - wKeyId := wStat.GetKeyId() - - wConf := servers.NewWorkerForStatus(scope.Global.String(), + wConf := servers.NewWorker(scope.Global.String(), servers.WithName(wStat.GetName()), servers.WithAddress(wStat.GetAddress()), - servers.WithWorkerTags(workerTags...), - servers.WithKeyId(wKeyId)) + servers.WithWorkerTags(workerTags...)) opts := []servers.Option{servers.WithUpdateTags(req.GetUpdateTags())} if wStat.GetPublicId() != "" { opts = append(opts, servers.WithPublicId(wStat.GetPublicId())) } + if wStat.GetKeyId() != "" { + opts = append(opts, servers.WithKeyId(wStat.GetKeyId())) + } wrk, err := serverRepo.UpsertWorkerStatus(ctx, wConf, opts...) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error storing worker status")) @@ -229,7 +231,7 @@ func (ws *workerServiceServer) LookupSession(ctx context.Context, req *pbs.Looku } var w *servers.Worker if req.WorkerKeyId != "" { - workerId, err := serversRepo.LookupWorkerIdByWorkerReportedKeyId(ctx, req.WorkerKeyId) + workerId, err := serversRepo.LookupWorkerIdByKeyId(ctx, req.WorkerKeyId) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error looking up worker by keyId", "keyId", req.WorkerKeyId)) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error looking up tags for server: %v", err) @@ -240,7 +242,7 @@ func (ws *workerServiceServer) LookupSession(ctx context.Context, req *pbs.Looku return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error looking up tags for server: %v", err) } } else { - w, err = serversRepo.LookupWorkerByWorkerReportedName(ctx, req.GetServerId()) + w, err = serversRepo.LookupWorkerByName(ctx, req.GetServerId()) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error looking up worker by name", "name", req.ServerId)) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error looking up tags for server: %v", err) @@ -255,12 +257,8 @@ func (ws *workerServiceServer) LookupSession(ctx context.Context, req *pbs.Looku event.WriteError(ctx, op, err, event.WithInfoMsg("error creating worker filter evaluator", "server_id", req.ServerId)) return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Error creating worker filter evaluator: %v", err) } - name := w.GetName() - if name == "" { - name = w.GetWorkerReportedName() - } filterInput := map[string]interface{}{ - "name": name, + "name": w.GetName(), "tags": tagMap, } ok, err := eval.Evaluate(filterInput) @@ -391,7 +389,7 @@ func (ws *workerServiceServer) AuthorizeConnection(ctx context.Context, req *pbs if err != nil { return nil, status.Errorf(codes.Internal, "error getting servers repo: %v", err) } - w, err := serversRepo.LookupWorkerByWorkerReportedName(ctx, req.GetWorkerId()) + w, err := serversRepo.LookupWorkerByName(ctx, req.GetWorkerId()) if err != nil { return nil, status.Errorf(codes.Internal, "error looking up worker: %v", err) } diff --git a/internal/daemon/cluster/handlers/worker_service_status_test.go b/internal/daemon/cluster/handlers/worker_service_status_test.go index 144a753b2..1500eab42 100644 --- a/internal/daemon/cluster/handlers/worker_service_status_test.go +++ b/internal/daemon/cluster/handlers/worker_service_status_test.go @@ -71,7 +71,7 @@ func TestStatus(t *testing.T) { target.WithSessionConnectionLimit(-1), ) - worker1 := servers.TestWorker(t, conn, wrapper) + worker1 := servers.TestKmsWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -257,7 +257,7 @@ func TestStatusSessionClosed(t *testing.T) { target.WithSessionConnectionLimit(-1), ) - worker1 := servers.TestWorker(t, conn, wrapper) + worker1 := servers.TestKmsWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -408,7 +408,7 @@ func TestStatusDeadConnection(t *testing.T) { Address: "127.0.0.1", }) - worker1 := servers.TestWorker(t, conn, wrapper) + worker1 := servers.TestKmsWorker(t, conn, wrapper) serversRepoFn := func() (*servers.Repository, error) { return serverRepo, nil @@ -578,7 +578,7 @@ func TestStatusWorkerWithKeyId(t *testing.T) { target.WithSessionConnectionLimit(-1), ) - worker1 := servers.TestWorker(t, conn, wrapper) + worker1 := servers.TestKmsWorker(t, conn, wrapper) rootStorage, err := servers.NewRepositoryStorage(ctx, rw, rw, kms) require.NoError(t, err) diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index ad2c33956..787e50cd7 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -2159,15 +2159,8 @@ func (w workerList) workerInfos() []*pb.WorkerInfo { func (w workerList) filtered(eval *bexpr.Evaluator) (workerList, error) { var ret []*servers.Worker for _, worker := range w { - - // TODO: Decide how we are actually going to filter by name now that - // we are separating reported status name and the resource name. - name := worker.GetName() - if name == "" { - name = worker.GetWorkerReportedName() - } filterInput := map[string]interface{}{ - "name": name, + "name": worker.GetName(), "tags": worker.CanonicalTags(), } ok, err := eval.Evaluate(filterInput) diff --git a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go index 940963743..cb0f2a8a1 100644 --- a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go @@ -2784,7 +2784,7 @@ func TestAuthorizeSession(t *testing.T) { require.NoError(t, err) // Tell our DB that there is a worker ready to serve the data - servers.TestWorker(t, conn, wrapper) + servers.TestKmsWorker(t, conn, wrapper) asRes1, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{ Id: tar.GetPublicId(), @@ -3062,7 +3062,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { require.NoError(t, err) // Tell our DB that there is a worker ready to serve the data - servers.TestWorker(t, conn, wrapper) + servers.TestKmsWorker(t, conn, wrapper) asRes, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{ Id: tar.GetPublicId(), diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index 52ea12c3b..a98abf7a7 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -457,7 +457,7 @@ func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (* out.AuthorizedActions = opts.WithAuthorizedActions if in.Type == KmsWorkerType { // KMS workers cannot be updated through the API - out.AuthorizedActions = strutil.StrListDelete(action.Update.String()) + out.AuthorizedActions = strutil.StrListDelete(out.AuthorizedActions, action.Update.String()) } } if outputFields.Has(globals.AddressField) && in.GetAddress() != "" { diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index a09c0c530..f384e97c7 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -63,7 +63,7 @@ func TestGet(t *testing.T) { return repo, nil } - worker := servers.TestWorker(t, conn, wrap, + worker := servers.TestKmsWorker(t, conn, wrap, servers.WithName("test worker names"), servers.WithDescription("test worker description"), servers.WithAddress("test worker address"), @@ -174,7 +174,7 @@ func TestList(t *testing.T) { var wantWorkers []*pb.Worker for i := 0; i < 10; i++ { - w := servers.TestWorker(t, conn, wrap, servers.WithName(fmt.Sprintf("worker%d", i))) + w := servers.TestKmsWorker(t, conn, wrap, servers.WithName(fmt.Sprintf("worker%d", i))) wantWorkers = append(wantWorkers, &pb.Worker{ Id: w.GetPublicId(), ScopeId: w.GetScopeId(), @@ -285,7 +285,7 @@ func TestDelete(t *testing.T) { s, err := NewService(ctx, repoFn, iamRepoFn) require.NoError(t, err, "Error when getting new worker service.") - w := servers.TestWorker(t, conn, wrap) + w := servers.TestKmsWorker(t, conn, wrap) cases := []struct { name string @@ -349,7 +349,7 @@ func TestUpdate(t *testing.T) { return repo, nil } - wkr := servers.TestWorker(t, conn, wrapper, + wkr := servers.TestKmsWorker(t, conn, wrapper, servers.WithName("default"), servers.WithDescription("default"), servers.WithAddress("default")) @@ -806,7 +806,7 @@ func TestUpdate_BadVersion(t *testing.T) { workerService, err := NewService(ctx, repoFn, iamRepoFn) require.NoError(t, err, "Failed to create a new host set service.") - wkr := servers.TestWorker(t, conn, wrapper) + wkr := servers.TestKmsWorker(t, conn, wrapper) upTar, err := workerService.UpdateWorker(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), &pbs.UpdateWorkerRequest{ Id: wkr.GetPublicId(), diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql index c0ebbfea6..e67230adb 100644 --- a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql @@ -120,6 +120,7 @@ begin table = tg_table_name, column = 'name'; end if; + return new; end; $$ language plpgsql; comment on function immutable_kms_name is @@ -134,8 +135,8 @@ as $$ begin if new.type = 'kms' then new.last_status_time = now(); - return new; end if; + return new; end; $$ language plpgsql; comment on function update_kms_server_worker_update_last_status_time_column is @@ -150,8 +151,8 @@ as $$ begin if new.type = 'pki' then new.last_status_time = now(); - return new; end if; + return new; end; $$ language plpgsql; comment on function update_pki_server_worker_update_last_status_time_column is @@ -166,8 +167,8 @@ as $$ begin if new.type = 'kms' then new.last_status_time = now(); - return new; end if; + return new; end; $$ language plpgsql; comment on function insert_kms_server_worker_update_last_status_time_column is @@ -176,26 +177,6 @@ comment on function insert_kms_server_worker_update_last_status_time_column is create trigger insert_server_worker_last_update_time_column before insert on server_worker for each row execute procedure insert_kms_server_worker_update_last_status_time_column(); -create function insert_server_worker_type_column() - returns trigger -as $$ -begin - perform from worker_auth_authorized where worker_id = new.public_id; - case - when new.type = 'pki' and not found then - raise exception 'invalid type: pki worker is not authorized in worker_auth_authorized table'; - when new.type = 'kms' and found then - raise exception 'invalid type: kms worker cannot authorized in worker_auth_authorized table'; - end case; - return new; -end; -$$ language plpgsql; -comment on function insert_server_worker_type_column is - 'function used to constraint the type column in server_worker'; - -create trigger insert_server_worker_type_column before insert on server_worker - for each row execute procedure insert_server_worker_type_column(); - -- Create table worker tag create table server_worker_tag_enm ( source text primary key diff --git a/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql b/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql index 33d71f06e..b6e39a48e 100644 --- a/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql @@ -112,6 +112,8 @@ create table worker_auth_authorized( on update cascade, nonce bytea -- encrypted value ); +comment on table worker_auth_authorized is + 'worker_auth_authorized is a table where each row represents key and cert data associated with an authorized worker.'; create trigger immutable_columns @@ -120,8 +122,22 @@ create trigger for each row execute procedure immutable_columns('worker_key_identifier', 'worker_id', 'worker_signing_pub_key', 'worker_encryption_pub_key', 'controller_encryption_priv_key', 'key_id', 'nonce'); -comment on table worker_auth_authorized is - 'worker_auth_authorized is a table where each row represents key and cert data associated with an authorized worker.'; +create function insert_worker_auth_authorized_type_check() + returns trigger +as $$ +begin + perform from server_worker where type = 'pki' and public_id = new.worker_id; + if not found then + raise exception 'invalid type: non pki worker cannot be authorized in worker_auth_authorized table'; + end if; + return new; +end; +$$ language plpgsql; +comment on function insert_worker_auth_authorized_type_check is + 'function used to only allow workers of type pki to be authorized'; + +create trigger insert_worker_auth_authorized_type_check before insert on worker_auth_authorized + for each row execute procedure insert_worker_auth_authorized_type_check(); create table worker_auth_certificate_bundle( root_certificate_public_key bytea not null diff --git a/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql b/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql index a323a08fc..c67d3ee4a 100644 --- a/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql +++ b/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql @@ -30,6 +30,7 @@ select w.update_time, w.version, w.last_status_time, + w.type, cc.count as active_connection_count, -- keys and tags can be any lowercase printable character so use uppercase characters as delimitors. wt.tags as api_tags, @@ -44,7 +45,6 @@ from server_worker w comment on view server_worker_aggregate is 'server_worker_aggregate contains the worker resource with its worker provided config values and its configuration and api provided tags.'; - -- Replaces the view created in 9/01. -- Remove the worker id from this view. In actuality this is almost a no-op -- because no server information was ever getting populated here due to a bug diff --git a/internal/servers/public_ids.go b/internal/servers/public_ids.go index 16d9a7829..0658c36f2 100644 --- a/internal/servers/public_ids.go +++ b/internal/servers/public_ids.go @@ -19,3 +19,15 @@ func newWorkerId(ctx context.Context) (string, error) { } return id, nil } + +// newWorkerIdFromScopeAndName generates a predictable public id based on the +// scope and the worker name. This should only be used on kms workers at +// upsert time. +func newWorkerIdFromScopeAndName(ctx context.Context, scope, name string) (string, error) { + const op = "servers.newWorkerIdFromScopeAndName" + id, err := db.NewPublicId(WorkerPrefix, db.WithPrngValues([]string{scope, name})) + if err != nil { + return "", errors.Wrap(ctx, err, "servers.newWorkerId") + } + return id, nil +} diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index 836509f3f..0105de633 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -57,19 +57,32 @@ func (r *Repository) DeleteWorker(ctx context.Context, publicId string, _ ...Opt return rowsDeleted, nil } -// LookupWorkerByWorkerReportedName returns the worker which has had a status update -// where the worker has reported this name. This is different from the name -// on the resource itself which is set over the API. In the event that no -// worker is found that matches then nil, nil will be returned. -func (r *Repository) LookupWorkerByWorkerReportedName(ctx context.Context, name string) (*Worker, error) { - const op = "servers.(Repository).LookupWorkerByWorkerReportedName" +// LookupWorkerByName returns the worker with the provided name. In the event +// that no worker is found that matches then nil, nil will be returned. +func (r *Repository) LookupWorkerByName(ctx context.Context, name string) (*Worker, error) { + const op = "servers.(Repository).LookupWorkerByName" switch { case name == "": return nil, errors.New(ctx, errors.InvalidParameter, op, "name is empty") } + w, err := lookupWorkerByName(ctx, r.reader, name) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return w, nil +} + +func lookupWorkerByName(ctx context.Context, reader db.Reader, name string) (*Worker, error) { + const op = "servers.lookupWorkerByName" + switch { + case isNil(reader): + return nil, errors.New(ctx, errors.InvalidParameter, op, "reader is nil") + case name == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "name is empty") + } wAgg := &workerAggregate{} - err := r.reader.LookupWhere(ctx, &wAgg, "worker_reported_name = ?", []interface{}{name}) + err := reader.LookupWhere(ctx, &wAgg, "name = ?", []interface{}{name}) if err != nil { if errors.IsNotFoundError(err) { return nil, nil @@ -83,8 +96,8 @@ func (r *Repository) LookupWorkerByWorkerReportedName(ctx context.Context, name return w, nil } -func (r *Repository) LookupWorkerIdByWorkerReportedKeyId(ctx context.Context, keyId string) (string, error) { - const op = "servers.(Repository).LookupWorkerIdByWorkerReportedKeyId" +func (r *Repository) LookupWorkerIdByKeyId(ctx context.Context, keyId string) (string, error) { + const op = "servers.(Repository).LookupWorkerIdByKeyId" switch { case keyId == "": return "", errors.New(ctx, errors.InvalidParameter, op, "keyId is empty") @@ -200,62 +213,51 @@ func (r *Repository) ListWorkers(ctx context.Context, scopeIds []string, opt ... return workers, nil } -// UpsertWorkerStatus creates a new worker if one with the provided public id -// doesn't already exist. If it does, UpsertWorkerStatus updates the worker -// status. This returns the Worker object with the updated WorkerStatus applied. -// The WithPublicId and WithUpdateTags options are the only ones used. All others are ignored. +// UpsertWorkerStatus will update the address and last status time for a worker. +// If the worker is a kms worker that hasn't been seen yet, it'll attempt to +// create a new one, but will return an error if another worker (kms or other) +// has the same name. This returns the Worker object with the changes applied. +// The WithPublicId, WithKeyId, and WithUpdateTags options are +// the only ones used. All others are ignored. // Workers are intentionally not oplogged. func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt ...Option) (*Worker, error) { const op = "servers.UpsertWorkerStatus" + opts := getOpts(opt...) switch { case worker == nil: return nil, errors.New(ctx, errors.InvalidParameter, op, "worker is nil") - case worker.GetWorkerReportedAddress() == "": + case worker.GetAddress() == "": return nil, errors.New(ctx, errors.InvalidParameter, op, "worker reported address is empty") case worker.ScopeId == "": return nil, errors.New(ctx, errors.InvalidParameter, op, "scope id is empty") case worker.PublicId != "": return nil, errors.New(ctx, errors.InvalidParameter, op, "worker id is not empty") - case worker.GetWorkerReportedName() == "" && worker.WorkerReportedKeyId == "": - return nil, errors.New(ctx, errors.InvalidParameter, op, "worker keyId and reported name are both empty; at least one is required") - case len(worker.apiTags) > 0: - return nil, errors.New(ctx, errors.InvalidParameter, op, "api tags is not empty") + case worker.GetName() == "" && opts.withKeyId == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "worker keyId and reported name are both empty; one is required") + case worker.GetName() != "" && opts.withKeyId != "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "worker keyId and reported name are both set; no more than one is allowed") } - // Only retain the worker reported fields. - worker = NewWorkerForStatus(worker.GetScopeId(), - WithName(worker.GetWorkerReportedName()), - WithAddress(worker.GetWorkerReportedAddress()), - WithWorkerTags(worker.configTags...), - WithKeyId(worker.WorkerReportedKeyId)) - - opts := getOpts(opt...) - + var workerId string var err error switch { case opts.withPublicId != "": - worker.PublicId = opts.withPublicId - case worker.WorkerReportedKeyId != "": - workerId, err := r.LookupWorkerIdByWorkerReportedKeyId(ctx, worker.WorkerReportedKeyId) + workerId = opts.withPublicId + case opts.withKeyId != "": + workerId, err = r.LookupWorkerIdByKeyId(ctx, opts.withKeyId) if err != nil || workerId == "" { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error finding worker by keyId")) } - worker.PublicId = workerId default: - // Attempt to find workerId by name. If not found, generate an id - w, err := r.LookupWorkerByWorkerReportedName(ctx, worker.GetWorkerReportedName()) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error finding worker by name")) - } - if w != nil { - worker.PublicId = w.PublicId - } else { - workerId, err := newWorkerId(ctx) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error error generating worker id")) - } - worker.PublicId = workerId + // generating the worker id based off of the scope and name ensures + // that if 2 kms workers are making requests with the same name they + // are treated as the same worker. Allowing this only for kms workers + // also ensures that we maintain the unique name constraint between pki + // workers and kms workers. + workerId, err = newWorkerIdFromScopeAndName(ctx, worker.GetScopeId(), worker.GetName()) + if err != nil || workerId == "" { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error creating a worker id")) } } @@ -266,13 +268,31 @@ func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt db.ExpBackoff{}, func(reader db.Reader, w db.Writer) error { worker := worker.clone() - workerCreateConflict := &db.OnConflict{ - Target: db.Columns{"public_id"}, - Action: append(db.SetColumns([]string{"worker_reported_name", "worker_reported_address"}), - db.SetColumnValues(map[string]interface{}{"last_status_time": "now()"})...), - } - if err := w.Create(ctx, worker, db.WithOnConflict(workerCreateConflict)); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("error creating a worker")) + worker.PublicId = workerId + switch { + case worker.GetName() != "": + worker.Type = KmsWorkerType.String() + workerCreateConflict := &db.OnConflict{ + Target: db.Columns{"public_id"}, + Action: append(db.SetColumns([]string{"address"}), + db.SetColumnValues(map[string]interface{}{"last_status_time": "now()"})...), + } + if err := w.Create(ctx, worker, db.WithOnConflict(workerCreateConflict)); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("error creating a worker")) + } + case opts.withKeyId != "": + n, err := w.Update(ctx, worker, []string{"address"}, nil) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to update status of pki worker")) + } + switch n { + case 0: + return errors.New(ctx, errors.RecordNotFound, op, fmt.Sprintf("failed to find worker with key id %q", opts.withKeyId)) + case 1: + break + default: + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("multiple records found when updating worker with id %q", worker.GetPublicId())) + } } // If we've been told to update tags, we need to clean out old @@ -280,7 +300,7 @@ func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt // delete all tags for the given worker, then add the new ones // we've been sent. if opts.withUpdateTags { - setWorkerTags(ctx, w, worker.GetPublicId(), ConfigurationTagSource, worker.configTags) + setWorkerTags(ctx, w, worker.GetPublicId(), ConfigurationTagSource, worker.inputTags) } wAgg := &workerAggregate{PublicId: worker.GetPublicId()} @@ -355,9 +375,8 @@ func setWorkerTags(ctx context.Context, w db.Writer, id string, ts TagSource, ta // the path then an error is returned. func (r *Repository) UpdateWorker(ctx context.Context, worker *Worker, version uint32, fieldMaskPaths []string, opt ...Option) (*Worker, int, error) { const ( - nameField = "name" - descField = "description" - addressField = "address" + nameField = "name" + descField = "description" ) const op = "servers.(Repository).UpdateWorker" switch { @@ -373,7 +392,6 @@ func (r *Repository) UpdateWorker(ctx context.Context, worker *Worker, version u switch { case strings.EqualFold(nameField, f): case strings.EqualFold(descField, f): - case strings.EqualFold(addressField, f): default: return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) } @@ -382,9 +400,8 @@ func (r *Repository) UpdateWorker(ctx context.Context, worker *Worker, version u var dbMask, nullFields []string dbMask, nullFields = dbcommon.BuildUpdatePaths( map[string]interface{}{ - nameField: worker.Name, - descField: worker.Description, - addressField: worker.Address, + nameField: worker.Name, + descField: worker.Description, }, fieldMaskPaths, nil, @@ -448,10 +465,8 @@ func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Op return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") case worker.ScopeId != scope.Global.String(): return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("scope id must be %q", scope.Global.String())) - case worker.WorkerReportedAddress != "": - return nil, errors.New(ctx, errors.InvalidParameter, op, "worker reported address is not empty") - case worker.WorkerReportedName != "": - return nil, errors.New(ctx, errors.InvalidParameter, op, "worker reported name is not empty") + case worker.Address != "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "address is not empty") case worker.LastStatusTime != nil: return nil, errors.New(ctx, errors.InvalidParameter, op, "last status time is not nil") } @@ -476,7 +491,6 @@ func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Op if err != nil { return nil, errors.Wrap(ctx, err, op) } - } var returnedWorker *Worker @@ -486,6 +500,7 @@ func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Op db.ExpBackoff{}, func(read db.Reader, w db.Writer) error { returnedWorker = worker.clone() + returnedWorker.Type = PkiWorkerType.String() if err := w.Create( ctx, returnedWorker, diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 73df90403..d5848006b 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -54,7 +54,7 @@ func TestDeleteWorker(t *testing.T) { { name: "valid", args: args{ - worker: servers.TestWorker(t, conn, wrapper), + worker: servers.TestKmsWorker(t, conn, wrapper), }, wantRowsDeleted: 1, wantErr: false, @@ -108,7 +108,7 @@ func TestDeleteWorker(t *testing.T) { } } -func TestLookupWorkerByWorkerReportedName(t *testing.T) { +func TestLookupWorkerByName(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -117,14 +117,14 @@ func TestLookupWorkerByWorkerReportedName(t *testing.T) { require.NoError(t, err) ctx := context.Background() - w := servers.TestWorker(t, conn, wrapper) + w := servers.TestKmsWorker(t, conn, wrapper) t.Run("success", func(t *testing.T) { - got, err := repo.LookupWorkerByWorkerReportedName(ctx, w.GetWorkerReportedName()) + got, err := repo.LookupWorkerByName(ctx, w.GetName()) require.NoError(t, err) assert.Empty(t, cmp.Diff(w.Worker, got.Worker, protocmp.Transform())) }) t.Run("not found", func(t *testing.T) { - got, err := repo.LookupWorkerByWorkerReportedName(ctx, "unknown_name") + got, err := repo.LookupWorkerByName(ctx, "unknown_name") require.NoError(t, err) assert.Nil(t, got) }) @@ -134,14 +134,14 @@ func TestLookupWorkerByWorkerReportedName(t *testing.T) { mock.ExpectQuery(`SELECT`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "lookup-error")) r, err := servers.NewRepository(rw, rw, kms) require.NoError(t, err) - got, err := r.LookupWorkerByWorkerReportedName(ctx, w.GetWorkerReportedName()) + got, err := r.LookupWorkerByName(ctx, w.GetName()) assert.NoError(t, mock.ExpectationsWereMet()) - assert.Truef(t, errors.Match(errors.T(errors.Op("servers.(Repository).LookupWorkerByWorkerReportedName")), err), "got error %v", err) + assert.Truef(t, errors.Match(errors.T(errors.Op("servers.(Repository).LookupWorkerByName")), err), "got error %v", err) assert.Nil(t, got) }) } -func TestLookupWorkerByWorkerReportedKeyId(t *testing.T) { +func TestLookupWorkerIdByKeyId(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -153,7 +153,7 @@ func TestLookupWorkerByWorkerReportedKeyId(t *testing.T) { err = kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) require.NoError(t, err) - w := servers.TestWorker(t, conn, wrapper) + w := servers.TestPkiWorker(t, conn, wrapper) rootStorage, err := servers.NewRepositoryStorage(ctx, rw, rw, kmsCache) require.NoError(t, err) @@ -176,12 +176,12 @@ func TestLookupWorkerByWorkerReportedKeyId(t *testing.T) { registeredNode, err := registration.AuthorizeNode(ctx, rootStorage, fetchReq, nodeenrollment.WithState(state)) require.NoError(t, err) t.Run("success", func(t *testing.T) { - got, err := repo.LookupWorkerIdByWorkerReportedKeyId(ctx, registeredNode.Id) + got, err := repo.LookupWorkerIdByKeyId(ctx, registeredNode.Id) require.NoError(t, err) assert.Equal(t, w.PublicId, got) }) t.Run("not found", func(t *testing.T) { - got, err := repo.LookupWorkerIdByWorkerReportedKeyId(ctx, "unknown_key") + got, err := repo.LookupWorkerIdByKeyId(ctx, "unknown_key") require.NoError(t, err) assert.Empty(t, got) }) @@ -191,9 +191,9 @@ func TestLookupWorkerByWorkerReportedKeyId(t *testing.T) { mock.ExpectQuery(`SELECT`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "lookup-error")) r, err := servers.NewRepository(rw, rw, kmsCache) require.NoError(t, err) - got, err := r.LookupWorkerIdByWorkerReportedKeyId(ctx, w.GetWorkerReportedName()) + got, err := r.LookupWorkerIdByKeyId(ctx, "somekey") assert.NoError(t, mock.ExpectationsWereMet()) - assert.Truef(t, errors.Match(errors.T(errors.Op("servers.(Repository).LookupWorkerIdByWorkerReportedKeyId")), err), "got error %v", err) + assert.Truef(t, errors.Match(errors.T(errors.Op("servers.(Repository).LookupWorkerIdByKeyId")), err), "got error %v", err) assert.Empty(t, got) }) } @@ -207,15 +207,15 @@ func TestLookupWorker(t *testing.T) { require.NoError(t, err) ctx := context.Background() - w := servers.TestWorker(t, conn, wrapper, + w := servers.TestKmsWorker(t, conn, wrapper, servers.WithName("name"), servers.WithDescription("description"), servers.WithAddress("address"), servers.WithWorkerTags(&servers.Tag{"key", "val"})) w, err = repo.UpsertWorkerStatus(context.Background(), servers.NewWorkerForStatus(w.GetScopeId(), - servers.WithName(w.GetWorkerReportedName()), - servers.WithAddress(w.GetWorkerReportedAddress()), + servers.WithName(w.GetName()), + servers.WithAddress(w.GetAddress()), servers.WithWorkerTags(&servers.Tag{ Key: "config", Value: "test", @@ -301,14 +301,18 @@ func TestUpsertWorkerStatus(t *testing.T) { require.NoError(t, err) { assert.True(t, strings.HasPrefix(worker.GetPublicId(), "w_")) - assert.Equal(t, wStatus1.GetWorkerReportedAddress(), worker.CanonicalAddress()) - assert.Empty(t, worker.Name) + assert.Equal(t, wStatus1.GetAddress(), worker.CanonicalAddress()) + assert.Equal(t, "config_name1", worker.Name) assert.Equal(t, worker.GetLastStatusTime(), worker.UpdateTime) assert.Equal(t, uint32(1), worker.Version) - assert.Empty(t, worker.Address) + assert.Equal(t, "address", worker.Address) assert.Empty(t, worker.Description) } + // TODO: Add tests that attempt to upsert a KMS worker where there already + // exists a PKI worker with the same name. It should just error with an + // easy to identify error. + failureCases := []struct { name string repo *servers.Repository @@ -370,21 +374,6 @@ func TestUpsertWorkerStatus(t *testing.T) { assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) }, }, - { - name: "providing api tags", - repo: repo, - status: func() *servers.Worker { - w := servers.NewWorker(scope.Global.String(), - servers.WithWorkerTags(&servers.Tag{Key: "test", Value: "test"})) - w.WorkerReportedAddress = "some_address" - w.WorkerReportedName = "providing api tags" - return w - }(), - errAssert: func(t *testing.T, err error) { - t.Helper() - assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) - }, - }, { name: "database failure", repo: func() *servers.Repository { @@ -443,10 +432,10 @@ func TestTagUpdatingListing(t *testing.T) { require.NoError(err) ctx := context.Background() - worker1 := servers.TestWorker(t, conn, wrapper) + worker1 := servers.TestKmsWorker(t, conn, wrapper) wStatus := servers.NewWorkerForStatus(scope.Global.String(), - servers.WithName(worker1.GetWorkerReportedName()), - servers.WithAddress("127.0.0.1"), + servers.WithName(worker1.GetName()), + servers.WithAddress("somethingnew"), servers.WithWorkerTags( &servers.Tag{ Key: "tag1", @@ -465,8 +454,8 @@ func TestTagUpdatingListing(t *testing.T) { // Update without saying to update tags wStatus = servers.NewWorkerForStatus(scope.Global.String(), - servers.WithName(worker1.GetWorkerReportedName()), - servers.WithAddress(worker1.GetWorkerReportedAddress()), + servers.WithName(worker1.GetName()), + servers.WithAddress(worker1.GetAddress()), servers.WithWorkerTags( &servers.Tag{ Key: "tag22", @@ -542,7 +531,7 @@ func TestListWorkers(t *testing.T) { w := servers.NewWorker(scope.Global.String()) db.TestDeleteWhere(t, conn, w, "true") for i := 0; i < tt.createCnt; i++ { - servers.TestWorker(t, conn, wrapper) + servers.TestKmsWorker(t, conn, wrapper) } // the purpose of these tests isn't to check liveness, so disable // liveness checking. @@ -569,18 +558,20 @@ func TestListWorkers_WithLiveness(t *testing.T) { require.NoError(err) ctx := context.Background() - worker1 := servers.TestWorker(t, conn, wrapper) - worker2 := servers.TestWorker(t, conn, wrapper) - worker3 := servers.TestWorker(t, conn, wrapper) + worker1 := servers.TestKmsWorker(t, conn, wrapper) + worker2 := servers.TestKmsWorker(t, conn, wrapper) + worker3 := servers.TestKmsWorker(t, conn, wrapper) // Sleep the default liveness time (15sec currently) +1s time.Sleep(time.Second * 16) // Push an upsert to the first worker so that its status has been // updated. - _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), - servers.WithName(worker1.GetWorkerReportedName()), - servers.WithAddress(worker1.GetWorkerReportedAddress()))) + _, err = serversRepo.UpsertWorkerStatus(ctx, + servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName(worker1.GetName()), + servers.WithAddress(worker1.GetAddress())), + servers.WithPublicId(worker1.GetPublicId())) require.NoError(err) requireIds := func(expected []string, actual []*servers.Worker) { @@ -605,9 +596,11 @@ func TestListWorkers_WithLiveness(t *testing.T) { requireIds([]string{worker1.GetPublicId()}, result) // Upsert second server. - _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), - servers.WithName(worker2.GetWorkerReportedName()), - servers.WithAddress(worker1.GetWorkerReportedAddress()))) + _, err = serversRepo.UpsertWorkerStatus(ctx, + servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName(worker2.GetName()), + servers.WithAddress(worker2.GetAddress())), + servers.WithPublicId(worker2.GetPublicId())) require.NoError(err) // Static liveness. Should get two, so long as this did not take @@ -739,25 +732,13 @@ func TestRepository_CreateWorker(t *testing.T) { name: "no worker reported address", setup: func() *servers.Worker { w := servers.NewWorker(scope.Global.String()) - w.WorkerReportedAddress = "foo" + w.Address = "foo" return w }, repo: testRepo, wantErr: true, wantErrIs: errors.InvalidParameter, - wantErrContains: "worker reported address is not empty", - }, - { - name: "no worker reported name", - setup: func() *servers.Worker { - w := servers.NewWorker(scope.Global.String()) - w.WorkerReportedName = "foo" - return w - }, - repo: testRepo, - wantErr: true, - wantErrIs: errors.InvalidParameter, - wantErrContains: "worker reported name is not empty", + wantErrContains: "address is not empty", }, { name: "no last status update", @@ -819,6 +800,7 @@ func TestRepository_CreateWorker(t *testing.T) { name: "success-with-fetch-node-req", setup: func() *servers.Worker { w := servers.NewWorker(scope.Global.String()) + w.Name = "success-with-fetch-node-req" return w }, reader: rw, @@ -893,28 +875,13 @@ func TestRepository_UpdateWorker(t *testing.T) { repo, err := servers.NewRepository(rw, rw, kmsCache) require.NoError(t, err) - setCases := []struct { + pkiCases := []struct { name string modifyWorker func(*testing.T, *servers.Worker) path []string assertGot func(*testing.T, *servers.Worker) wantErr bool }{ - { - name: "update address", - modifyWorker: func(t *testing.T, w *servers.Worker) { - t.Helper() - w.Address = "foo" - }, - path: []string{"address"}, - assertGot: func(t *testing.T, w *servers.Worker) { - t.Helper() - assert.Equal(t, "foo", w.GetAddress()) - assert.Equal(t, uint32(2), w.GetVersion()) - assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) - assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) - }, - }, { name: "update name", modifyWorker: func(t *testing.T, w *servers.Worker) { @@ -926,7 +893,7 @@ func TestRepository_UpdateWorker(t *testing.T) { t.Helper() assert.Equal(t, "foo", w.GetName()) assert.Equal(t, uint32(2), w.GetVersion()) - assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) + assert.Nil(t, w.GetLastStatusTime()) assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) }, }, @@ -941,39 +908,18 @@ func TestRepository_UpdateWorker(t *testing.T) { t.Helper() assert.Equal(t, "foo", w.GetDescription()) assert.Equal(t, uint32(2), w.GetVersion()) - assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) + assert.Nil(t, w.GetLastStatusTime()) assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) }, }, - { - name: "update worker reported name", - modifyWorker: func(t *testing.T, w *servers.Worker) { - t.Helper() - w.WorkerReportedName = "foo" - }, - path: []string{"WorkerReportedName"}, - wantErr: true, - }, - { - name: "update worker reported name and name", - modifyWorker: func(t *testing.T, w *servers.Worker) { - t.Helper() - w.WorkerReportedName = "foo" - w.Name = "another" - }, - path: []string{"WorkerReportedName", "Name"}, - wantErr: true, - }, - { - name: "Clear worker reported name", - modifyWorker: func(t *testing.T, w *servers.Worker) {}, - path: []string{"WorkerReportedName"}, - wantErr: true, - }, } - for _, tt := range setCases { + for _, tt := range pkiCases { t.Run(tt.name, func(t *testing.T) { - wkr := servers.TestWorker(t, conn, wrapper) + wkr := servers.TestPkiWorker(t, conn, wrapper) + defer func() { + _, err := repo.DeleteWorker(ctx, wkr.GetPublicId()) + require.NoError(t, err) + }() tt.modifyWorker(t, wkr) got, _, err := repo.UpdateWorker(ctx, wkr, 1, tt.path) if tt.wantErr { @@ -994,20 +940,6 @@ func TestRepository_UpdateWorker(t *testing.T) { assertGot func(*testing.T, *servers.Worker) wantErr bool }{ - { - name: "clear address", - modifyWorker: func(t *testing.T, w *servers.Worker) { - t.Helper() - w.Address = "" - }, - path: []string{"address"}, - assertGot: func(t *testing.T, w *servers.Worker) { - t.Helper() - assert.Empty(t, w.GetAddress()) - assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) - assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) - }, - }, { name: "clear name", modifyWorker: func(t *testing.T, w *servers.Worker) { @@ -1018,7 +950,7 @@ func TestRepository_UpdateWorker(t *testing.T) { assertGot: func(t *testing.T, w *servers.Worker) { t.Helper() assert.Empty(t, w.GetName()) - assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) + assert.Nil(t, w.GetLastStatusTime()) assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) }, }, @@ -1032,18 +964,17 @@ func TestRepository_UpdateWorker(t *testing.T) { assertGot: func(t *testing.T, w *servers.Worker) { t.Helper() assert.Empty(t, w.GetDescription()) - assert.Equal(t, w.GetLastStatusTime().AsTime(), w.GetCreateTime().AsTime()) + assert.Nil(t, w.GetLastStatusTime()) assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime()) }, }, } for _, tt := range clearCases { t.Run(tt.name, func(t *testing.T) { - wkr := servers.TestWorker(t, conn, wrapper) + wkr := servers.TestPkiWorker(t, conn, wrapper) wkr.Name = tt.name wkr.Description = tt.name - wkr.Address = tt.name - wkr, _, err := repo.UpdateWorker(ctx, wkr, 1, []string{"name", "description", "address"}) + wkr, _, err := repo.UpdateWorker(ctx, wkr, 1, []string{"name", "description"}) require.NoError(t, err) tt.modifyWorker(t, wkr) @@ -1059,9 +990,9 @@ func TestRepository_UpdateWorker(t *testing.T) { } t.Run("version is wrong", func(t *testing.T) { - wkr := servers.TestWorker(t, conn, wrapper) - wkr.Address = "version is wrong" - result, numUpdated, err := repo.UpdateWorker(ctx, wkr, 2, []string{"address"}) + wkr := servers.TestPkiWorker(t, conn, wrapper) + wkr.Name = "version is wrong" + result, numUpdated, err := repo.UpdateWorker(ctx, wkr, 2, []string{"name"}) require.NoError(t, err) assert.Zero(t, numUpdated) assert.Equal(t, wkr.GetUpdateTime().AsTime(), result.GetUpdateTime().AsTime()) @@ -1082,21 +1013,41 @@ func TestRepository_UpdateWorker(t *testing.T) { }, { name: "empty path", - input: servers.TestWorker(t, conn, wrapper), + input: servers.TestPkiWorker(t, conn, wrapper), version: 1, wantErr: errors.T(errors.EmptyFieldMask), }, { name: "0 version", - input: servers.TestWorker(t, conn, wrapper), + input: servers.TestPkiWorker(t, conn, wrapper), path: []string{"name"}, version: 0, wantErr: errors.T(errors.InvalidParameter), }, + { + name: "changing kms name", + input: func() *servers.Worker { + w := servers.TestKmsWorker(t, conn, wrapper) + w.Name = "some change" + return w + }(), path: []string{"name"}, + version: 1, + wantErr: errors.T(errors.NotSpecificIntegrity), + }, + { + name: "clearing kms name", + input: func() *servers.Worker { + w := servers.TestKmsWorker(t, conn, wrapper) + w.Name = "" + return w + }(), path: []string{"name"}, + version: 1, + wantErr: errors.T(errors.NotSpecificIntegrity), + }, { name: "no public id", input: func() *servers.Worker { - w := servers.TestWorker(t, conn, wrapper) + w := servers.TestPkiWorker(t, conn, wrapper) w.PublicId = "" return w }(), @@ -1106,7 +1057,7 @@ func TestRepository_UpdateWorker(t *testing.T) { }, { name: "unrecognized path", - input: servers.TestWorker(t, conn, wrapper), + input: servers.TestPkiWorker(t, conn, wrapper), path: []string{"UnrecognizedField"}, version: 1, wantErr: errors.T(errors.InvalidFieldMask), @@ -1114,7 +1065,7 @@ func TestRepository_UpdateWorker(t *testing.T) { { name: "not found worker", input: func() *servers.Worker { - w := servers.TestWorker(t, conn, wrapper) + w := servers.TestPkiWorker(t, conn, wrapper) w.PublicId = "w_notfoundworker" return w }(), @@ -1125,11 +1076,11 @@ func TestRepository_UpdateWorker(t *testing.T) { { name: "duplicate name", input: func() *servers.Worker { - w1 := servers.TestWorker(t, conn, wrapper) + w1 := servers.TestPkiWorker(t, conn, wrapper) w1.Name = "somenamethatijustmadeup" w1, _, err := repo.UpdateWorker(ctx, w1, w1.Version, []string{"name"}, nil) require.NoError(t, err) - w2 := servers.TestWorker(t, conn, wrapper) + w2 := servers.TestPkiWorker(t, conn, wrapper) w2.Name = w1.Name return w2 }(), diff --git a/internal/servers/repository_workerauth_test.go b/internal/servers/repository_workerauth_test.go index 95b85853d..162eca4b9 100644 --- a/internal/servers/repository_workerauth_test.go +++ b/internal/servers/repository_workerauth_test.go @@ -146,7 +146,7 @@ func TestStoreWorkerAuth(t *testing.T) { require.NoError(err) require.NotNil(wrapper) - worker := TestWorker(t, conn, wrapper) + worker := TestPkiWorker(t, conn, wrapper) rw := db.New(conn) rootStorage, err := NewRepositoryStorage(ctx, rw, rw, kmsCache) @@ -266,7 +266,7 @@ func TestStoreNodeInformationTx(t *testing.T) { // Create struct to pass in with workerId that will be passed along to // storage - testWorker := TestWorker(t, conn, testWrapper) + testWorker := TestPkiWorker(t, conn, testWrapper) testState, err := AttachWorkerIdToState(testCtx, testWorker.PublicId) require.NoError(t, err) diff --git a/internal/servers/testing.go b/internal/servers/testing.go index 9314cc51e..fb0f16dc5 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -81,30 +81,37 @@ func TestWorkerAuth(ctx context.Context, t *testing.T, conn *db.DB, worker *Work return workerAuth } -// TestWorker inserts a worker into the db to satisfy foreign key constraints. -// The worker provided fields are auto generated. WithName, WithDescription, -// and WithAddress are applied to the resource name, description and address if +// TestKmsWorker inserts a worker into the db to satisfy foreign key constraints. +// The worker provided fields are auto generated. WithName and WithDescription, +// are applied to the resource name, description and address if // present. -func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Worker { +func TestKmsWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Worker { t.Helper() rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) serversRepo, err := NewRepository(rw, rw, kms) require.NoError(t, err) ctx := context.Background() + opts := getOpts(opt...) namePart, err := newWorkerId(ctx) require.NoError(t, err) name := "test-worker-" + strings.ToLower(namePart) - id, err := newWorkerId(ctx) + if opts.withName != "" { + name = opts.withName + } + address := "127.0.0.1" + if opts.withAddress != "" { + address = opts.withAddress + } + id, err := newWorkerIdFromScopeAndName(ctx, scope.Global.String(), name) require.NoError(t, err) - wrk := NewWorkerForStatus(scope.Global.String(), + wrk := NewWorker(scope.Global.String(), WithName(name), - WithAddress("127.0.0.1")) + WithAddress(address)) wrk, err = serversRepo.UpsertWorkerStatus(ctx, wrk, WithPublicId(id)) require.NoError(t, err) require.NotNil(t, wrk) - opts := getOpts(opt...) if len(opts.withWorkerTags) > 0 { var tags []interface{} @@ -119,18 +126,10 @@ func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Opti require.NoError(t, rw.CreateItems(ctx, tags)) } var mask []string - if opts.withName != "" { - wrk.Name = opts.withName - mask = append(mask, "name") - } if opts.withDescription != "" { wrk.Description = opts.withDescription mask = append(mask, "description") } - if opts.withAddress != "" { - wrk.Address = opts.withAddress - mask = append(mask, "address") - } if len(mask) > 0 { var n int wrk, n, err = serversRepo.UpdateWorker(ctx, wrk, wrk.Version, mask) @@ -141,3 +140,38 @@ func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Opti return wrk } + +// TestKmsWorker inserts a worker into the db to satisfy foreign key constraints. +// The worker provided fields are auto generated. WithName and WithDescription, +// are applied to the resource name, description and address if +// present. +func TestPkiWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Worker { + t.Helper() + rw := db.New(conn) + kms := kms.TestKms(t, conn, wrapper) + serversRepo, err := NewRepository(rw, rw, kms) + require.NoError(t, err) + ctx := context.Background() + opts := getOpts(opt...) + + require.NoError(t, err) + wrk := NewWorker(scope.Global.String(), + opt...) + wrk, err = serversRepo.CreateWorker(ctx, wrk, opt...) + require.NoError(t, err) + require.NotNil(t, wrk) + + if len(opts.withWorkerTags) > 0 { + var tags []interface{} + for _, t := range opts.withWorkerTags { + tags = append(tags, &store.WorkerTag{ + WorkerId: wrk.GetPublicId(), + Key: t.Key, + Value: t.Value, + Source: "config", + }) + } + require.NoError(t, rw.CreateItems(ctx, tags)) + } + return wrk +} diff --git a/internal/servers/testing_test.go b/internal/servers/testing_test.go index c12687450..26f84a2f1 100644 --- a/internal/servers/testing_test.go +++ b/internal/servers/testing_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestTestWorker(t *testing.T) { +func TestTestKmsWorker(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) const ( @@ -19,7 +19,7 @@ func TestTestWorker(t *testing.T) { description = "test description" address = "test address" ) - tWorker := TestWorker(t, conn, wrapper, WithName(name), WithDescription(description), WithAddress(address)) + tWorker := TestKmsWorker(t, conn, wrapper, WithName(name), WithDescription(description), WithAddress(address)) assert.NotNil(t, tWorker) assert.True(t, strings.HasPrefix(tWorker.GetPublicId(), WorkerPrefix)) @@ -29,7 +29,30 @@ func TestTestWorker(t *testing.T) { require.NoError(t, rw.LookupById(context.Background(), lkpWorker)) assert.NotNil(t, lkpWorker) assert.NotNil(t, lkpWorker.GetLastStatusTime()) + assert.Equal(t, KmsWorkerType.String(), lkpWorker.GetType()) assert.Equal(t, name, lkpWorker.GetName()) assert.Equal(t, description, lkpWorker.GetDescription()) assert.Equal(t, address, lkpWorker.GetAddress()) } + +func TestTestPkiWorker(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + const ( + name = "test name" + description = "test description" + ) + tWorker := TestPkiWorker(t, conn, wrapper, WithName(name), WithDescription(description)) + assert.NotNil(t, tWorker) + assert.True(t, strings.HasPrefix(tWorker.GetPublicId(), WorkerPrefix)) + + lkpWorker := NewWorker(scope.Global.String()) + lkpWorker.PublicId = tWorker.GetPublicId() + rw := db.New(conn) + require.NoError(t, rw.LookupById(context.Background(), lkpWorker)) + assert.NotNil(t, lkpWorker) + assert.Equal(t, PkiWorkerType.String(), lkpWorker.GetType()) + assert.Equal(t, name, lkpWorker.GetName()) + assert.Equal(t, description, lkpWorker.GetDescription()) + assert.Nil(t, lkpWorker.GetLastStatusTime()) +} diff --git a/internal/servers/worker.go b/internal/servers/worker.go index a80383508..fb2e7f665 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -12,6 +12,26 @@ import ( "google.golang.org/protobuf/types/known/structpb" ) +type WorkerType string + +const ( + UnknownWorkerType WorkerType = "unknown" + KmsWorkerType WorkerType = "kms" + PkiWorkerType WorkerType = "pki" +) + +func (t WorkerType) Valid() bool { + switch t { + case KmsWorkerType, PkiWorkerType: + return true + } + return false +} + +func (t WorkerType) String() string { + return string(t) +} + type workerAuthWorkerId struct { WorkerId string `mapstructure:"worker_id"` } @@ -40,6 +60,13 @@ type Worker struct { activeConnectionCount uint32 `gorm:"-"` apiTags []*Tag `gorm:"-"` configTags []*Tag `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:"-"` } // NewWorker returns a new Worker. Valid options are WithName, WithDescription @@ -54,7 +81,7 @@ func NewWorker(scopeId string, opt ...Option) *Worker { Description: opts.withDescription, Address: opts.withAddress, }, - apiTags: opts.withWorkerTags, + inputTags: opts.withWorkerTags, } } @@ -63,16 +90,7 @@ func NewWorker(scopeId string, opt ...Option) *Worker { // are assigned to the worker reported variations of these fields. // All other options are ignored. func NewWorkerForStatus(scopeId string, opt ...Option) *Worker { - opts := getOpts(opt...) - return &Worker{ - Worker: &store.Worker{ - ScopeId: scopeId, - WorkerReportedName: opts.withName, - WorkerReportedAddress: opts.withAddress, - WorkerReportedKeyId: opts.withKeyId, - }, - configTags: opts.withWorkerTags, - } + return NewWorker(scopeId, opt...) } // allocWorker will allocate a Worker @@ -100,6 +118,12 @@ func (w *Worker) clone() *Worker { cWorker.configTags = append(cWorker.configTags, &Tag{Key: t.Key, Value: t.Value}) } } + 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 } @@ -109,10 +133,7 @@ func (w *Worker) clone() *Worker { // in its connection status updates. If neither is available, an empty string // is returned. func (w *Worker) CanonicalAddress() string { - if w.GetAddress() != "" { - return w.GetAddress() - } - return w.GetWorkerReportedAddress() + return w.GetAddress() } // ActiveConnectionCount is the current number of sessions this worker is handling @@ -183,32 +204,28 @@ type workerAggregate struct { UpdateTime *timestamp.Timestamp Address string Version uint32 + Type string ApiTags string ActiveConnectionCount uint32 // Config Fields - WorkerReportedName string - WorkerReportedAddress string - LastStatusTime *timestamp.Timestamp - WorkerReportedKeyId string - WorkerConfigTags string + LastStatusTime *timestamp.Timestamp + WorkerConfigTags string } func (a *workerAggregate) toWorker(ctx context.Context) (*Worker, error) { const op = "servers.(workerAggregate).toWorker" worker := &Worker{ Worker: &store.Worker{ - PublicId: a.PublicId, - Name: a.Name, - Description: a.Description, - Address: a.Address, - CreateTime: a.CreateTime, - UpdateTime: a.UpdateTime, - ScopeId: a.ScopeId, - Version: a.Version, - WorkerReportedAddress: a.WorkerReportedAddress, - WorkerReportedName: a.WorkerReportedName, - LastStatusTime: a.LastStatusTime, - WorkerReportedKeyId: a.WorkerReportedKeyId, + PublicId: a.PublicId, + Name: a.Name, + Description: a.Description, + Address: a.Address, + CreateTime: a.CreateTime, + UpdateTime: a.UpdateTime, + ScopeId: a.ScopeId, + Version: a.Version, + LastStatusTime: a.LastStatusTime, + Type: a.Type, }, activeConnectionCount: a.ActiveConnectionCount, } diff --git a/internal/servers/worker_tags_test.go b/internal/servers/worker_tags_test.go index 679832e6a..db371cd43 100644 --- a/internal/servers/worker_tags_test.go +++ b/internal/servers/worker_tags_test.go @@ -15,7 +15,7 @@ func TestWorkerTags_Create(t *testing.T) { wrapper := db.TestWrapper(t) rw := db.New(conn) - worker := TestWorker(t, conn, wrapper) + worker := TestKmsWorker(t, conn, wrapper) tests := []struct { name string diff --git a/internal/servers/worker_test.go b/internal/servers/worker_test.go index 4e6d26a2d..4b514091d 100644 --- a/internal/servers/worker_test.go +++ b/internal/servers/worker_test.go @@ -5,15 +5,18 @@ import ( "fmt" "strings" "testing" + "time" "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/timestamppb" ) func TestWorkerCanonicalAddress(t *testing.T) { @@ -24,12 +27,12 @@ func TestWorkerCanonicalAddress(t *testing.T) { } func TestWorkerCanonicalTags(t *testing.T) { - w := NewWorker(scope.Global.String(), - WithWorkerTags( - &Tag{Key: "key", Value: "apis unique"}, - &Tag{Key: "key", Value: "shared"}, - &Tag{Key: "key2", Value: "apis key2 unique"}, - )) + w := NewWorker(scope.Global.String()) + w.apiTags = []*Tag{ + {Key: "key", Value: "apis unique"}, + {Key: "key", Value: "shared"}, + {Key: "key2", Value: "apis key2 unique"}, + } w.configTags = []*Tag{ {Key: "key", Value: "configs unique"}, {Key: "key", Value: "shared"}, @@ -57,33 +60,36 @@ func TestWorkerAggregate(t *testing.T) { return got } - // Worker without a status - { + t.Run("kms worker", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) + id = strings.ToLower(id) w := NewWorker(scope.Global.String(), WithName(id), WithAddress("address")) + w.Type = KmsWorkerType.String() w.PublicId = id require.NoError(t, rw.Create(ctx, w)) got := getAggWorker(id) + assert.Equal(t, KmsWorkerType.String(), got.GetType()) assert.Equal(t, id, got.GetPublicId()) assert.Equal(t, scope.Global.String(), got.GetScopeId()) assert.Equal(t, id, got.GetName()) assert.Equal(t, "address", got.GetAddress()) assert.Equal(t, uint32(1), got.GetVersion()) - assert.Nil(t, got.GetLastStatusTime()) + assert.NotNil(t, got.GetLastStatusTime()) assert.Empty(t, got.CanonicalTags()) - } + }) // Worker with status - { + t.Run("Worker with status", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) w := NewWorkerForStatus(scope.Global.String(), WithAddress("address"), WithName(strings.ToLower(id))) + w.Type = KmsWorkerType.String() w.PublicId = id require.NoError(t, rw.Create(ctx, w)) @@ -91,19 +97,19 @@ func TestWorkerAggregate(t *testing.T) { assert.Equal(t, id, got.GetPublicId()) assert.Equal(t, uint32(1), got.GetVersion()) assert.NotNil(t, got.GetLastStatusTime()) - assert.Equal(t, strings.ToLower(id), got.GetWorkerReportedName()) - assert.Equal(t, "address", got.GetWorkerReportedAddress()) - } + assert.Equal(t, strings.ToLower(id), got.GetName()) + assert.Equal(t, "address", got.GetAddress()) + }) - // Worker with a config tag - { + t.Run("Worker with a config tag", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) - ws := NewWorkerForStatus(scope.Global.String(), + w := NewWorkerForStatus(scope.Global.String(), WithAddress("address"), WithName(strings.ToLower(id))) - ws.PublicId = id - require.NoError(t, rw.Create(ctx, ws)) + w.Type = KmsWorkerType.String() + w.PublicId = id + require.NoError(t, rw.Create(ctx, w)) require.NoError(t, rw.Create(ctx, &store.WorkerTag{ WorkerId: id, @@ -118,17 +124,17 @@ func TestWorkerAggregate(t *testing.T) { assert.NotNil(t, got.GetLastStatusTime()) assert.Empty(t, got.apiTags) assert.Equal(t, got.configTags, []*Tag{{Key: "key", Value: "val"}}) - } + }) - // Worker with many config tag - { + t.Run("Worker with many config tag", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) - ws := NewWorkerForStatus(scope.Global.String(), + w := NewWorkerForStatus(scope.Global.String(), WithAddress("address"), WithName(strings.ToLower(id))) - ws.PublicId = id - require.NoError(t, rw.Create(ctx, ws)) + w.Type = KmsWorkerType.String() + w.PublicId = id + require.NoError(t, rw.Create(ctx, w)) require.NoError(t, rw.Create(ctx, &store.WorkerTag{ WorkerId: id, Key: "key", @@ -156,17 +162,17 @@ func TestWorkerAggregate(t *testing.T) { {Key: "key", Value: "val2"}, {Key: "key2", Value: "val2"}, }) - } + }) - // Worker with an api tag - { + t.Run("Worker with an api tag", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) - ws := NewWorkerForStatus(scope.Global.String(), + w := NewWorkerForStatus(scope.Global.String(), WithAddress("address"), WithName(strings.ToLower(id))) - ws.PublicId = id - require.NoError(t, rw.Create(ctx, ws)) + w.Type = KmsWorkerType.String() + w.PublicId = id + require.NoError(t, rw.Create(ctx, w)) require.NoError(t, rw.Create(ctx, &store.WorkerTag{ WorkerId: id, @@ -181,17 +187,18 @@ func TestWorkerAggregate(t *testing.T) { assert.NotNil(t, got.GetLastStatusTime()) assert.Empty(t, got.GetConfigTags()) assert.Equal(t, got.apiTags, []*Tag{{Key: "key", Value: "val"}}) - } + }) // Worker with mix of tag sources - { + t.Run("Worker with mix of tag sources", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) - ws := NewWorkerForStatus(scope.Global.String(), + w := NewWorkerForStatus(scope.Global.String(), WithAddress("address"), WithName(strings.ToLower(id))) - ws.PublicId = id - require.NoError(t, rw.Create(ctx, ws)) + w.Type = KmsWorkerType.String() + w.PublicId = id + require.NoError(t, rw.Create(ctx, w)) require.NoError(t, rw.Create(ctx, &store.WorkerTag{ WorkerId: id, Key: "key", @@ -220,7 +227,7 @@ func TestWorkerAggregate(t *testing.T) { assert.ElementsMatch(t, got.configTags, []*Tag{ {Key: "key", Value: "val"}, }) - } + }) } func TestWorker_Update(t *testing.T) { @@ -245,260 +252,213 @@ func TestWorker_Update(t *testing.T) { wantUpdateError bool }{ { - name: "base update with base", + name: "kms update address", initial: Worker{ Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Description: "base update with base", + Type: KmsWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "kms update address", + Address: "kms update address", }, }, update: Worker{ Worker: &store.Worker{ - Description: "base update with base updated", + Address: "updated kms update address", }, }, - mask: []string{"Description"}, + mask: []string{"Address"}, assert: func(t *testing.T, init, up *Worker) { t.Helper() - assert.Equal(t, "base update with base updated", up.Description) + assert.Equal(t, "kms update address", up.Name) + assert.Equal(t, "updated kms update address", up.Address) + assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) + assert.Equal(t, up.GetLastStatusTime().AsTime(), up.GetUpdateTime().AsTime()) + }, + }, + { + name: "kms update name", + initial: Worker{ + Worker: &store.Worker{ + Type: KmsWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "kms update name", + Address: "kms update name", + }, + }, + update: Worker{ + Worker: &store.Worker{ + Name: "updated kms update name", + }, + }, + mask: []string{"Name"}, + wantUpdateError: true, + }, + { + name: "kms clear name", + initial: Worker{ + Worker: &store.Worker{ + Type: KmsWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "kms clear name", + Address: "kms clear name", + }, + }, + update: Worker{ + Worker: &store.Worker{}, + }, + nullMask: []string{"Name"}, + wantUpdateError: true, + }, + { + name: "pki update address", + initial: Worker{ + Worker: &store.Worker{ + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "pki update address", + }, + }, + update: Worker{ + Worker: &store.Worker{ + Address: "updated pki update address", + }, + }, + mask: []string{"Address"}, + assert: func(t *testing.T, init, up *Worker) { + t.Helper() + assert.Equal(t, "pki update address", up.Name) + assert.Equal(t, "updated pki update address", up.Address) + assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) + assert.Equal(t, up.GetLastStatusTime().AsTime(), up.GetUpdateTime().AsTime()) + }, + }, + { + name: "pki update name", + initial: Worker{ + Worker: &store.Worker{ + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "pki update name", + }, + }, + update: Worker{ + Worker: &store.Worker{ + Name: "updated pki update name", + }, + }, + mask: []string{"Name"}, + assert: func(t *testing.T, init, up *Worker) { + t.Helper() + assert.Equal(t, "updated pki update name", up.Name) assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) - assert.Equal(t, uint32(2), up.Version) assert.Nil(t, up.GetLastStatusTime()) }, }, { - name: "base update with worker reported address and name", + name: "pki update description", initial: Worker{ Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Description: "base update with status", + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "pki update description", }, }, update: Worker{ Worker: &store.Worker{ - WorkerReportedName: "base update with worker reported address and name", - WorkerReportedAddress: "base update with worker reported address and name", - }, - }, - mask: []string{"WorkerReportedAddress", "WorkerReportedName"}, - assert: func(t *testing.T, init, up *Worker) { - t.Helper() - assert.Equal(t, "base update with worker reported address and name", up.WorkerReportedAddress) - assert.Equal(t, "base update with worker reported address and name", up.WorkerReportedName) - assert.Equal(t, uint32(1), up.Version) - assert.NotNil(t, up.GetLastStatusTime()) - assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) - assert.Equal(t, up.GetLastStatusTime().AsTime(), up.GetUpdateTime().AsTime()) - }, - }, - { - name: "base update with worker reported address and keyId", - initial: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Description: "base update with status", - }, - }, - update: Worker{ - Worker: &store.Worker{ - WorkerReportedKeyId: "base update with worker reported address and keyId", - WorkerReportedAddress: "base update with worker reported address and keyId", - }, - }, - mask: []string{"WorkerReportedAddress", "WorkerReportedKeyId"}, - assert: func(t *testing.T, init, up *Worker) { - t.Helper() - assert.Equal(t, "base update with worker reported address and keyId", up.WorkerReportedAddress) - assert.Equal(t, "base update with worker reported address and keyId", up.WorkerReportedKeyId) - assert.Equal(t, uint32(1), up.Version) - assert.NotNil(t, up.GetLastStatusTime()) - assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) - assert.Equal(t, up.GetLastStatusTime().AsTime(), up.GetUpdateTime().AsTime()) - }, - }, - { - name: "base update with worker reported address", - initial: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Description: "base update with status", - }, - }, - update: Worker{ - Worker: &store.Worker{ - WorkerReportedAddress: "base update with worker reported address", - }, - }, - mask: []string{"WorkerReportedAddress"}, - wantUpdateError: true, - }, - { - // If any status fields are set then worker reported address must - // be set. - name: "base update with worker reported name", - initial: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Description: "base update with status", - }, - }, - update: Worker{ - Worker: &store.Worker{ - WorkerReportedName: "base update with worker reported name", - }, - }, - mask: []string{"WorkerReportedName"}, - wantUpdateError: true, - }, - { - // If any status fields are set then worker reported address must - // be set. - name: "base update with worker reported keyId", - initial: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Description: "base update with status", - }, - }, - update: Worker{ - Worker: &store.Worker{ - WorkerReportedKeyId: "base update with worker reported keyId", - }, - }, - mask: []string{"WorkerReportedKeyId"}, - wantUpdateError: true, - }, - { - name: "base update with worker reported fields", - initial: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Description: "base update with status", - }, - }, - update: Worker{ - Worker: &store.Worker{ - WorkerReportedName: "base update with worker reported fields", - WorkerReportedAddress: "base update with worker reported fields", - }, - }, - mask: []string{"WorkerReportedName", "WorkerReportedAddress"}, - assert: func(t *testing.T, init, up *Worker) { - t.Helper() - assert.Equal(t, "base update with worker reported fields", up.WorkerReportedAddress) - assert.Equal(t, "base update with worker reported fields", up.WorkerReportedName) - assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) - assert.Equal(t, uint32(1), up.Version) - assert.NotNil(t, up.GetLastStatusTime()) - assert.Equal(t, up.GetLastStatusTime().AsTime(), up.GetUpdateTime().AsTime()) - }, - }, - { - name: "worker reported update with base", - initial: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - WorkerReportedAddress: "worker reported update with base", - WorkerReportedName: "worker reported update with base", - }, - }, - update: Worker{ - Worker: &store.Worker{ - Description: "worker reported update with base", + Description: "updated pki update description", }, }, mask: []string{"Description"}, assert: func(t *testing.T, init, up *Worker) { t.Helper() - assert.Equal(t, "worker reported update with base", up.Description) + assert.Equal(t, "pki update description", up.Name) + assert.Equal(t, "updated pki update description", up.Description) assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) - assert.Equal(t, uint32(2), up.Version) - assert.NotNil(t, up.GetLastStatusTime()) - assert.Equal(t, init.GetLastStatusTime(), up.GetLastStatusTime()) + assert.Nil(t, up.GetLastStatusTime()) }, }, { - name: "worker reported update name to unprintable", + name: "pki clear address", initial: Worker{ Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - WorkerReportedAddress: "worker reported update name to unprintable", - WorkerReportedName: "worker reported update name to unprintable", - }, - }, - update: Worker{ - Worker: &store.Worker{ - WorkerReportedName: "unprintable \u0008 name", - }, - }, - mask: []string{"WorkerReportedName"}, - wantUpdateError: true, - }, - { - name: "worker reported clearing address", - initial: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - WorkerReportedAddress: "worker reported clearing address", - WorkerReportedName: "worker reported clearing address", + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "pki clear address", + Address: "pki clear address", + LastStatusTime: ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(time.Hour))}, }, }, update: Worker{ Worker: &store.Worker{}, }, - nullMask: []string{"WorkerReportedAddress"}, + nullMask: []string{"Address"}, wantUpdateError: true, }, { - name: "worker reported updating worker reported", + name: "pki clear name", initial: Worker{ Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - WorkerReportedAddress: "start", - WorkerReportedName: "start", + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "pki clear name", + Address: "pki clear name", + LastStatusTime: ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(time.Hour))}, }, }, update: Worker{ - Worker: &store.Worker{ - WorkerReportedAddress: "worker reported updating worker reported", - }, + Worker: &store.Worker{}, }, - mask: []string{"WorkerReportedAddress"}, + nullMask: []string{"Name"}, assert: func(t *testing.T, init, up *Worker) { t.Helper() - assert.Equal(t, "worker reported updating worker reported", up.GetWorkerReportedAddress()) - assert.NotNil(t, up.GetLastStatusTime()) - assert.Greater(t, up.GetLastStatusTime().AsTime(), init.GetLastStatusTime().AsTime()) - // We don't modify the worker version while operating only on the worker fields + assert.Empty(t, up.Name) assert.Greater(t, up.GetUpdateTime().AsTime(), up.GetCreateTime().AsTime()) - assert.Equal(t, uint32(1), up.Version) + assert.Greater(t, up.GetLastStatusTime().AsTime(), up.GetUpdateTime().AsTime()) + assert.Less(t, up.GetLastStatusTime().AsTime(), time.Now().Add(time.Hour)) }, }, { - name: "worker reported clearing worker reported name", + name: "pki update name to capital", initial: Worker{ Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - WorkerReportedAddress: "worker reported clearing worker reported name", - WorkerReportedName: "worker reported clearing worker reported name", + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "pki update name to capital", }, }, update: Worker{ - Worker: &store.Worker{}, + Worker: &store.Worker{ + Name: "PKI Update Name To Capital", + }, }, - nullMask: []string{"WorkerReportedName"}, + mask: []string{"Name"}, + wantUpdateError: true, + }, + { + name: "pki update name to unprintable", + initial: Worker{ + Worker: &store.Worker{ + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "worker reported update name to unprintable", + }, + }, + update: Worker{ + Worker: &store.Worker{ + Name: "unprintable \u0008 name", + }, + }, + mask: []string{"Name"}, wantUpdateError: true, }, } @@ -544,9 +504,88 @@ func TestWorker_Create(t *testing.T) { want wanted }{ { - name: "base", + name: "kms base", in: Worker{ Worker: &store.Worker{ + Type: KmsWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Address: "address", + Name: "kms base", + Description: "kms base", + }, + }, + want: wanted{lastStatusUpdated: true}, + }, + { + name: "kms with no name field", + in: Worker{ + Worker: &store.Worker{ + Type: KmsWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Address: "address", + }, + }, + want: wanted{createError: true}, + }, + { + name: "kms with no address field", + in: Worker{ + Worker: &store.Worker{ + Type: KmsWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "kms with no address field", + }, + }, + want: wanted{createError: true}, + }, + { + name: "kms with upper case name", + in: Worker{ + Worker: &store.Worker{ + Type: KmsWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "With Upper Case Worker Reported Name", + Address: "address", + }, + }, + want: wanted{createError: true}, + }, + { + name: "kms with unprintable worker reported name", + in: Worker{ + Worker: &store.Worker{ + Type: KmsWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "unprintable \u0008 worker reported name", + Address: "address", + }, + }, + want: wanted{createError: true}, + }, + // PKI workers are not created in a way that should set the address + // or the last_status_time. + { + name: "pki base", + in: Worker{ + Worker: &store.Worker{ + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "pki base", + }, + }, + want: wanted{}, + }, + { + name: "pki with no name field", + in: Worker{ + Worker: &store.Worker{ + Type: PkiWorkerType.String(), ScopeId: scope.Global.String(), PublicId: newId(), }, @@ -554,134 +593,42 @@ func TestWorker_Create(t *testing.T) { want: wanted{}, }, { - name: "with non status fields", + name: "pki with address field", in: Worker{ Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Name: "with non status fields", - Description: "with non status fields", - Address: "address", - }, - }, - want: wanted{}, - }, - { - name: "with upper case worker reported name", - in: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - WorkerReportedName: "With Upper Case Worker Reported Name", - WorkerReportedAddress: "address", + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Address: "address", }, }, want: wanted{createError: true}, }, { - name: "with unprintable worker reported name", + name: "pki with upper case name", in: Worker{ Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - WorkerReportedName: "unprintable \u0008 worker reported name", - WorkerReportedAddress: "address", + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "PKI With Upper Case Worker Reported Name", + Address: "address", }, }, want: wanted{createError: true}, }, { - name: "with status fields", + name: "pki with unprintable worker reported name", in: Worker{ Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - WorkerReportedName: "with non status fields", - WorkerReportedAddress: "with non status fields", + Type: PkiWorkerType.String(), + ScopeId: scope.Global.String(), + PublicId: newId(), + Name: "unprintable \u0008 worker reported name", + Address: "address", }, }, - want: wanted{ - lastStatusUpdated: true, - }, - }, - { - name: "with worker reported address", - in: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - WorkerReportedAddress: "with worker reported address", - }, - }, - want: wanted{ - createError: true, - }, - }, - { - // The worker reported address is a required field if any of the - // worker reported fields are set. - name: "with worker reported name", - in: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - WorkerReportedName: "with worker reported name", - }, - }, - want: wanted{ - createError: true, - }, - }, - { - name: "non status fields with worker reported fields", - in: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Name: "non status fields with worker reported fields", - Description: "non status fields with worker reported fields", - Address: "address", - WorkerReportedAddress: "non status fields with worker reported fields", - WorkerReportedName: "non status fields with worker reported fields", - }, - }, - want: wanted{ - lastStatusUpdated: true, - }, - }, - { - // The worker reported address is a required field if any of the - // worker reported fields are set. - name: "non status fields with worker reported name", - in: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Name: "non status fields with worker reported name", - Description: "non status fields with worker reported name", - Address: "address", - WorkerReportedName: "non status fields with worker reported name", - }, - }, - want: wanted{ - createError: true, - }, - }, - { - name: "non status fields with worker reported address", - in: Worker{ - Worker: &store.Worker{ - ScopeId: scope.Global.String(), - PublicId: newId(), - Name: "non status fields with worker reported address", - Description: "non status fields with worker reported address", - Address: "address", - WorkerReportedAddress: "non status fields with worker reported address", - }, - }, - want: wanted{ - createError: true, - }, + want: wanted{createError: true}, }, } for _, tc := range cases { @@ -711,7 +658,6 @@ func TestWorker_New(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) - rw := db.New(conn) org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) @@ -721,10 +667,9 @@ func TestWorker_New(t *testing.T) { } tests := []struct { - name string - args args - want *Worker - wantCreateErr bool + name string + args args + want *Worker }{ { name: "missing-scope-id", @@ -732,7 +677,6 @@ func TestWorker_New(t *testing.T) { want: &Worker{ Worker: &store.Worker{}, }, - wantCreateErr: true, }, { name: "global-scope-id", @@ -755,7 +699,6 @@ func TestWorker_New(t *testing.T) { ScopeId: org.GetPublicId(), }, }, - wantCreateErr: true, }, { name: "project-scope-id", @@ -767,7 +710,6 @@ func TestWorker_New(t *testing.T) { ScopeId: prj.GetPublicId(), }, }, - wantCreateErr: true, }, { name: "with name", @@ -829,7 +771,7 @@ func TestWorker_New(t *testing.T) { Worker: &store.Worker{ ScopeId: scope.Global.String(), }, - apiTags: []*Tag{ + inputTags: []*Tag{ { Key: "key", Value: "val", @@ -845,20 +787,6 @@ func TestWorker_New(t *testing.T) { assert := assert.New(t) got := NewWorker(tt.args.scopeId, tt.args.opts...) assert.Empty(cmp.Diff(tt.want, got.clone(), protocmp.Transform())) - - id, err := newWorkerId(context.Background()) - assert.NoError(err) - - tt.want.PublicId = id - got.PublicId = id - - err2 := rw.Create(context.Background(), got) - if tt.wantCreateErr { - assert.Error(err2) - } else { - assert.NoError(err2) - assert.Equal(uint32(1), got.Version) - } }) } } diff --git a/internal/servers/workerauth_store_test.go b/internal/servers/workerauth_store_test.go index fdbe415d6..10f8099a3 100644 --- a/internal/servers/workerauth_store_test.go +++ b/internal/servers/workerauth_store_test.go @@ -291,7 +291,7 @@ func TestWorkerAuthStore(t *testing.T) { testKey, err := databaseWrapper.KeyId(ctx) require.NoError(t, err) - worker := TestWorker(t, conn, wrapper) + worker := TestPkiWorker(t, conn, wrapper) wSignPubKey := populateBytes(defaultLength) wEncPubKey := populateBytes(defaultLength) @@ -430,7 +430,7 @@ func TestWorkerCertBundle(t *testing.T) { wrapper := db.TestWrapper(t) testKey := TestKmsKey(ctx, t, conn, wrapper) - worker := TestWorker(t, conn, wrapper) + worker := TestPkiWorker(t, conn, wrapper) workerAuth := TestWorkerAuth(ctx, t, conn, worker, testKey) rootCA := TestRootCertificate(ctx, t, conn, testKey) certBundle := populateBytes(defaultLength) diff --git a/internal/session/job_session_cleanup_test.go b/internal/session/job_session_cleanup_test.go index b261506dc..126d76411 100644 --- a/internal/session/job_session_cleanup_test.go +++ b/internal/session/job_session_cleanup_test.go @@ -45,8 +45,8 @@ func TestSessionConnectionCleanupJob(t *testing.T) { // Create two "workers". One will remain untouched while the other "goes // away and comes back" (worker 2). - worker1 := servers.TestWorker(t, conn, wrapper) - worker2 := servers.TestWorker(t, conn, wrapper) + worker1 := servers.TestKmsWorker(t, conn, wrapper) + worker2 := servers.TestKmsWorker(t, conn, wrapper) // Create a few sessions on each, activate, and authorize a connection var connIds []string @@ -110,8 +110,9 @@ func TestSessionConnectionCleanupJob(t *testing.T) { // Push an upsert to the first worker so that its status has been // updated. _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), - servers.WithName(worker1.GetWorkerReportedName()), - servers.WithAddress(worker1.GetWorkerReportedAddress()))) + servers.WithName(worker1.GetName()), + servers.WithAddress(worker1.GetAddress())), + servers.WithPublicId(worker1.GetPublicId())) require.NoError(err) // Run the job. @@ -198,10 +199,10 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // firstly, the last worker will have no connections at all, and we will be // closing the others in stages to test multiple servers being closed at // once. - worker1 := servers.TestWorker(t, conn, wrapper) - worker2 := servers.TestWorker(t, conn, wrapper) - worker3 := servers.TestWorker(t, conn, wrapper) - worker4 := servers.TestWorker(t, conn, wrapper) + worker1 := servers.TestKmsWorker(t, conn, wrapper) + worker2 := servers.TestKmsWorker(t, conn, wrapper) + worker3 := servers.TestKmsWorker(t, conn, wrapper) + worker4 := servers.TestKmsWorker(t, conn, wrapper) // Create sessions on the first three, activate, and authorize connections var worker1ConnIds, worker2ConnIds, worker3ConnIds []string @@ -292,11 +293,9 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) { // the most up-to-date fields. updateServer := func(t *testing.T, w *servers.Worker) *servers.Worker { t.Helper() - // The worker provided name will derive the WorkerId every time - // UpsertWorkerStatus is called. Setting the Worker Id on the caller - // side is an error. + pubId := w.GetPublicId() w.PublicId = "" - wkr, err := serversRepo.UpsertWorkerStatus(ctx, w) + wkr, err := serversRepo.UpsertWorkerStatus(ctx, w, servers.WithPublicId(pubId)) require.NoError(err) return wkr } @@ -465,7 +464,7 @@ func TestCloseWorkerlessConnections(t *testing.T) { } // Setup deleted worker connections - deletedWorker := servers.TestWorker(t, conn, wrapper) + deletedWorker := servers.TestKmsWorker(t, conn, wrapper) dActiveConn := createConnection(deletedWorker.GetPublicId()) dClosedConn := createConnection(deletedWorker.GetPublicId()) connRepo.closeConnections(ctx, []CloseWith{{ @@ -478,7 +477,7 @@ func TestCloseWorkerlessConnections(t *testing.T) { require.NoError(err) // Non deleted worker case - activeWorker := servers.TestWorker(t, conn, wrapper) + activeWorker := servers.TestKmsWorker(t, conn, wrapper) activeConn := createConnection(activeWorker.GetPublicId()) closedConn := createConnection(activeWorker.GetPublicId()) connRepo.closeConnections(ctx, []CloseWith{{ diff --git a/internal/session/repository_connection_test.go b/internal/session/repository_connection_test.go index 200df8272..df43a59c9 100644 --- a/internal/session/repository_connection_test.go +++ b/internal/session/repository_connection_test.go @@ -359,8 +359,8 @@ func TestRepository_orphanedConnections(t *testing.T) { // Create two "workers". One will remain untouched while the other "goes // away and comes back" (worker 2). - worker1 := servers.TestWorker(t, conn, wrapper) - worker2 := servers.TestWorker(t, conn, wrapper) + worker1 := servers.TestKmsWorker(t, conn, wrapper) + worker2 := servers.TestKmsWorker(t, conn, wrapper) // Create a few sessions on each, activate, and authorize a connection var connIds []string diff --git a/internal/session/service_authorize_connection_test.go b/internal/session/service_authorize_connection_test.go index 6261bfb06..e0d3cf739 100644 --- a/internal/session/service_authorize_connection_test.go +++ b/internal/session/service_authorize_connection_test.go @@ -36,7 +36,7 @@ func TestService_AuthorizeConnection(t *testing.T) { composedOf.ExpirationTime = exp } s := TestSession(t, conn, wrapper, composedOf) - srv := servers.TestWorker(t, conn, wrapper) + srv := servers.TestKmsWorker(t, conn, wrapper) testServer = srv.PublicId tofu := TestTofu(t) _, _, err := repo.ActivateSession(context.Background(), s.PublicId, s.Version, tofu) diff --git a/internal/session/service_worker_status_report_test.go b/internal/session/service_worker_status_report_test.go index 706c2db56..d9956ccfd 100644 --- a/internal/session/service_worker_status_report_test.go +++ b/internal/session/service_worker_status_report_test.go @@ -66,7 +66,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "No Sessions", caseFn: func(t *testing.T) testCase { - worker := servers.TestWorker(t, conn, wrapper) + worker := servers.TestKmsWorker(t, conn, wrapper) return testCase{ worker: worker, req: []session.StateReport{}, @@ -77,7 +77,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "No Sessions already canceled", caseFn: func(t *testing.T) testCase { - worker := servers.TestWorker(t, conn, wrapper) + worker := servers.TestKmsWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -109,7 +109,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "Still Active", caseFn: func(t *testing.T) testCase { - worker := servers.TestWorker(t, conn, wrapper) + worker := servers.TestKmsWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -143,7 +143,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "SessionClosed", caseFn: func(t *testing.T) testCase { - worker := servers.TestWorker(t, conn, wrapper) + worker := servers.TestKmsWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -183,7 +183,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "MultipleSessionsClosed", caseFn: func(t *testing.T) testCase { - worker := servers.TestWorker(t, conn, wrapper) + worker := servers.TestKmsWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -250,7 +250,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "OrphanedConnection", caseFn: func(t *testing.T) testCase { - worker := servers.TestWorker(t, conn, wrapper) + worker := servers.TestKmsWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -301,7 +301,7 @@ func TestWorkerStatusReport(t *testing.T) { { name: "MultipleSessionsAndOrphanedConnections", caseFn: func(t *testing.T) testCase { - worker := servers.TestWorker(t, conn, wrapper) + worker := servers.TestKmsWorker(t, conn, wrapper) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), diff --git a/internal/session/testing_test.go b/internal/session/testing_test.go index f344ae896..1403a3296 100644 --- a/internal/session/testing_test.go +++ b/internal/session/testing_test.go @@ -74,7 +74,7 @@ func Test_TestWorker(t *testing.T) { require := require.New(t) conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) - w := servers.TestWorker(t, conn, wrapper) + w := servers.TestKmsWorker(t, conn, wrapper) require.NotNil(w) } From 90a999d3fe61b4e37e6a7a7f98a705c44e02ebaa Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 15 Jun 2022 10:47:25 -0700 Subject: [PATCH 090/100] Some testing fixes (#2193) --- .../handlers/worker_service_status_test.go | 21 ++- .../handlers/targets/target_service_test.go | 7 +- internal/servers/repository_worker_test.go | 170 ++++++++++++++---- internal/servers/testing.go | 18 +- internal/servers/workerauth_store_test.go | 2 +- 5 files changed, 158 insertions(+), 60 deletions(-) diff --git a/internal/daemon/cluster/handlers/worker_service_status_test.go b/internal/daemon/cluster/handlers/worker_service_status_test.go index 1500eab42..8c7abb54f 100644 --- a/internal/daemon/cluster/handlers/worker_service_status_test.go +++ b/internal/daemon/cluster/handlers/worker_service_status_test.go @@ -107,7 +107,7 @@ func TestStatus(t *testing.T) { req: &pbs.StatusRequest{ WorkerStatus: &servers.ServerWorkerStatus{ PublicId: worker1.GetPublicId(), - Name: worker1.GetWorkerReportedName(), + Name: worker1.GetName(), Address: worker1.CanonicalAddress(), }, }, @@ -127,7 +127,7 @@ func TestStatus(t *testing.T) { req: &pbs.StatusRequest{ WorkerStatus: &servers.ServerWorkerStatus{ PublicId: worker1.GetPublicId(), - Name: worker1.GetWorkerReportedName(), + Name: worker1.GetName(), Address: worker1.CanonicalAddress(), }, Jobs: []*pbs.JobStatus{ @@ -169,7 +169,7 @@ func TestStatus(t *testing.T) { Address: worker1.CanonicalAddress(), }, }, - wantErrMsg: status.Error(codes.InvalidArgument, "Name and keyId are not set in the request; at least one is required.").Error(), + wantErrMsg: status.Error(codes.InvalidArgument, "Name and keyId are not set in the request; one is required.").Error(), }, { name: "No Address", @@ -177,7 +177,7 @@ func TestStatus(t *testing.T) { req: &pbs.StatusRequest{ WorkerStatus: &servers.ServerWorkerStatus{ PublicId: worker1.GetPublicId(), - Name: worker1.GetWorkerReportedName(), + Name: worker1.GetName(), }, }, wantErrMsg: status.Error(codes.InvalidArgument, "Address is not set but is required.").Error(), @@ -310,7 +310,7 @@ func TestStatusSessionClosed(t *testing.T) { req: &pbs.StatusRequest{ WorkerStatus: &servers.ServerWorkerStatus{ PublicId: worker1.GetPublicId(), - Name: worker1.GetWorkerReportedName(), + Name: worker1.GetName(), Address: worker1.CanonicalAddress(), }, Jobs: []*pbs.JobStatus{ @@ -477,7 +477,7 @@ func TestStatusDeadConnection(t *testing.T) { req := &pbs.StatusRequest{ WorkerStatus: &servers.ServerWorkerStatus{ PublicId: worker1.GetPublicId(), - Name: worker1.GetWorkerReportedName(), + Name: worker1.GetName(), Address: worker1.CanonicalAddress(), }, Jobs: []*pbs.JobStatus{ @@ -578,7 +578,7 @@ func TestStatusWorkerWithKeyId(t *testing.T) { target.WithSessionConnectionLimit(-1), ) - worker1 := servers.TestKmsWorker(t, conn, wrapper) + worker1 := servers.TestPkiWorker(t, conn, wrapper) rootStorage, err := servers.NewRepositoryStorage(ctx, rw, rw, kms) require.NoError(t, err) @@ -636,8 +636,7 @@ func TestStatusWorkerWithKeyId(t *testing.T) { wantErr: false, req: &pbs.StatusRequest{ WorkerStatus: &servers.ServerWorkerStatus{ - Name: worker1.GetWorkerReportedName(), - Address: worker1.CanonicalAddress(), + Address: "someaddress", KeyId: nodeInfo.Id, }, }, @@ -657,8 +656,7 @@ func TestStatusWorkerWithKeyId(t *testing.T) { req: &pbs.StatusRequest{ WorkerStatus: &servers.ServerWorkerStatus{ KeyId: nodeInfo.Id, - Name: worker1.GetWorkerReportedName(), - Address: worker1.CanonicalAddress(), + Address: "someaddress", }, Jobs: []*pbs.JobStatus{ { @@ -703,6 +701,7 @@ func TestStatusWorkerWithKeyId(t *testing.T) { assert.Equal(tc.wantErrMsg, err.Error()) return } + require.NoError(err) assert.Empty( cmp.Diff( tc.want, diff --git a/internal/daemon/controller/handlers/targets/target_service_test.go b/internal/daemon/controller/handlers/targets/target_service_test.go index 675946ac4..e7f635326 100644 --- a/internal/daemon/controller/handlers/targets/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/target_service_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/types/scope" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" @@ -32,18 +33,20 @@ func TestWorkerList_Addresses(t *testing.T) { } func TestWorkerList_Filter(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) var workers []*servers.Worker for i := 0; i < 5; i++ { switch { case i%2 == 0: - workers = append(workers, servers.NewWorker(scope.Global.String(), + workers = append(workers, servers.TestKmsWorker(t, conn, wrapper, servers.WithName(fmt.Sprintf("test_worker_%d", i)), servers.WithWorkerTags(&servers.Tag{ Key: fmt.Sprintf("key%d", i), Value: fmt.Sprintf("value%d", i), }))) default: - workers = append(workers, servers.NewWorkerForStatus(scope.Global.String(), + workers = append(workers, servers.TestPkiWorker(t, conn, wrapper, servers.WithName(fmt.Sprintf("test_worker_%d", i)), servers.WithWorkerTags(&servers.Tag{ Key: "key", diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index d5848006b..133ace946 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -212,16 +212,6 @@ func TestLookupWorker(t *testing.T) { servers.WithDescription("description"), servers.WithAddress("address"), servers.WithWorkerTags(&servers.Tag{"key", "val"})) - w, err = repo.UpsertWorkerStatus(context.Background(), - servers.NewWorkerForStatus(w.GetScopeId(), - servers.WithName(w.GetName()), - servers.WithAddress(w.GetAddress()), - servers.WithWorkerTags(&servers.Tag{ - Key: "config", - Value: "test", - })), - servers.WithUpdateTags(true), - servers.WithPublicId(w.GetPublicId())) require.NoError(t, err) sessRepo, err := session.NewRepository(rw, rw, kms) @@ -261,12 +251,11 @@ func TestLookupWorker(t *testing.T) { require.NoError(t, err) assert.Empty(t, cmp.Diff(w, got, protocmp.Transform())) assert.Equal(t, uint32(3), got.ActiveConnectionCount()) - assert.Equal(t, map[string][]string{"key": {"val"}}, got.GetApiTags()) - assert.Equal(t, map[string][]string{"config": {"test"}}, got.GetConfigTags()) + assert.Empty(t, got.GetApiTags()) assert.Equal(t, map[string][]string{ - "key": {"val"}, - "config": {"test"}, + "key": {"val"}, }, got.CanonicalTags()) + assert.Equal(t, got.CanonicalTags(), got.GetConfigTags()) }) t.Run("not found", func(t *testing.T) { got, err := repo.LookupWorker(ctx, "w_unknownid") @@ -290,35 +279,92 @@ func TestUpsertWorkerStatus(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) - kms := kms.TestKms(t, conn, wrapper) - repo, err := servers.NewRepository(rw, rw, kms) + kmsCache := kms.TestKms(t, conn, wrapper) + repo, err := servers.NewRepository(rw, rw, kmsCache) require.NoError(t, err) + + err = kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + require.NoError(t, err) + ctx := context.Background() - wStatus1 := servers.NewWorkerForStatus(scope.Global.String(), - servers.WithAddress("address"), servers.WithName("config_name1")) - worker, err := repo.UpsertWorkerStatus(ctx, wStatus1) - require.NoError(t, err) - { + t.Run("create an initial kms worker", func(t *testing.T) { + wStatus1 := servers.NewWorkerForStatus(scope.Global.String(), + servers.WithAddress("address"), servers.WithName("config_name1")) + worker, err := repo.UpsertWorkerStatus(ctx, wStatus1) + require.NoError(t, err) + assert.True(t, strings.HasPrefix(worker.GetPublicId(), "w_")) assert.Equal(t, wStatus1.GetAddress(), worker.CanonicalAddress()) assert.Equal(t, "config_name1", worker.Name) - assert.Equal(t, worker.GetLastStatusTime(), worker.UpdateTime) + assert.Equal(t, worker.GetLastStatusTime().AsTime(), worker.GetUpdateTime().AsTime()) assert.Equal(t, uint32(1), worker.Version) - assert.Equal(t, "address", worker.Address) + assert.Equal(t, "address", worker.GetAddress()) assert.Empty(t, worker.Description) - } - // TODO: Add tests that attempt to upsert a KMS worker where there already - // exists a PKI worker with the same name. It should just error with an - // easy to identify error. + // update again and see updated last status time + worker, err = repo.UpsertWorkerStatus(ctx, wStatus1) + require.NoError(t, err) + assert.Greater(t, worker.GetLastStatusTime().AsTime(), worker.GetCreateTime().AsTime()) + }) + + // Setup and use a pki worker + pkiWorker := servers.TestPkiWorker(t, conn, wrapper, servers.WithName("pki")) + rootStorage, err := servers.NewRepositoryStorage(ctx, rw, rw, kmsCache) + require.NoError(t, err) + _, err = rotation.RotateRootCertificates(ctx, rootStorage) + require.NoError(t, err) + // Create struct to pass in with workerId that will be passed along to storage + state, err := servers.AttachWorkerIdToState(ctx, pkiWorker.GetPublicId()) + require.NoError(t, err) + + // This happens on the worker + fileStorage, err := file.New(ctx) + require.NoError(t, err) + nodeCreds, err := types.NewNodeCredentials(ctx, fileStorage) + require.NoError(t, err) + // Create request using worker id + fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(ctx) + require.NoError(t, err) + + registeredNode, err := registration.AuthorizeNode(ctx, rootStorage, fetchReq, nodeenrollment.WithState(state)) + require.NoError(t, err) + pkiWorkerKeyId := registeredNode.GetId() + + t.Run("update status for pki worker", func(t *testing.T) { + wStatus1 := servers.NewWorkerForStatus(scope.Global.String(), + servers.WithAddress("pki_address")) + worker, err := repo.UpsertWorkerStatus(ctx, wStatus1, servers.WithKeyId(pkiWorkerKeyId)) + require.NoError(t, err) + + assert.True(t, strings.HasPrefix(worker.GetPublicId(), "w_")) + assert.Equal(t, wStatus1.GetAddress(), worker.CanonicalAddress()) + assert.Equal(t, "pki", worker.Name) + assert.Greater(t, worker.GetLastStatusTime().AsTime(), worker.GetCreateTime().AsTime()) + assert.Equal(t, worker.GetLastStatusTime().AsTime(), worker.GetUpdateTime().AsTime()) + assert.Equal(t, uint32(1), worker.Version) + assert.Equal(t, "pki_address", worker.GetAddress()) + assert.Empty(t, worker.Description) + }) failureCases := []struct { name string repo *servers.Repository status *servers.Worker + options []servers.Option errAssert func(*testing.T, error) }{ + { + name: "conflicting name with pki", + repo: repo, + status: servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName(pkiWorker.GetName()), + servers.WithAddress("someaddress")), + errAssert: func(t *testing.T, err error) { + t.Helper() + assert.True(t, errors.Match(errors.T(errors.NotUnique), err), err) + }, + }, { name: "no address", repo: repo, @@ -345,7 +391,19 @@ func TestUpsertWorkerStatus(t *testing.T) { }, }, { - name: "no name", + name: "name and key id provided", + repo: repo, + status: servers.NewWorkerForStatus(scope.Global.String(), + servers.WithName("name and key id provided"), + servers.WithAddress("someaddress")), + options: []servers.Option{servers.WithKeyId(pkiWorkerKeyId)}, + errAssert: func(t *testing.T, err error) { + t.Helper() + assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err), err) + }, + }, + { + name: "no name or key id", repo: repo, status: servers.NewWorkerForStatus(scope.Global.String(), servers.WithAddress("no_name_address")), @@ -382,7 +440,7 @@ func TestUpsertWorkerStatus(t *testing.T) { mock.ExpectBegin() mock.ExpectQuery(`INSERT`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "create-error")) mock.ExpectRollback() - r, err := servers.NewRepository(rw, rw, kms) + r, err := servers.NewRepository(rw, rw, kmsCache) require.NoError(t, err) return r }(), @@ -397,29 +455,28 @@ func TestUpsertWorkerStatus(t *testing.T) { } for _, tc := range failureCases { t.Run(fmt.Sprintf("Failures %s", tc.name), func(t *testing.T) { - _, err = tc.repo.UpsertWorkerStatus(ctx, tc.status) + _, err = tc.repo.UpsertWorkerStatus(ctx, tc.status, tc.options...) assert.Error(t, err) tc.errAssert(t, err) // Still only the original worker exists. workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()}) require.NoError(t, err) - assert.Len(t, workers, 1) + assert.Len(t, workers, 2) }) } - { + t.Run("add another status", func(t *testing.T) { anotherStatus := servers.NewWorkerForStatus(scope.Global.String(), servers.WithName("another_test_worker"), servers.WithAddress("address")) _, err = repo.UpsertWorkerStatus(ctx, anotherStatus) require.NoError(t, err) - { - workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()}) - require.NoError(t, err) - assert.Len(t, workers, 2) - } - } + + workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()}) + require.NoError(t, err) + assert.Len(t, workers, 3) + }) } func TestTagUpdatingListing(t *testing.T) { @@ -634,6 +691,8 @@ func TestRepository_CreateWorker(t *testing.T) { return "", errors.New(testCtx, errors.Internal, "test", "testNewIdFn-error") } + kmsWorker := servers.TestKmsWorker(t, conn, wrapper) + rootStorage, err := servers.NewRepositoryStorage(testCtx, rw, rw, testKms) require.NoError(t, err) _, err = rotation.RotateRootCertificates(testCtx, rootStorage) @@ -796,6 +855,41 @@ func TestRepository_CreateWorker(t *testing.T) { wantErr: true, wantErrContains: "unable to authorize node", }, + { + name: "unique name violation with kms worker", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + w.Name = kmsWorker.GetName() + return w + }, + reader: rw, + repo: testRepo, + wantErr: true, + wantErrIs: errors.NotUnique, + }, + { + name: "success", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + w.Name = "success" + return w + }, + reader: rw, + repo: testRepo, + }, + // This case must follow the one above it. + { + name: "unique name violation with pki worker", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + w.Name = "success" + return w + }, + reader: rw, + repo: testRepo, + wantErr: true, + wantErrIs: errors.NotUnique, + }, { name: "success-with-fetch-node-req", setup: func() *servers.Worker { diff --git a/internal/servers/testing.go b/internal/servers/testing.go index fb0f16dc5..9645afdea 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -58,8 +58,9 @@ func TestRootCertificate(ctx context.Context, t *testing.T, conn *db.DB, kmsKey return cert } -func TestWorkerAuth(ctx context.Context, t *testing.T, conn *db.DB, worker *Worker, kmsKey string) *WorkerAuth { +func TestWorkerAuth(t *testing.T, conn *db.DB, worker *Worker, kmsKey string) *WorkerAuth { t.Helper() + ctx := context.Background() rw := db.New(conn) wSignPubKey := populateBytes(defaultLength) wEncPubKey := populateBytes(defaultLength) @@ -104,12 +105,10 @@ func TestKmsWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...O if opts.withAddress != "" { address = opts.withAddress } - id, err := newWorkerIdFromScopeAndName(ctx, scope.Global.String(), name) - require.NoError(t, err) wrk := NewWorker(scope.Global.String(), WithName(name), WithAddress(address)) - wrk, err = serversRepo.UpsertWorkerStatus(ctx, wrk, WithPublicId(id)) + wrk, err = serversRepo.UpsertWorkerStatus(ctx, wrk) require.NoError(t, err) require.NotNil(t, wrk) @@ -120,7 +119,7 @@ func TestKmsWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...O WorkerId: wrk.GetPublicId(), Key: t.Key, Value: t.Value, - Source: "api", + Source: "configuration", }) } require.NoError(t, rw.CreateItems(ctx, tags)) @@ -137,11 +136,12 @@ func TestKmsWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...O require.Equal(t, 1, n) require.NotNil(t, wrk) } - + wrk, err = serversRepo.LookupWorker(ctx, wrk.GetPublicId()) + require.NoError(t, err) return wrk } -// TestKmsWorker inserts a worker into the db to satisfy foreign key constraints. +// TestPkiWorker inserts a worker into the db to satisfy foreign key constraints. // The worker provided fields are auto generated. WithName and WithDescription, // are applied to the resource name, description and address if // present. @@ -168,10 +168,12 @@ func TestPkiWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...O WorkerId: wrk.GetPublicId(), Key: t.Key, Value: t.Value, - Source: "config", + Source: "configuration", }) } require.NoError(t, rw.CreateItems(ctx, tags)) } + wrk, err = serversRepo.LookupWorker(ctx, wrk.GetPublicId()) + require.NoError(t, err) return wrk } diff --git a/internal/servers/workerauth_store_test.go b/internal/servers/workerauth_store_test.go index 10f8099a3..4bddd2ca4 100644 --- a/internal/servers/workerauth_store_test.go +++ b/internal/servers/workerauth_store_test.go @@ -431,7 +431,7 @@ func TestWorkerCertBundle(t *testing.T) { testKey := TestKmsKey(ctx, t, conn, wrapper) worker := TestPkiWorker(t, conn, wrapper) - workerAuth := TestWorkerAuth(ctx, t, conn, worker, testKey) + workerAuth := TestWorkerAuth(t, conn, worker, testKey) rootCA := TestRootCertificate(ctx, t, conn, testKey) certBundle := populateBytes(defaultLength) From 11b025c016748ea63a5505efbcc44f7336c79051 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 15 Jun 2022 14:24:42 -0400 Subject: [PATCH 091/100] Start updates on worker service test (#2195) --- .../handlers/workers/worker_service.go | 22 +- .../handlers/workers/worker_service_test.go | 351 +++++++++--------- 2 files changed, 192 insertions(+), 181 deletions(-) diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index a98abf7a7..7d9981b54 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -455,14 +455,23 @@ func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (* } if outputFields.Has(globals.AuthorizedActionsField) { out.AuthorizedActions = opts.WithAuthorizedActions - if in.Type == KmsWorkerType { + if in.Type == KmsWorkerType && out.AuthorizedActions != nil { // KMS workers cannot be updated through the API - out.AuthorizedActions = strutil.StrListDelete(out.AuthorizedActions, action.Update.String()) + allActions := out.AuthorizedActions + out.AuthorizedActions = make([]string, 0, len(allActions)) + for _, act := range allActions { + if act != action.Update.String() { + out.AuthorizedActions = append(out.AuthorizedActions, act) + } + } } } if outputFields.Has(globals.AddressField) && in.GetAddress() != "" { out.Address = in.GetAddress() } + if outputFields.Has(globals.TypeField) && in.GetType() != "" { + out.Type = in.GetType() + } if outputFields.Has(globals.LastStatusTimeField) { out.LastStatusTime = in.GetLastStatusTime().GetTimestamp() } @@ -583,6 +592,9 @@ func validateCreateRequest(req *pbs.CreateWorkerLedRequest) error { if req.GetItem().CanonicalTags != nil { badFields[globals.CanonicalTagsField] = readOnlyFieldMsg } + if req.GetItem().ConfigTags != nil { + badFields[globals.ConfigTagsField] = readOnlyFieldMsg + } if req.GetItem().LastStatusTime != nil { badFields[globals.LastStatusTimeField] = readOnlyFieldMsg } @@ -591,14 +603,14 @@ func validateCreateRequest(req *pbs.CreateWorkerLedRequest) error { } nameString := req.GetItem().GetName().String() if !strutil.Printable(nameString) { - badFields[globals.NameField] = "Contains non-printable characters." + badFields[globals.NameField] = "Name contains non-printable characters." } if strings.ToLower(nameString) != nameString { - badFields[globals.NameField] = "Must be all lowercase" + badFields[globals.NameField] = "Name must be all lowercase." } descriptionString := req.GetItem().GetDescription().String() if !strutil.Printable(descriptionString) { - badFields[globals.DescriptionField] = "Contains non-printable characters." + badFields[globals.DescriptionField] = "Description contains non-printable characters." } return badFields }) diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index f384e97c7..80e453229 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -16,10 +16,12 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/types/action" "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers" + "github.com/hashicorp/go-secure-stdlib/strutil" "github.com/hashicorp/nodeenrollment/rotation" "github.com/hashicorp/nodeenrollment/storage/file" "github.com/hashicorp/nodeenrollment/types" @@ -63,51 +65,84 @@ func TestGet(t *testing.T) { return repo, nil } - worker := servers.TestKmsWorker(t, conn, wrap, - servers.WithName("test worker names"), - servers.WithDescription("test worker description"), - servers.WithAddress("test worker address"), + kmsWorker := servers.TestKmsWorker(t, conn, wrap, + servers.WithName("test kms worker names"), + servers.WithDescription("test kms worker description"), + servers.WithAddress("test kms worker address"), servers.WithWorkerTags(&servers.Tag{Key: "key", Value: "val"})) // Add config tags to the created worker - worker, err = repo.UpsertWorkerStatus(context.Background(), - servers.NewWorkerForStatus(worker.GetScopeId(), - servers.WithName(worker.GetWorkerReportedName()), - servers.WithAddress(worker.GetWorkerReportedAddress()), + kmsWorker, err = repo.UpsertWorkerStatus(context.Background(), + servers.NewWorkerForStatus(kmsWorker.GetScopeId(), + servers.WithName(kmsWorker.GetName()), + servers.WithAddress(kmsWorker.GetAddress()), servers.WithWorkerTags(&servers.Tag{ Key: "config", Value: "test", })), servers.WithUpdateTags(true), - servers.WithPublicId(worker.GetPublicId())) + servers.WithPublicId(kmsWorker.GetPublicId())) require.NoError(t, err) - wantWorker := &pb.Worker{ - Id: worker.GetPublicId(), - ScopeId: worker.GetScopeId(), - Scope: &scopes.ScopeInfo{Id: worker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"}, - CreatedTime: worker.CreateTime.GetTimestamp(), - UpdatedTime: worker.UpdateTime.GetTimestamp(), - Version: worker.GetVersion(), - Name: wrapperspb.String(worker.GetName()), - Description: wrapperspb.String(worker.GetDescription()), - Address: wrapperspb.String(worker.GetAddress()), - AuthorizedActions: testAuthorizedActions, - CanonicalAddress: worker.CanonicalAddress(), - LastStatusTime: worker.GetLastStatusTime().GetTimestamp(), + kmsAuthzActions := make([]string, len(testAuthorizedActions)) + copy(kmsAuthzActions, testAuthorizedActions) + + wantKmsWorker := &pb.Worker{ + Id: kmsWorker.GetPublicId(), + ScopeId: kmsWorker.GetScopeId(), + Scope: &scopes.ScopeInfo{Id: kmsWorker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"}, + CreatedTime: kmsWorker.CreateTime.GetTimestamp(), + UpdatedTime: kmsWorker.UpdateTime.GetTimestamp(), + Version: kmsWorker.GetVersion(), + Name: wrapperspb.String(kmsWorker.GetName()), + Description: wrapperspb.String(kmsWorker.GetDescription()), + Address: kmsWorker.GetAddress(), + AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()), + LastStatusTime: kmsWorker.GetLastStatusTime().GetTimestamp(), CanonicalTags: map[string]*structpb.ListValue{ "key": structListValue(t, "val"), "config": structListValue(t, "test"), }, - Tags: map[string]*structpb.ListValue{ - "key": structListValue(t, "val"), + ConfigTags: map[string]*structpb.ListValue{ + "config": structListValue(t, "test"), }, - WorkerProvidedConfiguration: &pb.WorkerProvidedConfiguration{ - Address: worker.GetWorkerReportedAddress(), - Name: worker.GetWorkerReportedName(), - Tags: map[string]*structpb.ListValue{ - "config": structListValue(t, "test"), - }, + Type: KmsWorkerType, + } + + pkiWorker := servers.TestPkiWorker(t, conn, wrap, + servers.WithName("test pki worker names"), + servers.WithDescription("test pki worker description")) + // Add config tags to the created worker + pkiWorker, err = repo.UpsertWorkerStatus(context.Background(), + servers.NewWorkerForStatus(pkiWorker.GetScopeId(), + servers.WithName(pkiWorker.GetName()), + servers.WithAddress("test kms worker address"), + servers.WithWorkerTags(&servers.Tag{ + Key: "config", + Value: "test", + })), + servers.WithUpdateTags(true), + servers.WithPublicId(pkiWorker.GetPublicId())) + require.NoError(t, err) + + wantPkiWorker := &pb.Worker{ + Id: pkiWorker.GetPublicId(), + ScopeId: pkiWorker.GetScopeId(), + Scope: &scopes.ScopeInfo{Id: pkiWorker.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"}, + CreatedTime: pkiWorker.CreateTime.GetTimestamp(), + UpdatedTime: pkiWorker.UpdateTime.GetTimestamp(), + Version: pkiWorker.GetVersion(), + Name: wrapperspb.String(pkiWorker.GetName()), + Description: wrapperspb.String(pkiWorker.GetDescription()), + Address: pkiWorker.GetAddress(), + AuthorizedActions: testAuthorizedActions, + LastStatusTime: pkiWorker.GetLastStatusTime().GetTimestamp(), + CanonicalTags: map[string]*structpb.ListValue{ + "config": structListValue(t, "test"), }, + ConfigTags: map[string]*structpb.ListValue{ + "config": structListValue(t, "test"), + }, + Type: PkiWorkerType, } cases := []struct { @@ -118,10 +153,16 @@ func TestGet(t *testing.T) { err error }{ { - name: "Get an Existing Worker", - scopeId: worker.GetScopeId(), - req: &pbs.GetWorkerRequest{Id: worker.GetPublicId()}, - res: &pbs.GetWorkerResponse{Item: wantWorker}, + name: "Get an Existing KMS Worker", + scopeId: kmsWorker.GetScopeId(), + req: &pbs.GetWorkerRequest{Id: kmsWorker.GetPublicId()}, + res: &pbs.GetWorkerResponse{Item: wantKmsWorker}, + }, + { + name: "Get an Existing PKI Worker", + scopeId: pkiWorker.GetScopeId(), + req: &pbs.GetWorkerRequest{Id: pkiWorker.GetPublicId()}, + res: &pbs.GetWorkerResponse{Item: wantPkiWorker}, }, { name: "Get a non-existent Worker", @@ -172,10 +213,30 @@ func TestList(t *testing.T) { return servers.NewRepository(rw, rw, kms) } - var wantWorkers []*pb.Worker + var wantKmsWorkers []*pb.Worker for i := 0; i < 10; i++ { - w := servers.TestKmsWorker(t, conn, wrap, servers.WithName(fmt.Sprintf("worker%d", i))) - wantWorkers = append(wantWorkers, &pb.Worker{ + w := servers.TestKmsWorker(t, conn, wrap, servers.WithName(fmt.Sprintf("kms-worker%d", i))) + kmsAuthzActions := make([]string, len(testAuthorizedActions)) + copy(kmsAuthzActions, testAuthorizedActions) + wantKmsWorkers = append(wantKmsWorkers, &pb.Worker{ + Id: w.GetPublicId(), + ScopeId: w.GetScopeId(), + Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"}, + CreatedTime: w.CreateTime.GetTimestamp(), + UpdatedTime: w.UpdateTime.GetTimestamp(), + Version: w.GetVersion(), + Name: wrapperspb.String(w.GetName()), + AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()), + Address: w.GetAddress(), + Type: KmsWorkerType, + LastStatusTime: w.GetLastStatusTime().GetTimestamp(), + }) + } + + var wantPkiWorkers []*pb.Worker + for i := 0; i < 10; i++ { + w := servers.TestPkiWorker(t, conn, wrap, servers.WithName(fmt.Sprintf("pki-worker%d", i))) + wantPkiWorkers = append(wantPkiWorkers, &pb.Worker{ Id: w.GetPublicId(), ScopeId: w.GetScopeId(), Scope: &scopes.ScopeInfo{Id: w.GetScopeId(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"}, @@ -184,12 +245,9 @@ func TestList(t *testing.T) { Version: w.GetVersion(), Name: wrapperspb.String(w.GetName()), AuthorizedActions: testAuthorizedActions, - CanonicalAddress: w.CanonicalAddress(), + Address: w.GetAddress(), + Type: PkiWorkerType, LastStatusTime: w.GetLastStatusTime().GetTimestamp(), - WorkerProvidedConfiguration: &pb.WorkerProvidedConfiguration{ - Address: w.GetWorkerReportedAddress(), - Name: w.GetWorkerReportedName(), - }, }) } @@ -202,27 +260,27 @@ func TestList(t *testing.T) { { name: "List All Workers", req: &pbs.ListWorkersRequest{ScopeId: scope.Global.String()}, - res: &pbs.ListWorkersResponse{Items: wantWorkers}, + res: &pbs.ListWorkersResponse{Items: append(wantKmsWorkers, wantPkiWorkers...)}, }, { name: "List global workers recursively", req: &pbs.ListWorkersRequest{ScopeId: "global", Recursive: true}, res: &pbs.ListWorkersResponse{ - Items: wantWorkers, + Items: append(wantKmsWorkers, wantPkiWorkers...), }, }, { - name: "Filter to a single workers", - req: &pbs.ListWorkersRequest{ScopeId: "global", Recursive: true, Filter: `"/item/name"=="worker2"`}, + name: "Filter to a single worker of each type", + req: &pbs.ListWorkersRequest{ScopeId: "global", Recursive: true, Filter: `"/item/name"=="kms-worker2" or "/item/name"=="pki-worker2"`}, res: &pbs.ListWorkersResponse{ - Items: wantWorkers[2:3], + Items: []*pb.Worker{wantKmsWorkers[2], wantPkiWorkers[2]}, }, }, { - name: "Filter to 2 workers", - req: &pbs.ListWorkersRequest{ScopeId: "global", Recursive: true, Filter: `"/item/name" matches "worker[23]"`}, + name: "Filter to 2 workers of each type", + req: &pbs.ListWorkersRequest{ScopeId: "global", Recursive: true, Filter: `"/item/name" matches "kms-worker[23]" or "/item/name" matches "pki-worker[23]"`}, res: &pbs.ListWorkersResponse{ - Items: wantWorkers[2:4], + Items: []*pb.Worker{wantKmsWorkers[2], wantKmsWorkers[3], wantPkiWorkers[2], wantPkiWorkers[3]}, }, }, { @@ -253,6 +311,10 @@ func TestList(t *testing.T) { sort.Slice(got.Items, func(i, j int) bool { return got.Items[i].GetName().GetValue() < got.Items[j].GetName().GetValue() }) + require.NoError(gErr) + sort.Slice(tc.res.Items, func(i, j int) bool { + return tc.res.Items[i].GetName().GetValue() < tc.res.Items[j].GetName().GetValue() + }) assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListWorkers(%q) got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res) // Test the anon case @@ -370,10 +432,6 @@ func TestUpdate(t *testing.T) { workerService, err := NewService(ctx, repoFn, iamRepoFn) require.NoError(t, err) expectedScope := &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"} - expectedConfig := &pb.WorkerProvidedConfiguration{ - Address: wkr.GetWorkerReportedAddress(), - Name: wkr.GetWorkerReportedName(), - } cases := []struct { name string @@ -390,22 +448,20 @@ func TestUpdate(t *testing.T) { Item: &pb.Worker{ Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), - Address: wrapperspb.String("address"), + Address: "address", }, }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Name: wrapperspb.String("name"), - Description: wrapperspb.String("desc"), - Address: wrapperspb.String("address"), - CanonicalAddress: "address", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerProvidedConfiguration: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("name"), + Description: wrapperspb.String("desc"), + Address: "address", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -422,17 +478,15 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Name: wrapperspb.String("name"), - Description: wrapperspb.String("desc"), - Address: wrapperspb.String("default"), - CanonicalAddress: "default", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerProvidedConfiguration: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("name"), + Description: wrapperspb.String("desc"), + Address: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -488,16 +542,14 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Description: wrapperspb.String("default"), - Address: wrapperspb.String("default"), - CanonicalAddress: "default", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerProvidedConfiguration: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Description: wrapperspb.String("default"), + Address: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -513,16 +565,14 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Name: wrapperspb.String("default"), - Address: wrapperspb.String("default"), - CanonicalAddress: "default", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerProvidedConfiguration: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("default"), + Address: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -539,17 +589,15 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Name: wrapperspb.String("updated"), - Description: wrapperspb.String("default"), - Address: wrapperspb.String("default"), - CanonicalAddress: "default", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerProvidedConfiguration: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("updated"), + Description: wrapperspb.String("default"), + Address: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -566,17 +614,15 @@ func TestUpdate(t *testing.T) { }, res: &pbs.UpdateWorkerResponse{ Item: &pb.Worker{ - Id: wkr.GetPublicId(), - ScopeId: wkr.GetScopeId(), - Scope: expectedScope, - Name: wrapperspb.String("default"), - Description: wrapperspb.String("notignored"), - Address: wrapperspb.String("default"), - CanonicalAddress: "default", - CreatedTime: wkr.GetCreateTime().GetTimestamp(), - LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), - WorkerProvidedConfiguration: expectedConfig, - AuthorizedActions: testAuthorizedActions, + Id: wkr.GetPublicId(), + ScopeId: wkr.GetScopeId(), + Scope: expectedScope, + Name: wrapperspb.String("default"), + Description: wrapperspb.String("notignored"), + Address: "default", + CreatedTime: wkr.GetCreateTime().GetTimestamp(), + LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), + AuthorizedActions: testAuthorizedActions, }, }, }, @@ -593,38 +639,6 @@ func TestUpdate(t *testing.T) { }, err: handlers.ApiErrorWithCode(codes.NotFound), }, - { - name: "Cant change Worker Defined Name", - req: &pbs.UpdateWorkerRequest{ - Id: wkr.GetPublicId(), - UpdateMask: &field_mask.FieldMask{ - Paths: []string{"worker_provided_configuration.name"}, - }, - Item: &pb.Worker{ - WorkerProvidedConfiguration: &pb.WorkerProvidedConfiguration{ - Name: "name", - }, - }, - }, - res: nil, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, - { - name: "Cant change Worker Defined Address", - req: &pbs.UpdateWorkerRequest{ - Id: wkr.GetPublicId(), - UpdateMask: &field_mask.FieldMask{ - Paths: []string{"worker_provided_configuration.address"}, - }, - Item: &pb.Worker{ - WorkerProvidedConfiguration: &pb.WorkerProvidedConfiguration{ - Address: "address", - }, - }, - }, - res: nil, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, { name: "Cant change Id", req: &pbs.UpdateWorkerRequest{ @@ -672,7 +686,7 @@ func TestUpdate(t *testing.T) { Paths: []string{"tags"}, }, Item: &pb.Worker{ - Tags: map[string]*structpb.ListValue{ + ConfigTags: map[string]*structpb.ListValue{ "foo": structListValue(t, "bar"), }, }, @@ -696,13 +710,13 @@ func TestUpdate(t *testing.T) { err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { - name: "Cant specify canonical address", + name: "Cant specify address", req: &pbs.UpdateWorkerRequest{ UpdateMask: &field_mask.FieldMask{ - Paths: []string{"canonical_address"}, + Paths: []string{globals.AddressField}, }, Item: &pb.Worker{ - CanonicalAddress: "should_fail", + Address: "should_fail", }, }, res: nil, @@ -916,29 +930,29 @@ func TestCreateWorkerLed(t *testing.T) { wantErrContains: globals.IdField, }, { - name: "invalid-canonical-address", + name: "invalid-address", service: testSrv, scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ ScopeId: scope.Global.String(), WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - CanonicalAddress: "invalid-canonical-address", + Address: "invalid-address", }, }, wantErr: true, wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), - wantErrContains: globals.CanonicalAddressField, + wantErrContains: globals.AddressField, }, { - name: "invalid-tags", + name: "invalid-config-tags", service: testSrv, scopeId: scope.Global.String(), req: &pbs.CreateWorkerLedRequest{ Item: &workers.Worker{ ScopeId: scope.Global.String(), WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - Tags: map[string]*structpb.ListValue{ + ConfigTags: map[string]*structpb.ListValue{ "invalid": {Values: []*structpb.Value{ structpb.NewStringValue("invalid-tags"), }}, @@ -947,7 +961,7 @@ func TestCreateWorkerLed(t *testing.T) { }, wantErr: true, wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), - wantErrContains: globals.TagsField, + wantErrContains: globals.ConfigTagsField, }, { name: "invalid-canonical-tags", @@ -983,21 +997,6 @@ func TestCreateWorkerLed(t *testing.T) { wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), wantErrContains: globals.LastStatusTimeField, }, - { - name: "invalid-worker-config", - service: testSrv, - scopeId: scope.Global.String(), - req: &pbs.CreateWorkerLedRequest{ - Item: &workers.Worker{ - ScopeId: scope.Global.String(), - WorkerGeneratedAuthToken: &wrapperspb.StringValue{Value: fetchReqFn()}, - WorkerProvidedConfiguration: &pb.WorkerProvidedConfiguration{}, - }, - }, - wantErr: true, - wantErrIs: handlers.ApiErrorWithCode(codes.InvalidArgument), - wantErrContains: globals.WorkerProvidedConfigurationField, - }, { name: "invalid-authorized-actions", service: testSrv, From d7aaa9f7e2061d5e20f6bf576358c3bdce6f16ff Mon Sep 17 00:00:00 2001 From: Jim Date: Wed, 15 Jun 2022 14:43:39 -0400 Subject: [PATCH 092/100] fix (byow): minor updates to UpsertWorkerStatus(...) on conflict (#2194) --- internal/servers/repository_worker.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index 0105de633..06e1fe1b7 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -277,8 +277,17 @@ func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt Action: append(db.SetColumns([]string{"address"}), db.SetColumnValues(map[string]interface{}{"last_status_time": "now()"})...), } - if err := w.Create(ctx, worker, db.WithOnConflict(workerCreateConflict)); err != nil { + var withRowsAffected int64 + err := w.Create(ctx, worker, db.WithOnConflict(workerCreateConflict), db.WithReturnRowsAffected(&withRowsAffected), + // The intent of this WithWhere option is to operate with the OnConflict such that the action + // taken by the OnConflict only applies if the conflict is on a row that is returned by this where + // statement, otherwise it should error out. + db.WithWhere("server_worker.type = 'kms'")) + switch { + case err != nil: return errors.Wrap(ctx, err, op, errors.WithMsg("error creating a worker")) + case withRowsAffected == 0: + return errors.New(ctx, errors.NotUnique, op, "error updating worker") } case opts.withKeyId != "": n, err := w.Update(ctx, worker, []string{"address"}, nil) From e37923425922b0d38f5c31c6c41e3b5089c3a064 Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 15 Jun 2022 11:47:38 -0700 Subject: [PATCH 093/100] Remove CanonicalAddress, GetApiTags(), and NewWorkerForStatus. (#2196) Minor testing tweaks are also included. --- .../handlers/worker_service_status_test.go | 10 +++--- .../handlers/targets/target_service.go | 4 +-- .../handlers/workers/worker_service_test.go | 19 ++--------- internal/servers/repository_worker_test.go | 33 +++++++++---------- internal/servers/worker.go | 26 --------------- internal/servers/worker_test.go | 17 +++------- internal/session/job_session_cleanup_test.go | 2 +- .../service_worker_status_report_test.go | 6 ++-- ...on_list_benchmarks_dump_generation_test.go | 7 +--- 9 files changed, 35 insertions(+), 89 deletions(-) diff --git a/internal/daemon/cluster/handlers/worker_service_status_test.go b/internal/daemon/cluster/handlers/worker_service_status_test.go index 8c7abb54f..9a9c77d4f 100644 --- a/internal/daemon/cluster/handlers/worker_service_status_test.go +++ b/internal/daemon/cluster/handlers/worker_service_status_test.go @@ -108,7 +108,7 @@ func TestStatus(t *testing.T) { WorkerStatus: &servers.ServerWorkerStatus{ PublicId: worker1.GetPublicId(), Name: worker1.GetName(), - Address: worker1.CanonicalAddress(), + Address: worker1.GetAddress(), }, }, want: &pbs.StatusResponse{ @@ -128,7 +128,7 @@ func TestStatus(t *testing.T) { WorkerStatus: &servers.ServerWorkerStatus{ PublicId: worker1.GetPublicId(), Name: worker1.GetName(), - Address: worker1.CanonicalAddress(), + Address: worker1.GetAddress(), }, Jobs: []*pbs.JobStatus{ { @@ -166,7 +166,7 @@ func TestStatus(t *testing.T) { req: &pbs.StatusRequest{ WorkerStatus: &servers.ServerWorkerStatus{ PublicId: worker1.GetPublicId(), - Address: worker1.CanonicalAddress(), + Address: worker1.GetAddress(), }, }, wantErrMsg: status.Error(codes.InvalidArgument, "Name and keyId are not set in the request; one is required.").Error(), @@ -311,7 +311,7 @@ func TestStatusSessionClosed(t *testing.T) { WorkerStatus: &servers.ServerWorkerStatus{ PublicId: worker1.GetPublicId(), Name: worker1.GetName(), - Address: worker1.CanonicalAddress(), + Address: worker1.GetAddress(), }, Jobs: []*pbs.JobStatus{ { @@ -478,7 +478,7 @@ func TestStatusDeadConnection(t *testing.T) { WorkerStatus: &servers.ServerWorkerStatus{ PublicId: worker1.GetPublicId(), Name: worker1.GetName(), - Address: worker1.CanonicalAddress(), + Address: worker1.GetAddress(), }, Jobs: []*pbs.JobStatus{ { diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index 787e50cd7..164a5fafe 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -2140,7 +2140,7 @@ type workerList []*servers.Worker func (w workerList) addresses() []string { ret := make([]string, 0, len(w)) for _, worker := range w { - ret = append(ret, worker.CanonicalAddress()) + ret = append(ret, worker.GetAddress()) } return ret } @@ -2149,7 +2149,7 @@ func (w workerList) addresses() []string { func (w workerList) workerInfos() []*pb.WorkerInfo { ret := make([]*pb.WorkerInfo, 0, len(w)) for _, worker := range w { - ret = append(ret, &pb.WorkerInfo{Address: worker.CanonicalAddress()}) + ret = append(ret, &pb.WorkerInfo{Address: worker.GetAddress()}) } return ret } diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index 80e453229..5705e4d46 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -70,18 +70,6 @@ func TestGet(t *testing.T) { servers.WithDescription("test kms worker description"), servers.WithAddress("test kms worker address"), servers.WithWorkerTags(&servers.Tag{Key: "key", Value: "val"})) - // Add config tags to the created worker - kmsWorker, err = repo.UpsertWorkerStatus(context.Background(), - servers.NewWorkerForStatus(kmsWorker.GetScopeId(), - servers.WithName(kmsWorker.GetName()), - servers.WithAddress(kmsWorker.GetAddress()), - servers.WithWorkerTags(&servers.Tag{ - Key: "config", - Value: "test", - })), - servers.WithUpdateTags(true), - servers.WithPublicId(kmsWorker.GetPublicId())) - require.NoError(t, err) kmsAuthzActions := make([]string, len(testAuthorizedActions)) copy(kmsAuthzActions, testAuthorizedActions) @@ -99,11 +87,10 @@ func TestGet(t *testing.T) { AuthorizedActions: strutil.StrListDelete(kmsAuthzActions, action.Update.String()), LastStatusTime: kmsWorker.GetLastStatusTime().GetTimestamp(), CanonicalTags: map[string]*structpb.ListValue{ - "key": structListValue(t, "val"), - "config": structListValue(t, "test"), + "key": structListValue(t, "val"), }, ConfigTags: map[string]*structpb.ListValue{ - "config": structListValue(t, "test"), + "key": structListValue(t, "val"), }, Type: KmsWorkerType, } @@ -113,7 +100,7 @@ func TestGet(t *testing.T) { servers.WithDescription("test pki worker description")) // Add config tags to the created worker pkiWorker, err = repo.UpsertWorkerStatus(context.Background(), - servers.NewWorkerForStatus(pkiWorker.GetScopeId(), + servers.NewWorker(pkiWorker.GetScopeId(), servers.WithName(pkiWorker.GetName()), servers.WithAddress("test kms worker address"), servers.WithWorkerTags(&servers.Tag{ diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 133ace946..2dd49af60 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -251,7 +251,6 @@ func TestLookupWorker(t *testing.T) { require.NoError(t, err) assert.Empty(t, cmp.Diff(w, got, protocmp.Transform())) assert.Equal(t, uint32(3), got.ActiveConnectionCount()) - assert.Empty(t, got.GetApiTags()) assert.Equal(t, map[string][]string{ "key": {"val"}, }, got.CanonicalTags()) @@ -289,13 +288,13 @@ func TestUpsertWorkerStatus(t *testing.T) { ctx := context.Background() t.Run("create an initial kms worker", func(t *testing.T) { - wStatus1 := servers.NewWorkerForStatus(scope.Global.String(), + wStatus1 := servers.NewWorker(scope.Global.String(), servers.WithAddress("address"), servers.WithName("config_name1")) worker, err := repo.UpsertWorkerStatus(ctx, wStatus1) require.NoError(t, err) assert.True(t, strings.HasPrefix(worker.GetPublicId(), "w_")) - assert.Equal(t, wStatus1.GetAddress(), worker.CanonicalAddress()) + assert.Equal(t, wStatus1.GetAddress(), worker.GetAddress()) assert.Equal(t, "config_name1", worker.Name) assert.Equal(t, worker.GetLastStatusTime().AsTime(), worker.GetUpdateTime().AsTime()) assert.Equal(t, uint32(1), worker.Version) @@ -332,13 +331,13 @@ func TestUpsertWorkerStatus(t *testing.T) { pkiWorkerKeyId := registeredNode.GetId() t.Run("update status for pki worker", func(t *testing.T) { - wStatus1 := servers.NewWorkerForStatus(scope.Global.String(), + wStatus1 := servers.NewWorker(scope.Global.String(), servers.WithAddress("pki_address")) worker, err := repo.UpsertWorkerStatus(ctx, wStatus1, servers.WithKeyId(pkiWorkerKeyId)) require.NoError(t, err) assert.True(t, strings.HasPrefix(worker.GetPublicId(), "w_")) - assert.Equal(t, wStatus1.GetAddress(), worker.CanonicalAddress()) + assert.Equal(t, wStatus1.GetAddress(), worker.GetAddress()) assert.Equal(t, "pki", worker.Name) assert.Greater(t, worker.GetLastStatusTime().AsTime(), worker.GetCreateTime().AsTime()) assert.Equal(t, worker.GetLastStatusTime().AsTime(), worker.GetUpdateTime().AsTime()) @@ -357,7 +356,7 @@ func TestUpsertWorkerStatus(t *testing.T) { { name: "conflicting name with pki", repo: repo, - status: servers.NewWorkerForStatus(scope.Global.String(), + status: servers.NewWorker(scope.Global.String(), servers.WithName(pkiWorker.GetName()), servers.WithAddress("someaddress")), errAssert: func(t *testing.T, err error) { @@ -368,7 +367,7 @@ func TestUpsertWorkerStatus(t *testing.T) { { name: "no address", repo: repo, - status: servers.NewWorkerForStatus(scope.Global.String(), + status: servers.NewWorker(scope.Global.String(), servers.WithName("worker_with_no_address")), errAssert: func(t *testing.T, err error) { t.Helper() @@ -379,7 +378,7 @@ func TestUpsertWorkerStatus(t *testing.T) { name: "cant specifying public id", repo: repo, status: func() *servers.Worker { - w := servers.NewWorkerForStatus(scope.Global.String(), + w := servers.NewWorker(scope.Global.String(), servers.WithName("worker_with_no_address"), servers.WithAddress("workeraddress")) w.PublicId = "w_specified" @@ -393,7 +392,7 @@ func TestUpsertWorkerStatus(t *testing.T) { { name: "name and key id provided", repo: repo, - status: servers.NewWorkerForStatus(scope.Global.String(), + status: servers.NewWorker(scope.Global.String(), servers.WithName("name and key id provided"), servers.WithAddress("someaddress")), options: []servers.Option{servers.WithKeyId(pkiWorkerKeyId)}, @@ -405,7 +404,7 @@ func TestUpsertWorkerStatus(t *testing.T) { { name: "no name or key id", repo: repo, - status: servers.NewWorkerForStatus(scope.Global.String(), + status: servers.NewWorker(scope.Global.String(), servers.WithAddress("no_name_address")), errAssert: func(t *testing.T, err error) { t.Helper() @@ -424,7 +423,7 @@ func TestUpsertWorkerStatus(t *testing.T) { { name: "empty scope", repo: repo, - status: servers.NewWorkerForStatus("", + status: servers.NewWorker("", servers.WithAddress("address"), servers.WithName("config_name1")), errAssert: func(t *testing.T, err error) { @@ -444,7 +443,7 @@ func TestUpsertWorkerStatus(t *testing.T) { require.NoError(t, err) return r }(), - status: servers.NewWorkerForStatus(scope.Global.String(), + status: servers.NewWorker(scope.Global.String(), servers.WithName("database failure"), servers.WithAddress("address")), errAssert: func(t *testing.T, err error) { @@ -467,7 +466,7 @@ func TestUpsertWorkerStatus(t *testing.T) { } t.Run("add another status", func(t *testing.T) { - anotherStatus := servers.NewWorkerForStatus(scope.Global.String(), + anotherStatus := servers.NewWorker(scope.Global.String(), servers.WithName("another_test_worker"), servers.WithAddress("address")) _, err = repo.UpsertWorkerStatus(ctx, anotherStatus) @@ -490,7 +489,7 @@ func TestTagUpdatingListing(t *testing.T) { ctx := context.Background() worker1 := servers.TestKmsWorker(t, conn, wrapper) - wStatus := servers.NewWorkerForStatus(scope.Global.String(), + wStatus := servers.NewWorker(scope.Global.String(), servers.WithName(worker1.GetName()), servers.WithAddress("somethingnew"), servers.WithWorkerTags( @@ -510,7 +509,7 @@ func TestTagUpdatingListing(t *testing.T) { assert.ElementsMatch(t, []string{"value1", "value2"}, worker1.CanonicalTags()["tag1"]) // Update without saying to update tags - wStatus = servers.NewWorkerForStatus(scope.Global.String(), + wStatus = servers.NewWorker(scope.Global.String(), servers.WithName(worker1.GetName()), servers.WithAddress(worker1.GetAddress()), servers.WithWorkerTags( @@ -625,7 +624,7 @@ func TestListWorkers_WithLiveness(t *testing.T) { // Push an upsert to the first worker so that its status has been // updated. _, err = serversRepo.UpsertWorkerStatus(ctx, - servers.NewWorkerForStatus(scope.Global.String(), + servers.NewWorker(scope.Global.String(), servers.WithName(worker1.GetName()), servers.WithAddress(worker1.GetAddress())), servers.WithPublicId(worker1.GetPublicId())) @@ -654,7 +653,7 @@ func TestListWorkers_WithLiveness(t *testing.T) { // Upsert second server. _, err = serversRepo.UpsertWorkerStatus(ctx, - servers.NewWorkerForStatus(scope.Global.String(), + servers.NewWorker(scope.Global.String(), servers.WithName(worker2.GetName()), servers.WithAddress(worker2.GetAddress())), servers.WithPublicId(worker2.GetPublicId())) diff --git a/internal/servers/worker.go b/internal/servers/worker.go index fb2e7f665..8f7986e19 100644 --- a/internal/servers/worker.go +++ b/internal/servers/worker.go @@ -85,14 +85,6 @@ func NewWorker(scopeId string, opt ...Option) *Worker { } } -// NewWorkerForStatus returns a new Worker usable for status updates. -// Valid options are WithName, WithAddress, and WithWorkerTags, all of which -// are assigned to the worker reported variations of these fields. -// All other options are ignored. -func NewWorkerForStatus(scopeId string, opt ...Option) *Worker { - return NewWorker(scopeId, opt...) -} - // allocWorker will allocate a Worker func allocWorker() Worker { return Worker{Worker: &store.Worker{}} @@ -127,15 +119,6 @@ func (w *Worker) clone() *Worker { return cWorker } -// CanonicalAddress returns the actual address boundary believes should be used -// to communicate with this worker. This will be the worker resource's address -// unless it is not set in which case it will use address the worker provides -// in its connection status updates. If neither is available, an empty string -// is returned. -func (w *Worker) CanonicalAddress() string { - return w.GetAddress() -} - // ActiveConnectionCount is the current number of sessions this worker is handling // according to the controllers. func (w *Worker) ActiveConnectionCount() uint32 { @@ -159,15 +142,6 @@ func (w *Worker) CanonicalTags() map[string][]string { return tags } -// GetApiTags returns the tags for this worker which has been set by the api. -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 -} - // 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 { diff --git a/internal/servers/worker_test.go b/internal/servers/worker_test.go index 4b514091d..7c80297f5 100644 --- a/internal/servers/worker_test.go +++ b/internal/servers/worker_test.go @@ -19,13 +19,6 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) -func TestWorkerCanonicalAddress(t *testing.T) { - worker := NewWorkerForStatus(scope.Global.String(), WithAddress("status")) - assert.Equal(t, "status", worker.CanonicalAddress()) - worker.Address = "worker" - assert.Equal(t, "worker", worker.CanonicalAddress()) -} - func TestWorkerCanonicalTags(t *testing.T) { w := NewWorker(scope.Global.String()) w.apiTags = []*Tag{ @@ -86,7 +79,7 @@ func TestWorkerAggregate(t *testing.T) { t.Run("Worker with status", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) - w := NewWorkerForStatus(scope.Global.String(), + w := NewWorker(scope.Global.String(), WithAddress("address"), WithName(strings.ToLower(id))) w.Type = KmsWorkerType.String() @@ -104,7 +97,7 @@ func TestWorkerAggregate(t *testing.T) { t.Run("Worker with a config tag", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) - w := NewWorkerForStatus(scope.Global.String(), + w := NewWorker(scope.Global.String(), WithAddress("address"), WithName(strings.ToLower(id))) w.Type = KmsWorkerType.String() @@ -129,7 +122,7 @@ func TestWorkerAggregate(t *testing.T) { t.Run("Worker with many config tag", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) - w := NewWorkerForStatus(scope.Global.String(), + w := NewWorker(scope.Global.String(), WithAddress("address"), WithName(strings.ToLower(id))) w.Type = KmsWorkerType.String() @@ -167,7 +160,7 @@ func TestWorkerAggregate(t *testing.T) { t.Run("Worker with an api tag", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) - w := NewWorkerForStatus(scope.Global.String(), + w := NewWorker(scope.Global.String(), WithAddress("address"), WithName(strings.ToLower(id))) w.Type = KmsWorkerType.String() @@ -193,7 +186,7 @@ func TestWorkerAggregate(t *testing.T) { t.Run("Worker with mix of tag sources", func(t *testing.T) { id, err := newWorkerId(ctx) require.NoError(t, err) - w := NewWorkerForStatus(scope.Global.String(), + w := NewWorker(scope.Global.String(), WithAddress("address"), WithName(strings.ToLower(id))) w.Type = KmsWorkerType.String() diff --git a/internal/session/job_session_cleanup_test.go b/internal/session/job_session_cleanup_test.go index 126d76411..837567c74 100644 --- a/internal/session/job_session_cleanup_test.go +++ b/internal/session/job_session_cleanup_test.go @@ -109,7 +109,7 @@ func TestSessionConnectionCleanupJob(t *testing.T) { // Push an upsert to the first worker so that its status has been // updated. - _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), + _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorker(scope.Global.String(), servers.WithName(worker1.GetName()), servers.WithAddress(worker1.GetAddress())), servers.WithPublicId(worker1.GetPublicId())) diff --git a/internal/session/service_worker_status_report_test.go b/internal/session/service_worker_status_report_test.go index d9956ccfd..4cf5a608b 100644 --- a/internal/session/service_worker_status_report_test.go +++ b/internal/session/service_worker_status_report_test.go @@ -14,7 +14,6 @@ import ( "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" - "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -28,12 +27,11 @@ func TestWorkerStatusReport(t *testing.T) { org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) serverRepo, _ := servers.NewRepository(rw, rw, kms) - serverRepo.UpsertController(ctx, &store.Controller{ + _, err := serverRepo.UpsertController(ctx, &store.Controller{ PrivateId: "test_controller1", Address: "127.0.0.1", }) - serverRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), - servers.WithAddress("127.0.0.1"))) + require.NoError(t, err) repo, err := session.NewRepository(rw, rw, kms) require.NoError(t, err) diff --git a/testing/dbtest/session_list_benchmarks_dump_generation_test.go b/testing/dbtest/session_list_benchmarks_dump_generation_test.go index daccd5945..2b7beb032 100644 --- a/testing/dbtest/session_list_benchmarks_dump_generation_test.go +++ b/testing/dbtest/session_list_benchmarks_dump_generation_test.go @@ -22,7 +22,6 @@ import ( "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" - "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/testing/dbtest" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" @@ -130,11 +129,7 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) { require.NoError(err) connRepo, err := session.NewConnectionRepository(ctx, rw, rw, kms) require.NoError(err) - serversRepo, err := servers.NewRepository(rw, rw, kms) - require.NoError(err) - _, err = serversRepo.UpsertWorkerStatus(ctx, servers.NewWorkerForStatus(scope.Global.String(), - servers.WithAddress("127.0.0.1"))) - require.NoError(err) + _ = servers.TestKmsWorker(t, conn, wrap) usersStart := time.Now() t.Logf("Populating %d users", scenario.users) From b3efe0eb3a3332ebb6aebd2c4e65fd14790efc0c Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 15 Jun 2022 12:42:50 -0700 Subject: [PATCH 094/100] Allow TestPkiWorker to be created in an authorized state. (#2198) * Allow TestPkiWorker to be created in an authorized state. --- .../handlers/workers/worker_service_test.go | 16 +++--- internal/servers/options.go | 12 +++++ internal/servers/options_test.go | 13 +++++ internal/servers/repository_worker_test.go | 51 ++----------------- internal/servers/testing.go | 39 ++++++++++++-- internal/servers/testing_test.go | 6 +++ 6 files changed, 79 insertions(+), 58 deletions(-) diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index 5705e4d46..2d29c473f 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -95,20 +95,22 @@ func TestGet(t *testing.T) { Type: KmsWorkerType, } + var pkiWorkerKeyId string pkiWorker := servers.TestPkiWorker(t, conn, wrap, servers.WithName("test pki worker names"), - servers.WithDescription("test pki worker description")) + servers.WithDescription("test pki worker description"), + servers.WithTestPkiWorkerAuthorizedKeyId(&pkiWorkerKeyId)) // Add config tags to the created worker pkiWorker, err = repo.UpsertWorkerStatus(context.Background(), servers.NewWorker(pkiWorker.GetScopeId(), - servers.WithName(pkiWorker.GetName()), servers.WithAddress("test kms worker address"), servers.WithWorkerTags(&servers.Tag{ Key: "config", Value: "test", })), servers.WithUpdateTags(true), - servers.WithPublicId(pkiWorker.GetPublicId())) + servers.WithPublicId(pkiWorker.GetPublicId()), + servers.WithKeyId(pkiWorkerKeyId)) require.NoError(t, err) wantPkiWorker := &pb.Worker{ @@ -407,7 +409,7 @@ func TestUpdate(t *testing.T) { resetWorker := func() { version++ - _, _, err = repo.UpdateWorker(context.Background(), wkr, version, []string{"Name", "Description", "Address"}) + _, _, err = repo.UpdateWorker(context.Background(), wkr, version, []string{"Name", "Description"}) require.NoError(t, err, "Failed to reset worker.") version++ } @@ -430,12 +432,11 @@ func TestUpdate(t *testing.T) { name: "Update an Existing Worker", req: &pbs.UpdateWorkerRequest{ UpdateMask: &field_mask.FieldMask{ - Paths: []string{"name", "description", "address"}, + Paths: []string{"name", "description"}, }, Item: &pb.Worker{ Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), - Address: "address", }, }, res: &pbs.UpdateWorkerResponse{ @@ -445,7 +446,7 @@ func TestUpdate(t *testing.T) { Scope: expectedScope, Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), - Address: "address", + Address: "default", CreatedTime: wkr.GetCreateTime().GetTimestamp(), LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), AuthorizedActions: testAuthorizedActions, @@ -1156,6 +1157,7 @@ func TestCreateWorkerLed(t *testing.T) { Name: &wrapperspb.StringValue{Value: "success"}, Description: &wrapperspb.StringValue{Value: "success-description"}, Version: 1, + Type: PkiWorkerType, }, }, }, diff --git a/internal/servers/options.go b/internal/servers/options.go index 052ed55f5..b2a883624 100644 --- a/internal/servers/options.go +++ b/internal/servers/options.go @@ -36,6 +36,8 @@ type options struct { withNonce []byte withNewIdFunc func(context.Context) (string, error) withFetchNodeCredentialsRequest *types.FetchNodeCredentialsRequest + withTestPkiWorkerAuthorized bool + withTestPkiWorkerKeyId *string } func getDefaultOptions() options { @@ -151,3 +153,13 @@ func WithFetchNodeCredentialsRequest(req *types.FetchNodeCredentialsRequest) Opt o.withFetchNodeCredentialsRequest = req } } + +// WithTestPkiWorkerAuthorizedKeyId should only be used in tests. +// It specifies that the test worker should be authorized when returned and +// assigns the key id for that worker to the string pointer in this option. +func WithTestPkiWorkerAuthorizedKeyId(id *string) Option { + return func(o *options) { + o.withTestPkiWorkerAuthorized = true + o.withTestPkiWorkerKeyId = id + } +} diff --git a/internal/servers/options_test.go b/internal/servers/options_test.go index a1ef9ad8e..d5839c21d 100644 --- a/internal/servers/options_test.go +++ b/internal/servers/options_test.go @@ -145,4 +145,17 @@ func Test_GetOpts(t *testing.T) { runtime.FuncForPC(reflect.ValueOf(testOpts.withNewIdFunc).Pointer()).Name(), ) }) + t.Run("WithTestPkiWorkerAuthorizedKeyId", func(t *testing.T) { + assert := assert.New(t) + testOpts := getDefaultOptions() + + var keyId string + opts := getOpts(WithTestPkiWorkerAuthorizedKeyId(&keyId)) + testOpts = getDefaultOptions() + testOpts.withTestPkiWorkerAuthorized = true + testOpts.withTestPkiWorkerKeyId = &keyId + testOpts.withNewIdFunc = nil + opts.withNewIdFunc = nil + assert.Equal(opts, testOpts) + }) } diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 2dd49af60..4bcfe1468 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -21,8 +21,6 @@ import ( "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-dbw" "github.com/hashicorp/go-kms-wrapping/extras/kms/v2/migrations" - "github.com/hashicorp/nodeenrollment" - "github.com/hashicorp/nodeenrollment/registration" "github.com/hashicorp/nodeenrollment/rotation" "github.com/hashicorp/nodeenrollment/storage/file" "github.com/hashicorp/nodeenrollment/types" @@ -153,30 +151,10 @@ func TestLookupWorkerIdByKeyId(t *testing.T) { err = kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) require.NoError(t, err) - w := servers.TestPkiWorker(t, conn, wrapper) - - rootStorage, err := servers.NewRepositoryStorage(ctx, rw, rw, kmsCache) - require.NoError(t, err) - _, err = rotation.RotateRootCertificates(ctx, rootStorage) - require.NoError(t, err) - // Create struct to pass in with workerId that will be passed along to storage - state, err := servers.AttachWorkerIdToState(ctx, w.PublicId) - require.NoError(t, err) - - // This happens on the worker - fileStorage, err := file.New(ctx) - require.NoError(t, err) - nodeCreds, err := types.NewNodeCredentials(ctx, fileStorage) - require.NoError(t, err) - // Create request using worker id - fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(ctx) - require.NoError(t, err) - - // The AuthorizeNode request will result in a WorkerAuth record being stored - registeredNode, err := registration.AuthorizeNode(ctx, rootStorage, fetchReq, nodeenrollment.WithState(state)) - require.NoError(t, err) + var workerKeyId string + w := servers.TestPkiWorker(t, conn, wrapper, servers.WithTestPkiWorkerAuthorizedKeyId(&workerKeyId)) t.Run("success", func(t *testing.T) { - got, err := repo.LookupWorkerIdByKeyId(ctx, registeredNode.Id) + got, err := repo.LookupWorkerIdByKeyId(ctx, workerKeyId) require.NoError(t, err) assert.Equal(t, w.PublicId, got) }) @@ -308,27 +286,8 @@ func TestUpsertWorkerStatus(t *testing.T) { }) // Setup and use a pki worker - pkiWorker := servers.TestPkiWorker(t, conn, wrapper, servers.WithName("pki")) - rootStorage, err := servers.NewRepositoryStorage(ctx, rw, rw, kmsCache) - require.NoError(t, err) - _, err = rotation.RotateRootCertificates(ctx, rootStorage) - require.NoError(t, err) - // Create struct to pass in with workerId that will be passed along to storage - state, err := servers.AttachWorkerIdToState(ctx, pkiWorker.GetPublicId()) - require.NoError(t, err) - - // This happens on the worker - fileStorage, err := file.New(ctx) - require.NoError(t, err) - nodeCreds, err := types.NewNodeCredentials(ctx, fileStorage) - require.NoError(t, err) - // Create request using worker id - fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(ctx) - require.NoError(t, err) - - registeredNode, err := registration.AuthorizeNode(ctx, rootStorage, fetchReq, nodeenrollment.WithState(state)) - require.NoError(t, err) - pkiWorkerKeyId := registeredNode.GetId() + var pkiWorkerKeyId string + pkiWorker := servers.TestPkiWorker(t, conn, wrapper, servers.WithName("pki"), servers.WithTestPkiWorkerAuthorizedKeyId(&pkiWorkerKeyId)) t.Run("update status for pki worker", func(t *testing.T) { wStatus1 := servers.NewWorker(scope.Global.String(), diff --git a/internal/servers/testing.go b/internal/servers/testing.go index 9645afdea..1e79cad95 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -14,6 +14,11 @@ import ( "github.com/hashicorp/boundary/internal/servers/store" "github.com/hashicorp/boundary/internal/types/scope" wrapping "github.com/hashicorp/go-kms-wrapping/v2" + "github.com/hashicorp/nodeenrollment" + "github.com/hashicorp/nodeenrollment/registration" + "github.com/hashicorp/nodeenrollment/rotation" + "github.com/hashicorp/nodeenrollment/storage/file" + "github.com/hashicorp/nodeenrollment/types" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -83,9 +88,8 @@ func TestWorkerAuth(t *testing.T, conn *db.DB, worker *Worker, kmsKey string) *W } // TestKmsWorker inserts a worker into the db to satisfy foreign key constraints. -// The worker provided fields are auto generated. WithName and WithDescription, -// are applied to the resource name, description and address if -// present. +// The worker provided fields are auto generated. if WithName is not present a +// random name will be generated and assigned to the worker. func TestKmsWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Worker { t.Helper() rw := db.New(conn) @@ -143,8 +147,9 @@ func TestKmsWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...O // TestPkiWorker inserts a worker into the db to satisfy foreign key constraints. // The worker provided fields are auto generated. WithName and WithDescription, -// are applied to the resource name, description and address if -// present. +// are applied to the resource name, description if present. WithTestPkiWorkerAuthorizedKeyId +// can be used to make the PkiWorker authorized in which case the string pointer +// passed to WithTestPkiWorkerAuthorizedKeyId is set to the key id. func TestPkiWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Worker { t.Helper() rw := db.New(conn) @@ -173,6 +178,30 @@ func TestPkiWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...O } require.NoError(t, rw.CreateItems(ctx, tags)) } + if opts.withTestPkiWorkerAuthorized { + rootStorage, err := NewRepositoryStorage(ctx, rw, rw, kms) + require.NoError(t, err) + _, err = rotation.RotateRootCertificates(ctx, rootStorage) + require.NoError(t, err) + // Create struct to pass in with workerId that will be passed along to storage + state, err := AttachWorkerIdToState(ctx, wrk.PublicId) + require.NoError(t, err) + + // This happens on the worker + fileStorage, err := file.New(ctx) + require.NoError(t, err) + nodeCreds, err := types.NewNodeCredentials(ctx, fileStorage) + require.NoError(t, err) + // Create request using worker id + fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(ctx) + require.NoError(t, err) + registeredNode, err := registration.AuthorizeNode(ctx, rootStorage, fetchReq, nodeenrollment.WithState(state)) + require.NoError(t, err) + + if opts.withTestPkiWorkerKeyId != nil { + *opts.withTestPkiWorkerKeyId = registeredNode.Id + } + } wrk, err = serversRepo.LookupWorker(ctx, wrk.GetPublicId()) require.NoError(t, err) return wrk diff --git a/internal/servers/testing_test.go b/internal/servers/testing_test.go index 26f84a2f1..bcbcb7364 100644 --- a/internal/servers/testing_test.go +++ b/internal/servers/testing_test.go @@ -55,4 +55,10 @@ func TestTestPkiWorker(t *testing.T) { assert.Equal(t, name, lkpWorker.GetName()) assert.Equal(t, description, lkpWorker.GetDescription()) assert.Nil(t, lkpWorker.GetLastStatusTime()) + + var keyId string + authorizedWorker := TestPkiWorker(t, conn, wrapper, WithTestPkiWorkerAuthorizedKeyId(&keyId)) + assert.NotNil(t, authorizedWorker) + assert.True(t, strings.HasPrefix(authorizedWorker.GetPublicId(), WorkerPrefix)) + assert.NotEmpty(t, keyId) } From a83a4112ce401d0c573fea5596a73d32fef8acd1 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 15 Jun 2022 14:47:32 -0500 Subject: [PATCH 095/100] Fix hcpb URL suffix --- internal/daemon/worker/controller_connection.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/daemon/worker/controller_connection.go b/internal/daemon/worker/controller_connection.go index 9f51475d4..29eaa3bec 100644 --- a/internal/daemon/worker/controller_connection.go +++ b/internal/daemon/worker/controller_connection.go @@ -35,7 +35,7 @@ import ( "google.golang.org/protobuf/proto" ) -const hcpbUrlSuffix = ".proxy.boundary.hashicorp.cloud" +const hcpbUrlSuffix = ".proxy.boundary.hashicorp.cloud:9202" // StartControllerConnections starts up the resolver and initiates controller // connection client creation From 4efbce31121facfa9755597b99960ff7e60ceab4 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 15 Jun 2022 15:50:36 -0400 Subject: [PATCH 096/100] Add description updating via worker status (#2197) --- internal/cmd/commands/dev/dev.go | 2 + internal/cmd/commands/server/server.go | 4 ++ .../daemon/cluster/handlers/worker_service.go | 1 + internal/daemon/worker/status.go | 9 ++-- .../proto/controller/servers/v1/servers.proto | 3 ++ internal/servers/repository_worker.go | 44 ++++++++++++------- internal/servers/repository_worker_test.go | 25 +++++++---- internal/servers/servers.pb.go | 35 ++++++++++----- 8 files changed, 84 insertions(+), 39 deletions(-) diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 8081efa49..fe8b9b8bc 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -587,6 +587,8 @@ func (c *Command) Run(args []string) int { c.UI.Error("Worker Auth KMS not found after parsing KMS blocks") return base.CommandUserError } + c.Config.Worker.Name = "" + c.Config.Worker.Description = "" c.InfoKeys = append(c.InfoKeys, "[Worker-Auth] AEAD Key Bytes") c.Info["[Worker-Auth] AEAD Key Bytes"] = c.Config.DevWorkerAuthKey } diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index 9a7a07897..cf819ae9a 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -197,6 +197,10 @@ func (c *Command) Run(args []string) int { c.UI.Error("No worker auth KMS specified and no worker auth storage path specified.") return base.CommandUserError } + if c.Config.Worker.Name != "" || c.Config.Worker.Description != "" { + c.UI.Error("Worker config cannot contain name or description when using PKI-based worker authentication; it must be set via the API.") + return base.CommandUserError + } default: if c.Config.Worker.Name == "" { c.UI.Error("Worker is using KMS auth but has no name set. It must be the unique name of this instance.") diff --git a/internal/daemon/cluster/handlers/worker_service.go b/internal/daemon/cluster/handlers/worker_service.go index 3b2810754..581182341 100644 --- a/internal/daemon/cluster/handlers/worker_service.go +++ b/internal/daemon/cluster/handlers/worker_service.go @@ -92,6 +92,7 @@ func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusReques wConf := servers.NewWorker(scope.Global.String(), servers.WithName(wStat.GetName()), + servers.WithDescription(wStat.GetDescription()), servers.WithAddress(wStat.GetAddress()), servers.WithWorkerTags(workerTags...)) opts := []servers.Option{servers.WithUpdateTags(req.GetUpdateTags())} diff --git a/internal/daemon/worker/status.go b/internal/daemon/worker/status.go index aaeffe710..9d312a6d7 100644 --- a/internal/daemon/worker/status.go +++ b/internal/daemon/worker/status.go @@ -157,10 +157,11 @@ func (w *Worker) sendWorkerStatus(cancelCtx context.Context) { result, err := client.Status(statusCtx, &pbs.StatusRequest{ Jobs: activeJobs, WorkerStatus: &servers.ServerWorkerStatus{ - Name: w.conf.RawConfig.Worker.Name, - Address: w.conf.RawConfig.Worker.PublicAddr, - Tags: tags, - KeyId: keyId, + Name: w.conf.RawConfig.Worker.Name, + Description: w.conf.RawConfig.Worker.Description, + Address: w.conf.RawConfig.Worker.PublicAddr, + Tags: tags, + KeyId: keyId, }, UpdateTags: w.updateTags.Load(), }) diff --git a/internal/proto/controller/servers/v1/servers.proto b/internal/proto/controller/servers/v1/servers.proto index 33b06d696..2b2ac63cd 100644 --- a/internal/proto/controller/servers/v1/servers.proto +++ b/internal/proto/controller/servers/v1/servers.proto @@ -18,6 +18,9 @@ message ServerWorkerStatus { // Name of the resource (optional) string name = 20; // @gotags: `class:"public"` + // Description of the resource (optional) + string description = 25; // @gotags: `class:"public"` + // Address for the worker. This must be set. string address = 30; // @gotags: `class:"public"` diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index 06e1fe1b7..e652bc317 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -270,11 +270,36 @@ func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt worker := worker.clone() worker.PublicId = workerId switch { + case opts.withKeyId != "": + // This case goes first in case we want to relax the restriction + // around both name and key ID being supplied to account for + // e.g. config processing bugs. In that case, a key ID being + // supplied should be a clear indicator that we are working with + // a PKI worker, and a lack of one a clear indication we are + // working with a KMS worker. + // + // Note: unlike in the below case, this purposefully leaves out + // "description" since we want description changes for PKI-based + // workers to come via API only. We can't really guard on this + // in the DB so we need to be sure to not include it here. + n, err := w.Update(ctx, worker, []string{"address"}, nil) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to update status of pki worker")) + } + switch n { + case 0: + return errors.New(ctx, errors.RecordNotFound, op, fmt.Sprintf("failed to find worker with key id %q", opts.withKeyId)) + case 1: + break + default: + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("multiple records found when updating worker with id %q", worker.GetPublicId())) + } + case worker.GetName() != "": worker.Type = KmsWorkerType.String() workerCreateConflict := &db.OnConflict{ Target: db.Columns{"public_id"}, - Action: append(db.SetColumns([]string{"address"}), + Action: append(db.SetColumns([]string{"description", "address"}), db.SetColumnValues(map[string]interface{}{"last_status_time": "now()"})...), } var withRowsAffected int64 @@ -289,19 +314,6 @@ func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt case withRowsAffected == 0: return errors.New(ctx, errors.NotUnique, op, "error updating worker") } - case opts.withKeyId != "": - n, err := w.Update(ctx, worker, []string{"address"}, nil) - if err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to update status of pki worker")) - } - switch n { - case 0: - return errors.New(ctx, errors.RecordNotFound, op, fmt.Sprintf("failed to find worker with key id %q", opts.withKeyId)) - case 1: - break - default: - return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("multiple records found when updating worker with id %q", worker.GetPublicId())) - } } // If we've been told to update tags, we need to clean out old @@ -309,7 +321,9 @@ func (r *Repository) UpsertWorkerStatus(ctx context.Context, worker *Worker, opt // delete all tags for the given worker, then add the new ones // we've been sent. if opts.withUpdateTags { - setWorkerTags(ctx, w, worker.GetPublicId(), ConfigurationTagSource, worker.inputTags) + if err := setWorkerTags(ctx, w, worker.GetPublicId(), ConfigurationTagSource, worker.inputTags); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("error setting worker tags")) + } } wAgg := &workerAggregate{PublicId: worker.GetPublicId()} diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 4bcfe1468..147947339 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -265,44 +265,53 @@ func TestUpsertWorkerStatus(t *testing.T) { ctx := context.Background() - t.Run("create an initial kms worker", func(t *testing.T) { + t.Run("create an initial kms worker and update status", func(t *testing.T) { wStatus1 := servers.NewWorker(scope.Global.String(), - servers.WithAddress("address"), servers.WithName("config_name1")) + servers.WithAddress("address"), servers.WithName("config_name1"), + servers.WithDescription("kms_description1")) worker, err := repo.UpsertWorkerStatus(ctx, wStatus1) require.NoError(t, err) assert.True(t, strings.HasPrefix(worker.GetPublicId(), "w_")) assert.Equal(t, wStatus1.GetAddress(), worker.GetAddress()) assert.Equal(t, "config_name1", worker.Name) + assert.Equal(t, "kms_description1", worker.Description) assert.Equal(t, worker.GetLastStatusTime().AsTime(), worker.GetUpdateTime().AsTime()) assert.Equal(t, uint32(1), worker.Version) assert.Equal(t, "address", worker.GetAddress()) - assert.Empty(t, worker.Description) // update again and see updated last status time - worker, err = repo.UpsertWorkerStatus(ctx, wStatus1) + wStatus2 := servers.NewWorker(scope.Global.String(), + servers.WithAddress("new_address"), servers.WithName("config_name1"), + servers.WithDescription("kms_description2")) + worker, err = repo.UpsertWorkerStatus(ctx, wStatus2) require.NoError(t, err) assert.Greater(t, worker.GetLastStatusTime().AsTime(), worker.GetCreateTime().AsTime()) + assert.Equal(t, "config_name1", worker.Name) + assert.Equal(t, "kms_description2", worker.Description) + assert.Equal(t, worker.GetLastStatusTime().AsTime(), worker.GetUpdateTime().AsTime()) + assert.Equal(t, uint32(2), worker.Version) + assert.Equal(t, "new_address", worker.GetAddress()) }) // Setup and use a pki worker var pkiWorkerKeyId string - pkiWorker := servers.TestPkiWorker(t, conn, wrapper, servers.WithName("pki"), servers.WithTestPkiWorkerAuthorizedKeyId(&pkiWorkerKeyId)) + pkiWorker := servers.TestPkiWorker(t, conn, wrapper, servers.WithName("pki"), servers.WithDescription("pki_description1"), servers.WithTestPkiWorkerAuthorizedKeyId(&pkiWorkerKeyId)) t.Run("update status for pki worker", func(t *testing.T) { wStatus1 := servers.NewWorker(scope.Global.String(), - servers.WithAddress("pki_address")) + servers.WithAddress("pki_address"), servers.WithDescription("pki_description2")) worker, err := repo.UpsertWorkerStatus(ctx, wStatus1, servers.WithKeyId(pkiWorkerKeyId)) require.NoError(t, err) assert.True(t, strings.HasPrefix(worker.GetPublicId(), "w_")) assert.Equal(t, wStatus1.GetAddress(), worker.GetAddress()) assert.Equal(t, "pki", worker.Name) + assert.Equal(t, "pki_description1", worker.Description) // PKI workers don't update description via status assert.Greater(t, worker.GetLastStatusTime().AsTime(), worker.GetCreateTime().AsTime()) assert.Equal(t, worker.GetLastStatusTime().AsTime(), worker.GetUpdateTime().AsTime()) assert.Equal(t, uint32(1), worker.Version) assert.Equal(t, "pki_address", worker.GetAddress()) - assert.Empty(t, worker.Description) }) failureCases := []struct { @@ -334,7 +343,7 @@ func TestUpsertWorkerStatus(t *testing.T) { }, }, { - name: "cant specifying public id", + name: "cant specify public id", repo: repo, status: func() *servers.Worker { w := servers.NewWorker(scope.Global.String(), diff --git a/internal/servers/servers.pb.go b/internal/servers/servers.pb.go index 842ba9bd4..edf1bd10d 100644 --- a/internal/servers/servers.pb.go +++ b/internal/servers/servers.pb.go @@ -86,6 +86,8 @@ type ServerWorkerStatus struct { PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" class:"public"` // @gotags: `class:"public"` // Name of the resource (optional) Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty" class:"public"` // @gotags: `class:"public"` + // Description of the resource (optional) + Description string `protobuf:"bytes,25,opt,name=description,proto3" json:"description,omitempty" class:"public"` // @gotags: `class:"public"` // Address for the worker. This must be set. Address string `protobuf:"bytes,30,opt,name=address,proto3" json:"address,omitempty" class:"public"` // @gotags: `class:"public"` // Tags for workers @@ -140,6 +142,13 @@ func (x *ServerWorkerStatus) GetName() string { return "" } +func (x *ServerWorkerStatus) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + func (x *ServerWorkerStatus) GetAddress() string { if x != nil { return x.Address @@ -171,21 +180,23 @@ var file_controller_servers_v1_servers_proto_rawDesc = []byte{ 0x54, 0x61, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0xaa, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0xcc, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, - 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, - 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x42, 0x38, 0x5a, 0x36, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x3b, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x28, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x50, 0x61, 0x69, + 0x72, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, + 0x64, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x42, 0x38, + 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, + 0x3b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From 72d122ef5cf0eb3a487be03db11553229c2ca2dd Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 15 Jun 2022 13:47:02 -0700 Subject: [PATCH 097/100] TestUpdate for PKI and KMS workers fixed for worker service. (#2199) --- .../handlers/workers/worker_service_test.go | 116 +++++++++++++++--- 1 file changed, 99 insertions(+), 17 deletions(-) diff --git a/internal/daemon/controller/handlers/workers/worker_service_test.go b/internal/daemon/controller/handlers/workers/worker_service_test.go index 2d29c473f..4fb69d62b 100644 --- a/internal/daemon/controller/handlers/workers/worker_service_test.go +++ b/internal/daemon/controller/handlers/workers/worker_service_test.go @@ -400,10 +400,9 @@ func TestUpdate(t *testing.T) { return repo, nil } - wkr := servers.TestKmsWorker(t, conn, wrapper, + wkr := servers.TestPkiWorker(t, conn, wrapper, servers.WithName("default"), - servers.WithDescription("default"), - servers.WithAddress("default")) + servers.WithDescription("default")) version := wkr.GetVersion() @@ -446,10 +445,10 @@ func TestUpdate(t *testing.T) { Scope: expectedScope, Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), - Address: "default", CreatedTime: wkr.GetCreateTime().GetTimestamp(), LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), AuthorizedActions: testAuthorizedActions, + Type: PkiWorkerType, }, }, }, @@ -471,13 +470,25 @@ func TestUpdate(t *testing.T) { Scope: expectedScope, Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), - Address: "default", CreatedTime: wkr.GetCreateTime().GetTimestamp(), LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), AuthorizedActions: testAuthorizedActions, + Type: PkiWorkerType, }, }, }, + { + name: "cant update address", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"address"}, + }, + Item: &pb.Worker{ + Address: "updated", + }, + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, { name: "No Update Mask", req: &pbs.UpdateWorkerRequest{ @@ -499,14 +510,6 @@ func TestUpdate(t *testing.T) { }, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, - { - name: "Update port to 0", - req: &pbs.UpdateWorkerRequest{ - UpdateMask: &field_mask.FieldMask{Paths: []string{"default_port"}}, - Item: &pb.Worker{}, - }, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, { name: "Only non-existant paths in Mask", req: &pbs.UpdateWorkerRequest{ @@ -534,10 +537,10 @@ func TestUpdate(t *testing.T) { ScopeId: wkr.GetScopeId(), Scope: expectedScope, Description: wrapperspb.String("default"), - Address: "default", CreatedTime: wkr.GetCreateTime().GetTimestamp(), LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), AuthorizedActions: testAuthorizedActions, + Type: PkiWorkerType, }, }, }, @@ -557,10 +560,10 @@ func TestUpdate(t *testing.T) { ScopeId: wkr.GetScopeId(), Scope: expectedScope, Name: wrapperspb.String("default"), - Address: "default", CreatedTime: wkr.GetCreateTime().GetTimestamp(), LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), AuthorizedActions: testAuthorizedActions, + Type: PkiWorkerType, }, }, }, @@ -582,10 +585,10 @@ func TestUpdate(t *testing.T) { Scope: expectedScope, Name: wrapperspb.String("updated"), Description: wrapperspb.String("default"), - Address: "default", CreatedTime: wkr.GetCreateTime().GetTimestamp(), LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), AuthorizedActions: testAuthorizedActions, + Type: PkiWorkerType, }, }, }, @@ -607,10 +610,10 @@ func TestUpdate(t *testing.T) { Scope: expectedScope, Name: wrapperspb.String("default"), Description: wrapperspb.String("notignored"), - Address: "default", CreatedTime: wkr.GetCreateTime().GetTimestamp(), LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(), AuthorizedActions: testAuthorizedActions, + Type: PkiWorkerType, }, }, }, @@ -784,6 +787,85 @@ func TestUpdate(t *testing.T) { } } +func TestUpdate_KMS(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + ctx := context.Background() + rw := db.New(conn) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + repo, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + repoFn := func() (*servers.Repository, error) { + return repo, nil + } + + wkr := servers.TestKmsWorker(t, conn, wrapper, + servers.WithName("default"), + servers.WithDescription("default")) + + toMerge := &pbs.UpdateWorkerRequest{ + Id: wkr.GetPublicId(), + } + workerService, err := NewService(ctx, repoFn, iamRepoFn) + require.NoError(t, err) + + cases := []struct { + name string + req *pbs.UpdateWorkerRequest + res *pbs.UpdateWorkerResponse + err error + }{ + { + name: "Cant set name", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name"}, + }, + Item: &pb.Worker{ + Name: wrapperspb.String("name"), + }, + }, + }, + { + name: "Cant set description", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"description"}, + }, + Item: &pb.Worker{ + Description: wrapperspb.String("description"), + }, + }, + }, + { + name: "Cant set address", + req: &pbs.UpdateWorkerRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"address"}, + }, + Item: &pb.Worker{ + Address: "address", + }, + }, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + req := proto.Clone(toMerge).(*pbs.UpdateWorkerRequest) + proto.Merge(req, tc.req) + got, gErr := workerService.UpdateWorker(auth.DisabledAuthTestContext(iamRepoFn, scope.Global.String()), req) + assert.Error(t, gErr) + assert.Nil(t, got) + }) + } +} + func TestUpdate_BadVersion(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") From 3d42737789411e63d26b440625e5e9e8650d7d3b Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 15 Jun 2022 16:50:55 -0400 Subject: [PATCH 098/100] Don't display active connection count if it's not authorized (#2200) --- globals/fields.go | 2 +- internal/cmd/commands/workerscmd/funcs.go | 12 ++-- .../handlers/workers/worker_service.go | 2 +- .../api/resources/workers/v1/worker.proto | 2 +- .../api/resources/workers/worker.pb.go | 68 ++++++++++--------- 5 files changed, 45 insertions(+), 41 deletions(-) diff --git a/globals/fields.go b/globals/fields.go index 84c23f810..4dd361862 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -81,5 +81,5 @@ const ( ConfigurationField = "configuration" WorkerGeneratedAuthTokenField = "worker_generated_auth_token" WorkerProvidedConfigurationField = "worker_provided_configuration" - ActiveConnectionCountField = "active_session_count" + ActiveConnectionCountField = "active_connection_count" ) diff --git a/internal/cmd/commands/workerscmd/funcs.go b/internal/cmd/commands/workerscmd/funcs.go index 693c1d9f3..6103be64d 100644 --- a/internal/cmd/commands/workerscmd/funcs.go +++ b/internal/cmd/commands/workerscmd/funcs.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/boundary/api" "github.com/hashicorp/boundary/api/workers" + "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/cmd/base" ) @@ -83,11 +84,6 @@ func (c *Command) printListTable(items []*workers.Worker) string { fmt.Sprintf(" Last Status Time: %s", item.LastStatusTime.Format(time.RFC1123)), ) } - if true { - output = append(output, - fmt.Sprintf(" Active Connection Count: %d", item.ActiveConnectionCount), - ) - } if len(item.AuthorizedActions) > 0 { output = append(output, @@ -124,7 +120,11 @@ func printItemTable(result api.GenericResult) string { if !item.LastStatusTime.IsZero() { nonAttributeMap["Last Status Time"] = item.LastStatusTime } - nonAttributeMap["Active Connection Count"] = item.ActiveConnectionCount + + resultMap := result.GetResponse().Map + if count, ok := resultMap[globals.ActiveConnectionCountField]; ok { + nonAttributeMap["Active Connection Count"] = count + } maxLength := base.MaxAttributesLength(nonAttributeMap, nil, nil) diff --git a/internal/daemon/controller/handlers/workers/worker_service.go b/internal/daemon/controller/handlers/workers/worker_service.go index b19540974..922fbb41b 100644 --- a/internal/daemon/controller/handlers/workers/worker_service.go +++ b/internal/daemon/controller/handlers/workers/worker_service.go @@ -463,7 +463,7 @@ func toProto(ctx context.Context, in *servers.Worker, opt ...handlers.Option) (* out.LastStatusTime = in.GetLastStatusTime().GetTimestamp() } if outputFields.Has(globals.ActiveConnectionCountField) { - out.ActiveConnectionCount = in.ActiveConnectionCount() + out.ActiveConnectionCount = &wrapperspb.UInt32Value{Value: in.ActiveConnectionCount()} } if outputFields.Has(globals.CanonicalTagsField) && len(in.CanonicalTags()) > 0 { var err error diff --git a/internal/proto/controller/api/resources/workers/v1/worker.proto b/internal/proto/controller/api/resources/workers/v1/worker.proto index f54a7d955..ec1bbfa86 100644 --- a/internal/proto/controller/api/resources/workers/v1/worker.proto +++ b/internal/proto/controller/api/resources/workers/v1/worker.proto @@ -97,7 +97,7 @@ message Worker { google.protobuf.StringValue worker_generated_auth_token = 150 [json_name = "worker_generated_auth_token"]; // @gotags: `class:"public"` // Output only. The number of connections that this worker is currently handling. - uint32 active_connection_count = 160 [json_name = "active_connection_count"]; // @gotags: `class:"public"` + google.protobuf.UInt32Value active_connection_count = 160 [json_name = "active_connection_count"]; // @gotags: `class:"public"` // Output only. The available actions on this resource for the requester. repeated string authorized_actions = 300 [json_name = "authorized_actions"]; // @gotags: `class:"public"` diff --git a/sdk/pbs/controller/api/resources/workers/worker.pb.go b/sdk/pbs/controller/api/resources/workers/worker.pb.go index 57e01edab..cdead7367 100644 --- a/sdk/pbs/controller/api/resources/workers/worker.pb.go +++ b/sdk/pbs/controller/api/resources/workers/worker.pb.go @@ -138,7 +138,7 @@ type Worker struct { // https://github.com/hashicorp/nodeenrollment WorkerGeneratedAuthToken *wrapperspb.StringValue `protobuf:"bytes,150,opt,name=worker_generated_auth_token,proto3" json:"worker_generated_auth_token,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The number of connections that this worker is currently handling. - ActiveConnectionCount uint32 `protobuf:"varint,160,opt,name=active_connection_count,proto3" json:"active_connection_count,omitempty" class:"public"` // @gotags: `class:"public"` + ActiveConnectionCount *wrapperspb.UInt32Value `protobuf:"bytes,160,opt,name=active_connection_count,proto3" json:"active_connection_count,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The available actions on this resource for the requester. AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty" class:"public"` // @gotags: `class:"public"` } @@ -280,11 +280,11 @@ func (x *Worker) GetWorkerGeneratedAuthToken() *wrapperspb.StringValue { return nil } -func (x *Worker) GetActiveConnectionCount() uint32 { +func (x *Worker) GetActiveConnectionCount() *wrapperspb.UInt32Value { if x != nil { return x.ActiveConnectionCount } - return 0 + return nil } func (x *Worker) GetAuthorizedActions() []string { @@ -330,7 +330,7 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc7, 0x0a, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe5, 0x0a, 0x0a, 0x06, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, @@ -397,30 +397,32 @@ var file_controller_api_resources_workers_v1_worker_proto_rawDesc = []byte{ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x1b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x12, 0x39, 0x0a, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x12, 0x57, 0x0a, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0xa0, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x12, 0x61, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x09, - 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, - 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, - 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x5c, 0x0a, 0x12, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x54, 0x61, 0x67, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x50, 0x5a, + 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, + 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, + 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, + 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x3b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -445,7 +447,8 @@ var file_controller_api_resources_workers_v1_worker_proto_goTypes = []interface{ (*scopes.ScopeInfo)(nil), // 5: controller.api.resources.scopes.v1.ScopeInfo (*wrapperspb.StringValue)(nil), // 6: google.protobuf.StringValue (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp - (*structpb.ListValue)(nil), // 8: google.protobuf.ListValue + (*wrapperspb.UInt32Value)(nil), // 8: google.protobuf.UInt32Value + (*structpb.ListValue)(nil), // 9: google.protobuf.ListValue } var file_controller_api_resources_workers_v1_worker_proto_depIdxs = []int32{ 2, // 0: controller.api.resources.workers.v1.WorkerProvidedConfiguration.tags:type_name -> controller.api.resources.workers.v1.WorkerProvidedConfiguration.TagsEntry @@ -460,14 +463,15 @@ var file_controller_api_resources_workers_v1_worker_proto_depIdxs = []int32{ 7, // 9: controller.api.resources.workers.v1.Worker.last_status_time:type_name -> google.protobuf.Timestamp 0, // 10: controller.api.resources.workers.v1.Worker.worker_provided_configuration:type_name -> controller.api.resources.workers.v1.WorkerProvidedConfiguration 6, // 11: controller.api.resources.workers.v1.Worker.worker_generated_auth_token:type_name -> google.protobuf.StringValue - 8, // 12: controller.api.resources.workers.v1.WorkerProvidedConfiguration.TagsEntry.value:type_name -> google.protobuf.ListValue - 8, // 13: controller.api.resources.workers.v1.Worker.TagsEntry.value:type_name -> google.protobuf.ListValue - 8, // 14: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry.value:type_name -> google.protobuf.ListValue - 15, // [15:15] is the sub-list for method output_type - 15, // [15:15] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name + 8, // 12: controller.api.resources.workers.v1.Worker.active_connection_count:type_name -> google.protobuf.UInt32Value + 9, // 13: controller.api.resources.workers.v1.WorkerProvidedConfiguration.TagsEntry.value:type_name -> google.protobuf.ListValue + 9, // 14: controller.api.resources.workers.v1.Worker.TagsEntry.value:type_name -> google.protobuf.ListValue + 9, // 15: controller.api.resources.workers.v1.Worker.CanonicalTagsEntry.value:type_name -> google.protobuf.ListValue + 16, // [16:16] is the sub-list for method output_type + 16, // [16:16] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name } func init() { file_controller_api_resources_workers_v1_worker_proto_init() } From b0480eb7b31cc64313ef80486fbe1e1d7aac1daf Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 15 Jun 2022 16:01:08 -0500 Subject: [PATCH 099/100] Move migrations to 32 --- .../migrations/oss/postgres/{99 => 32}/01_domain_types.up.sql | 0 .../oss/postgres/{99 => 32}/02_worker_controller_tables.up.sql | 0 .../oss/postgres/{99 => 32}/03_worker_authentication.up.sql | 0 .../db/schema/migrations/oss/postgres/{99 => 32}/04_views.up.sql | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename internal/db/schema/migrations/oss/postgres/{99 => 32}/01_domain_types.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{99 => 32}/02_worker_controller_tables.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{99 => 32}/03_worker_authentication.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{99 => 32}/04_views.up.sql (100%) diff --git a/internal/db/schema/migrations/oss/postgres/99/01_domain_types.up.sql b/internal/db/schema/migrations/oss/postgres/32/01_domain_types.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/99/01_domain_types.up.sql rename to internal/db/schema/migrations/oss/postgres/32/01_domain_types.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql b/internal/db/schema/migrations/oss/postgres/32/02_worker_controller_tables.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/99/02_worker_controller_tables.up.sql rename to internal/db/schema/migrations/oss/postgres/32/02_worker_controller_tables.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql b/internal/db/schema/migrations/oss/postgres/32/03_worker_authentication.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/99/03_worker_authentication.up.sql rename to internal/db/schema/migrations/oss/postgres/32/03_worker_authentication.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/99/04_views.up.sql b/internal/db/schema/migrations/oss/postgres/32/04_views.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/99/04_views.up.sql rename to internal/db/schema/migrations/oss/postgres/32/04_views.up.sql From 2ed18da2146925c0026447220e5043aad119f612 Mon Sep 17 00:00:00 2001 From: Todd Date: Wed, 15 Jun 2022 14:02:11 -0700 Subject: [PATCH 100/100] TestPkiWorker always tries to create valid globally scoped root certificates (#2201) --- internal/servers/repository_worker_test.go | 8 -------- internal/servers/testing.go | 15 ++++++++++----- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index 147947339..217a255c8 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -2,7 +2,6 @@ package servers_test import ( "context" - "crypto/rand" "fmt" "strings" "testing" @@ -147,10 +146,6 @@ func TestLookupWorkerIdByKeyId(t *testing.T) { repo, err := servers.NewRepository(rw, rw, kmsCache) require.NoError(t, err) ctx := context.Background() - // Ensures the global scope contains a valid root key - err = kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) - require.NoError(t, err) - var workerKeyId string w := servers.TestPkiWorker(t, conn, wrapper, servers.WithTestPkiWorkerAuthorizedKeyId(&workerKeyId)) t.Run("success", func(t *testing.T) { @@ -260,9 +255,6 @@ func TestUpsertWorkerStatus(t *testing.T) { repo, err := servers.NewRepository(rw, rw, kmsCache) require.NoError(t, err) - err = kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) - require.NoError(t, err) - ctx := context.Background() t.Run("create an initial kms worker and update status", func(t *testing.T) { diff --git a/internal/servers/testing.go b/internal/servers/testing.go index 1e79cad95..8e5a6ee05 100644 --- a/internal/servers/testing.go +++ b/internal/servers/testing.go @@ -2,13 +2,15 @@ package servers import ( "context" - "math/rand" + "crypto/rand" + mathRand "math/rand" "strings" "testing" "time" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers/store" @@ -56,7 +58,7 @@ func TestRootCertificate(ctx context.Context, t *testing.T, conn *db.DB, kmsKey privateKey: populateBytes(defaultLength), } - cert, err := newRootCertificate(ctx, rand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, + cert, err := newRootCertificate(ctx, mathRand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp, rootCertKeys, kmsKey, CurrentState) err = rw.Create(ctx, cert) require.NoError(t, err) @@ -153,8 +155,8 @@ func TestKmsWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...O func TestPkiWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Worker { t.Helper() rw := db.New(conn) - kms := kms.TestKms(t, conn, wrapper) - serversRepo, err := NewRepository(rw, rw, kms) + kmsCache := kms.TestKms(t, conn, wrapper) + serversRepo, err := NewRepository(rw, rw, kmsCache) require.NoError(t, err) ctx := context.Background() opts := getOpts(opt...) @@ -179,7 +181,10 @@ func TestPkiWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...O require.NoError(t, rw.CreateItems(ctx, tags)) } if opts.withTestPkiWorkerAuthorized { - rootStorage, err := NewRepositoryStorage(ctx, rw, rw, kms) + err = kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)) + // We always try to create root global scope keys, but if it already exists, just continue... + require.True(t, err == nil || errors.IsUniqueError(err), err) + rootStorage, err := NewRepositoryStorage(ctx, rw, rw, kmsCache) require.NoError(t, err) _, err = rotation.RotateRootCertificates(ctx, rootStorage) require.NoError(t, err)