Merge remote-tracking branch 'origin/llb-byow'

This commit is contained in:
irenarindos committed 2022-06-15 18:04:08 -04:00
commit be0f07cc65
195 files changed
+19423 -3433

No files matched your search

+2
View File
@@ -24,6 +24,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.
### Bug Fixes
+8
View File
@@ -148,6 +148,10 @@ protobuild:
@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/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
@@ -193,6 +197,10 @@ 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
@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
-1
View File
@@ -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"`
-6
View File
@@ -1,6 +0,0 @@
// Code generated by "make api"; DO NOT EDIT.
package sessions
type WorkerInfo struct {
Address string `json:"address,omitempty"`
}
-8
View File
@@ -1,13 +1,5 @@
package api
import (
"github.com/fatih/structs"
)
func init() {
structs.DefaultTagName = "json"
}
type GenericResult interface {
GetItem() interface{}
GetResponse() *Response
+112
View File
@@ -0,0 +1,112 @@
package workers
import (
"strconv"
"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
withRecursive bool
}
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
}
if opts.withRecursive {
opts.queryMap["recursive"] = strconv.FormatBool(opts.withRecursive)
}
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)
}
}
// 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 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
}
}
+330
View File
@@ -0,0 +1,330 @@
// Code generated by "make api"; DO NOT EDIT.
package workers
import (
"context"
"errors"
"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"`
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
}
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, workerGeneratedAuthToken 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 workerGeneratedAuthToken == "" {
return nil, fmt.Errorf("empty workerGeneratedAuthToken value passed into CreateWorkerLed request")
} else {
opts.postMap["worker_generated_auth_token"] = workerGeneratedAuthToken
}
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...)
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...)
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
}
+82 -70
View File
@@ -1,74 +1,86 @@
package globals
const (
IdField = "id"
VersionField = "version"
NameField = "name"
DescriptionField = "description"
CreatedTimeField = "created_time"
UpdatedTimeField = "updated_time"
TypeField = "type"
AttributesField = "attributes"
ScopeIdField = "scope_id"
ScopeField = "scope"
AuthMethodIdField = "auth_method_id"
AccountIdField = "account_id"
UserIdField = "user_id"
IsPrimaryField = "is_primary"
AuthorizedActionsField = "authorized_actions"
AuthorizedCollectionActionsField = "authorized_collection_actions"
ExpirationTimeField = "expiration_time"
ApproximateLastUsedTimeField = "approximate_last_used_time"
MembersField = "members"
MemberIdsField = "member_ids"
HostCatalogIdField = "host_catalog_id"
HostSetIdsField = "host_set_ids"
HostSourceIdsField = "host_source_ids"
HostIdsField = "host_ids"
PrincipalIdsField = "principal_ids"
PrincipalsField = "principals"
GrantScopeIdField = "grant_scope_id"
GrantsField = "grants"
GrantStringsField = "grant_strings"
PrimaryAuthMethodIdField = "primary_auth_method_id"
TargetIdField = "target_id"
HostIdField = "host_id"
HostSetIdField = "host_set_id"
HostSetsField = "host_sets"
HostSourcesField = "host_sources"
AuthTokenIdField = "auth_token_id"
EndpointField = "endpoint"
CertificateField = "certificate"
TerminationReasonField = "termination_reason"
StatusField = "status"
StatesField = "states"
SessionConnectionLimitField = "session_connection_limit"
SessionMaxSecondsField = "session_max_seconds"
WorkerFilterField = "worker_filter"
AccountIdsField = "account_ids"
AccountsField = "accounts"
LoginNameField = "login_name"
FullNameField = "full_name"
PrimaryAccountIdField = "primary_account_id"
EmailField = "email"
ManagedGroupIdsField = "managed_group_ids"
FilterField = "filter"
CredentialStoreIdField = "credential_store_id"
ApplicationCredentialSourceIdsField = "application_credential_source_ids"
ApplicationCredentialSourcesField = "application_credential_sources"
PreferredEndpointsField = "preferred_endpoints"
SyncIntervalSecondsField = "sync_interval_seconds"
PluginIdField = "plugin_id"
PluginField = "plugin"
PluginNameField = "plugin_name"
IpAddressesField = "ip_addresses"
DnsNamesField = "dns_names"
SecretsHmacField = "secrets_hmac"
ExternalIdField = "external_id"
EgressCredentialSourceIdsField = "egress_credential_source_ids"
EgressCredentialSourcesField = "egress_credential_sources"
ConnectionsField = "connections"
CredentialTypeField = "credential_type"
CredentialMappingOverridesField = "credential_mapping_overrides"
MetricNamespace = "boundary"
IdField = "id"
VersionField = "version"
NameField = "name"
DescriptionField = "description"
CreatedTimeField = "created_time"
UpdatedTimeField = "updated_time"
TypeField = "type"
AttributesField = "attributes"
ScopeIdField = "scope_id"
ScopeField = "scope"
AuthMethodIdField = "auth_method_id"
AccountIdField = "account_id"
UserIdField = "user_id"
IsPrimaryField = "is_primary"
AuthorizedActionsField = "authorized_actions"
AuthorizedCollectionActionsField = "authorized_collection_actions"
ExpirationTimeField = "expiration_time"
ApproximateLastUsedTimeField = "approximate_last_used_time"
MembersField = "members"
MemberIdsField = "member_ids"
HostCatalogIdField = "host_catalog_id"
HostSetIdsField = "host_set_ids"
HostSourceIdsField = "host_source_ids"
HostIdsField = "host_ids"
PrincipalIdsField = "principal_ids"
PrincipalsField = "principals"
GrantScopeIdField = "grant_scope_id"
GrantsField = "grants"
GrantStringsField = "grant_strings"
PrimaryAuthMethodIdField = "primary_auth_method_id"
TargetIdField = "target_id"
HostIdField = "host_id"
HostSetIdField = "host_set_id"
HostSetsField = "host_sets"
HostSourcesField = "host_sources"
AuthTokenIdField = "auth_token_id"
EndpointField = "endpoint"
CertificateField = "certificate"
TerminationReasonField = "termination_reason"
StatusField = "status"
StatesField = "states"
SessionConnectionLimitField = "session_connection_limit"
SessionMaxSecondsField = "session_max_seconds"
WorkerFilterField = "worker_filter"
AccountIdsField = "account_ids"
AccountsField = "accounts"
LoginNameField = "login_name"
FullNameField = "full_name"
PrimaryAccountIdField = "primary_account_id"
EmailField = "email"
ManagedGroupIdsField = "managed_group_ids"
FilterField = "filter"
CredentialStoreIdField = "credential_store_id"
ApplicationCredentialLibraryIdsField = "application_credential_library_ids"
ApplicationCredentialLibrariesField = "application_credential_libraries"
ApplicationCredentialSourceIdsField = "application_credential_source_ids"
ApplicationCredentialSourcesField = "application_credential_sources"
PreferredEndpointsField = "preferred_endpoints"
SyncIntervalSecondsField = "sync_interval_seconds"
PluginIdField = "plugin_id"
PluginField = "plugin"
PluginNameField = "plugin_name"
IpAddressesField = "ip_addresses"
DnsNamesField = "dns_names"
SecretsHmacField = "secrets_hmac"
ExternalIdField = "external_id"
EgressCredentialSourceIdsField = "egress_credential_source_ids"
EgressCredentialSourcesField = "egress_credential_sources"
ConnectionsField = "connections"
CredentialTypeField = "credential_type"
CredentialMappingOverridesField = "credential_mapping_overrides"
MetricNamespace = "boundary"
LastStatusTimeField = "last_status_time"
AddressField = "address"
CanonicalAddressField = "canonical_address"
TagsField = "tags"
CanonicalTagsField = "canonical_tags"
ConfigTagsField = "config_tags"
ConfigurationField = "configuration"
WorkerGeneratedAuthTokenField = "worker_generated_auth_token"
WorkerProvidedConfigurationField = "worker_provided_configuration"
ActiveConnectionCountField = "active_connection_count"
)
+5 -4
View File
@@ -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"
)
+11 -7
View File
@@ -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
@@ -15,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
@@ -40,7 +42,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 +67,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.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
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
@@ -86,16 +90,15 @@ 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/DATA-DOG/go-sqlmock v1.5.0
github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b
github.com/prometheus/client_golang v1.12.1
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
github.com/hashicorp/nodeenrollment v0.1.0
)
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
@@ -170,6 +173,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.8.1 // indirect
github.com/urfave/cli/v2 v2.3.0 // indirect
@@ -187,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
)
+12 -6
View File
@@ -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=
@@ -715,8 +715,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=
@@ -740,6 +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.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=
@@ -1178,6 +1180,8 @@ github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiB
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
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 v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
@@ -1229,8 +1233,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.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=
@@ -1908,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=
+96 -31
View File
@@ -21,6 +21,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"
)
@@ -35,6 +36,12 @@ type structureInfo struct {
fields []fieldInfo
}
type requiredParam struct {
Name string
Typ string
PostType string
}
type fieldInfo struct {
Name string
ProtoName string
@@ -88,9 +95,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
@@ -150,7 +156,7 @@ var inputStructs = []*structInfo{
outFile: "scopes/scope.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -186,7 +192,7 @@ var inputStructs = []*structInfo{
outFile: "users/user.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -214,7 +220,7 @@ var inputStructs = []*structInfo{
outFile: "groups/group.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -252,7 +258,7 @@ var inputStructs = []*structInfo{
outFile: "roles/role.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -294,14 +300,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,
@@ -322,7 +334,7 @@ var inputStructs = []*structInfo{
outFile: "accounts/account.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -350,7 +362,7 @@ var inputStructs = []*structInfo{
outFile: "managedgroups/managedgroups.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -386,15 +398,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,
@@ -425,7 +443,7 @@ var inputStructs = []*structInfo{
outFile: "credentiallibraries/credential_library.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -456,17 +474,23 @@ var inputStructs = []*structInfo{
outFile: "credentials/credential.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
listTemplate,
},
extraRequiredParams: []requiredParam{
{
Name: "resourceType",
Typ: "string",
PostType: "type",
},
},
pluralResourceName: "credentials",
parentTypeName: "credential-store",
versionEnabled: true,
createResponseTypes: true,
typeOnCreate: true,
},
// Host related resources
@@ -475,7 +499,7 @@ var inputStructs = []*structInfo{
outFile: "hostcatalogs/host_catalog.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -496,8 +520,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,
@@ -507,7 +537,7 @@ var inputStructs = []*structInfo{
outFile: "hosts/host.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -528,7 +558,7 @@ var inputStructs = []*structInfo{
outFile: "hostsets/host_set.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -591,7 +621,7 @@ var inputStructs = []*structInfo{
outFile: "targets/target.gen.go",
templates: []*template.Template{
clientTemplate,
createTemplate,
commonCreateTemplate,
readTemplate,
updateTemplate,
deleteTemplate,
@@ -639,8 +669,14 @@ var inputStructs = []*structInfo{
FieldType: "[]string",
},
},
versionEnabled: true,
typeOnCreate: true,
versionEnabled: true,
extraRequiredParams: []requiredParam{
{
Name: "resourceType",
Typ: "string",
PostType: "type",
},
},
createResponseTypes: true,
recursiveListing: true,
},
@@ -648,10 +684,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",
@@ -677,4 +709,37 @@ 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: "workerGeneratedAuthToken",
Typ: "string",
PostType: "worker_generated_auth_token",
},
},
pluralResourceName: "workers",
createResponseTypes: true,
recursiveListing: true,
versionEnabled: true,
},
}
+1 -1
View File
@@ -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", "v1.ConfigTagsEntry":
fi.FieldType = "map[string][]string"
default:
fi.FieldType = sliceText + ptr + name
+25 -17
View File
@@ -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) {
+1
View File
@@ -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.
+27 -18
View File
@@ -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
}
}
+7 -2
View File
@@ -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)
}
+43 -7
View File
@@ -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() {
@@ -82,11 +86,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
@@ -341,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 != "" {
@@ -517,7 +545,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 +557,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, globals.KmsPurposeWorkerAuthStorage:
case globals.KmsPurposeRecovery:
if config.Controller != nil && config.DevRecoveryKey != "" {
kms.Config["key"] = config.DevRecoveryKey
@@ -592,6 +626,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:
+43
View File
@@ -26,6 +26,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"
)
@@ -1071,5 +1072,47 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
Func: "remove-accounts",
}, 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),
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
},
}
}
@@ -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
}
@@ -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
}
@@ -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"
@@ -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
}
@@ -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
}
@@ -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
}
+141 -22
View File
@@ -25,12 +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 (
@@ -84,6 +90,10 @@ type Command struct {
flagEveryEventDenyFilters []string
flagCreateLoopbackHostPlugin bool
flagPluginExecutionDir string
flagUseKmsWorkerAuthMethod bool
flagWorkerAuthStorageDir string
flagWorkerAuthStorageSkipCleanup bool
flagWorkerAuthRotationInterval time.Duration
}
func (c *Command) Synopsis() string {
@@ -327,6 +337,23 @@ 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-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{
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",
@@ -334,6 +361,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
}
@@ -384,6 +417,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")
@@ -449,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"
@@ -540,7 +574,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.flagUseKmsWorkerAuthMethod)); err != nil {
c.UI.Error(err.Error())
return base.CommandUserError
}
@@ -548,14 +582,18 @@ 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.flagUseKmsWorkerAuthMethod {
if c.WorkerAuthKms == nil {
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
}
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
@@ -592,7 +630,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
}
@@ -613,12 +651,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 +676,8 @@ func (c *Command) Run(args []string) int {
}
}
errorEncountered := atomic.NewBool(false)
if !c.flagControllerOnly {
conf := &worker.Config{
RawConfig: c.Config,
@@ -657,6 +691,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 {
@@ -669,6 +707,54 @@ func (c *Command) Run(args []string) int {
}
return base.CommandCliError
}
if !c.flagUseKmsWorkerAuthMethod {
req := c.worker.WorkerAuthRegistrationRequest
if req == "" {
c.UI.Error("No worker auth registration request found at worker start time")
return base.CommandCliError
}
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.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 {
// 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 {
case <-c.Context.Done():
return
case <-time.After(time.Second):
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
}
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 +769,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,11 +790,23 @@ 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)
}
if !c.flagControllerOnly {
if !c.flagWorkerAuthStorageSkipCleanup {
c.worker.WorkerAuthStorage.Cleanup()
}
if err := c.worker.Shutdown(); err != nil {
c.UI.Error(fmt.Errorf("Error shutting down worker: %w", err).Error())
}
@@ -722,15 +820,36 @@ 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]))
}
}
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
}
+72
View File
@@ -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-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()
}
@@ -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
}
+4 -1
View File
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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"
}
`
@@ -91,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,
@@ -131,7 +136,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)
+79 -25
View File
@@ -32,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"
@@ -177,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
@@ -187,9 +190,23 @@ 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.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
}
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.")
return base.CommandUserError
}
}
}
if c.Config.DefaultMaxRequestDuration != 0 {
@@ -267,6 +284,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())
@@ -276,25 +296,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)
@@ -305,17 +325,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
}
}
@@ -329,11 +349,19 @@ 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
}
}
}
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.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())
@@ -449,12 +477,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 {
@@ -473,6 +495,34 @@ func (c *Command) Run(args []string) int {
}
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"] = c.worker.WorkerAuthRegistrationRequest
c.InfoKeys = append(c.InfoKeys, "worker auth current key id")
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 {
// 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
}
}
}
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...)
@@ -520,10 +570,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 && c.Config.Worker.Name == "" {
c.UI.Error("Worker has no name set. It must be the unique name of this instance.")
return base.CommandUserError
}
return base.CommandSuccess
}
@@ -633,6 +679,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 {
@@ -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 = `
const shutdownReloadWorkerProvidedConfiguration = `
disable_mlock = true
worker {
name = "dev-worker"
name = "w_1234567890"
description = "A default worker created in dev mode"
controllers = ["127.0.0.1:9204"]
initial_upstreams = ["%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(shutdownReloadWorkerProvidedConfiguration, testController.ClusterAddrs()[0], key))
workerCodeChan := make(chan int)
go func() {
@@ -125,26 +66,32 @@ 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)
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))
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
@@ -160,14 +107,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
@@ -10,12 +10,19 @@
package server
import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"os"
"path/filepath"
"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 +31,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,75 +38,54 @@ 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:9203"
tls_disable = true
cors_enabled = true
cors_allowed_origins = ["*"]
}
listener "tcp" {
purpose = "cluster"
address = "127.0.0.1:9204"
}
listener "tcp" {
purpose = "proxy"
address = "127.0.0.1:9205"
address = "127.0.0.1:9405"
}
`
const tag1Config = `
worker {
name = "dev-worker"
name = "test"
description = "A default worker created in dev mode"
controllers = ["127.0.0.1:9204"]
initial_upstreams = ["%s"]
tags {
type = ["dev", "local"]
}
auth_storage_path = "%s"
}
`
const tag2Config = `
worker {
name = "dev-worker"
name = "test"
description = "A default worker created in dev mode"
controllers = ["127.0.0.1:9204"]
initial_upstreams = ["%s"]
tags {
foo = ["bar", "baz"]
}
auth_storage_path = "%s"
}
`
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()
authStoragePath, err := os.MkdirTemp("", "")
require.NoError(err)
t.Cleanup(func() { os.RemoveAll(authStoragePath) })
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], filepath.Join(authStoragePath, "tag1")))
wg.Add(1)
go func() {
@@ -138,22 +104,21 @@ 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.ListTagsForServers(cmd.Context, []string{name})
w, err := serversRepo.LookupWorkerByName(testController.Context(), 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.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("dev-worker", "type", []string{"dev", "local"})
fetchWorkerTags("test", "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], filepath.Join(authStoragePath, "tag2")))
cmd.SighupCh <- struct{}{}
select {
@@ -163,9 +128,31 @@ func TestServer_ReloadWorkerTags(t *testing.T) {
}
time.Sleep(10 * time.Second)
fetchWorkerTags("dev-worker", "foo", []string{"bar", "baz"})
fetchWorkerTags("test", "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)
}
@@ -227,19 +227,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:",
@@ -275,17 +262,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)
}
@@ -112,6 +112,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"
@@ -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
}
+192
View File
@@ -0,0 +1,192 @@
package workerscmd
import (
"fmt"
"time"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/workers"
"github.com/hashicorp/boundary/globals"
"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"
}
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.Type != "" {
output = append(output,
fmt.Sprintf(" Type: %s", item.Type),
)
}
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.Address != "" {
output = append(output,
fmt.Sprintf(" Address: %s", item.Address),
)
}
if !item.LastStatusTime.IsZero() {
output = append(output,
fmt.Sprintf(" Last Status Time: %s", item.LastStatusTime.Format(time.RFC1123)),
)
}
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.Type != "" {
nonAttributeMap["Type"] = item.Type
}
if item.Address != "" {
nonAttributeMap["Address"] = item.Address
}
if !item.LastStatusTime.IsZero() {
nonAttributeMap["Last Status Time"] = item.LastStatusTime
}
resultMap := result.GetResponse().Map
if count, ok := resultMap[globals.ActiveConnectionCountField]; ok {
nonAttributeMap["Active Connection Count"] = count
}
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),
)
}
if len(item.CanonicalTags) > 0 || len(item.ConfigTags) > 0 {
ret = append(ret,
"",
" Tags:",
)
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,
" 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)
}
@@ -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 <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
}
@@ -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 }
)
@@ -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 = c.extraHelpFunc(helpMap)
}
// 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 }
)
+1
View File
@@ -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 {
+77 -36
View File
@@ -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"
@@ -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"
@@ -87,9 +94,9 @@ listener "tcp" {
}
worker {
name = "dev-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"]
}
@@ -105,17 +112,21 @@ 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"`
// 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 {
@@ -170,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
@@ -187,6 +202,9 @@ type Worker struct {
//
// TODO: This field is currently internal.
StatusGracePeriodDuration time.Duration `hcl:"-"`
// AuthStoragePath represents the location a worker stores its node credentials, if set
AuthStoragePath string `hcl:"auth_storage_path"`
}
func (w *Worker) InitNameIfEmpty() (string, error) {
@@ -202,9 +220,10 @@ 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
}
*name = strings.ToLower(*name)
}
return nil
}
@@ -230,8 +249,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,
@@ -241,21 +260,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)
@@ -263,13 +282,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)
@@ -277,6 +300,7 @@ func DevCombined() (*Config, error) {
parsed.DevController = true
parsed.DevControllerKey = controllerKey
parsed.DevWorkerAuthKey = workerAuthKey
parsed.DevWorkerAuthStorageKey = workerAuthStorageKey
parsed.DevRecoveryKey = recoveryKey
return parsed, nil
}
@@ -505,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)
}
}
@@ -567,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)
+274 -66
View File
@@ -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)
@@ -180,10 +190,10 @@ func TestDevWorker(t *testing.T) {
},
},
Worker: &Worker{
Name: "dev-worker",
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"},
},
@@ -201,9 +211,9 @@ 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"]
initial_upstreams = ["127.0.0.1"]
tags = ["type=dev", "type=local"]
}
`
@@ -221,9 +231,9 @@ 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"]
initial_upstreams = ["127.0.0.1"]
tags {
type = "local"
}
@@ -246,9 +256,9 @@ 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"]
initial_upstreams = ["127.0.0.1"]
tags = ["tyPe=dev", "type=local"]
}
`
@@ -263,9 +273,9 @@ 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"]
initial_upstreams = ["127.0.0.1"]
tags = ["type=dev", "type=loCal"]
}
`
@@ -282,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"]
}
`
@@ -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",
InitialUpstreams: []string{"127.0.0.1"},
InitialUpstreamsRaw: []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
devWorkerProvidedConfiguration string
storagePath string
}{
{
name: "Relative Storage Directory",
devWorkerProvidedConfiguration: `
listener "tcp" {
purpose = "proxy"
}
worker {
name = "w_1234567890"
description = "A default worker created in dev mode"
initial_upstreams = ["127.0.0.1"]
tags {
type = ["dev", "local"]
}
auth_storage_path = ".."
}
`,
storagePath: "..",
},
{
name: "Nonexistent Storage Directory",
devWorkerProvidedConfiguration: `
listener "tcp" {
purpose = "proxy"
}
worker {
name = "w_1234567890"
description = "A default worker created in dev mode"
initial_upstreams = ["127.0.0.1"]
tags {
type = ["dev", "local"]
}
auth_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.devWorkerProvidedConfiguration)
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 := `
@@ -315,18 +489,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 +564,7 @@ func TestWorkerTags(t *testing.T) {
name: "no tags",
in: `
worker {
name = "testworker"
name = "w_1234567890"
}
`,
expWorkerTags: nil,
@@ -400,7 +574,7 @@ func TestWorkerTags(t *testing.T) {
name: "empty tags",
in: `
worker {
name = "testworker"
name = "w_1234567890"
tags = {}
}
`,
@@ -411,7 +585,7 @@ func TestWorkerTags(t *testing.T) {
name: "empty tags 2",
in: `
worker {
name = "testworker"
name = "w_1234567890"
tags = []
}
`,
@@ -865,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",
},
}
@@ -1000,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)
})
}
}
+32 -14
View File
@@ -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,
@@ -221,7 +218,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,
@@ -247,7 +243,6 @@ var inputStructs = map[string][]*cmdInfo{
ResourceType: resource.Credential.String(),
Pkg: "credentials",
StdActions: []string{"read", "delete", "list"},
IsAbstractType: true,
HasExtraHelpFunc: true,
Container: "CredentialStore",
HasId: true,
@@ -288,7 +283,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,
@@ -332,7 +326,6 @@ var inputStructs = map[string][]*cmdInfo{
StdActions: []string{"read", "delete", "list"},
HasExtraCommandVars: true,
HasExtraHelpFunc: true,
IsAbstractType: true,
Container: "HostCatalog",
HasId: true,
HasName: true,
@@ -374,7 +367,6 @@ var inputStructs = map[string][]*cmdInfo{
Pkg: "hosts",
StdActions: []string{"read", "delete", "list"},
HasExtraHelpFunc: true,
IsAbstractType: true,
Container: "HostCatalog",
HasId: true,
HasName: true,
@@ -400,7 +392,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,
@@ -468,7 +459,6 @@ var inputStructs = map[string][]*cmdInfo{
HasExtraCommandVars: true,
HasExtraHelpFunc: true,
HasExampleCliOutput: true,
IsAbstractType: true,
HasName: true,
HasDescription: true,
Container: "Scope",
@@ -504,4 +494,32 @@ 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"},
HasExtraHelpFunc: true,
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"},
},
},
}
+8 -2
View File
@@ -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...)
@@ -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]
@@ -0,0 +1,113 @@
package handlers
import (
"context"
"errors"
"fmt"
"sync/atomic"
"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"
)
type multihopServiceServer struct {
multihop.UnimplementedMultihopServiceServer
storage nodeenrollment.Storage
direct bool
client *atomic.Value
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"
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 _ 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 {
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)
}
}
// 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 {
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)
}
}
// 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)
}
}
@@ -0,0 +1,507 @@
package handlers
import (
"context"
"errors"
"fmt"
"sync"
"time"
"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"
"github.com/hashicorp/boundary/internal/kms"
"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"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
type workerServiceServer struct {
pbs.UnimplementedServerCoordinationServiceServer
pbs.UnimplementedSessionServiceServer
serversRepoFn common.ServersRepoFactory
sessionRepoFn common.SessionRepoFactory
connectionRepoFn common.ConnectionRepoFactory
updateTimes *sync.Map
kms *kms.Kms
}
func NewWorkerServiceServer(
serversRepoFn common.ServersRepoFactory,
sessionRepoFn common.SessionRepoFactory,
connectionRepoFn common.ConnectionRepoFactory,
updateTimes *sync.Map,
kms *kms.Kms,
) *workerServiceServer {
return &workerServiceServer{
serversRepoFn: serversRepoFn,
sessionRepoFn: sessionRepoFn,
connectionRepoFn: connectionRepoFn,
updateTimes: updateTimes,
kms: kms,
}
}
var (
_ pbs.SessionServiceServer = &workerServiceServer{}
_ pbs.ServerCoordinationServiceServer = &workerServiceServer{}
)
func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusRequest) (*pbs.StatusResponse, error) {
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?
wStat := req.GetWorkerStatus()
if wStat == nil {
return &pbs.StatusResponse{}, status.Error(codes.InvalidArgument, "Worker sent nil status.")
}
switch {
case wStat.GetName() == "" && wStat.GetKeyId() == "":
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.")
}
// 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)
}
// Convert API tags to storage tags
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.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())}
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"))
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 := []*pbs.UpstreamServer{}
for _, c := range controllers {
thisController := &pbs.UpstreamServer{
Address: c.Address,
Type: pbs.UpstreamServer_TYPE_CONTROLLER,
}
responseControllers = append(responseControllers, thisController)
}
ret := &pbs.StatusResponse{
CalculatedUpstreams: responseControllers,
WorkerId: wrk.GetPublicId(),
}
stateReport := make([]session.StateReport, 0, len(req.GetJobs()))
for _, jobStatus := range req.GetJobs() {
switch jobStatus.Job.GetType() {
case pbs.JOBTYPE_JOBTYPE_SESSION:
si := jobStatus.GetJob().GetSessionInfo()
if si == nil {
return nil, status.Error(codes.Internal, "Error getting session info at status time")
}
switch si.Status {
case pbs.SESSIONSTATUS_SESSIONSTATUS_CANCELING,
pbs.SESSIONSTATUS_SESSIONSTATUS_TERMINATED:
// No need to see about canceling anything
continue
}
sr := session.StateReport{
SessionId: si.GetSessionId(),
ConnectionIds: make([]string, 0, len(si.GetConnections())),
}
for _, conn := range si.GetConnections() {
switch conn.Status {
case pbs.CONNECTIONSTATUS_CONNECTIONSTATUS_AUTHORIZED,
pbs.CONNECTIONSTATUS_CONNECTIONSTATUS_CONNECTED:
sr.ConnectionIds = append(sr.ConnectionIds, conn.GetConnectionId())
}
}
stateReport = append(stateReport, sr)
}
}
sessRepo, err := ws.sessionRepoFn()
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)
}
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 with public id %q: %v",
wrk.GetPublicId(), err)
}
for _, na := range notActive {
var connChanges []*pbs.Connection
for _, connId := range na.ConnectionIds {
connChanges = append(connChanges, &pbs.Connection{
ConnectionId: connId,
Status: session.StatusClosed.ProtoVal(),
})
}
ret.JobsRequests = append(ret.JobsRequests, &pbs.JobChangeRequest{
Job: &pbs.Job{
Type: pbs.JOBTYPE_JOBTYPE_SESSION,
JobInfo: &pbs.Job_SessionInfo{
SessionInfo: &pbs.SessionJobInfo{
SessionId: na.SessionId,
Status: na.Status.ProtoVal(),
Connections: connChanges,
},
},
},
RequestType: pbs.CHANGETYPE_CHANGETYPE_UPDATE_STATE,
})
}
return ret, nil
}
func (ws *workerServiceServer) LookupSession(ctx context.Context, req *pbs.LookupSessionRequest) (*pbs.LookupSessionResponse, error) {
const op = "workers.(workerServiceServer).LookupSession"
sessRepo, err := ws.sessionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "Error getting session repo: %v", err)
}
sessionInfo, authzSummary, err := sessRepo.LookupSession(ctx, req.GetSessionId())
if err != nil {
return nil, status.Errorf(codes.Internal, "Error looking up session: %v", err)
}
if sessionInfo == nil {
return nil, status.Error(codes.PermissionDenied, "Unknown session ID.")
}
if len(sessionInfo.States) == 0 {
return nil, status.Error(codes.Internal, "Empty session states during lookup.")
}
if sessionInfo.WorkerFilter != "" {
if req.ServerId == "" {
event.WriteError(ctx, op, errors.New("worker filter enabled for session but got no server ID from worker"))
return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Did not receive server ID when looking up session but filtering is enabled: %v", err)
}
serversRepo, err := ws.serversRepoFn()
if err != nil {
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)
}
var w *servers.Worker
if 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)
}
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.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)
}
}
// Build the map for filtering.
tagMap := w.CanonicalTags()
// Create the evaluator
eval, err := bexpr.CreateEvaluator(sessionInfo.WorkerFilter)
if err != nil {
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)
}
filterInput := map[string]interface{}{
"name": w.GetName(),
"tags": tagMap,
}
ok, err := eval.Evaluate(filterInput)
if err != nil {
return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal,
fmt.Sprintf("Worker filter expression evaluation resulted in error: %s", err))
}
if !ok {
return nil, handlers.ApiErrorWithCodeAndMessage(
codes.FailedPrecondition,
"Worker filter expression precludes this worker from serving this session")
}
}
creds, err := sessRepo.ListSessionCredentials(ctx, sessionInfo.ScopeId, sessionInfo.PublicId)
if err != nil {
return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal,
fmt.Sprintf("Error retrieving session credentials: %s", err))
}
var workerCreds []*pbs.Credential
for _, c := range creds {
m := &pbs.Credential{}
err = proto.Unmarshal(c, m)
if err != nil {
return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal,
fmt.Sprintf("Error unmarshaling credentials: %s", err))
}
workerCreds = append(workerCreds, m)
}
resp := &pbs.LookupSessionResponse{
Authorization: &targets.SessionAuthorizationData{
SessionId: sessionInfo.GetPublicId(),
Certificate: sessionInfo.Certificate,
},
Status: sessionInfo.States[0].Status.ProtoVal(),
Version: sessionInfo.Version,
TofuToken: string(sessionInfo.TofuToken),
Endpoint: sessionInfo.Endpoint,
Expiration: sessionInfo.ExpirationTime.Timestamp,
ConnectionLimit: sessionInfo.ConnectionLimit,
ConnectionsLeft: authzSummary.ConnectionLimit,
HostId: sessionInfo.HostId,
HostSetId: sessionInfo.HostSetId,
TargetId: sessionInfo.TargetId,
UserId: sessionInfo.UserId,
Credentials: workerCreds,
}
if resp.ConnectionsLeft != -1 {
resp.ConnectionsLeft -= int32(authzSummary.CurrentConnectionCount)
}
wrapper, err := ws.kms.GetWrapper(ctx, sessionInfo.ScopeId, kms.KeyPurposeSessions, kms.WithKeyId(sessionInfo.KeyId))
if err != nil {
return nil, status.Errorf(codes.Internal, "Error getting sessions wrapper: %v", err)
}
// Derive the private key, which should match. Deriving on both ends allows
// us to not store it in the DB.
_, resp.Authorization.PrivateKey, err = session.DeriveED25519Key(ctx, wrapper, sessionInfo.UserId, sessionInfo.GetPublicId())
if err != nil {
return nil, status.Errorf(codes.Internal, "Error deriving session key: %v", err)
}
return resp, nil
}
func (ws *workerServiceServer) CancelSession(ctx context.Context, req *pbs.CancelSessionRequest) (*pbs.CancelSessionResponse, error) {
const op = "workers.(workerServiceServer).CancelSession"
sessRepo, err := ws.sessionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
}
ses, _, err := sessRepo.LookupSession(ctx, req.GetSessionId())
if err != nil {
return nil, err
}
ses, err = sessRepo.CancelSession(ctx, req.GetSessionId(), ses.Version)
if err != nil {
return nil, err
}
return &pbs.CancelSessionResponse{
Status: ses.States[0].Status.ProtoVal(),
}, nil
}
func (ws *workerServiceServer) ActivateSession(ctx context.Context, req *pbs.ActivateSessionRequest) (*pbs.ActivateSessionResponse, error) {
const op = "workers.(workerServiceServer).ActivateSession"
sessRepo, err := ws.sessionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
}
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)
}
if sessionInfo == nil {
return nil, status.Error(codes.PermissionDenied, "Unknown session ID.")
}
if len(sessionStates) == 0 {
return nil, status.Error(codes.Internal, "Invalid session state in activate response.")
}
return &pbs.ActivateSessionResponse{
Status: sessionStates[0].Status.ProtoVal(),
}, nil
}
func (ws *workerServiceServer) AuthorizeConnection(ctx context.Context, req *pbs.AuthorizeConnectionRequest) (*pbs.AuthorizeConnectionResponse, error) {
const op = "workers.(workerServiceServer).AuthorizeConnection"
connectionRepo, err := ws.connectionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
}
sessionRepo, err := ws.sessionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
}
serversRepo, err := ws.serversRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting servers repo: %v", err)
}
w, err := serversRepo.LookupWorkerByName(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
}
if connectionInfo == nil {
return nil, status.Error(codes.Internal, "Invalid authorize connection response.")
}
if len(connStates) == 0 {
return nil, status.Error(codes.Internal, "Invalid connection state in authorize response.")
}
ret := &pbs.AuthorizeConnectionResponse{
ConnectionId: connectionInfo.GetPublicId(),
Status: connStates[0].Status.ProtoVal(),
ConnectionsLeft: authzSummary.ConnectionLimit,
}
if ret.ConnectionsLeft != -1 {
ret.ConnectionsLeft -= int32(authzSummary.CurrentConnectionCount)
}
return ret, nil
}
func (ws *workerServiceServer) ConnectConnection(ctx context.Context, req *pbs.ConnectConnectionRequest) (*pbs.ConnectConnectionResponse, error) {
const op = "workers.(workerServiceServer).ConnectConnection"
connRepo, err := ws.connectionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
}
connectionInfo, connStates, err := connRepo.ConnectConnection(ctx, session.ConnectWith{
ConnectionId: req.GetConnectionId(),
ClientTcpAddress: req.GetClientTcpAddress(),
ClientTcpPort: req.GetClientTcpPort(),
EndpointTcpAddress: req.GetEndpointTcpAddress(),
EndpointTcpPort: req.GetEndpointTcpPort(),
UserClientIp: req.GetUserClientIp(),
})
if err != nil {
return nil, err
}
if connectionInfo == nil {
return nil, status.Error(codes.Internal, "Invalid connect connection response.")
}
return &pbs.ConnectConnectionResponse{
Status: connStates[0].Status.ProtoVal(),
}, nil
}
func (ws *workerServiceServer) CloseConnection(ctx context.Context, req *pbs.CloseConnectionRequest) (*pbs.CloseConnectionResponse, error) {
const op = "workers.(workerServiceServer).CloseConnection"
numCloses := len(req.GetCloseRequestData())
if numCloses == 0 {
return &pbs.CloseConnectionResponse{}, nil
}
closeWiths := make([]session.CloseWith, 0, numCloses)
closeIds := make([]string, 0, numCloses)
for _, v := range req.GetCloseRequestData() {
closeIds = append(closeIds, v.GetConnectionId())
closeWiths = append(closeWiths, session.CloseWith{
ConnectionId: v.GetConnectionId(),
BytesUp: v.GetBytesUp(),
BytesDown: v.GetBytesDown(),
ClosedReason: session.ClosedReason(v.GetReason()),
})
}
connRepo, err := ws.connectionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting connection repo: %v", err)
}
sessRepo, err := ws.sessionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
}
closeInfos, err := session.CloseConnections(ctx, sessRepo, connRepo, closeWiths)
if err != nil {
return nil, err
}
if closeInfos == nil {
return nil, status.Error(codes.Internal, "Invalid close connection response.")
}
closeData := make([]*pbs.CloseConnectionResponseData, 0, numCloses)
for _, v := range closeInfos {
if v.Connection == nil {
return nil, status.Errorf(codes.Internal, "No connection found while closing one of the connection IDs: %v", closeIds)
}
if len(v.ConnectionStates) == 0 {
return nil, status.Errorf(codes.Internal, "No connection states found while closing one of the connection IDs: %v", closeIds)
}
closeData = append(closeData, &pbs.CloseConnectionResponseData{
ConnectionId: v.Connection.GetPublicId(),
Status: v.ConnectionStates[0].Status.ProtoVal(),
})
}
ret := &pbs.CloseConnectionResponse{
CloseResponseData: closeData,
}
return ret, nil
}
@@ -1,4 +1,4 @@
package workers_test
package handlers_test
import (
"context"
@@ -8,18 +8,26 @@ 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"
"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/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"
"google.golang.org/grpc/status"
)
func TestStatus(t *testing.T) {
@@ -31,17 +39,10 @@ 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{
PrivateId: "test_worker1",
Type: "worker",
Address: "127.0.0.1",
})
serversRepoFn := func() (*servers.Repository, error) {
return serverRepo, nil
}
@@ -70,7 +71,7 @@ func TestStatus(t *testing.T) {
target.WithSessionConnectionLimit(-1),
)
worker1 := session.TestWorker(t, conn, wrapper)
worker1 := servers.TestKmsWorker(t, conn, wrapper)
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
UserId: uId,
@@ -83,14 +84,14 @@ 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, tofu)
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.PrivateId)
connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PublicId)
require.NoError(t, err)
cases := []struct {
@@ -104,23 +105,31 @@ func TestStatus(t *testing.T) {
name: "No Sessions",
wantErr: false,
req: &pbs.StatusRequest{
Worker: worker1,
WorkerStatus: &servers.ServerWorkerStatus{
PublicId: worker1.GetPublicId(),
Name: worker1.GetName(),
Address: worker1.GetAddress(),
},
},
want: &pbs.StatusResponse{
Controllers: []*servers.Server{
CalculatedUpstreams: []*pbs.UpstreamServer{
{
PrivateId: "test_controller1",
Type: "controller",
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: worker1,
WorkerStatus: &servers.ServerWorkerStatus{
PublicId: worker1.GetPublicId(),
Name: worker1.GetName(),
Address: worker1.GetAddress(),
},
Jobs: []*pbs.JobStatus{
{
Job: &pbs.Job{
@@ -142,15 +151,37 @@ func TestStatus(t *testing.T) {
},
},
want: &pbs.StatusResponse{
Controllers: []*servers.Server{
CalculatedUpstreams: []*pbs.UpstreamServer{
{
PrivateId: "test_controller1",
Type: "controller",
Address: "127.0.0.1",
Type: pbs.UpstreamServer_TYPE_CONTROLLER,
Address: "127.0.0.1",
},
},
WorkerId: worker1.PublicId,
},
},
{
name: "No Name or keyId",
wantErr: true,
req: &pbs.StatusRequest{
WorkerStatus: &servers.ServerWorkerStatus{
PublicId: worker1.GetPublicId(),
Address: worker1.GetAddress(),
},
},
wantErrMsg: status.Error(codes.InvalidArgument, "Name and keyId are not set in the request; one is required.").Error(),
},
{
name: "No Address",
wantErr: true,
req: &pbs.StatusRequest{
WorkerStatus: &servers.ServerWorkerStatus{
PublicId: worker1.GetPublicId(),
Name: worker1.GetName(),
},
},
wantErrMsg: status.Error(codes.InvalidArgument, "Address is not set but is required.").Error(),
},
}
for _, tc := range cases {
@@ -160,7 +191,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
}
@@ -170,14 +201,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"),
),
)
})
@@ -193,17 +225,10 @@ 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{
PrivateId: "test_worker1",
Type: "worker",
Address: "127.0.0.1",
})
serversRepoFn := func() (*servers.Repository, error) {
return serverRepo, nil
}
@@ -232,7 +257,7 @@ func TestStatusSessionClosed(t *testing.T) {
target.WithSessionConnectionLimit(-1),
)
worker1 := session.TestWorker(t, conn, wrapper)
worker1 := servers.TestKmsWorker(t, conn, wrapper)
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
UserId: uId,
@@ -245,7 +270,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, tofu)
require.NoError(t, err)
sess2 := session.TestSession(t, conn, wrapper, session.ComposedOf{
UserId: uId,
@@ -258,13 +283,13 @@ 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, 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.PrivateId)
connection, _, err := connRepo.AuthorizeConnection(ctx, sess.PublicId, worker1.PublicId)
require.NoError(t, err)
cases := []struct {
@@ -283,7 +308,11 @@ func TestStatusSessionClosed(t *testing.T) {
require.NoError(t, err)
},
req: &pbs.StatusRequest{
Worker: worker1,
WorkerStatus: &servers.ServerWorkerStatus{
PublicId: worker1.GetPublicId(),
Name: worker1.GetName(),
Address: worker1.GetAddress(),
},
Jobs: []*pbs.JobStatus{
{
Job: &pbs.Job{
@@ -305,11 +334,10 @@ func TestStatusSessionClosed(t *testing.T) {
},
},
want: &pbs.StatusResponse{
Controllers: []*servers.Server{
CalculatedUpstreams: []*pbs.UpstreamServer{
{
PrivateId: "test_controller1",
Type: "controller",
Address: "127.0.0.1",
Type: pbs.UpstreamServer_TYPE_CONTROLLER,
Address: "127.0.0.1",
},
},
JobsRequests: []*pbs.JobChangeRequest{
@@ -326,6 +354,7 @@ func TestStatusSessionClosed(t *testing.T) {
RequestType: pbs.CHANGETYPE_CHANGETYPE_UPDATE_STATE,
},
},
WorkerId: worker1.PublicId,
},
},
}
@@ -350,14 +379,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"),
),
)
})
@@ -373,17 +403,13 @@ 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{
PrivateId: "test_worker1",
Type: "worker",
Address: "127.0.0.1",
})
worker1 := servers.TestKmsWorker(t, conn, wrapper)
serversRepoFn := func() (*servers.Repository, error) {
return serverRepo, nil
}
@@ -412,8 +438,6 @@ func TestStatusDeadConnection(t *testing.T) {
target.WithSessionConnectionLimit(-1),
)
worker1 := session.TestWorker(t, conn, wrapper)
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
UserId: uId,
HostId: h.GetPublicId(),
@@ -425,7 +449,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, tofu)
require.NoError(t, err)
sess2 := session.TestSession(t, conn, wrapper, session.ComposedOf{
UserId: uId,
@@ -438,20 +462,24 @@ 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, 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.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: worker1,
WorkerStatus: &servers.ServerWorkerStatus{
PublicId: worker1.GetPublicId(),
Name: worker1.GetName(),
Address: worker1.GetAddress(),
},
Jobs: []*pbs.JobStatus{
{
Job: &pbs.Job{
@@ -473,13 +501,13 @@ func TestStatusDeadConnection(t *testing.T) {
},
}
want := &pbs.StatusResponse{
Controllers: []*servers.Server{
CalculatedUpstreams: []*pbs.UpstreamServer{
{
PrivateId: "test_controller1",
Type: "controller",
Address: "127.0.0.1",
Type: pbs.UpstreamServer_TYPE_CONTROLLER,
Address: "127.0.0.1",
},
},
WorkerId: worker1.PublicId,
}
got, err := s.Status(ctx, req)
@@ -489,14 +517,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"),
),
)
@@ -507,3 +536,189 @@ 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.TestPkiWorker(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{
Address: "someaddress",
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,
Address: "someaddress",
},
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
}
require.NoError(err)
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"),
),
)
})
}
}
@@ -0,0 +1,176 @@
package handlers_test
import (
"context"
"sync"
"testing"
"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/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"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/servers"
"github.com/hashicorp/boundary/internal/session"
"github.com/hashicorp/boundary/internal/target"
"github.com/hashicorp/boundary/internal/target/tcp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
func TestLookupSession(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))
serversRepoFn := func() (*servers.Repository, error) {
return servers.NewRepository(rw, rw, kms)
}
sessionRepoFn := func() (*session.Repository, error) {
return session.NewRepository(rw, rw, kms)
}
connectionRepoFn := func() (*session.ConnectionRepository, error) {
return session.NewConnectionRepository(ctx, rw, rw, kms)
}
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()}))
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",
})
egressSess := 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",
})
repo, err := sessionRepoFn()
require.NoError(t, err)
creds := []*pbs.Credential{
{
Credential: &pbs.Credential_UserPassword{
UserPassword: &pbs.UserPassword{
Username: "username",
Password: "password",
},
},
},
{
Credential: &pbs.Credential_UserPassword{
UserPassword: &pbs.UserPassword{
Username: "another-username",
Password: "a different password",
},
},
},
}
workerCreds := make([]session.Credential, 0, len(creds))
for _, c := range creds {
data, err := proto.Marshal(c)
require.NoError(t, err)
workerCreds = append(workerCreds, data)
}
err = repo.AddSessionCredentials(ctx, egressSess.ScopeId, egressSess.GetPublicId(), workerCreds)
require.NoError(t, err)
s := handlers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, new(sync.Map), kms)
require.NotNil(t, s)
cases := []struct {
name string
wantErr bool
wantErrMsg string
want *pbs.LookupSessionResponse
sessionId string
}{
{
name: "Invalid session id",
sessionId: "s_fakesession",
wantErr: true,
wantErrMsg: "rpc error: code = PermissionDenied desc = Unknown session ID.",
},
{
name: "Valid",
sessionId: sess.PublicId,
want: &pbs.LookupSessionResponse{
ConnectionLimit: 1,
ConnectionsLeft: 1,
Version: 1,
Endpoint: sess.Endpoint,
HostId: sess.HostId,
HostSetId: sess.HostSetId,
TargetId: sess.TargetId,
UserId: sess.UserId,
Status: pbs.SESSIONSTATUS_SESSIONSTATUS_PENDING,
},
},
{
name: "Valid-with-egress-creds",
sessionId: egressSess.PublicId,
want: &pbs.LookupSessionResponse{
ConnectionLimit: 1,
ConnectionsLeft: 1,
Version: 1,
Endpoint: egressSess.Endpoint,
HostId: egressSess.HostId,
HostSetId: egressSess.HostSetId,
TargetId: egressSess.TargetId,
UserId: egressSess.UserId,
Status: pbs.SESSIONSTATUS_SESSIONSTATUS_PENDING,
Credentials: creds,
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
req := &pbs.LookupSessionRequest{
SessionId: tc.sessionId,
}
got, err := s.LookupSession(ctx, req)
if tc.wantErr {
require.Error(err)
assert.Nil(got)
assert.Equal(tc.wantErrMsg, err.Error())
return
}
assert.Empty(
cmp.Diff(
tc.want,
got,
cmpopts.IgnoreUnexported(pbs.LookupSessionResponse{}, pbs.Credential{}, pbs.UserPassword{}),
cmpopts.IgnoreFields(pbs.LookupSessionResponse{}, "Expiration", "Authorization"),
),
)
})
}
}
+14 -13
View File
@@ -15,17 +15,18 @@ import (
)
type (
AuthTokenRepoFactory = oidc.AuthTokenRepoFactory
VaultCredentialRepoFactory = func() (*vault.Repository, error)
StaticCredentialRepoFactory = func() (*credstatic.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)
StaticCredentialRepoFactory = func() (*credstatic.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)
)
+27 -16
View File
@@ -28,6 +28,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/session"
"github.com/hashicorp/boundary/internal/target"
"github.com/hashicorp/boundary/internal/types/scope"
@@ -65,19 +66,20 @@ type Controller struct {
apiGrpcGatewayTicket string
// Repo factory methods
AuthTokenRepoFn common.AuthTokenRepoFactory
VaultCredentialRepoFn common.VaultCredentialRepoFactory
StaticCredentialRepoFn common.StaticCredentialRepoFactory
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
StaticCredentialRepoFn common.StaticCredentialRepoFactory
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
@@ -299,6 +301,10 @@ 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
}
@@ -308,7 +314,9 @@ func (c *Controller) Start() error {
event.WriteSysEvent(context.TODO(), op, "already started, skipping")
return nil
}
c.baseContext, c.baseCancel = context.WithCancel(context.Background())
if err := c.registerJobs(); err != nil {
return fmt.Errorf("error registering jobs: %w", err)
}
@@ -316,9 +324,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)
@@ -360,6 +368,9 @@ func (c *Controller) registerJobs() error {
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
}
return nil
}
+11
View File
@@ -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 := currentServices[services.CredentialService_ServiceDesc.ServiceName]; !ok {
c, err := credentials.NewService(c.StaticCredentialRepoFn, c.IamRepoFn)
if err != nil {
@@ -288,6 +296,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)
}
if err := services.RegisterCredentialServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil {
return fmt.Errorf("failed to register credential service handler: %w", err)
}
@@ -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(): {
@@ -95,6 +95,12 @@ var globalAuthorizedCollectionActions = map[string]*structpb.ListValue{
structpb.NewStringValue("list"),
},
},
"workers": {
Values: []*structpb.Value{
structpb.NewStringValue("create:worker-led"),
structpb.NewStringValue("list"),
},
},
}
var orgAuthorizedCollectionActions = map[string]*structpb.ListValue{
@@ -799,77 +799,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.ListServers(ctx, servers.ServerTypeWorker)
selectedWorkers, err := serversRepo.ListWorkers(ctx, []string{scope.Global.String()})
if err != nil {
return nil, err
}
for _, v := range servers {
if hasWorkerFilter {
workerIds = append(workerIds, v.GetPrivateId())
}
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
tags, err := serversRepo.ListTagsForServers(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 _, tag := range tags {
currWorkerMap := tagMap[tag.ServerId]
if currWorkerMap == nil {
currWorkerMap = make(map[string][]string)
tagMap[tag.ServerId] = currWorkerMap
}
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()
@@ -992,7 +942,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
}
@@ -1091,7 +1041,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)
@@ -1966,3 +1916,46 @@ func validateAuthorizeSessionRequest(req *pbs.AuthorizeSessionRequest) error {
}
return nil
}
// 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.GetAddress())
}
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.GetAddress()})
}
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 {
filterInput := map[string]interface{}{
"name": worker.GetName(),
"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
}
@@ -0,0 +1,79 @@
package targets
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"
"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) {
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.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.TestPkiWorker(t, conn, wrapper,
servers.WithName(fmt.Sprintf("test_worker_%d", i)),
servers.WithWorkerTags(&servers.Tag{
Key: "key",
Value: "configvalue",
})))
}
}
{
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 "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(`"configvalue" in "/tags/key"`)
require.NoError(t, err)
got, err := workerList(workers).filtered(ev)
require.NoError(t, err)
assert.Len(t, got, 2)
}
}
@@ -16,12 +16,12 @@ import (
"github.com/hashicorp/boundary/internal/credential"
credstatic "github.com/hashicorp/boundary/internal/credential/static"
"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/credentials"
"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"
@@ -2371,9 +2371,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)
}
staticRepo, err := static.NewRepository(rw, rw, kms)
require.NoError(t, err)
staticHostRepoFn := func() (*static.Repository, error) {
@@ -2532,14 +2529,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)
_, err = workerService.Status(ctx, &spbs.StatusRequest{
Worker: &spb.Server{
PrivateId: "testworker",
Address: "localhost:8457",
},
})
require.NoError(t, err)
servers.TestKmsWorker(t, conn, wrapper)
asRes1, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
Id: tar.GetPublicId(),
@@ -2641,9 +2631,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)
}
@@ -2887,14 +2874,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)
_, err = workerService.Status(ctx, &spbs.StatusRequest{
Worker: &spb.Server{
PrivateId: "testworker",
Address: "localhost:8457",
},
})
require.NoError(t, err)
servers.TestKmsWorker(t, conn, wrapper)
asRes, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
Id: tar.GetPublicId(),
@@ -3002,11 +2982,12 @@ 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: "testworker",
Address: "localhost:123",
WorkerStatus: &spb.ServerWorkerStatus{
PublicId: "w_1234567890",
Name: "w_1234567890",
Address: "localhost:123",
},
})
require.NoError(t, err)
@@ -2,432 +2,616 @@ package workers
import (
"context"
"errors"
"fmt"
"sync"
"time"
"strings"
"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"
pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/observability/event"
"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/session"
"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/sdk/pbs/controller/api/resources/targets"
"github.com/hashicorp/go-bexpr"
"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"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
type workerServiceServer struct {
pbs.UnimplementedServerCoordinationServiceServer
pbs.UnimplementedSessionServiceServer
serversRepoFn common.ServersRepoFactory
sessionRepoFn common.SessionRepoFactory
connectionRepoFn common.ConnectionRepoFactory
updateTimes *sync.Map
kms *kms.Kms
}
func NewWorkerServiceServer(
serversRepoFn common.ServersRepoFactory,
sessionRepoFn common.SessionRepoFactory,
connectionRepoFn common.ConnectionRepoFactory,
updateTimes *sync.Map,
kms *kms.Kms,
) *workerServiceServer {
return &workerServiceServer{
serversRepoFn: serversRepoFn,
sessionRepoFn: sessionRepoFn,
connectionRepoFn: connectionRepoFn,
updateTimes: updateTimes,
kms: kms,
}
}
const (
PkiWorkerType = "pki"
KmsWorkerType = "kms"
)
var (
_ pbs.SessionServiceServer = &workerServiceServer{}
_ pbs.ServerCoordinationServiceServer = &workerServiceServer{}
maskManager handlers.MaskManager
// 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.CreateWorkerLed,
action.List,
}
)
func (ws *workerServiceServer) Status(ctx context.Context, req *pbs.StatusRequest) (*pbs.StatusResponse, error) {
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())
serverRepo, err := ws.serversRepoFn()
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
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 {
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)
return nil, err
}
sessRepo, err := ws.sessionRepoFn()
connectionRepo, err := ws.connectionRepoFn()
// 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 {
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)
return nil, err
}
req.Worker.Type = resource.Worker.String()
controllers, _, err := serverRepo.UpsertServer(ctx, req.Worker, servers.WithUpdateTags(req.GetUpdateTags()))
if len(ul) == 0 {
return &pbs.ListWorkersResponse{}, nil
}
filter, err := handlers.NewFilter(req.GetFilter())
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)
return nil, err
}
ret := &pbs.StatusResponse{
Controllers: controllers,
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
}
// GetWorker 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
}
stateReport := make([]session.StateReport, 0, len(req.GetJobs()))
outputFields, ok := requests.OutputFields(ctx)
if !ok {
return nil, errors.New(ctx, errors.Internal, op, "no request context found")
}
for _, jobStatus := range req.GetJobs() {
switch jobStatus.Job.GetType() {
case pbs.JOBTYPE_JOBTYPE_SESSION:
si := jobStatus.GetJob().GetSessionInfo()
if si == nil {
return nil, status.Error(codes.Internal, "Error getting session info at status time")
}
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()))
}
switch si.Status {
case pbs.SESSIONSTATUS_SESSIONSTATUS_CANCELING,
pbs.SESSIONSTATUS_SESSIONSTATUS_TERMINATED:
// No need to see about canceling anything
continue
}
item, err := toProto(ctx, w, outputOpts...)
if err != nil {
return nil, err
}
sr := session.StateReport{
SessionId: si.GetSessionId(),
ConnectionIds: make([]string, 0, len(si.GetConnections())),
}
for _, conn := range si.GetConnections() {
switch conn.Status {
case pbs.CONNECTIONSTATUS_CONNECTIONSTATUS_AUTHORIZED,
pbs.CONNECTIONSTATUS_CONNECTIONSTATUS_CONNECTED:
sr.ConnectionIds = append(sr.ConnectionIds, conn.GetConnectionId())
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().WorkerGeneratedAuthToken.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 {
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
}
// 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 {
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) 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()),
)
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()
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) 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()))
}
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()
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.CreateWorkerLed:
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 in.Type == KmsWorkerType && out.AuthorizedActions != nil {
// KMS workers cannot be updated through the API
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)
}
}
stateReport = append(stateReport, sr)
}
}
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()
}
if outputFields.Has(globals.ActiveConnectionCountField) {
out.ActiveConnectionCount = &wrapperspb.UInt32Value{Value: 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())
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing canonical tags proto"))
}
}
notActive, err := session.WorkerStatusReport(ctx, sessRepo, connectionRepo, req.Worker.PrivateId, stateReport)
if err != nil {
return nil, status.Errorf(codes.Internal, "Error comparing state of sessions for worker: %s: %v", req.Worker.PrivateId, err)
}
for _, na := range notActive {
var connChanges []*pbs.Connection
for _, connId := range na.ConnectionIds {
connChanges = append(connChanges, &pbs.Connection{
ConnectionId: connId,
Status: session.StatusClosed.ProtoVal(),
})
}
ret.JobsRequests = append(ret.JobsRequests, &pbs.JobChangeRequest{
Job: &pbs.Job{
Type: pbs.JOBTYPE_JOBTYPE_SESSION,
JobInfo: &pbs.Job_SessionInfo{
SessionInfo: &pbs.SessionJobInfo{
SessionId: na.SessionId,
Status: na.Status.ProtoVal(),
Connections: connChanges,
},
},
},
RequestType: pbs.CHANGETYPE_CHANGETYPE_UPDATE_STATE,
})
}
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
}
func (ws *workerServiceServer) LookupSession(ctx context.Context, req *pbs.LookupSessionRequest) (*pbs.LookupSessionResponse, error) {
const op = "workers.(workerServiceServer).LookupSession"
sessRepo, err := ws.sessionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "Error getting session repo: %v", err)
}
sessionInfo, authzSummary, err := sessRepo.LookupSession(ctx, req.GetSessionId())
if err != nil {
return nil, status.Errorf(codes.Internal, "Error looking up session: %v", err)
}
if sessionInfo == nil {
return nil, status.Error(codes.PermissionDenied, "Unknown session ID.")
}
if len(sessionInfo.States) == 0 {
return nil, status.Error(codes.Internal, "Empty session states during lookup.")
}
if sessionInfo.WorkerFilter != "" {
if req.ServerId == "" {
event.WriteError(ctx, op, errors.New("worker filter enabled for session but got no server ID from worker"))
return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal, "Did not receive server ID when looking up session but filtering is enabled: %v", err)
}
serversRepo, err := ws.serversRepoFn()
if err != nil {
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})
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
}
// Create the evaluator
eval, err := bexpr.CreateEvaluator(sessionInfo.WorkerFilter)
if err != nil {
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)
}
filterInput := map[string]interface{}{
"name": req.ServerId,
"tags": tagMap,
}
ok, err := eval.Evaluate(filterInput)
if err != nil {
return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal,
fmt.Sprintf("Worker filter expression evaluation resulted in error: %s", err))
}
if !ok {
return nil, handlers.ApiErrorWithCodeAndMessage(
codes.FailedPrecondition,
"Worker filter expression precludes this worker from serving this session")
}
}
creds, err := sessRepo.ListSessionCredentials(ctx, sessionInfo.ScopeId, sessionInfo.PublicId)
if err != nil {
return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal,
fmt.Sprintf("Error retrieving session credentials: %s", err))
}
var workerCreds []*pbs.Credential
for _, c := range creds {
m := &pbs.Credential{}
err = proto.Unmarshal(c, m)
if err != nil {
return &pbs.LookupSessionResponse{}, status.Errorf(codes.Internal,
fmt.Sprintf("Error unmarshaling credentials: %s", err))
}
workerCreds = append(workerCreds, m)
}
resp := &pbs.LookupSessionResponse{
Authorization: &targets.SessionAuthorizationData{
SessionId: sessionInfo.GetPublicId(),
Certificate: sessionInfo.Certificate,
},
Status: sessionInfo.States[0].Status.ProtoVal(),
Version: sessionInfo.Version,
TofuToken: string(sessionInfo.TofuToken),
Endpoint: sessionInfo.Endpoint,
Expiration: sessionInfo.ExpirationTime.Timestamp,
ConnectionLimit: sessionInfo.ConnectionLimit,
ConnectionsLeft: authzSummary.ConnectionLimit,
HostId: sessionInfo.HostId,
HostSetId: sessionInfo.HostSetId,
TargetId: sessionInfo.TargetId,
UserId: sessionInfo.UserId,
Credentials: workerCreds,
}
if resp.ConnectionsLeft != -1 {
resp.ConnectionsLeft -= int32(authzSummary.CurrentConnectionCount)
}
wrapper, err := ws.kms.GetWrapper(ctx, sessionInfo.ScopeId, kms.KeyPurposeSessions, kms.WithKeyId(sessionInfo.KeyId))
if err != nil {
return nil, status.Errorf(codes.Internal, "Error getting sessions wrapper: %v", err)
}
// Derive the private key, which should match. Deriving on both ends allows
// us to not store it in the DB.
_, resp.Authorization.PrivateKey, err = session.DeriveED25519Key(ctx, wrapper, sessionInfo.UserId, sessionInfo.GetPublicId())
if err != nil {
return nil, status.Errorf(codes.Internal, "Error deriving session key: %v", err)
}
return resp, 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 (ws *workerServiceServer) CancelSession(ctx context.Context, req *pbs.CancelSessionRequest) (*pbs.CancelSessionResponse, error) {
const op = "workers.(workerServiceServer).CancelSession"
sessRepo, err := ws.sessionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
func validateListRequest(req *pbs.ListWorkersRequest) error {
badFields := map[string]string{}
if req.GetScopeId() != scope.Global.String() {
badFields["scope_id"] = "Must be 'global' when listing."
}
ses, _, err := sessRepo.LookupSession(ctx, req.GetSessionId())
if err != nil {
return nil, err
if _, err := handlers.NewFilter(req.GetFilter()); err != nil {
badFields["filter"] = fmt.Sprintf("This field could not be parsed. %v", err)
}
ses, err = sessRepo.CancelSession(ctx, req.GetSessionId(), ses.Version)
if err != nil {
return nil, err
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Error in provided request.", badFields)
}
return &pbs.CancelSessionResponse{
Status: ses.States[0].Status.ProtoVal(),
}, nil
return nil
}
func (ws *workerServiceServer) ActivateSession(ctx context.Context, req *pbs.ActivateSessionRequest) (*pbs.ActivateSessionResponse, error) {
const op = "workers.(workerServiceServer).ActivateSession"
sessRepo, err := ws.sessionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
}
sessionInfo, sessionStates, err := sessRepo.ActivateSession(
ctx,
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)
}
if sessionInfo == nil {
return nil, status.Error(codes.PermissionDenied, "Unknown session ID.")
}
if len(sessionStates) == 0 {
return nil, status.Error(codes.Internal, "Invalid session state in activate response.")
}
return &pbs.ActivateSessionResponse{
Status: sessionStates[0].Status.ProtoVal(),
}, nil
func validateDeleteRequest(req *pbs.DeleteWorkerRequest) error {
return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, servers.WorkerPrefix)
}
func (ws *workerServiceServer) AuthorizeConnection(ctx context.Context, req *pbs.AuthorizeConnectionRequest) (*pbs.AuthorizeConnectionResponse, error) {
const op = "workers.(workerServiceServer).AuthorizeConnection"
connectionRepo, err := ws.connectionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
}
sessionRepo, err := ws.sessionRepoFn()
if err != nil {
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())
if err != nil {
return nil, err
}
if connectionInfo == nil {
return nil, status.Error(codes.Internal, "Invalid authorize connection response.")
}
if len(connStates) == 0 {
return nil, status.Error(codes.Internal, "Invalid connection state in authorize response.")
}
ret := &pbs.AuthorizeConnectionResponse{
ConnectionId: connectionInfo.GetPublicId(),
Status: connStates[0].Status.ProtoVal(),
ConnectionsLeft: authzSummary.ConnectionLimit,
}
if ret.ConnectionsLeft != -1 {
ret.ConnectionsLeft -= int32(authzSummary.CurrentConnectionCount)
}
return ret, nil
func validateUpdateRequest(req *pbs.UpdateWorkerRequest) error {
return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
badFields := map[string]string{}
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().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)
}
func (ws *workerServiceServer) ConnectConnection(ctx context.Context, req *pbs.ConnectConnectionRequest) (*pbs.ConnectConnectionResponse, error) {
const op = "workers.(workerServiceServer).ConnectConnection"
connRepo, err := ws.connectionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
}
connectionInfo, connStates, err := connRepo.ConnectConnection(ctx, session.ConnectWith{
ConnectionId: req.GetConnectionId(),
ClientTcpAddress: req.GetClientTcpAddress(),
ClientTcpPort: req.GetClientTcpPort(),
EndpointTcpAddress: req.GetEndpointTcpAddress(),
EndpointTcpPort: req.GetEndpointTcpPort(),
UserClientIp: req.GetUserClientIp(),
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().WorkerGeneratedAuthToken == nil {
badFields[globals.WorkerGeneratedAuthTokenField] = cannotBeEmptyMsg
}
if req.GetItem().Address != "" {
badFields[globals.CanonicalAddressField] = readOnlyFieldMsg
}
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
}
if req.GetItem().AuthorizedActions != nil {
badFields[globals.AuthorizedActionsField] = readOnlyFieldMsg
}
nameString := req.GetItem().GetName().String()
if !strutil.Printable(nameString) {
badFields[globals.NameField] = "Name contains non-printable characters."
}
if strings.ToLower(nameString) != nameString {
badFields[globals.NameField] = "Name must be all lowercase."
}
descriptionString := req.GetItem().GetDescription().String()
if !strutil.Printable(descriptionString) {
badFields[globals.DescriptionField] = "Description contains non-printable characters."
}
return badFields
})
if err != nil {
return nil, err
}
if connectionInfo == nil {
return nil, status.Error(codes.Internal, "Invalid connect connection response.")
}
return &pbs.ConnectConnectionResponse{
Status: connStates[0].Status.ProtoVal(),
}, nil
}
func (ws *workerServiceServer) CloseConnection(ctx context.Context, req *pbs.CloseConnectionRequest) (*pbs.CloseConnectionResponse, error) {
const op = "workers.(workerServiceServer).CloseConnection"
numCloses := len(req.GetCloseRequestData())
if numCloses == 0 {
return &pbs.CloseConnectionResponse{}, nil
}
closeWiths := make([]session.CloseWith, 0, numCloses)
closeIds := make([]string, 0, numCloses)
for _, v := range req.GetCloseRequestData() {
closeIds = append(closeIds, v.GetConnectionId())
closeWiths = append(closeWiths, session.CloseWith{
ConnectionId: v.GetConnectionId(),
BytesUp: v.GetBytesUp(),
BytesDown: v.GetBytesDown(),
ClosedReason: session.ClosedReason(v.GetReason()),
})
}
connRepo, err := ws.connectionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting connection repo: %v", err)
}
sessRepo, err := ws.sessionRepoFn()
if err != nil {
return nil, status.Errorf(codes.Internal, "error getting session repo: %v", err)
}
closeInfos, err := session.CloseConnections(ctx, sessRepo, connRepo, closeWiths)
if err != nil {
return nil, err
}
if closeInfos == nil {
return nil, status.Error(codes.Internal, "Invalid close connection response.")
}
closeData := make([]*pbs.CloseConnectionResponseData, 0, numCloses)
for _, v := range closeInfos {
if v.Connection == nil {
return nil, status.Errorf(codes.Internal, "No connection found while closing one of the connection IDs: %v", closeIds)
}
if len(v.ConnectionStates) == 0 {
return nil, status.Errorf(codes.Internal, "No connection states found while closing one of the connection IDs: %v", closeIds)
}
closeData = append(closeData, &pbs.CloseConnectionResponseData{
ConnectionId: v.Connection.GetPublicId(),
Status: v.ConnectionStates[0].Status.ProtoVal(),
})
}
ret := &pbs.CloseConnectionResponse{
CloseResponseData: closeData,
}
return ret, nil
}
File diff suppressed because it is too large. Load diff
@@ -1,12 +1,14 @@
package controller
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"strings"
"github.com/hashicorp/boundary/internal/observability/event"
nodee "github.com/hashicorp/nodeenrollment"
)
// tempError is an error that satisfies the temporary error interface that is
@@ -62,42 +64,61 @@ 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, newTempError(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 nodee.ContainsKnownAlpnProto(tlsConn.ConnectionState().NegotiatedProtocol):
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))
}
return nil, newTempError(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"))
event.WriteSysEvent(ctx, op, "worker successfully authenticated", "key_id", keyId)
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, newTempError(fmt.Errorf("error reading nonce from connection: %w", err))
}
return nil, newTempError(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"))
if read != len(nonce) {
if err := conn.Close(); err != nil {
event.WriteError(ctx, op, err, event.WithInfoMsg("error closing worker connection"))
}
return nil, newTempError(fmt.Errorf("error reading nonce from worker, expected %d bytes, got %d", 20, read))
}
return nil, newTempError(errors.New("did not find valid nonce for incoming worker"))
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, newTempError(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, "description", workerInfo.Description, "proxy_address", workerInfo.ProxyAddress)
return tlsConn, nil
default:
return nil, 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 {
+34 -5
View File
@@ -13,10 +13,12 @@ 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"
"github.com/hashicorp/nodeenrollment/multihop"
"github.com/hashicorp/nodeenrollment/protocol"
"google.golang.org/grpc"
)
@@ -103,9 +105,25 @@ 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,
})
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: workerAuthStorage,
BaseListener: ln.ClusterListener,
BaseTlsConfiguration: &tls.Config{
GetConfigForClient: c.validateWorkerTls,
},
})
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 {
@@ -125,10 +143,21 @@ 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, err := handlers.NewMultihopServiceServer(
workerAuthStorage,
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)
ln.GrpcServer = workerServer
+5 -5
View File
@@ -735,10 +735,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()
@@ -777,7 +777,7 @@ func (tc *TestController) WaitForNextWorkerStatusUpdate(workerId string) error {
return false
}
if workerStatusUpdateId == workerId {
if workerStatusUpdateId == workerStatusName {
waitStatusCurrent = workerStatusUpdateTime
return false
}
@@ -795,10 +795,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
}
+9 -12
View File
@@ -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"))
}
@@ -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:
+202
View File
@@ -0,0 +1,202 @@
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.
newNodeCreds, err = newNodeCreds.HandleFetchNodeCredentialsResponse(
ctx,
w.WorkerAuthStorage,
fetchResp,
nodeenrollment.WithWrapper(w.conf.WorkerAuthKms),
)
if err != nil {
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
}
@@ -0,0 +1,118 @@
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{
InitialUpstreams: 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
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
// 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())
}
}
+107 -38
View File
@@ -14,6 +14,8 @@ import (
"math/big"
mathrand "math/rand"
"net"
"os"
"path/filepath"
"strings"
"time"
@@ -23,15 +25,24 @@ 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"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/resolver"
"google.golang.org/protobuf/proto"
)
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 {
const hcpbUrlSuffix = ".proxy.boundary.hashicorp.cloud:9202"
// 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.InitialUpstreams))
for _, addr := range w.conf.RawConfig.Worker.InitialUpstreams {
switch {
case strings.HasPrefix(addr, "/"):
initialAddrs = append(initialAddrs, resolver.Address{Addr: addr})
@@ -41,21 +52,27 @@ 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)})
}
}
if len(initialAddrs) == 0 {
return errors.New("no initial controller addresses found")
if w.conf.RawConfig.HCPBClusterId != "" {
clusterAddress := fmt.Sprintf("%s%s", w.conf.RawConfig.HCPBClusterId, hcpbUrlSuffix)
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 upstream addresses found")
}
}
w.Resolver().InitialState(resolver.State{
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
@@ -64,39 +81,79 @@ 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)
}
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"))
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)
}
}
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"))
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 err == nil && conn != nil {
if !w.everAuthenticated.Load() {
w.everAuthenticated.Store(true)
}
return nil, fmt.Errorf("expected to write %d bytes of connection nonce, wrote %d", len(authInfo.ConnectionNonce), written)
event.WriteSysEvent(ctx, op, "worker has successfully authenticated")
}
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 upstream: %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,35 +170,47 @@ 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)
}
w.grpcClientConn.Store(cc)
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
}
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)
+205 -57
View File
@@ -6,14 +6,24 @@ import (
"errors"
"fmt"
"log"
"math"
"net"
"net/http"
"os"
"time"
"github.com/hashicorp/boundary/internal/cmd/base"
"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"
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/temperror"
"google.golang.org/grpc"
)
func (w *Worker) startListeners() error {
@@ -27,32 +37,30 @@ 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
}
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
}
cancelCtx := w.baseContext
server := &http.Server{
httpServer := &http.Server{
Handler: handler,
ReadHeaderTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
@@ -61,80 +69,187 @@ 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 *types.FetchNodeCredentialsRequest,
_ ...nodee.Option,
) (*types.FetchNodeCredentialsResponse, error) {
client := w.controllerMultihopConn.Load()
if client == nil {
return nil, temperror.New(errors.New("error fetching controller connection, client is nil"))
}
multihopClient, ok := client.(multihop.MultihopServiceClient)
if !ok {
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)
}
return func() { go server.Serve(l) }, nil
generateServerCertificatesFn := func(
ctx context.Context,
_ nodee.Storage,
req *types.GenerateServerCertificatesRequest,
_ ...nodee.Option,
) (*types.GenerateServerCertificatesResponse, error) {
client := w.controllerMultihopConn.Load()
if client == nil {
return nil, temperror.New(errors.New("error fetching controller connection, client is nil"))
}
multihopClient, ok := client.(multihop.MultihopServiceClient)
if !ok {
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 := protocol.NewInterceptingListener(
&protocol.InterceptingListenerConfiguration{
Context: w.baseContext,
Storage: w.WorkerAuthStorage,
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.workerAuthSplitListener = nodeenet.NewSplitListener(interceptingListener)
downstreamServer := grpc.NewServer(
grpc.MaxRecvMsgSize(math.MaxInt32),
grpc.MaxSendMsgSize(math.MaxInt32),
)
multihopService, err := handlers.NewMultihopServiceServer(
w.WorkerAuthStorage,
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)
pbs.RegisterSessionServiceServer(downstreamServer, statusSessionService)
ln.GrpcServer = downstreamServer
return func() {
go w.workerAuthSplitListener.Start()
go httpServer.Serve(w.workerAuthSplitListener.OtherListener())
go ln.GrpcServer.Serve(
&eventingListener{
ctx: cancelCtx,
baseLn: w.workerAuthSplitListener.NodeEnrollmentListener(),
},
)
}, 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.workerAuthSplitListener != nil {
var ctx context.Context
ctx, cancel = context.WithTimeout(w.baseContext, 2*time.Second)
go func() {
<-ctx.Done()
w.workerAuthSplitListener.Stop()
cancel()
}()
}
err = w.stopAnyListeners()
if err != nil {
closeErrors = multierror.Append(closeErrors, err)
stopErrors := mg.Wait()
if w.workerAuthSplitListener != nil {
cancel()
err := w.workerAuthSplitListener.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.workerAuthSplitListener != nil {
err = w.workerAuthSplitListener.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()
@@ -160,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()
}
+31 -162
View File
@@ -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,
},
+46 -19
View File
@@ -3,6 +3,7 @@ package worker
import (
"context"
"errors"
"fmt"
"math/rand"
"time"
@@ -11,13 +12,14 @@ 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/boundary/internal/types/resource"
"github.com/hashicorp/go-secure-stdlib/strutil"
"google.golang.org/grpc/resolver"
)
type LastStatusInformation struct {
*pbs.StatusResponse
StatusTime time.Time
StatusTime time.Time
LastCalculatedUpstreams []string
}
func (w *Worker) startStatusTicking(cancelCtx context.Context) {
@@ -90,6 +92,15 @@ 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() {
event.WriteSysEvent(cancelCtx, op, "worker is not authenticated to an upstream, not sending status")
return
}
// First send info as-is. We'll perform cleanup duties after we
// get cancel/job change info back.
var activeJobs []*pbs.JobStatus
@@ -127,22 +138,30 @@ 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()
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"),
event.WithInfoMsg("error making status request to controller"))
}
result, err := client.Status(statusCtx, &pbs.StatusRequest{
Jobs: activeJobs,
Worker: &servers.Server{
PrivateId: w.conf.RawConfig.Worker.Name,
Type: resource.Worker.String(),
WorkerStatus: &servers.ServerWorkerStatus{
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(),
})
@@ -182,19 +201,27 @@ 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)
// 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 && !strutil.EquivalentSlices(lastStatus.LastCalculatedUpstreams, newUpstreams) {
upstreamsMessage := fmt.Sprintf("Upstreams has changed; old upstreams were: %s, new upstreams are: %s", lastStatus.LastCalculatedUpstreams, newUpstreams)
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(cancelCtx, op, fmt.Sprintf("Upstreams after first status set to: %s", newUpstreams))
}
}
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:
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: newUpstreams})
for _, request := range result.GetJobsRequests() {
switch request.GetRequestType() {
+26 -10
View File
@@ -4,17 +4,18 @@ import (
"context"
"fmt"
"net"
"strings"
"testing"
"time"
"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"
)
@@ -31,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
}
@@ -52,10 +53,10 @@ 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.GetControllers() {
for _, v := range lastStatus.GetCalculatedUpstreams() {
addrs = append(addrs, v.Address)
}
@@ -164,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
@@ -173,6 +174,12 @@ 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 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
@@ -191,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 {
@@ -223,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
@@ -260,6 +270,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)
@@ -288,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
}
@@ -309,17 +323,19 @@ 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(),
InitialUpstreams: tw.UpstreamAddrs(),
Logger: tw.w.conf.Logger,
StatusGracePeriodDuration: opts.StatusGracePeriodDuration,
}
if nextOpts.Name == "" {
var err error
nextOpts.Name, err = base62.Random(5)
nextOpts.Name, err = db.NewPublicId("w")
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)
+27
View File
@@ -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)
})
}
}
+235 -28
View File
@@ -14,6 +14,10 @@ import (
"sync/atomic"
"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"
"github.com/hashicorp/boundary/internal/cmd/config"
@@ -21,13 +25,18 @@ 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/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"
)
type randFn func(length int) (string, error)
@@ -42,7 +51,10 @@ type Worker struct {
tickerWg sync.WaitGroup
grpcClientConn *atomic.Value
controllerStatusConn *atomic.Value
everAuthenticated *ua.Bool
lastStatusSuccess *atomic.Value
workerStartTime time.Time
@@ -51,7 +63,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,11 +76,18 @@ 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
// Test-specific options
// The storage for node enrollment
WorkerAuthStorage *nodeefile.Storage
WorkerAuthCurrentKeyId *ua.String
WorkerAuthRegistrationRequest string
workerAuthSplitListener *nodeenet.SplitListener
// Test-specific options (and possibly hidden dev-mode flags)
TestOverrideX509VerifyDnsName string
TestOverrideX509VerifyCertPool *x509.CertPool
TestOverrideAuthRotationPeriod time.Duration
}
func New(conf *Config) (*Worker, error) {
@@ -75,16 +96,21 @@ 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),
grpcClientConn: new(atomic.Value),
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,
WorkerAuthCurrentKeyId: new(ua.String),
}
w.lastStatusSuccess.Store((*LastStatusInformation)(nil))
@@ -121,6 +147,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 +158,20 @@ 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")
}
w.WorkerAuthStorage, err = nodeefile.New(w.baseContext,
nodeefile.WithBaseDirectory(w.conf.RawConfig.Worker.AuthStoragePath))
if err != nil {
return nil, err
}
return w, nil
@@ -143,29 +179,144 @@ 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)
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
}
// 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.WithRandomReader(w.conf.SecureRandomReader),
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, nodeenrollment.WithRandomReader(w.conf.SecureRandomReader))
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)
}
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)
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 {
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)
@@ -241,19 +392,40 @@ 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)
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 {
@@ -270,6 +442,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
@@ -300,8 +506,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)
@@ -0,0 +1,67 @@
package worker
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) {
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.CalculatedUpstreams = nil
}
return resp, err
}
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)
}
+167 -7
View File
@@ -1,14 +1,27 @@
package worker
import (
"context"
"crypto/rand"
"os"
"testing"
"time"
"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/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) {
@@ -23,13 +36,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 +56,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 +73,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 +84,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",
@@ -112,3 +136,139 @@ func TestWorkerNew(t *testing.T) {
})
}
}
func TestSetupWorkerAuthStorage(t *testing.T) {
t.Parallel()
ctx := context.Background()
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,
WorkerAuthStoragePath: tmpDir,
DisableAutoStart: true,
})
t.Cleanup(tw.Shutdown)
err = tw.Worker().Start()
require.NoError(t, 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
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
},
expRegistrationRequest: true,
},
{
name: "valid creds",
in: func(t *testing.T, w *Worker) {
// Store the authorized creds
require.NoError(t, initNodeCreds.Store(ctx, w.WorkerAuthStorage))
},
expKeyId: initKeyId,
},
{
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))
},
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 existing key was loaded or new key was created and loaded
if tt.expKeyId != "" {
assert.Equal(t, tt.expKeyId, tw.Worker().WorkerAuthCurrentKeyId.Load())
} else {
assert.NotEmpty(t, tw.Worker().WorkerAuthCurrentKeyId.Load())
}
if tt.expRegistrationRequest {
assert.NotEmpty(t, tw.Worker().WorkerAuthRegistrationRequest)
} else {
assert.Empty(t, tw.Worker().WorkerAuthRegistrationRequest)
}
})
}
}
@@ -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;
@@ -0,0 +1,278 @@
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 wt_network_address not null,
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');
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 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
references iam_scope_global(scope_id)
on delete cascade
on update cascade,
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 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 (
-- 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,
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)
);
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', 'type', '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();
-- 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 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;
return new;
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 $$
begin
if new.type = 'kms' then
new.last_status_time = now();
end if;
return new;
end;
$$ language plpgsql;
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();
end if;
return new;
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();
end if;
return new;
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_kms_server_worker_update_last_status_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
references server_worker(public_id)
on delete cascade
on update cascade,
key wt_tagpair,
value wt_tagpair,
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
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;
-- 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 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 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 column server_id,
add column worker_id wt_public_id,
add constraint server_worker_fkey
foreign key (worker_id)
references server_worker (public_id)
on delete set null
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 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)
on delete set null
on update cascade;
-- 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';
-- Drop the server and server_type_enm tables
drop table server;
drop table server_type_enm;
commit;
@@ -0,0 +1,168 @@
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(
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(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.';
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,
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.';
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 -- 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
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');
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
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(root_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.';
create trigger
immutable_columns
before
update on worker_auth_certificate_bundle
for each row execute procedure immutable_columns('root_certificate_public_key', 'worker_key_identifier', 'cert_bundle');
commit;
@@ -0,0 +1,67 @@
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.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,
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;
+19
View File
@@ -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()
+1 -1
View File
@@ -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()
}
+294 -17
View File
@@ -52,6 +52,9 @@
},
{
"name": "controller.api.services.v1.UserService"
},
{
"name": "controller.api.services.v1.WorkerService"
}
],
"schemes": [
@@ -3661,6 +3664,155 @@
"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"
]
}
},
"/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"
]
}
},
"/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": {
@@ -4909,14 +5061,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",
@@ -4968,15 +5112,6 @@
}
}
},
"controller.api.resources.sessions.v1.WorkerInfo": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "The address of the worker."
}
}
},
"controller.api.resources.targets.v1.CredentialSource": {
"type": "object",
"properties": {
@@ -5386,6 +5521,107 @@
},
"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. 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. Can only be set\nthrough the API for `pki`-type workers; read-only for `kms`-type workers."
},
"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": "Output only. The address that this worker is reachable at, as sourced from\nthe worker's configuration file.",
"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 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": {
"type": "string",
"format": "date-time",
"description": "Output only. The time this worker daemon last reported its status.",
"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"
},
"active_connection_count": {
"type": "integer",
"format": "int64",
"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": {
"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"
},
"controller.api.services.v1.AddGroupMembersResponse": {
"type": "object",
"properties": {
@@ -5653,6 +5889,17 @@
}
}
},
"controller.api.services.v1.CreateWorkerLedResponse": {
"type": "object",
"properties": {
"uri": {
"type": "string"
},
"item": {
"$ref": "#/definitions/controller.api.resources.workers.v1.Worker"
}
}
},
"controller.api.services.v1.DeleteAccountResponse": {
"type": "object"
},
@@ -5698,6 +5945,9 @@
"controller.api.services.v1.DeleteUserResponse": {
"type": "object"
},
"controller.api.services.v1.DeleteWorkerResponse": {
"type": "object"
},
"controller.api.services.v1.GetAccountResponse": {
"type": "object",
"properties": {
@@ -5826,6 +6076,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": {
@@ -6002,6 +6260,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": {
@@ -6250,6 +6519,14 @@
}
}
},
"controller.api.services.v1.UpdateWorkerResponse": {
"type": "object",
"properties": {
"item": {
"$ref": "#/definitions/controller.api.resources.workers.v1.Worker"
}
}
},
"google.protobuf.NullValue": {
"type": "string",
"enum": [
@@ -0,0 +1,858 @@
// 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" class:"public"` // @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" 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() {
*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 CreateWorkerLedRequest 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 *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))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateWorkerLedRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateWorkerLedRequest) ProtoMessage() {}
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))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// 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 *CreateWorkerLedRequest) GetItem() *workers.Worker {
if x != nil {
return x.Item
}
return nil
}
type CreateWorkerLedResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
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"`
}
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))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateWorkerLedResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateWorkerLedResponse) ProtoMessage() {}
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))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// 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 *CreateWorkerLedResponse) GetUri() string {
if x != nil {
return x.Uri
}
return ""
}
func (x *CreateWorkerLedResponse) 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" 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"`
}
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" class:"public"` // @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, 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,
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,
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
(*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.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.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.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
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.(*CreateWorkerLedRequest); 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.(*CreateWorkerLedResponse); 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
}
@@ -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_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)
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.CreateWorkerLed(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
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)
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.CreateWorkerLed(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_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/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_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 {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_WorkerService_CreateWorkerLed_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_CreateWorkerLed_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_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/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_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_CreateWorkerLed_0(ctx, mux, outboundMarshaler, w, req, response_WorkerService_CreateWorkerLed_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_CreateWorkerLed_0 struct {
proto.Message
}
func (m response_WorkerService_CreateWorkerLed_0) XXX_ResponseBody() interface{} {
response := m.Message.(*CreateWorkerLedResponse)
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_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"}, ""))
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_CreateWorkerLed_0 = runtime.ForwardResponseMessage
forward_WorkerService_UpdateWorker_0 = runtime.ForwardResponseMessage
forward_WorkerService_DeleteWorker_0 = runtime.ForwardResponseMessage
)
@@ -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)
// 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.
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
// 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) 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
}
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)
// 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.
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
// 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) 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")
}
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_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).CreateWorkerLed(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.WorkerService/CreateWorkerLed",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WorkerServiceServer).CreateWorkerLed(ctx, req.(*CreateWorkerLedRequest))
}
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: "CreateWorkerLed",
Handler: _WorkerService_CreateWorkerLed_Handler,
},
{
MethodName: "UpdateWorker",
Handler: _WorkerService_UpdateWorker_Handler,
},
{
MethodName: "DeleteWorker",
Handler: _WorkerService_DeleteWorker_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "controller/api/services/v1/worker_service.proto",
}
@@ -222,12 +222,61 @@ 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
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"`
}
@@ -282,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"`
}
@@ -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" class:"public"` // @gotags: `class:"public"`
}
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 provijde 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,21 +702,23 @@ type StatusResponse struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Active controllers. This can be used (eventually) 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" class:"public"` // @gotags: `class:"public"`
}
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)
}
@@ -620,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 {
@@ -633,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 {
@@ -650,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{
@@ -696,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 (
@@ -783,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() }
@@ -876,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:
@@ -888,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:
@@ -900,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
@@ -920,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,
},
@@ -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 (
+5
View File
@@ -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:
+2 -1
View File
@@ -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())
+2 -1
View File
@@ -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
}
+2 -1
View File
@@ -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())
+19 -10
View File
@@ -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) {
@@ -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)
}
@@ -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 {
+4 -4
View File
@@ -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,
},
@@ -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:
@@ -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",
},
},
{
Loaded 100 of 195 files, more files were not shown because too many files have changed in this diff. Show more