Merge pull request #2613 from hashicorp/llb-target-address

Assign a Network Address to a Target
This commit is contained in:
Hugo authored and GitHub committed 2023-01-09 18:35:19 +00:00
commit 1fb960575f
104 files changed
+4037 -1040

No files matched your search

+3
View File
@@ -6,6 +6,9 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
### New and Improved
* Direct Address Targets: You can now set an address directly on a target,
bypassing the need for host catalogs, host sets and hosts.
([PR](https://github.com/hashicorp/boundary/pull/2613))
* Custom Response Headers: Adds ability to set api and ui response headers based
on status code. Includes default secure CSP and other headers.
([PR](https://github.com/hashicorp/boundary/pull/2587))
+12
View File
@@ -87,6 +87,18 @@ func WithRecursive(recurse bool) Option {
}
}
func WithAddress(inAddress string) Option {
return func(o *options) {
o.postMap["address"] = inAddress
}
}
func DefaultAddress() Option {
return func(o *options) {
o.postMap["address"] = nil
}
}
func WithApplicationCredentialSourceIds(inApplicationCredentialSourceIds []string) Option {
return func(o *options) {
o.postMap["application_credential_source_ids"] = inApplicationCredentialSourceIds
+1
View File
@@ -37,6 +37,7 @@ type Target struct {
InjectedApplicationCredentialSources []*CredentialSource `json:"injected_application_credential_sources,omitempty"`
Attributes map[string]interface{} `json:"attributes,omitempty"`
AuthorizedActions []string `json:"authorized_actions,omitempty"`
Address string `json:"address,omitempty"`
response *api.Response
}
+3 -2
View File
@@ -37,8 +37,9 @@ The first step in the process is to add it to the API/SDK protobufs.
* Define a `custom_options.v1.mask_mapping` tag for the field
which maps the API `this` field to the storage `that` field
(yes, it's the opposite of how it's defined for a storage protobuf)
* Define a `custom_options.v1.generate_sdk_option` tag for the SDK
* Define a data classification/filter tag for the field via `@gotags`
* Define a `custom_options.v1.generate_sdk_option` tag to change the SDK and add
API options (DefaultX, WithX functions).
* Define a data classification/filter tag for the field via `@gotags`
which specifies how sensitive/secret/public data
will be handled for the API's audit events.
See the [classification rubric](./classification-rubric.md)
+4 -1
View File
@@ -160,7 +160,10 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error {
return nil
}
if _, err := b.CreateInitialTarget(ctx); err != nil {
if _, err := b.CreateInitialTargetWithAddress(ctx); err != nil {
return err
}
if _, err := b.CreateInitialTargetWithHostSources(ctx); err != nil {
return err
}
+161 -67
View File
@@ -415,114 +415,132 @@ func (b *Server) CreateInitialHostResources(ctx context.Context) (*static.HostCa
return hc, hs, h, nil
}
func (b *Server) CreateInitialTarget(ctx context.Context) (target.Target, error) {
func (b *Server) CreateInitialTargetWithAddress(ctx context.Context) (target.Target, error) {
rw := db.New(b.Database)
kmsCache, err := kms.New(ctx, rw, rw)
if err != nil {
return nil, fmt.Errorf("error creating kms cache: %w", err)
return nil, fmt.Errorf("failed to create kms cache: %w", err)
}
if err := kmsCache.AddExternalWrappers(
b.Context,
kms.WithRootWrapper(b.RootKms),
); err != nil {
return nil, fmt.Errorf("error adding config keys to kms: %w", err)
if err := kmsCache.AddExternalWrappers(ctx, kms.WithRootWrapper(b.RootKms)); err != nil {
return nil, fmt.Errorf("failed to add config keys to kms: %w", err)
}
targetRepo, err := target.NewRepository(ctx, rw, rw, kmsCache)
if err != nil {
return nil, fmt.Errorf("error creating target repository: %w", err)
return nil, fmt.Errorf("failed to create target repository: %w", err)
}
// Host Catalog
if b.DevTargetId == "" {
// When this function is not called as part of boundary dev (eg: as part of
// boundary database init or tests), generate random target ids.
if len(b.DevTargetId) == 0 {
b.DevTargetId, err = db.NewPublicId(tcp.TargetPrefix)
if err != nil {
return nil, fmt.Errorf("error generating initial target id: %w", err)
return nil, fmt.Errorf("failed to generate initial target id: %w", err)
}
}
if b.DevTargetDefaultPort == 0 {
b.DevTargetDefaultPort = 22
}
if len(b.DevTargetAddress) == 0 {
b.DevTargetAddress = "127.0.0.1"
}
opts := []target.Option{
target.WithName("Generated target"),
target.WithDescription("Provides an initial target in Boundary"),
target.WithName("Generated target with a direct address"),
target.WithDescription("Provides an initial target using an address in Boundary"),
target.WithDefaultPort(uint32(b.DevTargetDefaultPort)),
target.WithSessionMaxSeconds(uint32(b.DevTargetSessionMaxSeconds)),
target.WithSessionConnectionLimit(int32(b.DevTargetSessionConnectionLimit)),
target.WithPublicId(b.DevTargetId),
target.WithAddress(b.DevTargetAddress),
}
t, err := target.New(ctx, tcp.Subtype, b.DevProjectId, opts...)
if err != nil {
return nil, fmt.Errorf("error creating in memory target: %w", err)
return nil, fmt.Errorf("failed to create target object: %w", err)
}
tt, _, _, err := targetRepo.CreateTarget(ctx, t, opts...)
if err != nil {
return nil, fmt.Errorf("error saving target to the db: %w", err)
return nil, fmt.Errorf("failed to save target to the db: %w", err)
}
tt, _, _, err = targetRepo.AddTargetHostSources(ctx, tt.GetPublicId(), tt.GetVersion(), []string{b.DevHostSetId})
if err != nil {
return nil, fmt.Errorf("error saving target to the db: %w", err)
}
b.InfoKeys = append(b.InfoKeys, "generated target id")
b.Info["generated target id"] = b.DevTargetId
b.InfoKeys = append(b.InfoKeys, "generated target with address id")
b.Info["generated target with address id"] = b.DevTargetId
// If we have an unprivileged dev user, add user to the role that grants
// list/read:self/cancel:self on sessions, read:self/delete:self/list on
// tokens, and an authorize-session role
if b.DevUnprivilegedUserId != "" {
iamRepo, err := iam.NewRepository(rw, rw, kmsCache, iam.WithRandomReader(b.SecureRandomReader))
if err != nil {
return nil, fmt.Errorf("unable to create repo for unprivileged user target connection role: %w", err)
return nil, fmt.Errorf("failed to create iam repository: %w", err)
}
err = unprivilegedDevUserRoleSetup(ctx, iamRepo, b.DevUnprivilegedUserId, b.DevProjectId, b.DevTargetId)
if err != nil {
return nil, fmt.Errorf("failed to set up unprivileged dev user: %w", err)
}
}
roles, err := iamRepo.ListRoles(ctx, []string{b.DevProjectId})
if err != nil {
return nil, fmt.Errorf("unable to list existing roles in project: %w", err)
}
if len(roles) != 2 {
return nil, fmt.Errorf("unexpected number of roles in default project, expected 2, got %d", len(roles))
}
var idx int = -1
for i, r := range roles {
// Hacky, I know, but saves a DB trip to look up other
// characteristics like "if any principals are currently attached".
// No matter what we pick here it's a bit heuristical.
if r.Name == "Default Grants" {
idx = i
break
}
}
if idx == -1 {
return nil, fmt.Errorf("couldn't find default grants role in default project")
}
if _, err := iamRepo.AddPrincipalRoles(ctx,
roles[idx].PublicId,
roles[idx].Version,
[]string{b.DevUnprivilegedUserId}); err != nil {
return nil, fmt.Errorf("error adding unpriv user ID to project default role: %w", err)
}
return tt, nil
}
pr, err := iam.NewRole(b.DevProjectId,
iam.WithName("Unprivileged User Session Authorization"),
iam.WithDescription(`Provides grants within the dev project scope to allow the initial unprivileged user to authorize sessions against the dev target`),
)
func (b *Server) CreateInitialTargetWithHostSources(ctx context.Context) (target.Target, error) {
rw := db.New(b.Database)
kmsCache, err := kms.New(ctx, rw, rw)
if err != nil {
return nil, fmt.Errorf("failed to create kms cache: %w", err)
}
if err := kmsCache.AddExternalWrappers(
b.Context,
kms.WithRootWrapper(b.RootKms),
); err != nil {
return nil, fmt.Errorf("failed to add config keys to kms: %w", err)
}
targetRepo, err := target.NewRepository(ctx, rw, rw, kmsCache)
if err != nil {
return nil, fmt.Errorf("failed to create target repository: %w", err)
}
// When this function is not called as part of boundary dev (eg: as part of
// boundary database init or tests), generate random target ids.
if len(b.DevSecondaryTargetId) == 0 {
b.DevSecondaryTargetId, err = db.NewPublicId(tcp.TargetPrefix)
if err != nil {
return nil, fmt.Errorf("error creating in memory role for generated grants: %w", err)
return nil, fmt.Errorf("failed to generate initial secondary target id: %w", err)
}
sessionRole, err := iamRepo.CreateRole(ctx, pr)
}
if b.DevTargetDefaultPort == 0 {
b.DevTargetDefaultPort = 22
}
opts := []target.Option{
target.WithName("Generated target using host sources"),
target.WithDescription("Provides a target using host sources in Boundary"),
target.WithDefaultPort(uint32(b.DevTargetDefaultPort)),
target.WithSessionMaxSeconds(uint32(b.DevTargetSessionMaxSeconds)),
target.WithSessionConnectionLimit(int32(b.DevTargetSessionConnectionLimit)),
target.WithPublicId(b.DevSecondaryTargetId),
}
t, err := target.New(ctx, tcp.Subtype, b.DevProjectId, opts...)
if err != nil {
return nil, fmt.Errorf("failed to create target object: %w", err)
}
tt, _, _, err := targetRepo.CreateTarget(ctx, t, opts...)
if err != nil {
return nil, fmt.Errorf("failed to save target to the db: %w", err)
}
tt, _, _, err = targetRepo.AddTargetHostSources(ctx, tt.GetPublicId(), tt.GetVersion(), []string{b.DevHostSetId})
if err != nil {
return nil, fmt.Errorf("failed to add host source %q to target: %w", b.DevHostSetId, err)
}
b.InfoKeys = append(b.InfoKeys, "generated target with host source id")
b.Info["generated target with host source id"] = b.DevSecondaryTargetId
if b.DevUnprivilegedUserId != "" {
iamRepo, err := iam.NewRepository(rw, rw, kmsCache, iam.WithRandomReader(b.SecureRandomReader))
if err != nil {
return nil, fmt.Errorf("error creating role for unprivileged user generated grants: %w", err)
return nil, fmt.Errorf("failed to create iam repository: %w", err)
}
if _, err := iamRepo.AddRoleGrants(ctx,
sessionRole.PublicId,
sessionRole.Version,
[]string{fmt.Sprintf("id=%s;actions=authorize-session", b.DevTargetId)},
); err != nil {
return nil, fmt.Errorf("error creating grant for unprivileged user generated grants: %w", err)
}
if _, err := iamRepo.AddPrincipalRoles(ctx, sessionRole.PublicId, sessionRole.Version+1, []string{b.DevUnprivilegedUserId}, nil); err != nil {
return nil, fmt.Errorf("error adding principal to role for unprivileged user generated grants: %w", err)
err = unprivilegedDevUserRoleSetup(ctx, iamRepo, b.DevUnprivilegedUserId, b.DevProjectId, b.DevSecondaryTargetId)
if err != nil {
return nil, fmt.Errorf("failed to set up unprivileged dev user: %w", err)
}
}
@@ -576,3 +594,79 @@ func (b *Server) RegisterHostPlugin(ctx context.Context, name string, plg plgpb.
return plugin, nil
}
// unprivilegedDevUserRoleSetup adds dev user to the role that grants
// list/read:self/cancel:self on sessions and read:self/delete:self/list on
// tokens. It also creates a role with an `authorize-session` grant for the
// provided targetId.
func unprivilegedDevUserRoleSetup(ctx context.Context, repo *iam.Repository, userId, projectId, targetId string) error {
roles, err := repo.ListRoles(ctx, []string{projectId})
if err != nil {
return fmt.Errorf("failed to list existing roles for project id %q: %w", projectId, err)
}
// Look for default grants role to set unprivileged user as a principal.
defaultRoleIdx := -1
for i, r := range roles {
// Hacky, I know, but saves a DB trip to look up other
// characteristics like "if any principals are currently attached".
// No matter what we pick here it's a bit heuristical.
if r.Name == "Default Grants" {
defaultRoleIdx = i
break
}
}
if defaultRoleIdx == -1 {
return fmt.Errorf("couldn't find default grants role for project id %q", projectId)
}
defaultRole := roles[defaultRoleIdx]
// This function may be called more than once for the same boundary
// deployment (eg: if we're creating more than one target), so we need to
// check if the unprivileged user is not already a principal for the default
// role in this project, as attempting to add an existing principal is an
// error.
principals, err := repo.ListPrincipalRoles(ctx, defaultRole.GetPublicId())
if err != nil {
return fmt.Errorf("failed to list principals for default project role: %w", err)
}
found := false
for _, p := range principals {
if p.PrincipalId == userId {
found = true
}
}
if !found {
_, err = repo.AddPrincipalRoles(ctx, defaultRole.GetPublicId(), defaultRole.GetVersion(), []string{userId})
if err != nil {
return fmt.Errorf("failed to add %q as principal for role id %q", userId, defaultRole.GetPublicId())
}
defaultRole.Version++ // The above call increments the role version in the database, so we must also track that with our state.
}
// Create a new role for the "authorize-session" grant and add the
// unprivileged user as a principal.
asRole, err := iam.NewRole(projectId,
iam.WithName(fmt.Sprintf("Session authorization for %s", targetId)),
iam.WithDescription(fmt.Sprintf("Provides grants within the dev project scope to allow the initial unprivileged user to authorize sessions against %s", targetId)),
)
if err != nil {
return fmt.Errorf("failed to create role object: %w", err)
}
asRole, err = repo.CreateRole(ctx, asRole)
if err != nil {
return fmt.Errorf("failed to create role for unprivileged user: %w", err)
}
if _, err := repo.AddPrincipalRoles(ctx, asRole.GetPublicId(), asRole.GetVersion(), []string{userId}, nil); err != nil {
return fmt.Errorf("failed to add unprivileged user as principal to new role: %w", err)
}
asRole.Version++
_, err = repo.AddRoleGrants(ctx, asRole.GetPublicId(), asRole.GetVersion(), []string{fmt.Sprintf("id=%s;actions=authorize-session", targetId)})
if err != nil {
return fmt.Errorf("failed to add authorize-session grant for unprivileged user: %w", err)
}
return nil
}
+4 -2
View File
@@ -106,8 +106,10 @@ type Server struct {
DevHostCatalogId string
DevHostSetId string
DevHostId string
DevTargetId string
DevHostAddress string
DevTargetId string // Target using address.
DevSecondaryTargetId string // Target using host sources.
DevHostAddress string // Host address for target using host sources.
DevTargetAddress string // Network address for target with address.
DevTargetDefaultPort int
DevTargetSessionMaxSeconds int
DevTargetSessionConnectionLimit int
+10 -1
View File
@@ -63,7 +63,16 @@ func (s *sshFlags) buildArgs(c *Command, port, ip, _ string, creds credentials)
case "tcp":
// SSH detects a host key change when the localhost proxy port changes
// This uses the host ID instead of 'localhost:port'.
args = append(args, "-o", fmt.Sprintf("HostKeyAlias=%s", c.sessionAuthzData.GetHostId()))
if len(c.sessionAuthzData.GetHostId()) > 0 {
args = append(args, "-o", fmt.Sprintf("HostKeyAlias=%s", c.sessionAuthzData.GetHostId()))
} else {
// In cases where the Target has no host sources and has an
// address directly attached to it, we have no Host Id. Use
// Target Id instead. Only one address can ever be present on a
// target, and no other host sources may be present at the same
// time, so this is a reasonable alternative.
args = append(args, "-o", fmt.Sprintf("HostKeyAlias=%s", c.sessionAuthzData.GetTargetId()))
}
case "ssh":
args = append(args, "-o", "NoHostAuthenticationForLocalhost=yes")
}
+27 -11
View File
@@ -426,26 +426,42 @@ func (c *InitCommand) Run(args []string) (retCode int) {
}
c.DevTargetSessionConnectionLimit = -1
t, err := c.CreateInitialTarget(c.Context)
ta, err := c.CreateInitialTargetWithAddress(c.Context)
if err != nil {
c.UI.Error(fmt.Errorf("Error creating initial target: %w", err).Error())
return base.CommandCliError
}
taInfo := &TargetInfo{
TargetId: ta.GetPublicId(),
DefaultPort: ta.GetDefaultPort(),
SessionMaxSeconds: ta.GetSessionMaxSeconds(),
SessionConnectionLimit: ta.GetSessionConnectionLimit(),
Type: string(ta.GetType()),
ScopeId: ta.GetProjectId(),
Name: ta.GetName(),
}
targetInfo := &TargetInfo{
TargetId: c.DevTargetId,
DefaultPort: t.GetDefaultPort(),
SessionMaxSeconds: t.GetSessionMaxSeconds(),
SessionConnectionLimit: t.GetSessionConnectionLimit(),
Type: "tcp",
ScopeId: c.DevProjectId,
Name: t.GetName(),
ths, err := c.CreateInitialTargetWithHostSources(c.Context)
if err != nil {
c.UI.Error(fmt.Errorf("Error creating initial secondary target: %w", err).Error())
return base.CommandCliError
}
thsInfo := &TargetInfo{
TargetId: ths.GetPublicId(),
DefaultPort: ths.GetDefaultPort(),
SessionMaxSeconds: ths.GetSessionMaxSeconds(),
SessionConnectionLimit: ths.GetSessionConnectionLimit(),
Type: string(ths.GetType()),
ScopeId: ths.GetProjectId(),
Name: ths.GetName(),
}
switch base.Format(c.UI) {
case "table":
c.UI.Output(generateInitialTargetTableOutput(targetInfo))
c.UI.Output(generateInitialTargetTableOutput(taInfo))
c.UI.Output(generateInitialTargetTableOutput(thsInfo))
case "json":
jsonMap["target"] = targetInfo
jsonMap["target"] = taInfo
jsonMap["target_secondary"] = thsInfo
}
return base.CommandSuccess
+27 -1
View File
@@ -66,6 +66,7 @@ type Command struct {
flagUnprivilegedLoginName string
flagUnprivilegedPassword string
flagIdSuffix string
flagSecondaryIdSuffix string
flagHostAddress string
flagTargetDefaultPort int
flagTargetSessionMaxSeconds int
@@ -141,7 +142,15 @@ func (c *Command) Flags() *base.FlagSets {
Target: &c.flagIdSuffix,
Default: "1234567890",
EnvVar: "BOUNDARY_DEV_ID_SUFFIX",
Usage: `If set, auto-created resources will use this value for their identifier (along with their resource-specific prefix). Must be 10 alphanumeric characters. As an example, if this is set to "1234567890", the generated password auth method ID will be "ampw_1234567890", the generated TCP target ID will be "ttcp_1234567890", and so on.`,
Usage: `If set, auto-created resources will use this value for their identifier (along with their resource-specific prefix). Must be 10 alphanumeric characters. As an example, if this is set to "1234567890", the generated password auth method ID will be "ampw_1234567890", the generated TCP target ID will be "ttcp_1234567890", and so on. Must be different from -secondary-id-suffix (BOUNDARY_DEV_SECONDARY_ID_SUFFIX).`,
})
f.StringVar(&base.StringVar{
Name: "secondary-id-suffix",
Target: &c.flagSecondaryIdSuffix,
Default: "0987654321",
EnvVar: "BOUNDARY_DEV_SECONDARY_ID_SUFFIX",
Usage: `If set, secondary auto-created resources will use this value for their identifier (along with their resource-specific prefix). Must be 10 alphanumeric characters. Currently only used for the target resource. Must be different from -id-suffix (BOUNDARY_DEV_ID_SUFFIX).`,
})
f.StringVar(&base.StringVar{
@@ -450,6 +459,23 @@ func (c *Command) Run(args []string) int {
c.DevHostId = fmt.Sprintf("%s_%s", static.HostPrefix, c.flagIdSuffix)
c.DevTargetId = fmt.Sprintf("%s_%s", tcp.TargetPrefix, c.flagIdSuffix)
}
if c.flagSecondaryIdSuffix != "" {
if len(c.flagSecondaryIdSuffix) != 10 {
c.UI.Error("Invalid secondary ID suffix, must be exactly 10 characters")
return base.CommandUserError
}
if !handlers.ValidId(handlers.Id("abc_"+c.flagSecondaryIdSuffix), "abc") {
c.UI.Error("Invalid secondary ID suffix, must be in the set A-Za-z0-9")
return base.CommandUserError
}
c.DevSecondaryTargetId = fmt.Sprintf("%s_%s", tcp.TargetPrefix, c.flagSecondaryIdSuffix)
}
if c.flagIdSuffix != "" && c.flagSecondaryIdSuffix != "" &&
strings.EqualFold(c.flagIdSuffix, c.flagSecondaryIdSuffix) {
c.UI.Error("Primary and secondary ID suffixes are equal, must be distinct")
return base.CommandUserError
}
host, port, err := net.SplitHostPort(c.flagHostAddress)
if err != nil {
@@ -99,7 +99,7 @@ func TestServer_ReloadListener(t *testing.T) {
CreateDevDatabase: true,
ControllerKey: controllerKey,
UseDevAuthMethod: true,
UseDevTarget: true,
UseDevTargets: true,
})
// Unset auto-created KMSes that are overwritten by config on startup
cmd.RootKms = nil
+9 -5
View File
@@ -14,15 +14,18 @@ import (
// We try to pull these from TestController, but targets et al are
// computed off of a suffix instead of having constants. Just define
// for now here, this can be tweaked later if need be.
const defaultTestTargetId = "ttcp_1234567890"
const (
defaultTestTargetId = "ttcp_1234567890"
defaultSecondaryTestTargetId = "ttcp_0987654321"
const rootKmsConfig = `
rootKmsConfig = `
kms "aead" {
purpose = "root"
aead_type = "aes-gcm"
key = "%s"
key_id = "global_root"
}`
)
type testServerCommandOpts struct {
// Whether or not to create the dev database
@@ -34,8 +37,8 @@ type testServerCommandOpts struct {
// Use the well-known dev mode auth method id (1234567890)
UseDevAuthMethod bool
// Use the well-known dev mode target method id (1234567890)
UseDevTarget bool
// Use the well-known dev mode target method ids (1234567890 and 0987654321)
UseDevTargets bool
// Whether or not to enable metric collection. If enable metrics will use
// prometheus' default registerer.
@@ -71,8 +74,9 @@ func testServerCommand(t *testing.T, opts testServerCommandOpts) *Command {
cmd.Server.DevPassword = controller.DefaultTestPassword
}
if opts.UseDevTarget {
if opts.UseDevTargets {
cmd.Server.DevTargetId = defaultTestTargetId
cmd.Server.DevSecondaryTargetId = defaultSecondaryTestTargetId
}
err = cmd.CreateDevDatabase(cmd.Context, base.WithDatabaseTemplate("boundary_template"), base.WithSkipOidcAuthMethodCreation())
@@ -78,9 +78,10 @@ func TestServer_ShutdownWorker(t *testing.T) {
tcl := targets.NewClient(client)
tgtL, err := tcl.List(ctx, scope.Global.String(), targets.WithRecursive(true))
require.NoError(err)
require.Len(tgtL.Items, 1)
require.Len(tgtL.Items, 2)
tgt := tgtL.Items[0]
require.NotNil(tgt)
require.NotNil(tgtL.GetItems()[1])
// Create test server, update default port on target
ts := helper.NewTestTcpServer(t)
@@ -482,6 +482,11 @@ func (c *Command) printListTable(items []*targets.Target) string {
fmt.Sprintf(" Description: %s", item.Description),
)
}
if item.Address != "" {
output = append(output,
fmt.Sprintf(" Address: %s", item.Address),
)
}
if len(item.AuthorizedActions) > 0 {
output = append(output,
" Authorized Actions:",
@@ -516,6 +521,9 @@ func printItemTable(item *targets.Target, resp *api.Response) string {
if item.Description != "" {
nonAttributeMap["Description"] = item.Description
}
if item.Address != "" {
nonAttributeMap["Address"] = item.Address
}
if item.WorkerFilter != "" {
nonAttributeMap["Worker Filter"] = item.WorkerFilter
}
+17 -2
View File
@@ -19,8 +19,8 @@ func init() {
func extraSshActionsFlagsMapFuncImpl() map[string][]string {
return map[string][]string{
"create": {"default-port", "session-max-seconds", "session-connection-limit", "egress-worker-filter", "ingress-worker-filter"},
"update": {"default-port", "session-max-seconds", "session-connection-limit", "worker-filter", "egress-worker-filter", "ingress-worker-filter"},
"create": {"address", "default-port", "session-max-seconds", "session-connection-limit", "egress-worker-filter", "ingress-worker-filter"},
"update": {"address", "default-port", "session-max-seconds", "session-connection-limit", "worker-filter", "egress-worker-filter", "ingress-worker-filter"},
}
}
@@ -31,6 +31,7 @@ type extraSshCmdVars struct {
flagWorkerFilter string
flagEgressWorkerFilter string
flagIngressWorkerFilter string
flagAddress string
}
func (c *SshCommand) extraSshHelpFunc(helpMap map[string]func() string) string {
@@ -66,6 +67,12 @@ func extraSshFlagsFuncImpl(c *SshCommand, set *base.FlagSets, f *base.FlagSet) {
for _, name := range flagsSshMap[c.Func] {
switch name {
case "address":
fs.StringVar(&base.StringVar{
Name: "address",
Target: &c.flagAddress,
Usage: "Optionally, a valid network address to connect to for this target. Can not be used alongside host sources.",
})
case "default-port":
fs.StringVar(&base.StringVar{
Name: "default-port",
@@ -188,6 +195,14 @@ func extraSshFlagsHandlingFuncImpl(c *SshCommand, _ *base.FlagSets, opts *[]targ
*opts = append(*opts, targets.WithIngressWorkerFilter(c.flagIngressWorkerFilter))
}
switch c.flagAddress {
case "":
case "null":
*opts = append(*opts, targets.DefaultAddress())
default:
*opts = append(*opts, targets.WithAddress(c.flagAddress))
}
return true
}
+17 -2
View File
@@ -18,8 +18,8 @@ func init() {
func extraTcpActionsFlagsMapFuncImpl() map[string][]string {
return map[string][]string{
"create": {"default-port", "session-max-seconds", "session-connection-limit", "egress-worker-filter", "ingress-worker-filter"},
"update": {"default-port", "session-max-seconds", "session-connection-limit", "worker-filter", "egress-worker-filter", "ingress-worker-filter"},
"create": {"address", "default-port", "session-max-seconds", "session-connection-limit", "egress-worker-filter", "ingress-worker-filter"},
"update": {"address", "default-port", "session-max-seconds", "session-connection-limit", "worker-filter", "egress-worker-filter", "ingress-worker-filter"},
}
}
@@ -30,6 +30,7 @@ type extraTcpCmdVars struct {
flagWorkerFilter string
flagEgressWorkerFilter string
flagIngressWorkerFilter string
flagAddress string
}
func (c *TcpCommand) extraTcpHelpFunc(helpMap map[string]func() string) string {
@@ -65,6 +66,12 @@ func extraTcpFlagsFuncImpl(c *TcpCommand, set *base.FlagSets, f *base.FlagSet) {
for _, name := range flagsTcpMap[c.Func] {
switch name {
case "address":
fs.StringVar(&base.StringVar{
Name: "address",
Target: &c.flagAddress,
Usage: "Optionally, a valid network address to connect to for this target. Can not be used alongside host sources.",
})
case "default-port":
fs.StringVar(&base.StringVar{
Name: "default-port",
@@ -187,5 +194,13 @@ func extraTcpFlagsHandlingFuncImpl(c *TcpCommand, _ *base.FlagSets, opts *[]targ
*opts = append(*opts, targets.WithIngressWorkerFilter(c.flagIngressWorkerFilter))
}
switch c.flagAddress {
case "":
case "null":
*opts = append(*opts, targets.DefaultAddress())
default:
*opts = append(*opts, targets.WithAddress(c.flagAddress))
}
return true
}
@@ -150,6 +150,22 @@ func InvalidArgumentErrorf(msg string, fields map[string]string) *ApiError {
return apiErr
}
// ConflictErrorf generates an ApiErr when a pre-conditional check is violated.
// Note, this deliberately doesn't translate to the similarly named '412
// Precondition Failed' HTTP response status. The ApiErr returned is a 400 bad
// request because this is how the grpc-gateway mapping is implemented for
// failed precondition protobuf errors.
func ConflictErrorf(msg string) *ApiError {
const op = "handlers.ConflictErrorf"
ctx := context.TODO()
err := ApiErrorWithCodeAndMessage(codes.FailedPrecondition, msg)
var apiErr *ApiError
if !errors.As(err, &apiErr) {
event.WriteError(ctx, op, err, event.WithInfoMsg("Unable to build conflict api error."))
}
return apiErr
}
var statusRegEx = regexp.MustCompile("Status: ([0-9]+), Kind: \"(.*)\", Error: \"(.*)\"")
// Converts a known errors into an error that can presented to an end user over the API.
@@ -184,6 +200,8 @@ func backendErrorToApiError(inErr error) *ApiError {
return InvalidArgumentErrorf("Error in provided request", map[string]string{"update_mask": "Invalid update mask provided."})
case errors.IsUniqueError(inErr):
return InvalidArgumentErrorf(genericUniquenessMsg, nil)
case errors.IsConflictError(inErr):
return ConflictErrorf(inErr.Error())
}
var statusCode int32 = http.StatusInternalServerError
@@ -721,6 +721,7 @@ func TestCancel(t *testing.T) {
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
got, gErr := s.CancelSession(ctx, tc.req)
if tc.err != nil {
require.Error(gErr)
// It's hard to mix and match api/error package errors right now
@@ -764,6 +765,8 @@ func TestCancel(t *testing.T) {
tc.res.GetItem().ExpirationTime = got.GetItem().GetExpirationTime()
}
assert.Equal(got.GetItem().HostId, tc.res.GetItem().HostId)
assert.Equal(got.GetItem().HostSetId, tc.res.GetItem().HostSetId)
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "CancelSession(%q) got response\n%q, wanted\n%q", tc.req, got, tc.res)
if tc.req != nil {
@@ -781,6 +781,10 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
endpoints = append(endpoints, eps...)
}
if len(endpoints) == 0 && t.GetAddress() == "" {
return nil, handlers.NotFoundErrorf("No host sources or address found for given target.")
}
var chosenEndpoint *host.Endpoint
if requestedId != "" {
for _, ep := range endpoints {
@@ -798,25 +802,32 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
}
}
if chosenEndpoint == nil {
if len(endpoints) == 0 {
// No hosts were found, error
return nil, handlers.NotFoundErrorf("No endpoint found from available target host sources.")
}
var h, p, hostId, hostSetId string
if chosenEndpoint == nil && len(endpoints) > 0 {
chosenEndpoint = endpoints[rand.Intn(len(endpoints))]
}
h, p, err := net.SplitHostPort(chosenEndpoint.Address)
switch {
case err != nil && strings.Contains(err.Error(), missingPortErrStr):
if t.GetDefaultPort() == 0 {
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("neither the selected host %q nor the target provides a port to use", chosenEndpoint.HostId))
if chosenEndpoint != nil {
hostId = chosenEndpoint.HostId
hostSetId = chosenEndpoint.SetId
h, p, err = net.SplitHostPort(chosenEndpoint.Address)
switch {
case err != nil && strings.Contains(err.Error(), missingPortErrStr):
if t.GetDefaultPort() == 0 {
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("neither the selected host %q nor the target provides a port to use", chosenEndpoint.HostId))
}
h = chosenEndpoint.Address
p = strconv.FormatUint(uint64(t.GetDefaultPort()), 10)
case err != nil:
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error when parsing the chosen endpoints host address"))
}
h = chosenEndpoint.Address
p = strconv.FormatUint(uint64(t.GetDefaultPort()), 10)
case err != nil:
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error when parsing the chosen endpoints host address"))
}
if t.GetAddress() != "" && hostId == "" && hostSetId == "" {
h = t.GetAddress()
p = strconv.FormatUint(uint64(t.GetDefaultPort()), 10)
}
// Generate the endpoint URL
endpointUrl := &url.URL{
Scheme: t.GetType().String(),
@@ -857,9 +868,9 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
expTime.Seconds += int64(t.GetSessionMaxSeconds())
sessionComposition := session.ComposedOf{
UserId: authResults.UserId,
HostId: chosenEndpoint.HostId,
HostId: hostId,
TargetId: t.GetPublicId(),
HostSetId: chosenEndpoint.SetId,
HostSetId: hostSetId,
AuthTokenId: authResults.AuthTokenId,
ProjectId: authResults.Scope.Id,
Endpoint: endpointUrl.String(),
@@ -871,7 +882,6 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
DynamicCredentials: dynCreds,
StaticCredentials: staticCreds,
}
sess, err := session.New(sessionComposition)
if err != nil {
return nil, err
@@ -977,7 +987,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
Type: t.GetType().String(),
Certificate: sess.Certificate,
PrivateKey: sess.CertificatePrivateKey,
HostId: chosenEndpoint.HostId,
HostId: hostId,
Endpoint: endpointUrl.String(),
WorkerInfo: wl.WorkerList(selectedWorkers).WorkerInfos(),
ConnectionLimit: t.GetSessionConnectionLimit(),
@@ -996,8 +1006,8 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
Type: t.GetType().String(),
AuthorizationToken: encodedMarshaledSad,
UserId: authResults.UserId,
HostId: chosenEndpoint.HostId,
HostSetId: chosenEndpoint.SetId,
HostId: hostId,
HostSetId: hostSetId,
Endpoint: endpointUrl.String(),
Credentials: creds,
}
@@ -1040,6 +1050,9 @@ func (s Service) createInRepo(ctx context.Context, item *pb.Target) (target.Targ
if item.GetIngressWorkerFilter() != nil {
opts = append(opts, target.WithIngressWorkerFilter(item.GetIngressWorkerFilter().GetValue()))
}
if item.GetAddress() != nil {
opts = append(opts, target.WithAddress(item.GetAddress().GetValue()))
}
attr, err := subtypeRegistry.newAttribute(target.SubtypeFromType(item.GetType()), item.GetAttrs())
if err != nil {
@@ -1067,6 +1080,7 @@ func (s Service) createInRepo(ctx context.Context, item *pb.Target) (target.Targ
func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Target) (target.Target, []target.HostSource, []target.CredentialSource, error) {
const op = "targets.(Service).updateInRepo"
var dbMask []string
var opts []target.Option
if desc := item.GetDescription(); desc != nil {
opts = append(opts, target.WithDescription(desc.GetValue()))
@@ -1090,6 +1104,10 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st
if ingressFilter := item.GetIngressWorkerFilter(); ingressFilter != nil {
opts = append(opts, target.WithIngressWorkerFilter(item.GetIngressWorkerFilter().GetValue()))
}
if item.GetAddress() != nil {
dbMask = append(dbMask, "Address")
opts = append(opts, target.WithAddress(item.GetAddress().GetValue()))
}
subtype := target.SubtypeFromId(id)
attr, err := subtypeRegistry.newAttribute(subtype, item.GetAttrs())
@@ -1113,8 +1131,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update target"))
}
dbMask := maskManager.Translate(mask)
dbMask = append(dbMask, maskManager.Translate(mask)...)
if len(dbMask) == 0 {
return nil, nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid paths provided in the update mask."})
}
@@ -1122,7 +1139,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st
if err != nil {
return nil, nil, nil, err
}
out, hs, cl, rowsUpdated, err := repo.UpdateTarget(ctx, u, version, dbMask)
out, hs, cl, rowsUpdated, err := repo.UpdateTarget(ctx, u, version, dbMask, opts...)
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update target"))
}
@@ -1167,8 +1184,14 @@ func (s Service) addHostSourcesInRepo(ctx context.Context, targetId string, host
}
out, hs, cl, err := repo.AddTargetHostSources(ctx, targetId, version, strutil.RemoveDuplicates(hostSourceIds, false))
if err != nil {
var internalErr *errors.Err
if stderrors.As(err, &internalErr) && internalErr.Code == errors.Conflict {
// The conflict error is surfaced directly as it's correctly
// converted all the way down to the HTTP status.
return nil, nil, nil, err
}
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add host sources to target: %v.", err)
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Failed to add target host sources")
}
if out == nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup target after adding host sources to it.")
@@ -1184,8 +1207,14 @@ func (s Service) setHostSourcesInRepo(ctx context.Context, targetId string, host
}
_, _, _, err = repo.SetTargetHostSources(ctx, targetId, version, strutil.RemoveDuplicates(hostSourceIds, false))
if err != nil {
var internalErr *errors.Err
if stderrors.As(err, &internalErr) && internalErr.Code == errors.Conflict {
// The conflict error is surfaced directly as it's correctly
// converted all the way down to the HTTP status.
return nil, nil, nil, err
}
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set host sources in target: %v.", err)
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Failed to set target host sources")
}
out, hs, cl, err := repo.LookupTarget(ctx, targetId)
@@ -1424,6 +1453,9 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou
})
}
}
if outputFields.Has(globals.AddressField) {
out.Address = wrapperspb.String(in.GetAddress())
}
var brokeredSources, injectedAppSources []*pb.CredentialSource
var brokeredSourceIds, injectedAppSourceIds []string
@@ -1525,7 +1557,20 @@ func validateCreateRequest(req *pbs.CreateTargetRequest) error {
badFields[globals.IngressWorkerFilterField] = err.Error()
}
}
if address := req.GetItem().GetAddress(); address != nil {
if len(address.GetValue()) < static.MinHostAddressLength ||
len(address.GetValue()) > static.MaxHostAddressLength {
badFields[globals.AddressField] = fmt.Sprintf("Address length must be between %d and %d characters.", static.MinHostAddressLength, static.MaxHostAddressLength)
}
_, _, err := net.SplitHostPort(address.GetValue())
switch {
case err == nil:
badFields[globals.AddressField] = "Address does not support a port."
case strings.Contains(err.Error(), missingPortErrStr):
default:
badFields[globals.AddressField] = fmt.Sprintf("Error parsing address: %v.", err)
}
}
subtype := target.SubtypeFromType(req.GetItem().GetType())
_, err := subtypeRegistry.get(subtype)
if err != nil {
@@ -1588,6 +1633,20 @@ func validateUpdateRequest(req *pbs.UpdateTargetRequest) error {
badFields[globals.IngressWorkerFilterField] = err.Error()
}
}
if address := req.GetItem().GetAddress(); address != nil {
if len(address.GetValue()) < static.MinHostAddressLength ||
len(address.GetValue()) > static.MaxHostAddressLength {
badFields[globals.AddressField] = fmt.Sprintf("Address length must be between %d and %d characters.", static.MinHostAddressLength, static.MaxHostAddressLength)
}
_, _, err := net.SplitHostPort(address.GetValue())
switch {
case err == nil:
badFields[globals.AddressField] = "Address does not support a port."
case strings.Contains(err.Error(), missingPortErrStr):
default:
badFields[globals.AddressField] = fmt.Sprintf("Error parsing address: %v.", err)
}
}
subtype := target.SubtypeFromId(req.GetId())
_, err := subtypeRegistry.get(subtype)
if err != nil {
@@ -139,6 +139,8 @@ func TestGet(t *testing.T) {
ctx := context.Background()
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()}))
tarAddr := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test address", target.WithAddress("8.8.8.8"))
pTar := &pb.Target{
Id: tar.GetPublicId(),
ScopeId: proj.GetPublicId(),
@@ -152,11 +154,27 @@ func TestGet(t *testing.T) {
SessionMaxSeconds: wrapperspb.UInt32(28800),
SessionConnectionLimit: wrapperspb.Int32(-1),
AuthorizedActions: testAuthorizedActions,
Address: &wrapperspb.StringValue{},
}
for _, ihs := range hs {
pTar.HostSources = append(pTar.HostSources, &pb.HostSource{Id: ihs.GetPublicId(), HostCatalogId: ihs.GetCatalogId()})
}
pTarAddr := &pb.Target{
Id: tarAddr.GetPublicId(),
ScopeId: proj.GetPublicId(),
Name: wrapperspb.String("test address"),
CreatedTime: tarAddr.GetCreateTime().GetTimestamp(),
UpdatedTime: tarAddr.GetUpdateTime().GetTimestamp(),
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: o.GetPublicId()},
Type: tcp.Subtype.String(),
Attrs: &pb.Target_TcpTargetAttributes{},
SessionMaxSeconds: wrapperspb.UInt32(28800),
SessionConnectionLimit: wrapperspb.Int32(-1),
AuthorizedActions: testAuthorizedActions,
Address: &wrapperspb.StringValue{Value: "8.8.8.8"},
}
cases := []struct {
name string
req *pbs.GetTargetRequest
@@ -168,6 +186,11 @@ func TestGet(t *testing.T) {
req: &pbs.GetTargetRequest{Id: tar.GetPublicId()},
res: &pbs.GetTargetResponse{Item: pTar},
},
{
name: "Get an Existing Target w/ address",
req: &pbs.GetTargetRequest{Id: tarAddr.GetPublicId()},
res: &pbs.GetTargetResponse{Item: pTarAddr},
},
{
name: "Get a non existing Target",
req: &pbs.GetTargetRequest{Id: tcp.TargetPrefix + "_DoesntExis"},
@@ -272,6 +295,7 @@ func TestList(t *testing.T) {
SessionMaxSeconds: wrapperspb.UInt32(28800),
SessionConnectionLimit: wrapperspb.Int32(-1),
AuthorizedActions: testAuthorizedActions,
Address: &wrapperspb.StringValue{},
})
totalTars = append(totalTars, wantTars[i])
tar = tcp.TestTarget(ctx, t, conn, otherProj.GetPublicId(), name, target.WithHostSources([]string{otherHss[0].GetPublicId(), otherHss[1].GetPublicId()}))
@@ -288,6 +312,7 @@ func TestList(t *testing.T) {
SessionMaxSeconds: wrapperspb.UInt32(28800),
SessionConnectionLimit: wrapperspb.Int32(-1),
AuthorizedActions: testAuthorizedActions,
Address: &wrapperspb.StringValue{},
})
}
@@ -575,6 +600,7 @@ func TestCreate(t *testing.T) {
SessionConnectionLimit: wrapperspb.Int32(-1),
AuthorizedActions: testAuthorizedActions,
EgressWorkerFilter: wrapperspb.String(`type == "bar"`),
Address: &wrapperspb.StringValue{},
},
},
},
@@ -668,6 +694,30 @@ func TestCreate(t *testing.T) {
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Invalid address length",
req: &pbs.CreateTargetRequest{Item: &pb.Target{
Address: wrapperspb.String("ab"),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Invalid address w/ port",
req: &pbs.CreateTargetRequest{Item: &pb.Target{
Address: wrapperspb.String("8.8.8.8:80"),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Invalid address not parsable",
req: &pbs.CreateTargetRequest{Item: &pb.Target{
Address: wrapperspb.String("aaa.8bc.8.8:80:abc"),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
@@ -824,6 +874,7 @@ func TestUpdate(t *testing.T) {
SessionMaxSeconds: wrapperspb.UInt32(3600),
SessionConnectionLimit: wrapperspb.Int32(5),
AuthorizedActions: testAuthorizedActions,
Address: &wrapperspb.StringValue{},
},
},
},
@@ -858,6 +909,7 @@ func TestUpdate(t *testing.T) {
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
AuthorizedActions: testAuthorizedActions,
Address: &wrapperspb.StringValue{},
},
},
},
@@ -955,6 +1007,7 @@ func TestUpdate(t *testing.T) {
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
AuthorizedActions: testAuthorizedActions,
Address: &wrapperspb.StringValue{},
},
},
},
@@ -988,6 +1041,7 @@ func TestUpdate(t *testing.T) {
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
AuthorizedActions: testAuthorizedActions,
Address: &wrapperspb.StringValue{},
},
},
},
@@ -1021,6 +1075,7 @@ func TestUpdate(t *testing.T) {
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
AuthorizedActions: testAuthorizedActions,
Address: &wrapperspb.StringValue{},
},
},
},
@@ -1109,6 +1164,45 @@ func TestUpdate(t *testing.T) {
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Invalid address length",
req: &pbs.UpdateTargetRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"address"},
},
Item: &pb.Target{
Address: wrapperspb.String("ab"),
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Invalid address w/ port",
req: &pbs.UpdateTargetRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"address"},
},
Item: &pb.Target{
Address: wrapperspb.String("8.8.8.8:80"),
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Invalid address not parsable",
req: &pbs.UpdateTargetRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"address"},
},
Item: &pb.Target{
Address: wrapperspb.String("aaa.8bc.8.8:80:abc"),
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
@@ -5,8 +5,9 @@ import (
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets"
"github.com/hashicorp/boundary/internal/target"
"github.com/hashicorp/boundary/internal/target/store"
"github.com/hashicorp/boundary/internal/target/tcp"
"github.com/hashicorp/boundary/internal/target/tcp/store"
tcpStore "github.com/hashicorp/boundary/internal/target/tcp/store"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
)
@@ -78,7 +79,7 @@ func init() {
var err error
if maskManager, err = handlers.NewMaskManager(
handlers.MaskDestination{&store.Target{}},
handlers.MaskDestination{&tcpStore.Target{}, &store.TargetAddress{}},
handlers.MaskSource{&pb.Target{}, &pb.TcpTargetAttributes{}},
); err != nil {
panic(err)
+1 -1
View File
@@ -709,7 +709,7 @@ func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController,
t.Fatal(err)
}
if !opts.DisableTargetCreation {
if _, err := tc.b.CreateInitialTarget(ctx); err != nil {
if _, err := tc.b.CreateInitialTargetWithHostSources(ctx); err != nil {
t.Fatal(err)
}
}
@@ -10,6 +10,7 @@ begin;
alter column credential_group_key drop default;
-- replaces function from 15/01_wh_rename_key_columns.up.sql
-- replaced function in 70/03_wh_sessions.up.sql
drop trigger wh_insert_session on session;
drop function wh_insert_session;
@@ -70,6 +70,7 @@ begin;
-- replaces function from 15/02_wh_rename_key_columns.up.sql
-- replaced function in 70/03_wh_sessions.up.sql
-- adds the network address key to the host dimension table.
drop function wh_upsert_host;
create function wh_upsert_host(p_host_id wt_public_id, p_host_set_id wt_public_id, p_target_id wt_public_id) returns wh_dim_key
@@ -82,6 +82,7 @@ begin;
-- warehouse
-- Replaces whx_host_dimension_source defined in 26/02_wh_network_address_dimensions.up.sql.
-- Replaced view in 70/03_wh_sessions.up.sql
drop view whx_host_dimension_source;
create view whx_host_dimension_source as
select -- id is the first column in the target view
@@ -35,6 +35,7 @@ begin;
$$ language plpgsql;
-- Replaces trigger from 01/50_session.up.sql
-- Replaced trigger in 70/02_sessions.up.sql
create or replace function insert_session() returns trigger
as $$
begin
@@ -32,6 +32,7 @@ begin;
on update cascade;
-- Replaces trigger from 44/04_session.up.sql
-- Replaced in 70/02_sessions.up.sql
-- Adding a foreign key reference to the kms key means we have
-- to set the key_id to null when the scope_id is set to null,
-- otherwise we can't delete scopes with sessions, since deleting
@@ -86,6 +86,7 @@ select public_id,
from target_tcp;
-- Replaces view from 56/06_add_session_private_key_column.up.sql
-- Replaced in 70/02_sessions.up.sql
drop view session_list;
create view session_list as
select
@@ -0,0 +1,27 @@
begin;
create table target_address (
target_id wt_public_id primary key,
address text not null
-- the following constraints are used to validate the length requirements of a dns name
-- note the address column can also contain other network types such as ipv4
constraint address_must_be_more_than_2_characters
check(length(trim(address)) > 2)
constraint address_must_be_less_than_256_characters
check(length(trim(address)) < 256),
constraint target_fkey foreign key (target_id)
references target (public_id)
on delete cascade
on update cascade
);
comment on table target_address is
'target_address entries represent a network address assigned to a target.';
create trigger immutable_columns before update on target_address
for each row execute function immutable_columns('target_id');
insert into oplog_ticket (name, version)
values
('target_address', 1);
commit;
@@ -0,0 +1,218 @@
begin;
-- Replaces trigger from 56/02_add_data_key_foreign_key_references.up.sql
drop trigger cancel_session_with_null_fk on session;
create or replace function cancel_session_with_null_fk() returns trigger
as $$
begin
-- Note that we need each of these to run in case
-- more than one of them is null.
if new.auth_token_id is null then
perform cancel_session(new.public_id);
end if;
if new.project_id is null then
-- Setting the key_id to null will allow the scope
-- to cascade delete its keys.
new.key_id = null;
perform cancel_session(new.public_id);
end if;
if new.target_id is null then
perform cancel_session(new.public_id);
end if;
if new.user_id is null then
perform cancel_session(new.public_id);
end if;
return new;
end;
$$ language plpgsql;
create trigger cancel_session_with_null_fk before update of auth_token_id, project_id, target_id, user_id on session
for each row execute procedure cancel_session_with_null_fk();
create table session_target_address (
session_id wt_public_id primary key,
target_id wt_public_id,
-- the following foreign key constraint is set to null on a delete
-- because we want to be able to invoke a trigger to call cancel_session
-- when the references column is deleted
-- rather than canceling a session for any reason that may cause a row to be deleted.
constraint target_address_fkey foreign key (target_id)
references target_address (target_id)
on delete set null
on update cascade,
constraint session_fkey foreign key (session_id)
references session (public_id)
on delete cascade
on update cascade,
constraint session_target_address_session_id_target_id_uq
unique(session_id, target_id)
);
comment on table session_target_address is
'session_target_address entries represent a session that is using a network address that is assigned directly to a Target.';
create trigger immutable_columns before update on session_target_address
for each row execute procedure immutable_columns('session_id');
create function cancel_session_with_null_target_address_fk() returns trigger
as $$
begin
if new.target_id is null then
perform cancel_session(new.session_id);
delete from session_target_address where session_id = new.session_id;
end if;
return new;
end;
$$ language plpgsql;
create trigger cancel_session_with_null_target_address_fk after update of target_id on session_target_address
for each row execute procedure cancel_session_with_null_target_address_fk();
create function insert_session_target_address() returns trigger
as $$
begin
if new.target_id is null then
raise exception 'target_id is null';
end if;
return new;
end;
$$ language plpgsql;
create trigger insert_session_target_address before insert on session_target_address
for each row execute procedure insert_session_target_address();
create table session_host_set_host (
session_id wt_public_id primary key,
host_set_id wt_public_id,
host_id wt_public_id,
-- the following foreign key constraints for host_set_id & host_id is set to null on a delete
-- because we want to be able to invoke a trigger to call cancel_session
-- when the references column is deleted
-- rather than canceling a session for any reason that may cause a row to be deleted.
constraint host_set_fkey foreign key (host_set_id)
references host_set (public_id)
on delete set null
on update cascade,
constraint host_fkey foreign key (host_id)
references host (public_id)
on delete set null
on update cascade,
constraint session_fkey foreign key (session_id)
references session (public_id)
on delete cascade
on update cascade,
constraint session_host_set_host_session_id_host_set_id_host_id_uq
unique(session_id, host_set_id, host_id)
);
comment on table session_host_set_host is
'session_host_set entries represent a session that is using a Host Set.';
create trigger immutable_columns before update on session_host_set_host
for each row execute procedure immutable_columns('session_id');
create function cancel_session_with_null_host_source_fk() returns trigger
as $$
begin
if new.host_set_id is null or new.host_id is null then
perform cancel_session(new.session_id);
delete from session_host_set_host where session_id = new.session_id;
end if;
return new;
end;
$$ language plpgsql;
create trigger cancel_session_with_null_host_source_fk after update of host_set_id, host_id on session_host_set_host
for each row execute procedure cancel_session_with_null_host_source_fk();
create function insert_session_host_set_host() returns trigger
as $$
begin
if new.host_set_id is null then
raise exception 'host_set_id is null';
end if;
if new.host_id is null then
raise exception 'host_id is null';
end if;
return new;
end;
$$ language plpgsql;
create trigger insert_session_host_set_host before insert on session_host_set_host
for each row execute procedure insert_session_host_set_host();
drop view session_list;
alter table session
drop constraint session_host_id_fkey,
drop constraint session_host_set_id_fkey,
drop column host_id,
drop column host_set_id
;
-- Replaces trigger from 44/04_sessions.up.sql
drop trigger insert_session on session;
create or replace function insert_session() returns trigger
as $$
begin
case
when new.user_id is null then
raise exception 'user_id is null';
when new.target_id is null then
raise exception 'target_id is null';
when new.auth_token_id is null then
raise exception 'auth_token_id is null';
when new.project_id is null then
raise exception 'project_id is null';
when new.endpoint is null then
raise exception 'endpoint is null';
else
end case;
return new;
end;
$$ language plpgsql;
create trigger insert_session before insert on session
for each row execute procedure insert_session();
-- Replaces view from 59/01_target_ingress_egress_worker_filters.up.sql
create view session_list as
select
s.public_id,
s.user_id,
shsh.host_id,
s.target_id,
shsh.host_set_id,
s.auth_token_id,
s.project_id,
s.certificate,
s.certificate_private_key,
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,
s.egress_worker_filter,
s.ingress_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
left join session_host_set_host shsh on s.public_id = shsh.session_id;
commit;
@@ -0,0 +1,424 @@
begin;
-- replaced view in 44/03_targets.up.sql
drop view whx_host_dimension_source;
create view whx_host_dimension_source as
with
host_sources (
host_id, host_type, host_name, host_description,
host_set_id, host_set_type, host_set_name, host_set_description,
host_catalog_id, host_catalog_type, host_catalog_name, host_catalog_description,
target_id, target_type, target_name, target_description,
target_default_port_number, target_session_max_seconds, target_session_connection_limit,
project_id, project_name, project_description,
organization_id, organization_name, organization_description
) as (
select
h.public_id as host_id,
case when sh.public_id is not null then 'static host'
when ph.public_id is not null then 'plugin host'
else 'Unknown' end as host_type,
case when sh.public_id is not null then coalesce(sh.name, 'None')
when ph.public_id is not null then coalesce(ph.name, 'None')
else 'Unknown' end as host_name,
case when sh.public_id is not null then coalesce(sh.description, 'None')
when ph.public_id is not null then coalesce(ph.description, 'None')
else 'Unknown' end as host_description,
hs.public_id as host_set_id,
case when shs.public_id is not null then 'static host set'
when phs.public_id is not null then 'plugin host set'
else 'Unknown' end as host_set_type,
case
when shs.public_id is not null then coalesce(shs.name, 'None')
when phs.public_id is not null then coalesce(phs.name, 'None')
else 'None'
end as host_set_name,
case
when shs.public_id is not null then coalesce(shs.description, 'None')
when phs.public_id is not null then coalesce(phs.description, 'None')
else 'None'
end as host_set_description,
hc.public_id as host_catalog_id,
case when shc.public_id is not null then 'static host catalog'
when phc.public_id is not null then 'plugin host catalog'
else 'Unknown' end as host_catalog_type,
case
when shc.public_id is not null then coalesce(shc.name, 'None')
when phc.public_id is not null then coalesce(phc.name, 'None')
else 'None'
end as host_catalog_name,
case
when shc.public_id is not null then coalesce(shc.description, 'None')
when phc.public_id is not null then coalesce(phc.description, 'None')
else 'None'
end as host_catalog_description,
t.public_id as target_id,
'tcp target' as target_type,
coalesce(t.name, 'None') as target_name,
coalesce(t.description, 'None') as target_description,
coalesce(t.default_port, 0) as target_default_port_number,
t.session_max_seconds as target_session_max_seconds,
t.session_connection_limit as target_session_connection_limit,
p.public_id as project_id,
coalesce(p.name, 'None') as project_name,
coalesce(p.description, 'None') as project_description,
o.public_id as organization_id,
coalesce(o.name, 'None') as organization_name,
coalesce(o.description, 'None') as organization_description
from host as h
join host_catalog as hc on h.catalog_id = hc.public_id
join host_set as hs on h.catalog_id = hs.catalog_id
join target_host_set as ts on hs.public_id = ts.host_set_id
join target_tcp as t on ts.target_id = t.public_id
join iam_scope as p on t.project_id = p.public_id and p.type = 'project'
join iam_scope as o on p.parent_id = o.public_id and o.type = 'org'
left join static_host as sh on sh.public_id = h.public_id
left join host_plugin_host as ph on ph.public_id = h.public_id
left join static_host_catalog as shc on shc.public_id = hc.public_id
left join host_plugin_catalog as phc on phc.public_id = hc.public_id
left join static_host_set as shs on shs.public_id = hs.public_id
left join host_plugin_set as phs on phs.public_id = hs.public_id
),
host_target_address (
host_id, host_type, host_name, host_description,
host_set_id, host_set_type, host_set_name, host_set_description,
host_catalog_id, host_catalog_type, host_catalog_name, host_catalog_description,
target_id, target_type, target_name, target_description,
target_default_port_number, target_session_max_seconds, target_session_connection_limit,
project_id, project_name, project_description,
organization_id, organization_name, organization_description
) as (
select
'Not Applicable' as host_id,
'direct address' as host_type,
'Not Applicable' as host_name,
'Not Applicable' as host_description,
'Not Applicable' as host_set_id,
'Not Applicable' as host_set_type,
'Not Applicable' as host_set_name,
'Not Applicable' as host_set_description,
'Not Applicable' as host_catalog_id,
'Not Applicable' as host_catalog_type,
'Not Applicable' as host_catalog_name,
'Not Applicable' as host_catalog_description,
t.public_id as target_id,
'tcp target' as target_type,
coalesce(t.name, 'None') as target_name,
coalesce(t.description, 'None') as target_description,
coalesce(t.default_port, 0) as target_default_port_number,
t.session_max_seconds as target_session_max_seconds,
t.session_connection_limit as target_session_connection_limit,
p.public_id as project_id,
coalesce(p.name, 'None') as project_name,
coalesce(p.description, 'None') as project_description,
o.public_id as organization_id,
coalesce(o.name, 'None') as organization_name,
coalesce(o.description, 'None') as organization_description
from target_tcp as t
right join target_address as ta on t.public_id = ta.target_id
left join iam_scope as p on p.public_id = t.project_id
left join iam_scope as o on o.public_id = p.parent_id
)
select * from host_sources
union
select * from host_target_address;
-- replaced function in 26/03_wh_network_address_dimensions.up.sql
create or replace function wh_upsert_host() returns trigger
as $$
declare
p_target_id wt_public_id;
p_host_key wh_dim_key;
src whx_host_dimension_target%rowtype;
target whx_host_dimension_target%rowtype;
addr_group_key wh_dim_key;
begin
select target_id into p_target_id
from session
where session.public_id = new.session_id;
if p_target_id is null then
raise exception 'target_id is null';
end if;
select * into target
from whx_host_dimension_target as t
where t.host_id = new.host_id
and t.host_set_id = new.host_set_id
and t.target_id = p_target_id;
select wh_upsert_network_address_dimension(new.host_id) into addr_group_key;
select target.key, addr_group_key, t.* into src
from whx_host_dimension_source as t
where t.host_id = new.host_id
and t.host_set_id = new.host_set_id
and t.target_id = p_target_id;
if src is distinct from target then
-- expire the current row
update wh_host_dimension
set current_row_indicator = 'Expired',
row_expiration_time = current_timestamp
where host_id = new.host_id
and host_set_id = new.host_set_id
and target_id = p_target_id
and current_row_indicator = 'Current';
-- insert a new row
insert into wh_host_dimension (
host_id, host_type, host_name, host_description,
network_address_group_key,
host_set_id, host_set_type, host_set_name, host_set_description,
host_catalog_id, host_catalog_type, host_catalog_name, host_catalog_description,
target_id, target_type, target_name, target_description,
target_default_port_number, target_session_max_seconds, target_session_connection_limit,
project_id, project_name, project_description,
organization_id, organization_name, organization_description,
current_row_indicator, row_effective_time, row_expiration_time
)
select host_id, host_type, host_name, host_description,
addr_group_key,
host_set_id, host_set_type, host_set_name, host_set_description,
host_catalog_id, host_catalog_type, host_catalog_name, host_catalog_description,
target_id, target_type, target_name, target_description,
target_default_port_number, target_session_max_seconds, target_session_connection_limit,
project_id, project_name, project_description,
organization_id, organization_name, organization_description,
'Current', current_timestamp, 'infinity'::timestamptz
from whx_host_dimension_source
where host_id = new.host_id
and host_set_id = new.host_set_id
and target_id = p_target_id;
end if;
select key into p_host_key
from wh_host_dimension as t
where t.current_row_indicator = 'Current'
and t.host_id = new.host_id
and t.host_set_id = new.host_set_id
and t.target_id = p_target_id;
update wh_session_accumulating_fact
set host_key = p_host_key
where session_id = new.session_id;
return new;
end;
$$ language plpgsql;
create trigger wh_update_session_connection_accumulating_fact after insert on session_host_set_host
for each row execute procedure wh_upsert_host();
-- replaced function in 16/04_wh_credential_dimension.up.sql
create or replace function wh_insert_session() returns trigger
as $$
declare
new_row wh_session_accumulating_fact%rowtype;
begin
with
pending_timestamp (date_dim_key, time_dim_key, ts) as (
select wh_date_key(start_time), wh_time_key(start_time), start_time
from session_state
where session_id = new.public_id
and state = 'pending'
)
insert into wh_session_accumulating_fact (
session_id,
auth_token_id,
host_key,
user_key,
credential_group_key,
session_pending_date_key,
session_pending_time_key,
session_pending_time
)
select new.public_id,
new.auth_token_id,
'no host source', -- will be updated by wh_upsert_host
wh_upsert_user(new.user_id, new.auth_token_id),
'no credentials', -- will be updated by wh_upsert_credential_group
pending_timestamp.date_dim_key,
pending_timestamp.time_dim_key,
pending_timestamp.ts
from pending_timestamp
returning * into strict new_row;
return null;
end;
$$ language plpgsql;
insert into wh_host_dimension (
key,
host_id, host_type, host_name, host_description,
host_set_id, host_set_type, host_set_name, host_set_description,
host_catalog_id, host_catalog_type, host_catalog_name, host_catalog_description,
target_id, target_type, target_name, target_description, target_default_port_number, target_session_max_seconds, target_session_connection_limit,
project_id, project_name, project_description, organization_id, organization_name, organization_description,
current_row_indicator, row_effective_time, row_expiration_time, network_address_group_key
)
values
(
'no host source',
'None', 'None', 'None', 'None',
'None', 'None', 'None', 'None',
'None', 'None', 'None', 'None',
'None', 'None', 'None', 'None', -1, -1, -1,
'00000000000', 'None', 'None', '00000000000', 'None', 'None',
'Current', now(), 'infinity'::timestamptz, 'Unknown'
);
create function wh_upsert_direct_network_address_dimension(p_address text, p_target_id text) returns wh_dim_key
as $$
declare
p_address_type text := 'Not Applicable';
p_ip_address_family text := 'Not Applicable';
p_private_ip_address_indicator text := 'Not Applicable';
p_dns_name text := 'Not Applicable';
p_ip4_address text := 'Not Applicable';
p_ip6_address text := 'Not Applicable';
nag_key wh_dim_key;
begin
if wh_try_cast_inet(p_address) is null then
p_address_type := 'DNS Name';
p_dns_name := p_address;
else
p_address_type := 'IP Address';
p_private_ip_address_indicator := wh_private_address_indicator(wh_try_cast_inet(p_address));
end if;
if family(wh_try_cast_inet(p_address)) = 4 then
p_ip_address_family := 'IPv4';
p_ip4_address := host(wh_try_cast_inet(p_address));
elsif family(wh_try_cast_inet(p_address)) = 6 then
p_ip_address_family := 'IPv6';
p_ip6_address := host(wh_try_cast_inet(p_address));
end if;
insert into wh_network_address_dimension
(address, address_type, ip_address_family, private_ip_address_indicator, dns_name, ip4_address, ip6_address)
values
(p_address, p_address_type, p_ip_address_family, p_private_ip_address_indicator, p_dns_name, p_ip4_address, p_ip6_address) on conflict do nothing;
select distinct g.network_address_group_key into nag_key
from wh_network_address_group_membership g
left join wh_host_dimension h on g.network_address_group_key = h.network_address_group_key
where g.network_address = p_address
and h.current_row_indicator = 'Current'
and h.target_id = p_target_id
and h.host_id = 'Not Applicable'
and h.host_set_id = 'Not Applicable';
if nag_key is null then
select network_address_group_key into nag_key
from wh_host_dimension h
where h.current_row_indicator = 'Current'
and h.host_id = 'Not Applicable'
and h.host_set_id = 'Not Applicable'
and h.target_id = p_target_id;
end if;
if nag_key is null then
insert into wh_network_address_group default values returning key into nag_key;
end if;
insert into wh_network_address_group_membership
(network_address_group_key, network_address)
values
(nag_key, p_address) on conflict do nothing;
return nag_key;
end
$$ language plpgsql;
create function wh_upsert_host_direct_network_address() returns trigger
as $$
declare
p_address text;
src whx_host_dimension_target%rowtype;
target whx_host_dimension_target%rowtype;
addr_group_key wh_dim_key;
p_host_key wh_dim_key;
begin
select address into p_address
from target_address
where target_address.target_id = new.target_id;
if p_address is null then
raise exception 'target address is null';
end if;
select * into target
from whx_host_dimension_target as t
where t.host_id = 'Not Applicable'
and t.host_set_id = 'Not Applicable'
and t.target_id = new.target_id;
select wh_upsert_direct_network_address_dimension(p_address, new.target_id) into addr_group_key;
select target.key, addr_group_key, t.* into src
from whx_host_dimension_source as t
where t.host_id = 'Not Applicable'
and t.host_set_id = 'Not Applicable'
and t.target_id = new.target_id;
if src is distinct from target then
-- expire the current row
update wh_host_dimension
set current_row_indicator = 'Expired',
row_expiration_time = current_timestamp
where host_id = 'Not Applicable'
and host_set_id = 'Not Applicable'
and target_id = new.target_id
and current_row_indicator = 'Current';
-- insert a new row
insert into wh_host_dimension (
host_id, host_type, host_name, host_description,
network_address_group_key,
host_set_id, host_set_type, host_set_name, host_set_description,
host_catalog_id, host_catalog_type, host_catalog_name, host_catalog_description,
target_id, target_type, target_name, target_description,
target_default_port_number, target_session_max_seconds, target_session_connection_limit,
project_id, project_name, project_description,
organization_id, organization_name, organization_description,
current_row_indicator, row_effective_time, row_expiration_time
)
select host_id, host_type, host_name, host_description,
addr_group_key,
host_set_id, host_set_type, host_set_name, host_set_description,
host_catalog_id, host_catalog_type, host_catalog_name, host_catalog_description,
target_id, target_type, target_name, target_description,
target_default_port_number, target_session_max_seconds, target_session_connection_limit,
project_id, project_name, project_description,
organization_id, organization_name, organization_description,
'Current', current_timestamp, 'infinity'::timestamptz
from whx_host_dimension_source
where host_id = 'Not Applicable'
and host_set_id = 'Not Applicable'
and target_id = new.target_id;
end if;
select key into p_host_key
from wh_host_dimension as t
where t.current_row_indicator = 'Current'
and t.host_id = 'Not Applicable'
and t.host_set_id = 'Not Applicable'
and t.target_id = new.target_id;
update wh_session_accumulating_fact
set host_key = p_host_key
where session_id = new.session_id;
return new;
end;
$$ language plpgsql;
create trigger wh_update_session_connection_accumulating_fact after insert on session_target_address
for each row execute procedure wh_upsert_host_direct_network_address();
commit;
@@ -1,188 +0,0 @@
package oss_test
import (
"context"
"crypto/rand"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/common"
"github.com/hashicorp/boundary/internal/db/schema"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/target"
"github.com/hashicorp/boundary/internal/target/targettest"
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/hashicorp/boundary/testing/dbtest"
"github.com/stretchr/testify/require"
)
const (
insertTargetQuery = `INSERT INTO "target_tcp" ("public_id","project_id","name","session_max_seconds","session_connection_limit","worker_filter") VALUES ($1,$2,$3,28800,-1,$4)`
)
func TestMigrations_AddEgressAndIngressFilters(t *testing.T) {
const (
priorMigration = 58001
currentMigration = 59001
)
t.Parallel()
ctx := context.Background()
dialect := dbtest.Postgres
c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1))
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, c())
})
d, err := common.SqlOpen(dialect, u)
require.NoError(t, err)
// migration to the prior migration (before the one we want to test)
m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}),
))
require.NoError(t, err)
_, err = m.ApplyMigrations(ctx)
require.NoError(t, err)
state, err := m.CurrentState(ctx)
require.NoError(t, err)
want := &schema.State{
Initialized: true,
Editions: []schema.EditionState{
{
Name: "oss",
BinarySchemaVersion: priorMigration,
DatabaseSchemaVersion: priorMigration,
DatabaseSchemaState: schema.Equal,
},
},
}
require.Equal(t, want, state)
// Get a connection
dbType, err := db.StringToDbType(dialect)
require.NoError(t, err)
conn, err := db.Open(ctx, dbType, u)
require.NoError(t, err)
wrapper := db.TestWrapper(t)
rw := db.New(conn)
kmsCache := kms.TestKms(t, conn, wrapper)
err = kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader))
require.NoError(t, err)
iamRepo := iam.TestRepo(t, conn, wrapper)
_, proj := iam.TestScopes(t, iamRepo)
// Create a target with a worker filter
oldTargetId1, err := db.NewPublicId("ttcp")
oldWorkerFilter := "foo==bar"
require.NoError(t, err)
execResult, err := d.ExecContext(ctx, insertTargetQuery, oldTargetId1, proj.PublicId, "old-target-1", oldWorkerFilter)
require.NoError(t, err)
rowsAffected, err := execResult.RowsAffected()
require.NoError(t, err)
require.Equal(t, int64(1), rowsAffected)
// Create another target with a worker filter to use in testing
oldTargetId2, err := db.NewPublicId("ttcp")
require.NoError(t, err)
execResult, err = d.ExecContext(ctx, insertTargetQuery, oldTargetId2, proj.PublicId, "old-target-2", oldWorkerFilter)
require.NoError(t, err)
rowsAffected, err = execResult.RowsAffected()
require.NoError(t, err)
require.Equal(t, int64(1), rowsAffected)
// now we're ready for the migration we want to test.
m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}),
))
require.NoError(t, err)
_, err = m.ApplyMigrations(ctx)
require.NoError(t, err)
state, err = m.CurrentState(ctx)
require.NoError(t, err)
want = &schema.State{
Initialized: true,
Editions: []schema.EditionState{
{
Name: "oss",
BinarySchemaVersion: currentMigration,
DatabaseSchemaVersion: currentMigration,
DatabaseSchemaState: schema.Equal,
},
},
}
require.Equal(t, want, state)
testRepo, err := target.NewRepository(ctx, rw, rw, kmsCache)
// Ensure old target 1 still have the same worker filter
result, _, _, err := testRepo.LookupTarget(ctx, oldTargetId1)
require.Equal(t, oldWorkerFilter, result.GetWorkerFilter())
// Update the old worker filter
newWorkerFilter := "bar==foo"
result.SetWorkerFilter(newWorkerFilter)
result, _, _, _, err = testRepo.UpdateTarget(ctx, result, result.GetVersion(), []string{"WorkerFilter"})
require.Equal(t, newWorkerFilter, result.GetWorkerFilter())
// Update to set an egress filter- expect worker filter to be cleared out
result.SetEgressWorkerFilter(newWorkerFilter)
result, _, _, _, err = testRepo.UpdateTarget(ctx, result, result.GetVersion(), []string{"EgressWorkerFilter"})
require.Equal(t, newWorkerFilter, result.GetEgressWorkerFilter())
require.Empty(t, result.GetWorkerFilter())
// Attempt to set worker filter again - expect failure
result.SetWorkerFilter(newWorkerFilter)
result, _, _, _, err = testRepo.UpdateTarget(ctx, result, result.GetVersion(), []string{"WorkerFilter"})
require.Error(t, err)
// Ensure old target 2 still has the same worker filter
result, _, _, err = testRepo.LookupTarget(ctx, oldTargetId2)
require.Equal(t, oldWorkerFilter, result.GetWorkerFilter())
// Clear out its worker filter
result.SetWorkerFilter("")
result, _, _, _, err = testRepo.UpdateTarget(ctx, result, result.GetVersion(), []string{"WorkerFilter"})
require.Equal(t, "", result.GetWorkerFilter())
// Update to set an ingress filter- expect worker filter to be cleared out
result.SetIngressWorkerFilter(newWorkerFilter)
result, _, _, _, err = testRepo.UpdateTarget(ctx, result, result.GetVersion(), []string{"IngressWorkerFilter"})
require.Equal(t, newWorkerFilter, result.GetIngressWorkerFilter())
require.Empty(t, result.GetWorkerFilter())
// Attempt to create a new target with a worker filter- expect failure
tar, err := targettest.New(proj.PublicId, target.WithName("new-worker"), target.WithWorkerFilter(oldWorkerFilter))
require.NoError(t, err)
id, err := db.NewPublicId("ttcp")
require.NoError(t, err)
tar.SetPublicId(ctx, id)
err = rw.Create(context.Background(), tar)
require.Error(t, err)
// Create a new target with egress and ingress filters
tar2, err := targettest.New(proj.PublicId, target.WithName("new-worker-filters"), target.WithEgressWorkerFilter(oldWorkerFilter),
target.WithIngressWorkerFilter(oldWorkerFilter))
require.NoError(t, err)
id, err = db.NewPublicId("ttcp")
require.NoError(t, err)
tar2.SetPublicId(ctx, id)
err = rw.Create(context.Background(), tar2)
require.NoError(t, err)
// Attempt to create a new target with all 3 filters- expect failure
tar3, err := targettest.New(proj.PublicId, target.WithName("new-worker-filters"), target.WithEgressWorkerFilter(oldWorkerFilter),
target.WithIngressWorkerFilter(oldWorkerFilter), target.WithEgressWorkerFilter(oldWorkerFilter))
require.NoError(t, err)
id, err = db.NewPublicId("ttcp")
require.NoError(t, err)
tar3.SetPublicId(ctx, id)
err = rw.Create(context.Background(), tar3)
require.Error(t, err)
}
@@ -10,7 +10,8 @@ begin;
(parent_id, type, public_id, name)
values
('o_____colors', 'project', 'p____bcolors', 'Blue Color Mill'),
('o_____colors', 'project', 'p____rcolors', 'Red Color Mill');
('o_____colors', 'project', 'p____rcolors', 'Red Color Mill'),
('o_____colors', 'project', 'p____gcolors', 'Green Color Mill');
-- Add global users
insert into iam_user
@@ -27,7 +28,8 @@ begin;
('o_____colors', 'u______clare', 'Clare'),
('o_____colors', 'u______cindy', 'Cindy'),
('o_____colors', 'u______carly', 'Carly'),
('o_____colors', 'u______ciara', 'Ciara');
('o_____colors', 'u______ciara', 'Ciara'),
('o_____colors', 'u_______cora', 'Cora');
-- Add keys
insert into kms_root_key
@@ -56,7 +58,8 @@ begin;
('global', 'g___gg-group', 'Global Group'),
('o_____colors', 'g___oc-group', 'Colors R Us Group'),
('p____bcolors', 'g___cb-group', 'Blue Color Group'),
('p____rcolors', 'g___cr-group', 'Red Color Group');
('p____rcolors', 'g___cr-group', 'Red Color Group'),
('p____gcolors', 'g___cg-group', 'Green Color Group');
insert into iam_group_member_user
(group_id, member_id)
@@ -64,15 +67,18 @@ begin;
('g___gg-group', 'u_______gary'),
('g___oc-group', 'u______clare'),
('g___cb-group', 'u______cindy'),
('g___cr-group', 'u______carly');
('g___cr-group', 'u______carly'),
('g___cg-group', 'u_______cora');
insert into iam_role
(scope_id, grant_scope_id, public_id, name)
values
('p____bcolors', 'p____bcolors', 'r_pp_bc__mix', 'Color Mixer'),
('p____rcolors', 'p____rcolors', 'r_pp_rc__mix', 'Color Mixer'),
('p____gcolors', 'p____gcolors', 'r_pp_gc__mix', 'Color Mixer'),
('o_____colors', 'p____bcolors', 'r_op_bc__art', 'Blue Color Artist'),
('o_____colors', 'p____rcolors', 'r_op_rc__art', 'Red Color Artist'),
('o_____colors', 'p____gcolors', 'r_op_gc__art', 'Green Color Artist'),
('o_____colors', 'o_____colors', 'r_oo_____art', 'Color Artist'),
('global', 'o_____colors', 'r_go____name', 'Color Namer'),
('global', 'p____bcolors', 'r_gp____spec', 'Blue Color Inspector'),
@@ -89,15 +95,18 @@ begin;
('r_oo_____art', 'type=color;action=create', 'create color'),
('r_op_bc__art', 'type=color;action=create', 'create color'),
('r_op_rc__art', 'type=color;action=create', 'create color'),
('r_op_gc__art', 'type=color;action=create', 'create color'),
('r_pp_bc__mix', 'type=color;action=mix', 'mix color'),
('r_pp_rc__mix', 'type=color;action=mix', 'mix color');
('r_pp_rc__mix', 'type=color;action=mix', 'mix color'),
('r_pp_gc__mix', 'type=color;action=mix', 'mix color');
insert into iam_group_role
(role_id, principal_id)
values
('r_op_rc__art', 'g___oc-group'), -- color
('r_pp_bc__mix', 'g___cb-group'), -- color
('r_pp_rc__mix', 'g___cr-group'); -- color
('r_pp_rc__mix', 'g___cr-group'), -- color
('r_pp_gc__mix', 'g___cg-group'); -- color
insert into iam_user_role
(role_id, principal_id)
@@ -125,18 +134,21 @@ begin;
('apm___colors', 'apa____clare', 'clare'),
('apm___colors', 'apa____cindy', 'cindy'),
('apm___colors', 'apa____carly', 'carly'),
('apm___colors', 'apa_____cora', 'cora'),
('apm___colors', 'apa____ciara', 'ciara');
update auth_account set iam_user_id = 'u______clare' where public_id = 'apa____clare';
update auth_account set iam_user_id = 'u______cindy' where public_id = 'apa____cindy';
update auth_account set iam_user_id = 'u______carly' where public_id = 'apa____carly';
update auth_account set iam_user_id = 'u_______cora' where public_id = 'apa_____cora';
update auth_account set iam_user_id = 'u______ciara' where public_id = 'apa____ciara';
insert into static_host_catalog
(project_id, public_id, name)
values
('p____bcolors', 'c___cb-sthcl', 'Blue Color Static Catalog'),
('p____rcolors', 'c___cr-sthcl', 'Red Color Static Catalog');
('p____rcolors', 'c___cr-sthcl', 'Red Color Static Catalog'),
('p____gcolors', 'c___cg-sthcl', 'Green Color Static Catalog');
insert into auth_token
(key_id, auth_account_id, public_id, token)
@@ -144,7 +156,8 @@ begin;
('kdkv_______colors', 'apa____clare', 'tok____clare', 'tok____clare'::bytea),
('kdkv_______colors', 'apa____cindy', 'tok____cindy', 'tok____cindy'::bytea),
('kdkv_______colors', 'apa____ciara', 'tok____ciara', 'tok____ciara'::bytea),
('kdkv_______colors', 'apa____carly', 'tok____carly', 'tok____carly'::bytea);
('kdkv_______colors', 'apa____carly', 'tok____carly', 'tok____carly'::bytea),
('kdkv_______colors', 'apa_____cora', 'tok_____cora', 'tok_____cora'::bytea);
insert into static_host
(catalog_id, public_id, address)
@@ -167,7 +180,17 @@ begin;
('c___cr-sthcl', 'h_____cr__06', '6.red.color'),
('c___cr-sthcl', 'h_____cr__07', '7.red.color'),
('c___cr-sthcl', 'h_____cr__08', '8.red.color'),
('c___cr-sthcl', 'h_____cr__09', '9.red.color');
('c___cr-sthcl', 'h_____cr__09', '9.red.color'),
('c___cg-sthcl', 'h_____cg__01', '1.green.color'),
('c___cg-sthcl', 'h_____cg__02', '2.green.color'),
('c___cg-sthcl', 'h_____cg__03', '3.green.color'),
('c___cg-sthcl', 'h_____cg__04', '4.green.color'),
('c___cg-sthcl', 'h_____cg__05', '5.green.color'),
('c___cg-sthcl', 'h_____cg__06', '6.green.color'),
('c___cg-sthcl', 'h_____cg__07', '7.green.color'),
('c___cg-sthcl', 'h_____cg__08', '8.green.color'),
('c___cg-sthcl', 'h_____cg__09', '9.green.color');
insert into host_dns_name
(host_id, name)
@@ -190,7 +213,17 @@ begin;
('h_____cr__06', '6.red.color'),
('h_____cr__07', '7.red.color'),
('h_____cr__08', '8.red.color'),
('h_____cr__09', '9.red.color');
('h_____cr__09', '9.red.color'),
('h_____cg__01', '1.green.color'),
('h_____cg__02', '2.green.color'),
('h_____cg__03', '3.green.color'),
('h_____cg__04', '4.green.color'),
('h_____cg__05', '5.green.color'),
('h_____cg__06', '6.green.color'),
('h_____cg__07', '7.green.color'),
('h_____cg__08', '8.green.color'),
('h_____cg__09', '9.green.color');
insert into host_ip_address
(host_id, address)
@@ -213,7 +246,17 @@ begin;
('h_____cr__06', '2001:4860:4860::6666'),
('h_____cr__07', '77.77.77.77'),
('h_____cr__08', '2001:4860:4860::8888'),
('h_____cr__09', '99.99.99.99');
('h_____cr__09', '99.99.99.99'),
('h_____cg__01', '111.111.111.111'),
('h_____cg__02', '3001:5860:5860::3333'),
('h_____cg__03', '112.112.112.112'),
('h_____cg__04', '3001:5860:5860::5555'),
('h_____cg__05', '113.113.113.113'),
('h_____cg__06', '3001:5860:5860::7777'),
('h_____cg__07', '114.114.114.114'),
('h_____cg__08', '3001:5860:5860::9999'),
('h_____cg__09', '115.115.115.115');
insert into static_host_set
(catalog_id, public_id, name)
@@ -221,7 +264,9 @@ begin;
('c___cb-sthcl', 's___1cb-sths', 'Blue Color Static Set 1'),
('c___cb-sthcl', 's___2cb-sths', 'Blue Color Static Set 2'),
('c___cr-sthcl', 's___1cr-sths', 'Red Color Static Set 1'),
('c___cr-sthcl', 's___2cr-sths', 'Red Color Static Set 2');
('c___cr-sthcl', 's___2cr-sths', 'Red Color Static Set 2'),
('c___cg-sthcl', 's___1cg-sths', 'Green Color Static Set 1'),
('c___cg-sthcl', 's___2cg-sths', 'Green Color Static Set 2');
insert
into static_host_set_member
@@ -235,7 +280,8 @@ begin;
(project_id, public_id, name)
values
('p____bcolors', 't_________cb', 'Blue Color Target'),
('p____rcolors', 't_________cr', 'Red Color Target');
('p____rcolors', 't_________cr', 'Red Color Target'),
('p____gcolors', 't_________cg', 'Green Color Target');
insert into target_host_set
(project_id, target_id, host_set_id)
@@ -245,6 +291,11 @@ begin;
('p____rcolors', 't_________cr', 's___1cr-sths'),
('p____rcolors', 't_________cr', 's___2cr-sths');
insert into target_address
(target_id, address)
values
('t_________cg', '8.8.8.8');
insert into credential_vault_store
(project_id, public_id, name, description, vault_address, namespace)
values
@@ -263,12 +314,26 @@ begin;
('p____bcolors', 't_________cb', 'vl______cvl', 'brokered');
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bcolors', 't_________cb', 's___1cb-sths', 'h_____cb__01', 'u______clare', 'tok____clare', 'abc'::bytea, 'ep1', 's1_____clare'),
('p____bcolors', 't_________cb', 's___1cb-sths', 'h_____cb__01', 'u______cindy', 'tok____cindy', 'abc'::bytea, 'ep1', 's1_____cindy'),
('p____bcolors', 't_________cb', 's___1cb-sths', 'h_____cb__01', 'u______cindy', 'tok____cindy', 'abc'::bytea, 'ep1', 's1_____ciara'),
('p____bcolors', 't_________cb', 's___1cb-sths', 'h_____cb__01', 'u______carly', 'tok____carly', 'abc'::bytea, 'ep1', 's1_____carly');
('p____bcolors', 't_________cb', 'u______clare', 'tok____clare', 'abc'::bytea, 'ep1', 's1_____clare'),
('p____bcolors', 't_________cb', 'u______cindy', 'tok____cindy', 'abc'::bytea, 'ep1', 's1_____cindy'),
('p____bcolors', 't_________cb', 'u______cindy', 'tok____cindy', 'abc'::bytea, 'ep1', 's1_____ciara'),
('p____bcolors', 't_________cb', 'u______carly', 'tok____carly', 'abc'::bytea, 'ep1', 's1_____carly'),
('p____gcolors', 't_________cg', 'u_______cora', 'tok_____cora', 'abc'::bytea, 'ep1', 's1______cora');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1_____clare', 's___1cb-sths', 'h_____cb__01'),
('s1_____cindy', 's___1cb-sths', 'h_____cb__01'),
('s1_____ciara', 's___1cb-sths', 'h_____cb__01'),
('s1_____carly', 's___1cb-sths', 'h_____cb__01');
insert into session_target_address
(session_id, target_id)
values
('s1______cora', 't_________cg');
insert into session_connection
(session_id, public_id)
@@ -122,8 +122,13 @@ begin;
('r_pp___tacos', 'u________bob');
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p______tacos', 't__________t', 's____1t-sths', 'h______t__01', 'u________jim', 'tok______jim', 'abc'::bytea, 'ep1', 's1_______jim');
('p______tacos', 't__________t', 'u________jim', 'tok______jim', 'abc'::bytea, 'ep1', 's1_______jim');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1_______jim', 's____1t-sths', 'h______t__01');
commit;
@@ -0,0 +1,28 @@
begin;
select plan(12);
-- Ensure session state table is populated
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____cindy' and state='terminated';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____ciara' and state='canceling';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='active';
-- Check that we have a session for both auth token
select is(count(*), 1::bigint) from session where auth_token_id = 'tok____clare';
select is(count(*), 1::bigint) from session where auth_token_id = 'tok____carly';
-- Delete auth tokens, expect no errors
delete from auth_token where public_id = 'tok____clare' or public_id = 'tok____carly';
select is(count(*), 0::bigint) from auth_token where public_id = 'tok____clare';
select is(count(*), 0::bigint) from auth_token where public_id = 'tok____carly';
-- Ensure we no longer have sessions associated with auth tokens
select is(count(*), 0::bigint) from session where auth_token_id = 'tok____clare';
select is(count(*), 0::bigint) from session where auth_token_id = 'tok____carly';
-- Ensure sessions that were pending or active are now in canceling state
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1_____clare';
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1_____carly';
select * from finish();
rollback;
@@ -0,0 +1,25 @@
begin;
select plan(9);
-- Ensure session state table is populated
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____cindy' and state='terminated';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____ciara' and state='canceling';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='active';
-- Check that we have 4 sessions using this host
select is(count(*), 4::bigint) from session_host_set_host where host_id = 'h_____cb__01';
-- Delete host, expect no errors
delete from host where public_id = 'h_____cb__01';
select is(count(*), 0::bigint) from host where public_id = 'h_____cb__01';
-- Ensure we no longer have sessions associated with this host
select is(count(*), 0::bigint) from session_host_set_host where host_id = 'h_____cb__01';
-- Ensure sessions that were pending or active are now in canceling state
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1_____clare';
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1_____carly';
select * from finish();
rollback;
@@ -0,0 +1,25 @@
begin;
select plan(9);
-- Ensure session state table is populated
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____cindy' and state='terminated';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____ciara' and state='canceling';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='active';
-- Check that we have 4 sessions using this host set
select is(count(*), 4::bigint) from session_host_set_host where host_set_id = 's___1cb-sths';
-- Delete host set, expect no errors
delete from host_set where public_id = 's___1cb-sths';
select is(count(*), 0::bigint) from host_set where public_id = 's___1cb-sths';
-- Ensure we no longer have sessions associated with this host set
select is(count(*), 0::bigint) from session_host_set_host where host_set_id = 's___1cb-sths';
-- Ensure sessions that were pending or active are now in canceling state
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1_____clare';
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1_____carly';
select * from finish();
rollback;
@@ -0,0 +1,25 @@
begin;
select plan(9);
-- Ensure session state table is populated
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____cindy' and state='terminated';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____ciara' and state='canceling';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='active';
-- Check that we have 4 sessions using this project
select is(count(*), 4::bigint) from session where project_id = 'p____bcolors';
-- Delete project, expect no errors
delete from iam_scope_project where scope_id = 'p____bcolors';
select is(count(*), 0::bigint) from iam_scope_project where scope_id = 'p____bcolors';
-- Ensure we no longer have sessions associated with this project
select is(count(*), 0::bigint) from session where project_id = 'p____bcolors';
-- Ensure sessions that were pending or active are now in canceling state
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1_____clare';
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1_____carly';
select * from finish();
rollback;
@@ -0,0 +1,25 @@
begin;
select plan(9);
-- Ensure session state table is populated
select is(count(*), 1::bigint) from session_state where session_id = 's1______cora' and state='pending';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____cindy' and state='terminated';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____ciara' and state='canceling';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='active';
-- Check that we have a session using this target address
select is(count(*), 1::bigint) from session_target_address where target_id = 't_________cg';
-- Delete target address, expect no errors
delete from target_address where target_id = 't_________cg';
select is(count(*), 0::bigint) from target_address where target_id = 't_________cg';
-- Ensure we no longer have sessions associated with this target address
select is(count(*), 0::bigint) from session_target_address where target_id = 't_________cg';
-- Ensure sessions that were pending or active are now in canceling state
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1______cora';
select * from finish();
rollback;
@@ -0,0 +1,28 @@
begin;
select plan(12);
-- Ensure session state table is populated
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____cindy' and state='terminated';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____ciara' and state='canceling';
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='active';
-- Check that we have a session for both users
select is(count(*), 1::bigint) from session where user_id = 'u______clare';
select is(count(*), 1::bigint) from session where user_id = 'u______carly';
-- Delete users, expect no errors
delete from iam_user where public_id = 'u______clare' or public_id = 'u______carly';
select is(count(*), 0::bigint) from iam_user where public_id = 'u______clare';
select is(count(*), 0::bigint) from iam_user where public_id = 'u______carly';
-- Ensure we no longer have sessions associated with the users
select is(count(*), 0::bigint) from session where user_id = 'u______clare';
select is(count(*), 0::bigint) from session where user_id = 'u______carly';
-- Ensure sessions that were pending or active are now in canceling state
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1_____clare';
select is(count(*), 1::bigint) from session_state where state = 'canceling' and session_id = 's1_____carly';
select * from finish();
rollback;
@@ -10,9 +10,13 @@ begin;
-- insert first session, should result in a new credentials dimension
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -25,9 +29,13 @@ begin;
-- * same host
-- should not result in a new credential dimension
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's2____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's2____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s2____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -39,9 +47,13 @@ begin;
-- start another session, should result in a new credential dimension
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's3____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's3____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s3____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -50,9 +62,13 @@ begin;
-- start another session, should result in a one new credential dimensions
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's4____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's4____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s4____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -65,9 +81,13 @@ begin;
-- start another session, should result in a one new credential dimensions since one changed
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's5____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's5____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s5____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -80,9 +100,13 @@ begin;
-- then test creating a session
delete from credential_vault_library;
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's6____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's6____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s6____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 4::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
select is(credential_group_key, 'no credentials') from wh_session_accumulating_fact where session_id = 's6____walter';
insert into session_connection
@@ -93,9 +117,13 @@ begin;
-- insert into a session for a target that never had any credentials associated with it.
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________ws', 's___1ws-sths', 'h_____ws__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's7____walter');
('p____bwidget', 't_________ws', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's7____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s7____walter', 's___1ws-sths', 'h_____ws__01');
select is(count(*), 4::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
select is(credential_group_key, 'no credentials') from wh_session_accumulating_fact where session_id = 's7____walter';
insert into session_connection
@@ -10,9 +10,13 @@ begin;
-- insert first session, should result in a new user dimension
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -14,9 +14,13 @@ begin;
-- insert session and session_credential_dynamic, should result in a three new credential dimensions
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -29,9 +33,13 @@ begin;
update credential_vault_library set vault_path = '/secrets/tcp/user' where public_id = 'vl______wvl2';
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's2____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's2____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s2____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -4,9 +4,13 @@ begin;
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -0,0 +1,78 @@
begin;
select plan(8);
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets');
-- ensure no existing dimensions
select is(count(*), 0::bigint) from wh_host_dimension where organization_id = 'o_____widget';
-- create network address dimension using static host
insert into session
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_host_dimension where organization_id = 'o_____widget';
select is(count(*), 1::bigint) from wh_network_address_dimension
where address = '1.big.widget'
and address_type = 'DNS Name'
and ip_address_family = 'Not Applicable'
and private_ip_address_indicator = 'Not Applicable'
and dns_name = '1.big.widget'
and ip4_address = 'Not Applicable'
and ip6_address = 'Not Applicable';
select is(count(*), 1::bigint)
from wh_network_address_group_membership g
left join wh_host_dimension h on g.network_address_group_key = h.network_address_group_key
where g.network_address = '1.big.widget'
and h.target_id = 't_________wb'
and h.host_id = 'h_____wb__01'
and h.host_set_id = 's___1wb-sths'
and h.current_row_indicator = 'Current';
-- delete target host source associations to enforce mutually exclusive relationship to a target address
delete from target_host_set where target_id = 't_________wb';
-- update target to use the same address, but as a direct network address association
insert into target_address
(target_id, address)
values
('t_________wb', '1.big.widget');
select is(count(*), 0::bigint) from target_host_set where target_id = 't_________wb';
insert into session
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's2____walter');
insert into session_target_address
(session_id, target_id)
values
('s2____walter', 't_________wb');
-- this should have expired the old host_dimension that had values inserted in the columns related to hosts, host sets, & host catalogs.
-- this should have created a new host_dimension for a static address with 'Not Applicable' values set for columns related to hosts, host sets, & host catalogs.
-- this should have used the existing network_address_dimension created from the session using a host source
select is(count(*), 2::bigint) from wh_host_dimension where organization_id = 'o_____widget';
select is(count(*), 1::bigint) from wh_network_address_dimension
where address = '1.big.widget'
and address_type = 'DNS Name'
and ip_address_family = 'Not Applicable'
and private_ip_address_indicator = 'Not Applicable'
and dns_name = '1.big.widget'
and ip4_address = 'Not Applicable'
and ip6_address = 'Not Applicable';
select is(count(*), 1::bigint)
from wh_network_address_group_membership g
left join wh_host_dimension h on g.network_address_group_key = h.network_address_group_key
where g.network_address = '1.big.widget'
and h.target_id = 't_________wb'
and h.host_id = 'Not Applicable'
and h.host_set_id = 'Not Applicable'
and h.current_row_indicator = 'Current';
select * from finish();
rollback;
@@ -10,10 +10,13 @@ begin;
-- insert first session, should result in a new host dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-plghs' , 'h_____wb__02-plgh' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-plghs', 'h_____wb__02-plgh');
select is(count(*), 1::bigint) from wh_host_dimension where organization_id = 'o_____widget';
-- another session with:
@@ -22,10 +25,14 @@ begin;
-- * same host
-- should not result in a new host dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-plghs' , 'h_____wb__02-plgh' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's2____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's2____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s2____walter', 's___1wb-plghs', 'h_____wb__02-plgh');
select is(count(*), 1::bigint) from wh_host_dimension where organization_id = 'o_____widget';
-- another session with:
@@ -34,9 +41,13 @@ begin;
-- * same host
-- should not result in a new host dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-plghs' , 'h_____wb__02-plgh' , 'u_____warren' , 'tok___warren' , 'abc'::bytea , 'ep1' , 's3____walter');
('p____bwidget' , 't_________wb' , 'u_____warren' , 'tok___warren' , 'abc'::bytea , 'ep1' , 's3____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s3____walter', 's___1wb-plghs', 'h_____wb__02-plgh');
select is(count(*), 1::bigint) from wh_host_dimension where organization_id = 'o_____widget';
@@ -52,9 +63,13 @@ begin;
-- should result in a new host dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-plghs' , 'h_____wb__02-plgh' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's4____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's4____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s4____walter', 's___1wb-plghs', 'h_____wb__02-plgh');
select is(count(*), 2::bigint) from wh_host_dimension where organization_id = 'o_____widget';
@@ -70,9 +85,13 @@ begin;
-- should result in a new host dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-plghs' , 'h_____wb__02-plgh' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's5____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's5____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s5____walter', 's___1wb-plghs', 'h_____wb__02-plgh');
select is(count(*), 3::bigint) from wh_host_dimension where organization_id = 'o_____widget';
@@ -0,0 +1,138 @@
begin;
select plan(12);
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets');
-- ensure no existing dimensions
select is(count(*), 0::bigint) from wh_host_dimension where organization_id = 'o_____widget';
-- setup target to use a ipv4 static address
insert into target_address
(target_id, address)
values
('t_________wb', '8.6.4.2');
-- insert first session, should result in a new host dimension
insert into session
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_target_address
(session_id, target_id)
values
('s1____walter', 't_________wb');
select is(count(*), 1::bigint) from wh_host_dimension where organization_id = 'o_____widget';
-- expected wh_network_address_dimension row is created for ipv4
select is(count(*), 1::bigint) from wh_network_address_dimension
where address = '8.6.4.2'
and address_type = 'IP Address'
and ip_address_family = 'IPv4'
and private_ip_address_indicator = 'Public IP address'
and dns_name = 'Not Applicable'
and ip4_address = '8.6.4.2'
and ip6_address = 'Not Applicable';
-- expected wh_network_address_group_membership has one row: '8.6.4.2'
select is(count(*), 1::bigint)
from wh_network_address_group_membership gm,
wh_host_dimension h
where h.network_address_group_key = gm.network_address_group_key
and h.host_id = 'Not Applicable'
and h.host_set_id = 'Not Applicable'
and h.host_catalog_id = 'Not Applicable'
and h.target_id = 't_________wb';
-- update target address association to ipv6
update target_address set address = 'fe60::6667:5556:4445:3334' where target_id = 't_________wb';
insert into session
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's2____walter');
insert into session_target_address
(session_id, target_id)
values
('s2____walter', 't_________wb');
-- should not result in a new host dimension
select is(count(*), 1::bigint) from wh_host_dimension where organization_id = 'o_____widget';
-- expected wh_network_address_dimension row is created for ipv6
select is(count(*), 1::bigint) from wh_network_address_dimension
where address = 'fe60::6667:5556:4445:3334'
and address_type = 'IP Address'
and ip_address_family = 'IPv6'
and private_ip_address_indicator = 'Public IP address'
and dns_name = 'Not Applicable'
and ip4_address = 'Not Applicable'
and ip6_address = 'fe60::6667:5556:4445:3334';
-- expected wh_network_address_group_membership has two rows: '8.6.4.2' & 'fe60::6667:5556:4445:3334'
select is(count(*), 2::bigint)
from wh_network_address_group_membership gm,
wh_host_dimension h
where h.network_address_group_key = gm.network_address_group_key
and h.host_id = 'Not Applicable'
and h.host_set_id = 'Not Applicable'
and h.host_catalog_id = 'Not Applicable'
and h.target_id = 't_________wb';
-- update static address association to dns name
update target_address set address = '0.blue.green' where target_id = 't_________wb';
insert into session
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's3____walter');
insert into session_target_address
(session_id, target_id)
values
('s3____walter', 't_________wb');
-- should not result in a new host dimension
select is(count(*), 1::bigint) from wh_host_dimension where organization_id = 'o_____widget';
-- expected wh_network_address_dimension row is created for dns
select is(count(*), 1::bigint) from wh_network_address_dimension
where address = '0.blue.green'
and address_type = 'DNS Name'
and ip_address_family = 'Not Applicable'
and private_ip_address_indicator = 'Not Applicable'
and dns_name = '0.blue.green'
and ip4_address = 'Not Applicable'
and ip6_address = 'Not Applicable';
-- expected wh_network_address_group_membership has three rows: '8.6.4.2' & 'fe60::6667:5556:4445:3334' & '0.blue.green'
select is(count(*), 3::bigint)
from wh_network_address_group_membership gm,
wh_host_dimension h
where h.network_address_group_key = gm.network_address_group_key
and h.host_id = 'Not Applicable'
and h.host_set_id = 'Not Applicable'
and h.host_catalog_id = 'Not Applicable'
and h.target_id = 't_________wb';
-- create second session with the same resources
insert into session
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's4____walter');
insert into session_target_address
(session_id, target_id)
values
('s4____walter', 't_________wb');
-- should not result in a new host dimension
select is(count(*), 1::bigint) from wh_host_dimension where organization_id = 'o_____widget';
-- expected wh_network_address_group_membership does not change.
select is(count(*), 3::bigint)
from wh_network_address_group_membership gm,
wh_host_dimension h
where h.network_address_group_key = gm.network_address_group_key
and h.host_id = 'Not Applicable'
and h.host_set_id = 'Not Applicable'
and h.host_catalog_id = 'Not Applicable'
and h.target_id = 't_________wb';
select * from finish();
rollback;
@@ -10,9 +10,13 @@ begin;
-- insert first session, should result in a new host dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-plghs' , 'h_____wb__01-plgh' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-plghs', 'h_____wb__01-plgh');
select is(count(*), 1::bigint) from wh_host_dimension where organization_id = 'o_____widget';
@@ -9,9 +9,13 @@ begin;
-- insert first session, should result in a new wh_session_accumulating_fact
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_session_accumulating_fact where session_id = 's1____walter';
@@ -8,10 +8,13 @@ begin;
select is(count(*), 0::bigint) from wh_session_connection_accumulating_fact where connection_id = 'sc1____walter';
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_connection
(session_id, public_id)
values
@@ -10,10 +10,13 @@ begin;
--- insert single credential
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -10,9 +10,13 @@ begin;
--- multiple single credentials
insert into session
( project_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
values
('p____bwidget', 't_________wb', 's___1wb-sths', 'h_____wb__01', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
('p____bwidget', 't_________wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
insert into session_credential_dynamic
( session_id, library_id, credential_id, credential_purpose)
values
@@ -10,9 +10,13 @@ begin;
-- insert first session, should result in a new user dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'oidc__walter' , 'abc'::bytea , 'ep1' , 's1____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'oidc__walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_user_dimension where user_id = 'u_____walter';
select is(user_id, 'u_____walter') from wh_user_dimension where user_id = 'u_____walter';
@@ -41,9 +45,13 @@ begin;
-- insert session without full name or email
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____warren' , 'oidc__warren' , 'abc'::bytea , 'ep1' , 's1____warren');
('p____bwidget' , 't_________wb' , 'u_____warren' , 'oidc__warren' , 'abc'::bytea , 'ep1' , 's1____warren');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____warren', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_user_dimension where user_id = 'u_____warren';
select is(user_name, 'Warren') from wh_user_dimension where user_id = 'u_____warren';
@@ -11,9 +11,13 @@ begin;
-- insert first session, should result in a new user dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_user_dimension where user_id = 'u_____walter';
select is(user_id, 'u_____walter') from wh_user_dimension where user_id = 'u_____walter';
@@ -52,9 +56,13 @@ begin;
-- * same host
-- should result in a new user dimension due to auth change.
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's2____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's2____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s2____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 2::bigint) from wh_user_dimension where user_organization_id = 'o_____widget';
@@ -10,9 +10,13 @@ begin;
-- insert first session, should result in a new user dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_user_dimension where user_id = 'u_____walter';
select is(user_id, 'u_____walter') from wh_user_dimension where user_id = 'u_____walter';
@@ -10,9 +10,13 @@ begin;
-- insert first session, should result in a new user dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_user_dimension where user_organization_id = 'o_____widget';
@@ -22,9 +26,13 @@ begin;
-- * same host
-- should not result in a new user dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's2____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's2____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s2____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_user_dimension where user_organization_id = 'o_____widget';
@@ -34,9 +42,13 @@ begin;
-- * different host
-- should not result in a new user dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__02' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's3____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's3____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s3____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_user_dimension where user_organization_id = 'o_____widget';
@@ -11,9 +11,13 @@ begin;
-- insert first session, should result in a new user dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_user_dimension where user_organization_id = 'o_____widget';
@@ -23,9 +27,13 @@ begin;
-- * same host
-- should result in a new user dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'tok1__walter' , 'abc'::bytea , 'ep1' , 's4____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok1__walter' , 'abc'::bytea , 'ep1' , 's4____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s4____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 2::bigint) from wh_user_dimension where user_organization_id = 'o_____widget';
select is(count(*), 1::bigint) from wh_user_dimension where user_id = 'u_____walter' and auth_account_id = 'apa1__walter';
@@ -10,9 +10,13 @@ begin;
-- insert first session, should result in a new user dimension
insert into session
( project_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
( project_id , target_id , user_id , auth_token_id , certificate , endpoint , public_id)
values
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
('p____bwidget' , 't_________wb' , 'u_____walter' , 'tok___walter' , 'abc'::bytea , 'ep1' , 's1____walter');
insert into session_host_set_host
(session_id, host_set_id, host_id)
values
('s1____walter', 's___1wb-sths', 'h_____wb__01');
select is(count(*), 1::bigint) from wh_user_dimension where user_organization_id = 'o_____widget';
+1
View File
@@ -79,6 +79,7 @@ const (
// client and server error codes
Unauthorized Code = 401 // Unauthorized represents the operation is unauthorized
Forbidden Code = 403 // Forbidden represents the operation is forbidden
Conflict Code = 409 // Conflict represents the operation failed due to failed pre-condition or was aborted.
Internal Code = 500 // InternalError represents the system encountered an unexpected condition.
// DB errors are reserved Codes from 1000-1999
+4
View File
@@ -152,6 +152,10 @@ var errorCodeInfo = map[Code]Info{
Message: "unauthorized",
Kind: Other,
},
Conflict: {
Message: "conflict",
Kind: Integrity,
},
CheckConstraint: {
Message: "constraint check failed",
Kind: Integrity,
+10
View File
@@ -94,3 +94,13 @@ func IsNotFoundError(err error) bool {
return false
}
// IsConflictError returns a boolean indicating whether the error is known to
// report a pre-conditional check violation or an aborted transaction.
func IsConflictError(err error) bool {
if err == nil {
return false
}
return Match(T(Conflict), err)
}
+6 -2
View File
@@ -3062,7 +3062,7 @@
},
"/v1/targets/{id}:add-host-sources": {
"post": {
"summary": "Adds existing Host Sources to a Target.",
"summary": "Adds existing Host Sources to a Target. Cannot be used on targets that have their address field set.",
"operationId": "TargetService_AddTargetHostSources",
"responses": {
"200": {
@@ -3329,7 +3329,7 @@
},
"/v1/targets/{id}:set-host-sources": {
"post": {
"summary": "Sources the Host Sources on the Target.",
"summary": "Sources the Host Sources on the Target. Cannot be used on targets that have their address field set.",
"operationId": "TargetService_SetTargetHostSources",
"responses": {
"200": {
@@ -5710,6 +5710,10 @@
},
"description": "Output only. The available actions on this resource for this user.",
"readOnly": true
},
"address": {
"type": "string",
"description": "Optional string value that represents a network resource and is used when establishing a session."
}
},
"title": "Target contains all fields related to a Target resource"
@@ -1619,7 +1619,7 @@ var file_controller_api_services_v1_target_service_proto_rawDesc = []byte{
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, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x99, 0x14,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x95, 0x15,
0x0a, 0x0d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
0xa2, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2c, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73,
@@ -1686,7 +1686,7 @@ var file_controller_api_services_v1_target_service_proto_rawDesc = []byte{
0x6f, 0x6e, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x74,
0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x7a, 0x65, 0x2d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x01, 0x2a,
0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xe9, 0x01, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x54, 0x61,
0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xa7, 0x02, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x54, 0x61,
0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12,
0x37, 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, 0x41, 0x64, 0x64,
@@ -1695,99 +1695,107 @@ var file_controller_api_services_v1_target_service_proto_rawDesc = []byte{
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48,
0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x5e, 0x92, 0x41, 0x29, 0x12, 0x27, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78,
0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72,
0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67,
0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x68, 0x6f, 0x73,
0x74, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74,
0x65, 0x6d, 0x12, 0xe9, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 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, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67,
0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 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, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e,
0x92, 0x41, 0x29, 0x12, 0x27, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65,
0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e,
0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f,
0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xf3,
0x01, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48,
0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 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, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61,
0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x73, 0x65, 0x22, 0x9b, 0x01, 0x92, 0x41, 0x66, 0x12, 0x64, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65,
0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
0x2e, 0x20, 0x43, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64,
0x20, 0x6f, 0x6e, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74,
0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x61, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x73, 0x65, 0x74, 0x2e, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x12, 0xa7, 0x02, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f,
0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 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, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x38, 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,
0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9b, 0x01, 0x92,
0x41, 0x66, 0x12, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20,
0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x20, 0x43, 0x61, 0x6e, 0x6e,
0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x61,
0x72, 0x67, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20,
0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x66, 0x69,
0x65, 0x6c, 0x64, 0x20, 0x73, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21,
0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d,
0x3a, 0x73, 0x65, 0x74, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xf3, 0x01, 0x0a, 0x17, 0x52,
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 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, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x27, 0x12, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
0x73, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66,
0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82,
0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65,
0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x68,
0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x12, 0x87, 0x02, 0x0a, 0x1a, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67,
0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72,
0x63, 0x65, 0x73, 0x12, 0x3d, 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, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x3b, 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,
0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x6a, 0x92, 0x41, 0x2f, 0x12, 0x2d, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78,
0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54,
0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x27, 0x2f, 0x76,
0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61,
0x64, 0x64, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x84,
0x02, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3d, 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, 0x53, 0x65, 0x74, 0x54, 0x61,
0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 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, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72,
0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x92, 0x41,
0x2c, 0x12, 0x2a, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f,
0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x32, 0x22, 0x27, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73,
0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x91, 0x02, 0x0a, 0x1d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74,
0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x5f, 0x92, 0x41, 0x27, 0x12, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x48, 0x6f,
0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20,
0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02,
0x2f, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b,
0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x12, 0x87, 0x02, 0x0a, 0x1a, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12,
0x3d, 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, 0x41, 0x64, 0x64,
0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x40, 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, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65,
0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e,
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, 0x41, 0x64, 0x64, 0x54,
0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6a,
0x92, 0x41, 0x2f, 0x12, 0x2d, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69,
0x6e, 0x67, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65,
0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x27, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61,
0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x63,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x84, 0x02, 0x0a, 0x1a, 0x53,
0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3d, 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, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72,
0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6b, 0x92, 0x41,
0x2d, 0x12, 0x2b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66, 0x72,
0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x35, 0x22, 0x2a, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x63, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x42, 0x57, 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, 0xa2, 0xe3, 0x29, 0x06, 0x74, 0x61, 0x72, 0x67,
0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 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, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x92, 0x41, 0x2c, 0x12, 0x2a, 0x53,
0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22,
0x27, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64,
0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65,
0x6d, 0x12, 0x91, 0x02, 0x0a, 0x1d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67,
0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72,
0x63, 0x65, 0x73, 0x12, 0x40, 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, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 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, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6b, 0x92, 0x41, 0x2d, 0x12, 0x2b, 0x52,
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74,
0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35,
0x22, 0x2a, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69,
0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x42, 0x57, 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, 0xa2, 0xe3, 0x29, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -53,8 +53,9 @@ type TargetServiceClient interface {
// Host Sources added to the provided Target must be a child of a Catalog that
// is a child of the same scope as this Target. If the scope or Target IDs are
// missing, malformed, or reference non-existing resources, an error is
// returned. An error is returned if a Host Source is attempted to be added to a
// target that is already present on the Target.
// returned. An error is returned if a Host Source is attempted to be added to
// a target that is already present on the Target. If the given target already
// has its address field set, a Bad Request error is returned.
AddTargetHostSources(ctx context.Context, in *AddTargetHostSourcesRequest, opts ...grpc.CallOption) (*AddTargetHostSourcesResponse, error)
// SetTargetHostSources sets the Target's Host Sources. Any existing Host
// Sources on the Target are deleted if they are not included in this request.
@@ -62,7 +63,8 @@ type TargetServiceClient interface {
// Host Sources will be set. All Host Sources in the request must be a child
// of a Catalog that is in the same scope as the provided Target. If any IDs
// are missing, malformed, or references a non-existing resource, an error is
// returned.
// returned. If the given target already has its address field set, a Bad
// Request error is returned.
SetTargetHostSources(ctx context.Context, in *SetTargetHostSourcesRequest, opts ...grpc.CallOption) (*SetTargetHostSourcesResponse, error)
// RemoveTargetHostSources removes the Host Sources from the specified Target.
// The provided request must include the Target ID for the Target from which
@@ -252,8 +254,9 @@ type TargetServiceServer interface {
// Host Sources added to the provided Target must be a child of a Catalog that
// is a child of the same scope as this Target. If the scope or Target IDs are
// missing, malformed, or reference non-existing resources, an error is
// returned. An error is returned if a Host Source is attempted to be added to a
// target that is already present on the Target.
// returned. An error is returned if a Host Source is attempted to be added to
// a target that is already present on the Target. If the given target already
// has its address field set, a Bad Request error is returned.
AddTargetHostSources(context.Context, *AddTargetHostSourcesRequest) (*AddTargetHostSourcesResponse, error)
// SetTargetHostSources sets the Target's Host Sources. Any existing Host
// Sources on the Target are deleted if they are not included in this request.
@@ -261,7 +264,8 @@ type TargetServiceServer interface {
// Host Sources will be set. All Host Sources in the request must be a child
// of a Catalog that is in the same scope as the provided Target. If any IDs
// are missing, malformed, or references a non-existing resource, an error is
// returned.
// returned. If the given target already has its address field set, a Bad
// Request error is returned.
SetTargetHostSources(context.Context, *SetTargetHostSourcesRequest) (*SetTargetHostSourcesResponse, error)
// RemoveTargetHostSources removes the Host Sources from the specified Target.
// The provided request must include the Target ID for the Target from which
@@ -211,6 +211,16 @@ message Target {
// Output only. The available actions on this resource for this user.
repeated string authorized_actions = 300 [json_name = "authorized_actions"]; // @gotags: `class:"public"`
// Optional string value that represents a network resource and is used when establishing a session.
google.protobuf.StringValue address = 540 [
json_name = "address",
(custom_options.v1.generate_sdk_option) = true,
(custom_options.v1.mask_mapping) = {
this: "address"
that: "address"
}
]; // @gotags: `class:"public"`
// Deprecated fields
reserved "application_credential_library_ids", "application_credential_libraries";
reserved 150, 180;
@@ -102,8 +102,9 @@ service TargetService {
// Host Sources added to the provided Target must be a child of a Catalog that
// is a child of the same scope as this Target. If the scope or Target IDs are
// missing, malformed, or reference non-existing resources, an error is
// returned. An error is returned if a Host Source is attempted to be added to a
// target that is already present on the Target.
// returned. An error is returned if a Host Source is attempted to be added to
// a target that is already present on the Target. If the given target already
// has its address field set, a Bad Request error is returned.
rpc AddTargetHostSources(AddTargetHostSourcesRequest) returns (AddTargetHostSourcesResponse) {
option (google.api.http) = {
post: "/v1/targets/{id}:add-host-sources"
@@ -111,7 +112,7 @@ service TargetService {
response_body: "item"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Adds existing Host Sources to a Target."
summary: "Adds existing Host Sources to a Target. Cannot be used on targets that have their address field set."
};
}
@@ -121,7 +122,8 @@ service TargetService {
// Host Sources will be set. All Host Sources in the request must be a child
// of a Catalog that is in the same scope as the provided Target. If any IDs
// are missing, malformed, or references a non-existing resource, an error is
// returned.
// returned. If the given target already has its address field set, a Bad
// Request error is returned.
rpc SetTargetHostSources(SetTargetHostSourcesRequest) returns (SetTargetHostSourcesResponse) {
option (google.api.http) = {
post: "/v1/targets/{id}:set-host-sources"
@@ -129,7 +131,7 @@ service TargetService {
response_body: "item"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Sources the Host Sources on the Target."
summary: "Sources the Host Sources on the Target. Cannot be used on targets that have their address field set."
};
}
@@ -14,7 +14,8 @@ extend google.protobuf.FieldOptions {
MaskMapping mask_mapping = 85464;
// generate_sdk_option is a directive used when generating the SDK to
// indicate that an option should be created for the field
// indicate that an option should be created for the field. This also controls
// API options (DefaultX, WithX functions).
bool generate_sdk_option = 85412;
// subtype is used for oneOf attribute subtypes, to identify that we only need to parse attributes
@@ -2,6 +2,7 @@ syntax = "proto3";
package controller.storage.target.store.v1;
import "controller/custom_options/v1/options.proto";
import "controller/storage/timestamp/v1/timestamp.proto";
option go_package = "github.com/hashicorp/boundary/internal/target/store;store";
@@ -79,6 +80,19 @@ message TargetHostSet {
timestamp.v1.Timestamp create_time = 30;
}
message TargetAddress {
// target_id of the Target
// @inject_tag: gorm:"primary_key"
string target_id = 10;
// address associated to the Target
// @inject_tag: `gorm:"not_null"`
string address = 20 [(custom_options.v1.mask_mapping) = {
this: "address"
that: "address"
}];
}
message CredentialLibrary {
// target_id of the Target
// @inject_tag: gorm:"primary_key"
+45 -6
View File
@@ -34,21 +34,18 @@ func (r *Repository) CreateSession(ctx context.Context, sessionWrapper wrapping.
if newSession.TargetId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing target id")
}
if newSession.HostId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing host id")
}
if newSession.UserId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing user id")
}
if newSession.HostSetId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing host set id")
}
if newSession.AuthTokenId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing auth token id")
}
if newSession.ProjectId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing project id")
}
if newSession.HostId == "" && newSession.HostSetId == "" && newSession.Endpoint == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing host source and endpoint")
}
if newSession.CtTofuToken != nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "ct is not empty")
}
@@ -91,6 +88,26 @@ func (r *Repository) CreateSession(ctx context.Context, sessionWrapper wrapping.
return errors.Wrap(ctx, err, op)
}
if newSession.HostSetId != "" && newSession.HostId != "" {
hs, err := NewSessionHostSetHost(newSession.PublicId, newSession.HostSetId, newSession.HostId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
if err = w.Create(ctx, hs); err != nil {
return errors.Wrap(ctx, err, op)
}
returnedSession.HostSetId = hs.HostSetId
returnedSession.HostId = hs.HostId
} else if newSession.Endpoint != "" {
ta, err := NewSessionTargetAddress(newSession.PublicId, newSession.TargetId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
if err = w.Create(ctx, ta); err != nil {
return errors.Wrap(ctx, err, op)
}
}
for _, cred := range newSession.DynamicCredentials {
cred.SessionId = newSession.PublicId
}
@@ -197,6 +214,13 @@ func (r *Repository) LookupSession(ctx context.Context, sessionId string, opt ..
session.StaticCredentials = staticCreds
}
sessionHostSetHost := AllocSessionHostSetHost()
if err := read.LookupWhere(ctx, sessionHostSetHost, "session_id = ?", []any{sessionId}); err != nil && !errors.IsNotFoundError(err) {
return errors.Wrap(ctx, err, op)
}
session.HostSetId = sessionHostSetHost.HostSetId
session.HostId = sessionHostSetHost.HostId
connections, err := fetchConnections(ctx, read, sessionId, db.WithOrder("create_time desc"))
if err != nil {
return errors.Wrap(ctx, err, op)
@@ -583,6 +607,12 @@ func (r *Repository) updateState(ctx context.Context, sessionId string, sessionV
if len(returnedStates) < 1 && returnedStates[0].Status != s {
return errors.New(ctx, errors.InvalidSessionState, op, fmt.Sprintf("failed to update %s to a state of %s", sessionId, s.String()))
}
hostSetHost, err := fetchHostSetHost(ctx, reader, sessionId)
if err != nil && !errors.IsNotFoundError(err) {
return errors.Wrap(ctx, err, op)
}
updatedSession.HostId = hostSetHost.HostId
updatedSession.HostSetId = hostSetHost.HostSetId
return nil
},
)
@@ -683,6 +713,15 @@ func fetchConnections(ctx context.Context, r db.Reader, sessionId string, opt ..
return connections, nil
}
func fetchHostSetHost(ctx context.Context, r db.Reader, sessionId string, opt ...db.Option) (*SessionHostSetHost, error) {
const op = "session.fetchHostSetHost"
var hostSetHost *SessionHostSetHost
if err := r.SearchWhere(ctx, &hostSetHost, "session_id = ?", []any{sessionId}, opt...); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
return hostSetHost, nil
}
// decryptAndMaybeUpdateSession switches between the database key and session key based on whether
// the session uses a legacy private key or not. It also updates the encrypted session
// in the database (if necessary). Eventually we should be able to remove this function
+31 -34
View File
@@ -307,6 +307,29 @@ func TestRepository_CreateSession(t *testing.T) {
},
wantErr: false,
},
{
name: "valid-static-address",
args: args{
composedOf: TestSessionTargetAddressParams(t, conn, wrapper, iamRepo),
workerAddresses: workerAddresses,
},
wantErr: false,
},
{
name: "empty-host-source-endpoint",
args: args{
composedOf: func() ComposedOf {
c := TestSessionParams(t, conn, wrapper, iamRepo)
c.HostId = ""
c.HostSetId = ""
c.Endpoint = ""
return c
}(),
workerAddresses: workerAddresses,
},
wantErr: true,
wantIsError: errors.InvalidParameter,
},
{
name: "empty-userId",
args: args{
@@ -320,19 +343,6 @@ func TestRepository_CreateSession(t *testing.T) {
wantErr: true,
wantIsError: errors.InvalidParameter,
},
{
name: "empty-hostId",
args: args{
composedOf: func() ComposedOf {
c := TestSessionParams(t, conn, wrapper, iamRepo)
c.HostId = ""
return c
}(),
workerAddresses: workerAddresses,
},
wantErr: true,
wantIsError: errors.InvalidParameter,
},
{
name: "empty-targetId",
args: args{
@@ -346,19 +356,6 @@ func TestRepository_CreateSession(t *testing.T) {
wantErr: true,
wantIsError: errors.InvalidParameter,
},
{
name: "empty-hostSetId",
args: args{
composedOf: func() ComposedOf {
c := TestSessionParams(t, conn, wrapper, iamRepo)
c.HostSetId = ""
return c
}(),
workerAddresses: workerAddresses,
},
wantErr: true,
wantIsError: errors.InvalidParameter,
},
{
name: "empty-authTokenId",
args: args{
@@ -440,7 +437,7 @@ func TestRepository_CreateSession(t *testing.T) {
HostSetId: tt.args.composedOf.HostSetId,
AuthTokenId: tt.args.composedOf.AuthTokenId,
ProjectId: tt.args.composedOf.ProjectId,
Endpoint: "tcp://127.0.0.1:22",
Endpoint: tt.args.composedOf.Endpoint,
ExpirationTime: tt.args.composedOf.ExpirationTime,
ConnectionLimit: tt.args.composedOf.ConnectionLimit,
DynamicCredentials: tt.args.composedOf.DynamicCredentials,
@@ -1254,19 +1251,19 @@ func TestRepository_CancelSessionViaFKNull(t *testing.T) {
name: "canceled-only-once",
cancelFk: func() cancelFk {
s := setupFn()
var err error
s, err = repo.CancelSession(context.Background(), s.PublicId, s.Version)
require.NoError(t, err)
require.Equal(t, StatusCanceling, s.States[0].Status)
t := &static.Host{
h := &static.Host{
Host: &staticStore.Host{
PublicId: s.HostId,
},
}
var err error
s, err = repo.CancelSession(context.Background(), s.PublicId, s.Version)
require.NoError(t, err)
require.Equal(t, StatusCanceling, s.States[0].Status)
return cancelFk{
s: s,
fkType: t,
fkType: h,
}
}(),
},
+6 -10
View File
@@ -68,12 +68,8 @@ type Session struct {
PublicId string `json:"public_id,omitempty" gorm:"primary_key"`
// UserId for the session
UserId string `json:"user_id,omitempty" gorm:"default:null"`
// HostId of the session
HostId string `json:"host_id,omitempty" gorm:"default:null"`
// TargetId for the session
TargetId string `json:"target_id,omitempty" gorm:"default:null"`
// HostSetId for the session
HostSetId string `json:"host_set_id,omitempty" gorm:"default:null"`
// AuthTokenId for the session
AuthTokenId string `json:"auth_token_id,omitempty" gorm:"default:null"`
// ProjectId for the session
@@ -124,6 +120,12 @@ type Session struct {
// StaticCredentials for the session.
StaticCredentials []*StaticCredential `gorm:"-"`
// HostSetId for the session
HostSetId string `gorm:"-"`
// HostId of the session
HostId string `gorm:"-"`
// Connections for the session are for read only and are ignored during write operations
Connections []*Connection `gorm:"-"`
@@ -347,15 +349,9 @@ func (s *Session) validateNewSession() error {
if s.UserId == "" {
return errors.NewDeprecated(errors.InvalidParameter, op, "missing user id")
}
if s.HostId == "" {
return errors.NewDeprecated(errors.InvalidParameter, op, "missing host id")
}
if s.TargetId == "" {
return errors.NewDeprecated(errors.InvalidParameter, op, "missing target id")
}
if s.HostSetId == "" {
return errors.NewDeprecated(errors.InvalidParameter, op, "missing host set id")
}
if s.AuthTokenId == "" {
return errors.NewDeprecated(errors.InvalidParameter, op, "missing auth token id")
}
+69
View File
@@ -0,0 +1,69 @@
package session
import "github.com/hashicorp/boundary/internal/errors"
const (
defaultSessionHostSetHostTableName = "session_host_set_host"
)
// SessionHostSetHost contains information about a user's session with a target that has a host source association.
type SessionHostSetHost struct {
// SessionId of the session
SessionId string `json:"session_id,omitempty" gorm:"primary_key"`
// HostSetId of the session
HostSetId string `json:"host_set_id,omitempty" gorm:"default:null"`
// HostId of the session
HostId string `json:"host_id,omitempty" gorm:"default:null"`
tableName string `gorm:"-"`
}
// NewSessionHostSetHost creates a new in memory session to host set & host association.
func NewSessionHostSetHost(sessionId, hostSetId, hostId string) (*SessionHostSetHost, error) {
const op = "session.NewSessionHostSetHost"
if sessionId == "" {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing session id")
}
if hostSetId == "" {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing host set id")
}
if hostId == "" {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing host id")
}
shs := &SessionHostSetHost{
SessionId: sessionId,
HostSetId: hostSetId,
HostId: hostId,
}
return shs, nil
}
// TableName returns the tablename to override the default gorm table name
func (s *SessionHostSetHost) TableName() string {
if s.tableName != "" {
return s.tableName
}
return defaultSessionHostSetHostTableName
}
// SetTableName sets the tablename and satisfies the ReplayableMessage
// interface. If the caller attempts to set the name to "" the name will be
// reset to the default name.
func (s *SessionHostSetHost) SetTableName(n string) {
s.tableName = n
}
// AllocSessionHostSet will allocate a SessionHostSetHost
func AllocSessionHostSetHost() *SessionHostSetHost {
return &SessionHostSetHost{}
}
// Clone creates a clone of the SessionHostSetHost
func (s *SessionHostSetHost) Clone() any {
clone := &SessionHostSetHost{
SessionId: s.SessionId,
HostSetId: s.HostSetId,
HostId: s.HostId,
}
return clone
}
@@ -0,0 +1,62 @@
package session
import "github.com/hashicorp/boundary/internal/errors"
const (
defaultSessionTargetAddressTableName = "session_target_address"
)
// SessionTargetAddress contains information about a user's session with a target that has a direct network address association.
type SessionTargetAddress struct {
// SessionId of the session
SessionId string `json:"session_id,omitempty" gorm:"primary_key"`
// TargetId of the session
TargetId string `json:"target_id,omitempty" gorm:"default:null"`
tableName string `gorm:"-"`
}
// NewSessionTargetAddress creates a new in memory session target address.
func NewSessionTargetAddress(sessionId, targetId string) (*SessionTargetAddress, error) {
const op = "sesssion.NewSessionTargetAddress"
if sessionId == "" {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing session id")
}
if targetId == "" {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing target id")
}
sta := &SessionTargetAddress{
SessionId: sessionId,
TargetId: targetId,
}
return sta, nil
}
// TableName returns the tablename to override the default gorm table name
func (s *SessionTargetAddress) TableName() string {
if s.tableName != "" {
return s.tableName
}
return defaultSessionTargetAddressTableName
}
// SetTableName sets the tablename and satisfies the ReplayableMessage
// interface. If the caller attempts to set the name to "" the name will be
// reset to the default name.
func (s *SessionTargetAddress) SetTableName(n string) {
s.tableName = n
}
// AllocSessionTargetAddress will allocate a SessionTargetAddress
func AllocSessionTargetAddress() *SessionTargetAddress {
return &SessionTargetAddress{}
}
// Clone creates a clone of the SessionTargetAddress
func (s *SessionTargetAddress) Clone() any {
clone := &SessionTargetAddress{
SessionId: s.SessionId,
TargetId: s.TargetId,
}
return clone
}
+43 -27
View File
@@ -45,7 +45,7 @@ func TestSession_Create(t *testing.T) {
wantCreateErr bool
}{
{
name: "valid",
name: "valid-hostset-host",
args: args{
composedOf: composedOf,
opt: []Option{WithExpirationTime(exp)},
@@ -66,6 +66,48 @@ func TestSession_Create(t *testing.T) {
},
create: true,
},
{
name: "valid-target-address",
args: args{
composedOf: func() ComposedOf {
c := composedOf
c.HostSetId = ""
c.HostId = ""
return c
}(),
opt: []Option{WithExpirationTime(exp)},
addresses: defaultAddresses,
},
want: &Session{
UserId: composedOf.UserId,
HostId: "",
HostSetId: "",
TargetId: composedOf.TargetId,
AuthTokenId: composedOf.AuthTokenId,
ProjectId: composedOf.ProjectId,
Endpoint: "tcp://127.0.0.1:22",
ExpirationTime: composedOf.ExpirationTime,
ConnectionLimit: composedOf.ConnectionLimit,
DynamicCredentials: composedOf.DynamicCredentials,
StaticCredentials: composedOf.StaticCredentials,
},
create: true,
},
{
name: "invalid-missing-target-address-host-source",
args: args{
composedOf: func() ComposedOf {
c := composedOf
c.HostSetId = ""
c.HostId = ""
c.Endpoint = ""
return c
}(),
addresses: defaultAddresses,
},
wantErr: true,
wantIsErr: errors.InvalidParameter,
},
{
name: "empty-userId",
args: args{
@@ -79,19 +121,6 @@ func TestSession_Create(t *testing.T) {
wantErr: true,
wantIsErr: errors.InvalidParameter,
},
{
name: "empty-hostId",
args: args{
composedOf: func() ComposedOf {
c := composedOf
c.HostId = ""
return c
}(),
addresses: defaultAddresses,
},
wantErr: true,
wantIsErr: errors.InvalidParameter,
},
{
name: "empty-targetId",
args: args{
@@ -105,19 +134,6 @@ func TestSession_Create(t *testing.T) {
wantErr: true,
wantIsErr: errors.InvalidParameter,
},
{
name: "empty-hostSetId",
args: args{
composedOf: func() ComposedOf {
c := composedOf
c.HostSetId = ""
return c
}(),
addresses: defaultAddresses,
},
wantErr: true,
wantIsErr: errors.InvalidParameter,
},
{
name: "empty-authTokenId",
args: args{
+70
View File
@@ -67,6 +67,28 @@ func TestState(t testing.TB, conn *db.DB, sessionId string, state Status) *State
return s
}
// TestSessionHostSetHost creates a test session to host set host association for the sessionId in the repository.
func TestSessionHostSetHost(t testing.TB, conn *db.DB, sessionId, hostSetId, hostId string) {
t.Helper()
require := require.New(t)
rw := db.New(conn)
hs, err := NewSessionHostSetHost(sessionId, hostSetId, hostId)
require.NoError(err)
err = rw.Create(context.Background(), hs)
require.NoError(err)
}
// TestSessionTargetAddress creates a test session to target address association for the sessionId in the repository.
func TestSessionTargetAddress(t testing.TB, conn *db.DB, sessionId, targetId string) {
t.Helper()
require := require.New(t)
rw := db.New(conn)
ta, err := NewSessionTargetAddress(sessionId, targetId)
require.NoError(err)
err = rw.Create(context.Background(), ta)
require.NoError(err)
}
// TestSession creates a test session composed of c in the repository. Options
// are passed into New, and withServerId is handled locally.
func TestSession(t testing.TB, conn *db.DB, rootWrapper wrapping.Wrapper, c ComposedOf, opt ...Option) *Session {
@@ -113,6 +135,12 @@ func TestSession(t testing.TB, conn *db.DB, rootWrapper wrapping.Wrapper, c Comp
require.NoError(err)
}
if s.HostId != "" && s.HostSetId != "" {
TestSessionHostSetHost(t, conn, s.PublicId, s.HostSetId, s.HostId)
} else if s.Endpoint != "" {
TestSessionTargetAddress(t, conn, s.PublicId, s.TargetId)
}
ss, err := fetchStates(ctx, rw, s.PublicId, append(opts.withDbOpts, db.WithOrder("start_time desc"))...)
require.NoError(err)
s.States = ss
@@ -120,6 +148,48 @@ func TestSession(t testing.TB, conn *db.DB, rootWrapper wrapping.Wrapper, c Comp
return s
}
func TestSessionWithTargetAddress(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository, opt ...Option) *Session {
t.Helper()
composedOf := TestSessionTargetAddressParams(t, conn, wrapper, iamRepo)
future := timestamppb.New(time.Now().Add(time.Hour))
exp := &timestamp.Timestamp{Timestamp: future}
return TestSession(t, conn, wrapper, composedOf, append(opt, WithExpirationTime(exp))...)
}
func TestSessionTargetAddressParams(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository) ComposedOf {
t.Helper()
ctx := context.Background()
require := require.New(t)
rw := db.New(conn)
org, proj := iam.TestScopes(t, iamRepo)
tcpTarget := tcp.TestTarget(ctx, t, conn, proj.PublicId, "test target")
target.TestTargetAddress(t, conn, tcpTarget.GetPublicId(), "tcp://127.0.0.1:22")
kms := kms.TestKms(t, conn, wrapper)
authMethod := password.TestAuthMethods(t, conn, org.PublicId, 1)[0]
acct := password.TestAccount(t, conn, authMethod.GetPublicId(), "name1")
user := iam.TestUser(t, iamRepo, org.PublicId, iam.WithAccountIds(acct.PublicId))
authTokenRepo, err := authtoken.NewRepository(rw, rw, kms)
require.NoError(err)
at, err := authTokenRepo.CreateAuthToken(ctx, user, acct.GetPublicId())
require.NoError(err)
expTime := timestamppb.Now()
expTime.Seconds += int64(tcpTarget.GetSessionMaxSeconds())
return ComposedOf{
UserId: user.PublicId,
TargetId: tcpTarget.GetPublicId(),
AuthTokenId: at.PublicId,
ProjectId: tcpTarget.GetProjectId(),
Endpoint: "tcp://127.0.0.1:22",
ExpirationTime: &timestamp.Timestamp{Timestamp: expTime},
ConnectionLimit: tcpTarget.GetSessionConnectionLimit(),
}
}
// TestDefaultSession creates a test session in the repository using defaults.
func TestDefaultSession(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository, opt ...Option) *Session {
t.Helper()
+113
View File
@@ -0,0 +1,113 @@
package target
import (
"context"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/hashicorp/boundary/internal/target/store"
"google.golang.org/protobuf/proto"
)
const (
DefaultTargetAddressTableName = "target_address"
)
// An Address represents the relationship between a target and a
// network address.
type Address struct {
*store.TargetAddress
tableName string `gorm:"-"`
}
// Ensure Address implements interfaces
var (
_ db.VetForWriter = (*Address)(nil)
_ oplog.ReplayableMessage = (*Address)(nil)
)
// NewAddress creates a new in memory address. No options are
// currently supported.
func NewAddress(targetId, address string, _ ...Option) (*Address, error) {
const op = "target.NewAddress"
if targetId == "" {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing target id")
}
if address == "" {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing address")
}
t := &Address{
TargetAddress: &store.TargetAddress{
TargetId: targetId,
Address: address,
},
}
return t, nil
}
// Clone creates a clone of the target address
func (t *Address) Clone() any {
cp := proto.Clone(t.TargetAddress)
return &Address{
TargetAddress: cp.(*store.TargetAddress),
}
}
// VetForWrite implements db.VetForWrite() interface and validates the target
// address before it's written.
func (t *Address) VetForWrite(ctx context.Context, _ db.Reader, opType db.OpType, _ ...db.Option) error {
const op = "target.(Address).VetForWrite"
if opType == db.CreateOp {
if t.GetTargetId() == "" {
return errors.New(ctx, errors.InvalidParameter, op, "missing target id")
}
if t.GetAddress() == "" {
return errors.New(ctx, errors.InvalidParameter, op, "missing address")
}
}
return nil
}
// TableName returns the tablename to override the default gorm table name
func (t *Address) TableName() string {
if t.tableName != "" {
return t.tableName
}
return DefaultTargetAddressTableName
}
// SetTableName sets the tablename and satisfies the ReplayableMessage
// interface. If the caller attempts to set the name to "" the name will be
// reset to the default name.
func (t *Address) SetTableName(n string) {
t.tableName = n
}
func (t *Address) oplog(op oplog.OpType) oplog.Metadata {
metadata := oplog.Metadata{
"resource-public-id": []string{t.GetTargetId()},
"resource-type": []string{"target address"},
"op-type": []string{op.String()},
}
return metadata
}
func (t *Address) TargetId() string {
return t.GetTargetId()
}
func (t *Address) Address() string {
return t.GetAddress()
}
func (t *Address) GetPublicId() string {
return t.GetTargetId()
}
// allocTargetAddress will allocate a address
func allocTargetAddress() *Address {
return &Address{
TargetAddress: &store.TargetAddress{},
}
}
+67
View File
@@ -0,0 +1,67 @@
package target_test
import (
"testing"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/target"
"github.com/hashicorp/boundary/internal/target/store"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAddress_New(t *testing.T) {
type args struct {
targetId string
address string
}
tests := []struct {
name string
args args
want *target.Address
wantErr errors.Code
}{
{
name: "no-target_id",
args: args{
address: "0.0.0.0",
},
wantErr: errors.InvalidParameter,
},
{
name: "no-address",
args: args{
targetId: "targ_0000000",
},
wantErr: errors.InvalidParameter,
},
{
name: "valid",
args: args{
targetId: "targ_0000000",
address: "0.0.0.0",
},
want: &target.Address{
TargetAddress: &store.TargetAddress{
TargetId: "targ_0000000",
Address: "0.0.0.0",
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := target.NewAddress(tt.args.targetId, tt.args.address)
if tt.wantErr != 0 {
assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err)
assert.Nil(got)
return
}
require.NoError(err)
require.NotNil(got)
assert.EqualValues(tt.want, got)
})
}
}
+9
View File
@@ -41,6 +41,7 @@ type options struct {
WithEgressWorkerFilter string
WithIngressWorkerFilter string
WithTargetIds []string
WithAddress string
}
func getDefaultOptions() options {
@@ -64,6 +65,7 @@ func getDefaultOptions() options {
WithWorkerFilter: "",
WithEgressWorkerFilter: "",
WithIngressWorkerFilter: "",
WithAddress: "",
}
}
@@ -207,3 +209,10 @@ func WithPermissions(perms []perms.Permission) Option {
o.WithPermissions = perms
}
}
// WithAddress provides an optional network address
func WithAddress(address string) Option {
return func(o *options) {
o.WithAddress = address
}
}
+154 -84
View File
@@ -125,6 +125,7 @@ func (r *Repository) LookupTarget(ctx context.Context, publicIdOrName string, op
target := allocTargetView()
target.PublicId = publicIdOrName
var address string
var hostSources []HostSource
var credSources []CredentialSource
_, err := r.writer.DoTx(
@@ -150,6 +151,13 @@ func (r *Repository) LookupTarget(ctx context.Context, publicIdOrName string, op
if credSources, err = fetchCredentialSources(ctx, read, target.PublicId); err != nil {
return errors.Wrap(ctx, err, op)
}
targetAddress, err := fetchAddress(ctx, read, target.PublicId)
if err != nil && !errors.IsNotFoundError(err) {
return errors.Wrap(ctx, err, op)
}
if targetAddress != nil {
address = targetAddress.GetAddress()
}
return nil
},
)
@@ -159,7 +167,7 @@ func (r *Repository) LookupTarget(ctx context.Context, publicIdOrName string, op
}
return nil, nil, nil, errors.Wrap(ctx, err, op)
}
subtype, err := target.targetSubtype(ctx)
subtype, err := target.targetSubtype(ctx, address)
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op)
}
@@ -242,9 +250,28 @@ func (r *Repository) ListTargets(ctx context.Context, opt ...Option) ([]Target,
return nil, errors.Wrap(ctx, err, op)
}
var targetIds []string
for _, t := range foundTargets {
targetIds = append(targetIds, t.GetPublicId())
}
addresses := map[string]string{}
var foundAddresses []*Address
err = r.reader.SearchWhere(ctx, &foundAddresses, "target_id in (?)", []any{targetIds})
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
for _, addr := range foundAddresses {
addresses[addr.TargetId()] = addr.Address()
}
targets := make([]Target, 0, len(foundTargets))
for _, t := range foundTargets {
subtype, err := t.targetSubtype(ctx)
var address string
if v, ok := addresses[t.GetPublicId()]; ok {
address = v
}
subtype, err := t.targetSubtype(ctx, address)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
@@ -336,80 +363,6 @@ func (r *Repository) DeleteTarget(ctx context.Context, publicId string, _ ...Opt
return rowsDeleted, nil
}
// update a target in the db repository with an oplog entry.
// It currently supports no options.
func (r *Repository) update(ctx context.Context, target Target, version uint32, fieldMaskPaths []string, setToNullPaths []string, _ ...Option) (Target, []HostSource, []CredentialSource, int, error) {
const op = "target.(Repository).update"
if version == 0 {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version")
}
if target == nil {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "nil target")
}
cloner, ok := target.(Cloneable)
if !ok {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "target is not cloneable")
}
dbOpts := []db.Option{
db.WithVersion(&version),
}
projectId := target.GetProjectId()
if projectId == "" {
t := allocTargetView()
t.PublicId = target.GetPublicId()
if err := r.reader.LookupByPublicId(ctx, &t); err != nil {
return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("lookup failed for %s", t.PublicId)))
}
projectId = t.ProjectId
}
oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog)
if err != nil {
return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
metadata := target.Oplog(oplog.OpType_OP_TYPE_UPDATE)
dbOpts = append(dbOpts, db.WithOplog(oplogWrapper, metadata))
var rowsUpdated int
var returnedTarget any
var hostSources []HostSource
var credSources []CredentialSource
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(reader db.Reader, w db.Writer) error {
returnedTarget = cloner.Clone()
rowsUpdated, err = w.Update(
ctx,
returnedTarget,
fieldMaskPaths,
setToNullPaths,
dbOpts...,
)
if err != nil {
return errors.Wrap(ctx, err, op)
}
if rowsUpdated > 1 {
// return err, which will result in a rollback of the update
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated")
}
if hostSources, err = fetchHostSources(ctx, reader, target.GetPublicId()); err != nil {
return errors.Wrap(ctx, err, op)
}
if credSources, err = fetchCredentialSources(ctx, reader, target.GetPublicId()); err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
},
)
if err != nil {
return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op)
}
return returnedTarget.(Target), hostSources, credSources, rowsUpdated, nil
}
// CreateTarget inserts into the repository and returns the new Target with
// its list of host sets and credential libraries.
// WithPublicId is the only supported option.
@@ -455,6 +408,15 @@ func (r *Repository) CreateTarget(ctx context.Context, target Target, opt ...Opt
t.SetPublicId(ctx, id)
}
var address *Address
var err error
if t.GetAddress() != "" {
address, err = NewAddress(t.GetPublicId(), t.GetAddress())
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op)
}
}
oplogWrapper, err := r.kms.GetWrapper(ctx, target.GetProjectId(), kms.KeyPurposeOplog)
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
@@ -480,6 +442,15 @@ func (r *Repository) CreateTarget(ctx context.Context, target Target, opt ...Opt
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target"))
}
msgs = append(msgs, &targetOplogMsg)
if address != nil {
var targetAddressOplogMsg oplog.Message
if err := w.Create(ctx, address, db.NewOplogMsg(&targetAddressOplogMsg)); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target address"))
}
msgs = append(msgs, &targetAddressOplogMsg)
}
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog"))
}
@@ -504,6 +475,15 @@ func (r *Repository) UpdateTarget(ctx context.Context, target Target, version ui
if target == nil {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target")
}
if target.GetPublicId() == "" {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target public id")
}
if target.GetProjectId() == "" {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing project id")
}
if version == 0 {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version")
}
vet, ok := subtypeRegistry.vetForUpdateFunc(target.GetType())
if !ok {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported target type %s", target.GetType()))
@@ -512,10 +492,7 @@ func (r *Repository) UpdateTarget(ctx context.Context, target Target, version ui
return nil, nil, nil, db.NoRowsAffected, err
}
if target.GetPublicId() == "" {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target public id")
}
var addressEndpoint string
for _, f := range fieldMaskPaths {
switch {
case strings.EqualFold("name", f):
@@ -526,10 +503,13 @@ func (r *Repository) UpdateTarget(ctx context.Context, target Target, version ui
case strings.EqualFold("workerfilter", f):
case strings.EqualFold("egressworkerfilter", f):
case strings.EqualFold("ingressworkerfilter", f):
case strings.EqualFold("address", f):
addressEndpoint = target.GetAddress()
default:
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f))
}
}
var dbMask, nullFields []string
dbMask, nullFields = dbw.BuildUpdatePaths(
map[string]any{
@@ -541,6 +521,7 @@ func (r *Repository) UpdateTarget(ctx context.Context, target Target, version ui
"WorkerFilter": target.GetWorkerFilter(),
"EgressWorkerFilter": target.GetEgressWorkerFilter(),
"IngressWorkerFilter": target.GetIngressWorkerFilter(),
"Address": target.GetAddress(),
},
fieldMaskPaths,
[]string{"SessionMaxSeconds", "SessionConnectionLimit"},
@@ -548,21 +529,110 @@ func (r *Repository) UpdateTarget(ctx context.Context, target Target, version ui
if len(dbMask) == 0 && len(nullFields) == 0 {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "empty field mask")
}
var returnedTarget Target
// The Address field is not apart of the target schema in the database.
// It is apart of a different table called target_address, which is why
// the Address field must be filtered out of the dbMask & nullFields slices.
var updateAddress, deleteAddress bool
var filteredDbMask, filteredNullFields []string
for _, f := range dbMask {
switch {
case strings.EqualFold("Address", f):
updateAddress = true
default:
filteredDbMask = append(filteredDbMask, f)
}
}
for _, f := range nullFields {
switch {
case strings.EqualFold("Address", f):
deleteAddress = true
default:
filteredNullFields = append(filteredNullFields, f)
}
}
// If the Address field is the only present change, then we must still
// update the target's version because a target address is a child object
// of the target.
if (len(filteredDbMask) == 0 && len(filteredNullFields) == 0) && (updateAddress || deleteAddress) {
target.SetVersion(version + 1)
filteredDbMask = append(filteredDbMask, "Version")
}
oplogWrapper, err := r.kms.GetWrapper(ctx, target.GetProjectId(), kms.KeyPurposeOplog)
if err != nil {
return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var rowsUpdated int
var returnedTarget Target
var hostSources []HostSource
var credSources []CredentialSource
_, err := r.writer.DoTx(
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(read db.Reader, w db.Writer) error {
var err error
t := target.Clone()
returnedTarget, hostSources, credSources, rowsUpdated, err = r.update(ctx, t, version, dbMask, nullFields)
rowsUpdated, err = w.Update(ctx, t, filteredDbMask, filteredNullFields,
db.WithOplog(oplogWrapper, t.Oplog(oplog.OpType_OP_TYPE_UPDATE)),
db.WithVersion(&version),
)
if err != nil {
return errors.Wrap(ctx, err, op)
}
if rowsUpdated > 1 {
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 target resource would have been updated")
}
if hostSources, err = fetchHostSources(ctx, read, t.GetPublicId()); err != nil {
return errors.Wrap(ctx, err, op)
}
if credSources, err = fetchCredentialSources(ctx, read, t.GetPublicId()); err != nil {
return errors.Wrap(ctx, err, op)
}
var address *Address
switch {
case updateAddress:
if len(hostSources) > 0 {
return errors.New(ctx, errors.Conflict, op, "unable to set address because one or more host sources is assigned to the given target")
}
address, err = NewAddress(t.GetPublicId(), addressEndpoint)
if err != nil {
return errors.Wrap(ctx, err, op)
}
if err := w.Create(ctx, address,
db.WithOplog(oplogWrapper, address.oplog(oplog.OpType_OP_TYPE_UPDATE)),
db.WithOnConflict(&db.OnConflict{
Target: db.Columns{"target_id"},
Action: db.UpdateAll(true),
})); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target address"))
}
case deleteAddress:
var err error
address = allocTargetAddress()
address.TargetAddress.TargetId = t.GetPublicId()
rowsUpdated, err = w.Delete(ctx, address, db.WithOplog(oplogWrapper, address.oplog(oplog.OpType_OP_TYPE_DELETE)))
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete target address"))
}
if rowsUpdated > 1 {
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 target resource would have been deleted")
}
default:
address, err = fetchAddress(ctx, read, t.GetPublicId())
if err != nil && !errors.IsNotFoundError(err) {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to fetch target address"))
}
}
if address != nil {
t.SetAddress(address.GetAddress())
}
returnedTarget = t.Clone()
return nil
},
)
+21
View File
@@ -0,0 +1,21 @@
package target
import (
"context"
"fmt"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
)
func fetchAddress(ctx context.Context, r db.Reader, targetId string) (*Address, error) {
const op = "target.fetchAddress"
var address *Address
if err := r.SearchWhere(ctx, &address, "target_id = ?", []any{targetId}); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if address.TargetAddress == nil {
return nil, errors.New(ctx, errors.RecordNotFound, op, fmt.Sprintf("%s target address not found", targetId), errors.WithoutEvent())
}
return address, nil
}
@@ -60,7 +60,7 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st
var hostSources []HostSource
var credSources []CredentialSource
var updatedTarget any
var updatedTarget Target
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
@@ -72,7 +72,7 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
}
updatedTarget = target.(Cloneable).Clone()
updatedTarget = target.Clone()
var targetOplogMsg oplog.Message
rowsUpdated, err := w.Update(ctx, updatedTarget, []string{"Version"}, nil, db.NewOplogMsg(&targetOplogMsg), db.WithVersion(&targetVersion))
if err != nil {
@@ -121,6 +121,13 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to retrieve credential sources after adding"))
}
address, err := fetchAddress(ctx, reader, targetId)
if err != nil && !errors.IsNotFoundError(err) {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to retrieve target address after adding"))
}
if address != nil {
updatedTarget.SetAddress(address.GetAddress())
}
return nil
},
)
+21 -3
View File
@@ -58,18 +58,27 @@ func (r *Repository) AddTargetHostSources(ctx context.Context, targetId string,
}
var currentHostSources []HostSource
var currentCredSources []CredentialSource
var updatedTarget any
var updatedTarget Target
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(reader db.Reader, w db.Writer) error {
address, err := fetchAddress(ctx, reader, targetId)
if err != nil && !errors.IsNotFoundError(err) {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to retrieve current target address"))
}
if address != nil && address.GetAddress() != "" {
return errors.New(ctx, errors.Conflict, op, "unable to add host sources because a network address is directly assigned to the given target")
}
msgs := make([]*oplog.Message, 0, 2)
targetTicket, err := w.GetTicket(ctx, target)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
}
updatedTarget = target.(Cloneable).Clone()
updatedTarget = target.Clone()
var targetOplogMsg oplog.Message
rowsUpdated, err := w.Update(ctx, updatedTarget, []string{"Version"}, nil, db.NewOplogMsg(&targetOplogMsg), db.WithVersion(&targetVersion))
if err != nil {
@@ -103,7 +112,8 @@ func (r *Repository) AddTargetHostSources(ctx context.Context, targetId string,
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("error creating sets"))
}
return updatedTarget.(Target), currentHostSources, currentCredSources, nil
return updatedTarget, currentHostSources, currentCredSources, nil
}
// DeleteTargeHostSources deletes host sources from a target (targetId). The
@@ -284,6 +294,14 @@ func (r *Repository) SetTargetHostSources(ctx context.Context, targetId string,
db.StdRetryCnt,
db.ExpBackoff{},
func(reader db.Reader, w db.Writer) error {
address, err := fetchAddress(ctx, reader, targetId)
if err != nil && !errors.IsNotFoundError(err) {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to retrieve current target address"))
}
if address != nil && address.GetAddress() != "" {
return errors.New(ctx, errors.Conflict, op, "unable to set host sources because a network address is directly assigned to the given target")
}
msgs := make([]*oplog.Message, 0, 2)
targetTicket, err := w.GetTicket(ctx, target)
if err != nil {
+210 -128
View File
@@ -8,6 +8,7 @@ package store
import (
timestamp "github.com/hashicorp/boundary/internal/db/timestamp"
_ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
@@ -270,6 +271,65 @@ func (x *TargetHostSet) GetCreateTime() *timestamp.Timestamp {
return nil
}
type TargetAddress struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// target_id of the Target
// @inject_tag: gorm:"primary_key"
TargetId string `protobuf:"bytes,10,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty" gorm:"primary_key"`
// address associated to the Target
// @inject_tag: `gorm:"not_null"`
Address string `protobuf:"bytes,20,opt,name=address,proto3" json:"address,omitempty" gorm:"not_null"`
}
func (x *TargetAddress) Reset() {
*x = TargetAddress{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TargetAddress) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TargetAddress) ProtoMessage() {}
func (x *TargetAddress) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_target_store_v1_target_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 TargetAddress.ProtoReflect.Descriptor instead.
func (*TargetAddress) Descriptor() ([]byte, []int) {
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{2}
}
func (x *TargetAddress) GetTargetId() string {
if x != nil {
return x.TargetId
}
return ""
}
func (x *TargetAddress) GetAddress() string {
if x != nil {
return x.Address
}
return ""
}
type CredentialLibrary struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -292,7 +352,7 @@ type CredentialLibrary struct {
func (x *CredentialLibrary) Reset() {
*x = CredentialLibrary{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[2]
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -305,7 +365,7 @@ func (x *CredentialLibrary) String() string {
func (*CredentialLibrary) ProtoMessage() {}
func (x *CredentialLibrary) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[2]
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -318,7 +378,7 @@ func (x *CredentialLibrary) ProtoReflect() protoreflect.Message {
// Deprecated: Use CredentialLibrary.ProtoReflect.Descriptor instead.
func (*CredentialLibrary) Descriptor() ([]byte, []int) {
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{2}
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{3}
}
func (x *CredentialLibrary) GetTargetId() string {
@@ -371,7 +431,7 @@ type StaticCredential struct {
func (x *StaticCredential) Reset() {
*x = StaticCredential{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[3]
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -384,7 +444,7 @@ func (x *StaticCredential) String() string {
func (*StaticCredential) ProtoMessage() {}
func (x *StaticCredential) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[3]
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -397,7 +457,7 @@ func (x *StaticCredential) ProtoReflect() protoreflect.Message {
// Deprecated: Use StaticCredential.ProtoReflect.Descriptor instead.
func (*StaticCredential) Descriptor() ([]byte, []int) {
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{3}
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{4}
}
func (x *StaticCredential) GetTargetId() string {
@@ -453,7 +513,7 @@ type CredentialSource struct {
func (x *CredentialSource) Reset() {
*x = CredentialSource{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[4]
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -466,7 +526,7 @@ func (x *CredentialSource) String() string {
func (*CredentialSource) ProtoMessage() {}
func (x *CredentialSource) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[4]
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -479,7 +539,7 @@ func (x *CredentialSource) ProtoReflect() protoreflect.Message {
// Deprecated: Use CredentialSource.ProtoReflect.Descriptor instead.
func (*CredentialSource) Descriptor() ([]byte, []int) {
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{4}
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{5}
}
func (x *CredentialSource) GetTargetId() string {
@@ -533,7 +593,7 @@ type CredentialSourceView struct {
func (x *CredentialSourceView) Reset() {
*x = CredentialSourceView{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[5]
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -546,7 +606,7 @@ func (x *CredentialSourceView) String() string {
func (*CredentialSourceView) ProtoMessage() {}
func (x *CredentialSourceView) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[5]
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -559,7 +619,7 @@ func (x *CredentialSourceView) ProtoReflect() protoreflect.Message {
// Deprecated: Use CredentialSourceView.ProtoReflect.Descriptor instead.
func (*CredentialSourceView) Descriptor() ([]byte, []int) {
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{5}
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{6}
}
func (x *CredentialSourceView) GetPublicId() string {
@@ -584,109 +644,118 @@ var file_controller_storage_target_store_v1_target_proto_rawDesc = []byte{
0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x6f,
0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe0, 0x04, 0x0a, 0x0a, 0x54, 0x61, 0x72, 0x67, 0x65,
0x74, 0x56, 0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f,
0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64,
0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49,
0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63,
0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f,
0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x22, 0xe0, 0x04, 0x0a, 0x0a, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x69, 0x65,
0x77, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x1d,
0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01,
0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69,
0x6d, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65,
0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18,
0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64,
0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x5a, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x2e,
0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65,
0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x73, 0x65, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x38,
0x0a, 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x05,
0x52, 0x16, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x78, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x31, 0x0a,
0x14, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x66,
0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x65, 0x67,
0x72, 0x65, 0x73, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72,
0x12, 0x33, 0x0a, 0x15, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x77, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x13, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46,
0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x99, 0x01, 0x0a, 0x0d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67,
0x65, 0x74, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74,
0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x53,
0x65, 0x74, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
0x65, 0x22, 0x5e, 0x0a, 0x0d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18,
0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12,
0x30, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09,
0x42, 0x16, 0xc2, 0xdd, 0x29, 0x12, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12,
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x22, 0xe0, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67,
0x65, 0x74, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20,
0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c,
0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x1e,
0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x50, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x46, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74,
0x79, 0x70, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,
0x21, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18,
0x5a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f,
0x72, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61,
0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x11, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e,
0x64, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f,
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x6e,
0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e,
0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d,
0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x78, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65,
0x72, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x77, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x12, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69,
0x6c, 0x74, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x15, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f,
0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x8c, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x57, 0x6f, 0x72,
0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x99, 0x01, 0x0a, 0x0d, 0x54, 0x61,
0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74,
0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0b, 0x68, 0x6f, 0x73, 0x74,
0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x68,
0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61,
0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xe0, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x74,
0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69,
0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12,
0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x75, 0x72, 0x70, 0x6f,
0x73, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x50, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e,
0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72,
0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x10, 0x53, 0x74, 0x61,
0x74, 0x69, 0x63, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1b, 0x0a,
0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12,
0x2d, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x75,
0x72, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x4b,
0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xf1, 0x01, 0x0a, 0x10,
0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a,
0x14, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72,
0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12,
0x2d, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x75,
0x72, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x4b,
0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74,
0x79, 0x70, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22,
0x47, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69,
0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c,
0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x14, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x42, 0x3b, 0x5a, 0x39, 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, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x54, 0x69, 0x6d, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72,
0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61,
0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x63,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73,
0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x50, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72,
0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f,
0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76,
0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65,
0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xf1, 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09,
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69,
0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x63,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73,
0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x50, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72,
0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f,
0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76,
0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65,
0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x47, 0x0a, 0x14, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56,
0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64,
0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64,
0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x74, 0x79, 0x70, 0x65, 0x42, 0x3b, 0x5a, 0x39, 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, 0x74,
0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72,
0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -701,23 +770,24 @@ func file_controller_storage_target_store_v1_target_proto_rawDescGZIP() []byte {
return file_controller_storage_target_store_v1_target_proto_rawDescData
}
var file_controller_storage_target_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_controller_storage_target_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_controller_storage_target_store_v1_target_proto_goTypes = []interface{}{
(*TargetView)(nil), // 0: controller.storage.target.store.v1.TargetView
(*TargetHostSet)(nil), // 1: controller.storage.target.store.v1.TargetHostSet
(*CredentialLibrary)(nil), // 2: controller.storage.target.store.v1.CredentialLibrary
(*StaticCredential)(nil), // 3: controller.storage.target.store.v1.StaticCredential
(*CredentialSource)(nil), // 4: controller.storage.target.store.v1.CredentialSource
(*CredentialSourceView)(nil), // 5: controller.storage.target.store.v1.CredentialSourceView
(*timestamp.Timestamp)(nil), // 6: controller.storage.timestamp.v1.Timestamp
(*TargetAddress)(nil), // 2: controller.storage.target.store.v1.TargetAddress
(*CredentialLibrary)(nil), // 3: controller.storage.target.store.v1.CredentialLibrary
(*StaticCredential)(nil), // 4: controller.storage.target.store.v1.StaticCredential
(*CredentialSource)(nil), // 5: controller.storage.target.store.v1.CredentialSource
(*CredentialSourceView)(nil), // 6: controller.storage.target.store.v1.CredentialSourceView
(*timestamp.Timestamp)(nil), // 7: controller.storage.timestamp.v1.Timestamp
}
var file_controller_storage_target_store_v1_target_proto_depIdxs = []int32{
6, // 0: controller.storage.target.store.v1.TargetView.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 1: controller.storage.target.store.v1.TargetView.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 2: controller.storage.target.store.v1.TargetHostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 3: controller.storage.target.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 4: controller.storage.target.store.v1.StaticCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // 5: controller.storage.target.store.v1.CredentialSource.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 0: controller.storage.target.store.v1.TargetView.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 1: controller.storage.target.store.v1.TargetView.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 2: controller.storage.target.store.v1.TargetHostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 3: controller.storage.target.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 4: controller.storage.target.store.v1.StaticCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
7, // 5: controller.storage.target.store.v1.CredentialSource.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
6, // [6:6] is the sub-list for method output_type
6, // [6:6] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
@@ -756,7 +826,7 @@ func file_controller_storage_target_store_v1_target_proto_init() {
}
}
file_controller_storage_target_store_v1_target_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CredentialLibrary); i {
switch v := v.(*TargetAddress); i {
case 0:
return &v.state
case 1:
@@ -768,7 +838,7 @@ func file_controller_storage_target_store_v1_target_proto_init() {
}
}
file_controller_storage_target_store_v1_target_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StaticCredential); i {
switch v := v.(*CredentialLibrary); i {
case 0:
return &v.state
case 1:
@@ -780,7 +850,7 @@ func file_controller_storage_target_store_v1_target_proto_init() {
}
}
file_controller_storage_target_store_v1_target_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CredentialSource); i {
switch v := v.(*StaticCredential); i {
case 0:
return &v.state
case 1:
@@ -792,6 +862,18 @@ func file_controller_storage_target_store_v1_target_proto_init() {
}
}
file_controller_storage_target_store_v1_target_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CredentialSource); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_storage_target_store_v1_target_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CredentialSourceView); i {
case 0:
return &v.state
@@ -810,7 +892,7 @@ func file_controller_storage_target_store_v1_target_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_storage_target_store_v1_target_proto_rawDesc,
NumEnums: 0,
NumMessages: 6,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
+6 -1
View File
@@ -28,6 +28,7 @@ type Target interface {
GetWorkerFilter() string
GetEgressWorkerFilter() string
GetIngressWorkerFilter() string
GetAddress() string
Clone() Target
SetPublicId(context.Context, string) error
SetProjectId(string)
@@ -42,6 +43,7 @@ type Target interface {
SetWorkerFilter(string)
SetEgressWorkerFilter(string)
SetIngressWorkerFilter(string)
SetAddress(string)
Oplog(op oplog.OpType) oplog.Metadata
}
@@ -55,6 +57,8 @@ var _ boundary.AuthzProtectedEntity = (*targetView)(nil)
// underlying type.
type targetView struct {
*store.TargetView
// Network address assigned to the Target.
Address string `json:"address,omitempty" gorm:"-"`
tableName string `gorm:"-"`
}
@@ -105,7 +109,7 @@ func (t *targetView) Subtype() subtypes.Subtype {
}
// targetSubtype converts the target view to the concrete subtype
func (t *targetView) targetSubtype(ctx context.Context) (Target, error) {
func (t *targetView) targetSubtype(ctx context.Context, address string) (Target, error) {
const op = "target.targetView.targetSubtype"
alloc, ok := subtypeRegistry.allocFunc(t.Subtype())
@@ -129,5 +133,6 @@ func (t *targetView) targetSubtype(ctx context.Context) (Target, error) {
tt.SetWorkerFilter(t.WorkerFilter)
tt.SetEgressWorkerFilter(t.EgressWorkerFilter)
tt.SetIngressWorkerFilter(t.IngressWorkerFilter)
tt.SetAddress(address)
return tt, nil
}
+11 -1
View File
@@ -27,6 +27,7 @@ const (
// Target is a target.Target used for tests.
type Target struct {
*store.Target
Address string `gorm:"-"`
tableName string `gorm:"-"`
}
@@ -125,10 +126,15 @@ func (t *Target) GetIngressWorkerFilter() string {
return t.IngressWorkerFilter
}
func (t *Target) GetAddress() string {
return t.Address
}
func (t *Target) Clone() target.Target {
cp := proto.Clone(t.Target)
return &Target{
Target: cp.(*store.Target),
Address: t.Address,
Target: cp.(*store.Target),
}
}
@@ -185,6 +191,10 @@ func (t *Target) SetIngressWorkerFilter(filter string) {
t.IngressWorkerFilter = filter
}
func (t *Target) SetAddress(a string) {
t.Address = a
}
func (t *Target) Oplog(op oplog.OpType) oplog.Metadata {
return oplog.Metadata{
"resource-public-id": []string{t.PublicId},
+13 -1
View File
@@ -1,6 +1,9 @@
package tcp
import "github.com/hashicorp/boundary/internal/target"
import (
"github.com/hashicorp/boundary/internal/target"
"github.com/hashicorp/boundary/internal/target/store"
)
// Expose functions and variables for tests.
var (
@@ -17,3 +20,12 @@ func NewTestTarget(projectId string, opt ...target.Option) target.Target {
t.SetProjectId(projectId)
return t
}
// NewTestAddress is a test helper that bypasses the targetId & address checks
// performed by NewAddress, allowing tests to create a target Address with
// nil fields for more robust testing.
func NewTestAddress() *target.Address {
return &target.Address{
TargetAddress: &store.TargetAddress{},
}
}
@@ -50,13 +50,15 @@ func TestRepository_AddTargetHostSets(t *testing.T) {
type args struct {
targetVersion uint32
wantTargetIds bool
wantAddress bool
opt []target.Option
}
tests := []struct {
name string
args args
wantErr bool
wantErrIs error
name string
args args
wantErr bool
wantErrMsg string
wantErrIs error
}{
{
name: "valid",
@@ -66,6 +68,16 @@ func TestRepository_AddTargetHostSets(t *testing.T) {
},
wantErr: false,
},
{
name: "address-mutually-exclusive-relationship",
args: args{
targetVersion: 1,
wantAddress: true,
wantTargetIds: true,
},
wantErr: true,
wantErrMsg: "unable to add host sources because a network address is directly assigned to the given target",
},
{
name: "bad-version",
args: args{
@@ -99,11 +111,20 @@ func TestRepository_AddTargetHostSets(t *testing.T) {
ctx := context.Background()
projTarget := tcp.TestTarget(ctx, t, conn, staticProj.PublicId, "static-proj")
var address *target.Address
if tt.args.wantAddress {
address = target.TestTargetAddress(t, conn, projTarget.GetPublicId(), "8.8.8.8")
}
var hostSourceIds []string
origTarget, origHostSet, _, err := repo.LookupTarget(ctx, projTarget.GetPublicId())
require.NoError(err)
require.Equal(0, len(origHostSet))
if address != nil {
require.Equal(address.GetAddress(), origTarget.GetAddress())
}
if tt.args.wantTargetIds {
hostSourceIds = createHostSetsFn([]string{staticProj.PublicId})
}
@@ -114,6 +135,9 @@ func TestRepository_AddTargetHostSets(t *testing.T) {
if tt.wantErrIs != nil {
assert.Truef(errors.Is(err, tt.wantErrIs), "unexpected error %s", err.Error())
}
if tt.wantErrMsg != "" {
assert.Contains(err.Error(), tt.wantErrMsg)
}
// test to see of the target version update oplog was not created
err = db.TestVerifyOplog(t, rw, projTarget.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
assert.Error(err)
@@ -448,6 +472,7 @@ func TestRepository_SetTargetHostSets(t *testing.T) {
setup func(target.Target) []target.HostSource
args args
wantAffectedRows int
wantErrMsg string
wantErr bool
}{
{
@@ -461,6 +486,17 @@ func TestRepository_SetTargetHostSets(t *testing.T) {
wantErr: false,
wantAffectedRows: 10,
},
{
name: "address-mutually-exclusive-relationship",
args: args{
target: tcp.TestTarget(ctx, t, conn, proj.PublicId, "invalid-host-source", target.WithAddress("8.8.8.8")),
targetVersion: 2, // yep, since setupFn will increment it to 2
hostSourceIds: []string{testHostSetIds[0], testHostSetIds[1]},
addToOrigHostSources: true,
},
wantErr: true,
wantErrMsg: "unable to set host sources because a network address is directly assigned to the given target",
},
{
name: "no-change",
setup: setupFn,
+165 -83
View File
@@ -64,6 +64,21 @@ func TestRepository_CreateTarget(t *testing.T) {
},
wantErr: false,
},
{
name: "with-address",
args: args{
target: func() target.Target {
target, err := target.New(ctx, tcp.Subtype, proj.PublicId,
target.WithName("with-address"),
target.WithDescription("with-address"),
target.WithDefaultPort(80),
target.WithAddress("8.8.8.8"))
require.NoError(t, err)
return target
}(),
},
wantErr: false,
},
{
name: "nil-target",
args: args{
@@ -187,6 +202,7 @@ func TestRepository_CreateTarget(t *testing.T) {
foundTarget, foundHostSources, foundCredLibs, err := repo.LookupTarget(context.Background(), tar.GetPublicId())
assert.NoError(err)
assert.Equal(tt.args.target.GetAddress(), tar.GetAddress())
assert.True(proto.Equal(tar.(*tcp.Target), foundTarget.(*tcp.Target)))
assert.Equal(hostSources, foundHostSources)
assert.Equal(credSources, foundCredLibs)
@@ -224,6 +240,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
type args struct {
name string
description string
address string
port uint32
fieldMaskPaths []string
opt []target.Option
@@ -231,16 +248,17 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
PublicId *string
}
tests := []struct {
name string
newProjectId string
newName string
newTargetOpts []target.Option
args args
wantRowsUpdate int
wantErr bool
wantErrMsg string
wantIsError errors.Code
wantDup bool
name string
newProjectId string
newName string
newTargetOpts []target.Option
args args
wantRowsUpdate int
wantErr bool
wantErrMsg string
wantIsError errors.Code
wantDup bool
wantHostSources bool
}{
{
name: "valid",
@@ -249,9 +267,63 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
fieldMaskPaths: []string{"Name"},
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
wantErr: false,
wantRowsUpdate: 1,
newProjectId: proj.PublicId,
wantErr: false,
wantRowsUpdate: 1,
wantHostSources: true,
},
{
name: "valid-address",
args: args{
name: "valid-address" + id,
fieldMaskPaths: []string{"Name", "Address"},
ProjectId: proj.PublicId,
address: "8.8.8.8",
},
newProjectId: proj.PublicId,
wantErr: false,
wantRowsUpdate: 1,
wantHostSources: false,
},
{
name: "null-address",
args: args{
fieldMaskPaths: []string{"Address"},
ProjectId: proj.PublicId,
address: "null",
},
newProjectId: proj.PublicId,
newTargetOpts: []target.Option{target.WithAddress("8.8.8.8")},
wantErr: false,
wantRowsUpdate: 1,
wantHostSources: false,
},
{
name: "delete-address",
args: args{
fieldMaskPaths: []string{"Address"},
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
newTargetOpts: []target.Option{target.WithAddress("8.8.8.8")},
wantErr: false,
wantRowsUpdate: 1,
wantHostSources: false,
},
{
name: "host-source-mutually-exclusive-relationship",
args: args{
name: "invalid-address" + id,
address: "8.8.8.8",
fieldMaskPaths: []string{"Name", "Address"},
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
wantErr: true,
wantErrMsg: "unable to set address because one or more host sources is assigned to the given target",
wantRowsUpdate: 0,
wantHostSources: true,
},
{
name: "valid-no-op",
@@ -260,10 +332,11 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
fieldMaskPaths: []string{"Name"},
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
newName: "valid-no-op" + id,
wantErr: false,
wantRowsUpdate: 1,
newProjectId: proj.PublicId,
newName: "valid-no-op" + id,
wantErr: false,
wantRowsUpdate: 1,
wantHostSources: true,
},
{
name: "not-found",
@@ -273,11 +346,12 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
ProjectId: proj.PublicId,
PublicId: func() *string { s := "1"; return &s }(),
},
newProjectId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "record not found, search issue: error #1100",
wantIsError: errors.RecordNotFound,
newProjectId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "record not found, search issue: error #1100",
wantIsError: errors.RecordNotFound,
wantHostSources: true,
},
{
name: "null-name",
@@ -286,11 +360,12 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
fieldMaskPaths: []string{"Name"},
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
newName: "null-name" + id,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "db.DoTx: target.(Repository).UpdateTarget: target.(Repository).update: db.DoTx: target.(Repository).update: db.Update: name must not be empty: not null constraint violated: integrity violation: error #1001",
newProjectId: proj.PublicId,
newName: "null-name" + id,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "db.DoTx: target.(Repository).UpdateTarget: db.Update: name must not be empty: not null constraint violated: integrity violation: error #1001",
wantHostSources: true,
},
{
name: "null-description",
@@ -299,10 +374,11 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
fieldMaskPaths: []string{"Description"},
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
newTargetOpts: []target.Option{target.WithDescription("null-description" + id)},
wantErr: false,
wantRowsUpdate: 1,
newProjectId: proj.PublicId,
newTargetOpts: []target.Option{target.WithDescription("null-description" + id)},
wantErr: false,
wantRowsUpdate: 1,
wantHostSources: true,
},
{
name: "empty-field-mask",
@@ -311,11 +387,12 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
fieldMaskPaths: []string{},
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "target.(Repository).UpdateTarget: empty field mask: parameter violation: error #104",
wantIsError: errors.EmptyFieldMask,
newProjectId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "target.(Repository).UpdateTarget: empty field mask: parameter violation: error #104",
wantIsError: errors.EmptyFieldMask,
wantHostSources: true,
},
{
name: "nil-fieldmask",
@@ -324,11 +401,12 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
fieldMaskPaths: nil,
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "target.(Repository).UpdateTarget: empty field mask: parameter violation: error #104",
wantIsError: errors.EmptyFieldMask,
newProjectId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "target.(Repository).UpdateTarget: empty field mask: parameter violation: error #104",
wantIsError: errors.EmptyFieldMask,
wantHostSources: true,
},
{
name: "read-only-fields",
@@ -337,11 +415,12 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
fieldMaskPaths: []string{"CreateTime"},
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "target.(Repository).UpdateTarget: invalid field mask: CreateTime: parameter violation: error #103",
wantIsError: errors.InvalidFieldMask,
newProjectId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "target.(Repository).UpdateTarget: invalid field mask: CreateTime: parameter violation: error #103",
wantIsError: errors.InvalidFieldMask,
wantHostSources: true,
},
{
name: "unknown-fields",
@@ -350,11 +429,12 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
fieldMaskPaths: []string{"Alice"},
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "target.(Repository).UpdateTarget: invalid field mask: Alice: parameter violation: error #103",
wantIsError: errors.InvalidFieldMask,
newProjectId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "target.(Repository).UpdateTarget: invalid field mask: Alice: parameter violation: error #103",
wantIsError: errors.InvalidFieldMask,
wantHostSources: true,
},
{
name: "no-public-id",
@@ -364,11 +444,12 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
ProjectId: proj.PublicId,
PublicId: pubId(""),
},
newProjectId: proj.PublicId,
wantErr: true,
wantErrMsg: "target.(Repository).UpdateTarget: missing target public id: parameter violation: error #100",
wantIsError: errors.InvalidParameter,
wantRowsUpdate: 0,
newProjectId: proj.PublicId,
wantErr: true,
wantErrMsg: "target.(Repository).UpdateTarget: missing target public id: parameter violation: error #100",
wantIsError: errors.InvalidParameter,
wantRowsUpdate: 0,
wantHostSources: true,
},
{
name: "project-id-no-mask",
@@ -376,21 +457,11 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
name: "project-id" + id,
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
wantErr: true,
wantErrMsg: "target.(Repository).UpdateTarget: empty field mask: parameter violation: error #104",
wantIsError: errors.EmptyFieldMask,
},
{
name: "empty-project-id-with-name-mask",
args: args{
name: "empty-project-id" + id,
fieldMaskPaths: []string{"Name"},
ProjectId: "",
},
newProjectId: proj.PublicId,
wantErr: false,
wantRowsUpdate: 1,
newProjectId: proj.PublicId,
wantErr: true,
wantErrMsg: "target.(Repository).UpdateTarget: empty field mask: parameter violation: error #104",
wantIsError: errors.EmptyFieldMask,
wantHostSources: true,
},
{
name: "dup-name",
@@ -399,11 +470,12 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
fieldMaskPaths: []string{"Name"},
ProjectId: proj.PublicId,
},
newProjectId: proj.PublicId,
wantErr: true,
wantDup: true,
wantErrMsg: " already exists in project " + proj.PublicId,
wantIsError: errors.NotUnique,
newProjectId: proj.PublicId,
wantErr: true,
wantDup: true,
wantErrMsg: " already exists in project " + proj.PublicId,
wantIsError: errors.NotUnique,
wantHostSources: true,
},
}
css := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), len(tests))
@@ -416,11 +488,18 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
_ = tcp.TestTarget(ctx, t, conn, proj.PublicId, tt.args.name)
}
testCats := static.TestCatalogs(t, conn, proj.PublicId, 1)
hsets := static.TestSets(t, conn, testCats[0].GetPublicId(), 5)
testHostSetIds := make([]string, 0, len(hsets))
for _, hs := range hsets {
testHostSetIds = append(testHostSetIds, hs.PublicId)
testHostSetIds := []string{}
if tt.wantHostSources {
testCats := static.TestCatalogs(t, conn, proj.PublicId, 1)
hsets := static.TestSets(t, conn, testCats[0].GetPublicId(), 5)
testHostSetIds = make([]string, 0, len(hsets))
for _, hs := range hsets {
testHostSetIds = append(testHostSetIds, hs.PublicId)
}
tt.newTargetOpts = append(
tt.newTargetOpts,
target.WithHostSources(testHostSetIds),
)
}
cls := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 5)
@@ -438,7 +517,6 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
tt.newTargetOpts = append(
tt.newTargetOpts,
target.WithHostSources(testHostSetIds),
target.WithCredentialLibraries(testCredLibs),
)
name := tt.newName
@@ -451,6 +529,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
target.WithName(tt.args.name),
target.WithDescription(tt.args.description),
target.WithDefaultPort(tt.args.port),
target.WithAddress(tt.args.address),
)
updateTarget.SetPublicId(ctx, tar.GetPublicId())
if tt.args.PublicId != nil {
@@ -464,6 +543,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
assert.True(errors.Match(errors.T(tt.wantIsError), err))
assert.Nil(targetAfterUpdate)
assert.Equal(0, updatedRows)
assert.NotEmpty(err.Error())
assert.Contains(err.Error(), tt.wantErrMsg)
err = db.TestVerifyOplog(t, rw, tar.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
assert.Error(err)
@@ -478,6 +558,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
afterUpdateIds = append(afterUpdateIds, hs.Id())
}
assert.Equal(testHostSetIds, afterUpdateIds)
assert.Equal(tt.args.address, targetAfterUpdate.GetAddress())
afterUpdateIds = make([]string, 0, len(credSources))
for _, cl := range credSources {
@@ -487,13 +568,14 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
switch tt.name {
case "valid-no-op":
assert.Equal(tar.GetUpdateTime(), targetAfterUpdate.GetUpdateTime())
assert.Equal(tar.GetUpdateTime().String(), targetAfterUpdate.GetUpdateTime().String())
default:
assert.NotEqual(tar.GetUpdateTime(), targetAfterUpdate.GetUpdateTime())
}
foundTarget, _, _, err := repo.LookupTarget(context.Background(), tar.GetPublicId())
assert.NoError(err)
assert.True(proto.Equal(targetAfterUpdate.((*tcp.Target)), foundTarget.((*tcp.Target))))
assert.Equal(targetAfterUpdate.GetAddress(), foundTarget.GetAddress())
underlyingDB, err := conn.SqlDB(ctx)
require.NoError(err)
dbassert := dbassert.New(t, underlyingDB)
+13 -1
View File
@@ -27,6 +27,8 @@ const (
// that can be accessed via TCP. It is a subtype of target.Target.
type Target struct {
*store.Target
// Network address assigned to the Target.
Address string `json:"address,omitempty" gorm:"-"`
tableName string `gorm:"-"`
}
@@ -57,6 +59,7 @@ func (h targetHooks) NewTarget(projectId string, opt ...target.Option) (target.T
EgressWorkerFilter: opts.WithEgressWorkerFilter,
IngressWorkerFilter: opts.WithIngressWorkerFilter,
},
Address: opts.WithAddress,
}
return t, nil
}
@@ -72,7 +75,8 @@ func (h targetHooks) AllocTarget() target.Target {
func (t *Target) Clone() target.Target {
cp := proto.Clone(t.Target)
return &Target{
Target: cp.(*store.Target),
Target: cp.(*store.Target),
Address: t.Address,
}
}
@@ -124,6 +128,10 @@ func (t *Target) GetType() subtypes.Subtype {
return Subtype
}
func (t *Target) GetAddress() string {
return t.Address
}
func (t *Target) SetPublicId(ctx context.Context, publicId string) error {
const op = "tcp.(Target).SetPublicId"
if !strings.HasPrefix(publicId, TargetPrefix+"_") {
@@ -181,3 +189,7 @@ func (t *Target) SetEgressWorkerFilter(filter string) {
func (t *Target) SetIngressWorkerFilter(filter string) {
t.IngressWorkerFilter = filter
}
func (t *Target) SetAddress(address string) {
t.Address = address
}
+3 -1
View File
@@ -324,7 +324,9 @@ func TestTarget_Clone(t *testing.T) {
t.Run("valid", func(t *testing.T) {
assert := assert.New(t)
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
tar := tcp.TestTarget(ctx, t, conn, proj.PublicId, tcp.TestTargetName(t, proj.PublicId))
tar := tcp.TestTarget(ctx, t, conn, proj.PublicId, tcp.TestTargetName(t, proj.PublicId),
target.WithAddress("8.8.8.8"),
)
cp := tar.Clone()
assert.True(proto.Equal(cp.(*tcp.Target).Target, tar.(*tcp.Target).Target))
})
+7
View File
@@ -26,6 +26,13 @@ func TestTarget(ctx context.Context, t testing.TB, conn *db.DB, projectId, name
err = rw.Create(context.Background(), tar)
require.NoError(err)
if opts.WithAddress != "" {
address, err := target.NewAddress(tar.GetPublicId(), opts.WithAddress)
require.NoError(err)
require.NotNil(address)
err = rw.Create(context.Background(), address)
require.NoError(err)
}
if len(opts.WithHostSources) > 0 {
newHostSets := make([]any, 0, len(opts.WithHostSources))
for _, s := range opts.WithHostSources {
+23
View File
@@ -23,6 +23,17 @@ func TestNewCredentialLibrary(targetId, credentialLibraryId string, purpose cred
}
}
// TestNewTargetAddress creates a new in memory TargetAddress
// representing the association between a Target and network address.
func TestNewTargetAddress(targetId, address string) *Address {
return &Address{
TargetAddress: &store.TargetAddress{
TargetId: targetId,
Address: address,
},
}
}
// TestNewStaticCredential creates a new in memory StaticCredential
// representing the relationship between targetId and credentialId with
// the given purpose.
@@ -47,3 +58,15 @@ func TestCredentialLibrary(t testing.TB, conn *db.DB, targetId, libraryId string
require.NoError(err)
return lib
}
// TestTargetAddress creates am association for targetId and
// a given address value.
func TestTargetAddress(t testing.TB, conn *db.DB, targetId, address string) *Address {
t.Helper()
require := require.New(t)
rw := db.New(conn)
staticAddress := TestNewTargetAddress(targetId, address)
err := rw.Create(context.Background(), staticAddress)
require.NoError(err)
return staticAddress
}
+167
View File
@@ -11,6 +11,7 @@ import (
"github.com/hashicorp/boundary/api/credentials"
"github.com/hashicorp/boundary/api/credentialstores"
"github.com/hashicorp/boundary/api/hostcatalogs"
"github.com/hashicorp/boundary/api/hosts"
"github.com/hashicorp/boundary/api/hostsets"
"github.com/hashicorp/boundary/api/roles"
"github.com/hashicorp/boundary/api/targets"
@@ -379,6 +380,172 @@ func TestList(t *testing.T) {
assert.Equal(filterItem.Id, ul.Items[0].Id)
}
func TestTarget_AddressMutualExclusiveRelationship(t *testing.T) {
tc := controller.NewTestController(t, nil)
t.Cleanup(tc.Shutdown)
client := tc.Client()
at := tc.Token()
client.SetToken(at.Token)
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(at.UserId))
tClient := targets.NewClient(client)
// Create target with a network address association
targetResp, err := tClient.Create(tc.Context(), "tcp", proj.GetPublicId(),
targets.WithName("test-address"), targets.WithAddress("localhost"), targets.WithTcpTargetDefaultPort(22))
require.NoError(t, err)
require.NotNil(t, targetResp)
require.Equal(t, "localhost", targetResp.GetItem().Address)
// Setup host catalog, host set, & host resources
hc, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId())
require.NoError(t, err)
require.NotNil(t, hc)
hs, err := hostsets.NewClient(client).Create(tc.Context(), hc.Item.Id)
require.NoError(t, err)
require.NotNil(t, hs)
h, err := hosts.NewClient(client).Create(tc.Context(), hc.Item.Id, hosts.WithStaticHostAddress("localhost"))
require.NoError(t, err)
require.NotNil(t, h)
hUpdate, err := hostsets.NewClient(client).AddHosts(tc.Context(), hs.Item.Id, hs.Item.Version, []string{h.GetItem().Id})
require.NoError(t, err)
require.NotNil(t, hUpdate)
// Expect error when associating a host source to the target with a direct network address association
targetId := targetResp.GetItem().Id
version := targetResp.GetItem().Version
updateResp, err := tClient.AddHostSources(tc.Context(), targetId, version, []string{hs.Item.Id})
require.Error(t, err)
require.Nil(t, updateResp)
apiErr := api.AsServerError(err)
require.NotNil(t, apiErr)
require.Equal(t, http.StatusBadRequest, apiErr.Response().StatusCode())
// Remove direct network address association. Successfully add a host source to target.
targetResp, err = tClient.Update(tc.Context(), targetId, version, targets.DefaultAddress())
require.NoError(t, err)
require.NotNil(t, targetResp)
require.Empty(t, targetResp.GetItem().Address)
version = targetResp.GetItem().Version
updateResp, err = tClient.AddHostSources(tc.Context(), targetId, version, []string{hs.Item.Id})
require.NoError(t, err)
require.NotNil(t, updateResp)
require.Empty(t, updateResp.GetItem().Address)
require.Equal(t, []string{hs.Item.Id}, updateResp.GetItem().HostSourceIds)
}
func TestTarget_HostSourceMutualExclusiveRelationship(t *testing.T) {
tc := controller.NewTestController(t, nil)
t.Cleanup(tc.Shutdown)
client := tc.Client()
at := tc.Token()
client.SetToken(at.Token)
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(at.UserId))
tClient := targets.NewClient(client)
// Setup host catalog, host set, & host resources
hc, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId())
require.NoError(t, err)
require.NotNil(t, hc)
hs, err := hostsets.NewClient(client).Create(tc.Context(), hc.Item.Id)
require.NoError(t, err)
require.NotNil(t, hs)
h, err := hosts.NewClient(client).Create(tc.Context(), hc.Item.Id, hosts.WithStaticHostAddress("localhost"))
require.NoError(t, err)
require.NotNil(t, h)
hUpdate, err := hostsets.NewClient(client).AddHosts(tc.Context(), hs.Item.Id, hs.Item.Version, []string{h.GetItem().Id})
require.NoError(t, err)
require.NotNil(t, hUpdate)
// Create target without a network address association
targetResp, err := tClient.Create(tc.Context(), "tcp", proj.GetPublicId(),
targets.WithName("test-host-source"), targets.WithTcpTargetDefaultPort(22))
require.NoError(t, err)
require.NotNil(t, targetResp)
require.Empty(t, targetResp.GetItem().Address)
// Expect error when associating a address to the target with a host source association
targetId := targetResp.GetItem().Id
version := targetResp.GetItem().Version
updateResp, err := tClient.AddHostSources(tc.Context(), targetId, version, []string{hs.Item.Id})
require.NoError(t, err)
require.NotNil(t, updateResp)
require.Empty(t, updateResp.GetItem().Address)
require.Equal(t, []string{hs.Item.Id}, updateResp.GetItem().HostSourceIds)
version = updateResp.GetItem().Version
updateResp, err = tClient.Update(tc.Context(), targetId, version, targets.WithAddress("localhost"))
require.Error(t, err)
require.Nil(t, updateResp)
apiErr := api.AsServerError(err)
require.NotNil(t, apiErr)
require.Equal(t, http.StatusBadRequest, apiErr.Response().StatusCode())
// Remove host source association. Successfully assign a network address to the target.
updateResp, err = tClient.RemoveHostSources(tc.Context(), targetId, version, []string{hs.Item.Id})
require.NoError(t, err)
require.NotNil(t, updateResp)
require.Empty(t, updateResp.GetItem().HostSourceIds)
version = updateResp.GetItem().Version
updateResp, err = tClient.Update(tc.Context(), targetId, version, targets.WithAddress("localhost"))
require.NoError(t, err)
require.NotNil(t, updateResp)
require.Equal(t, "localhost", updateResp.GetItem().Address)
require.Empty(t, updateResp.GetItem().HostSourceIds)
}
func TestCreateTarget_DirectlyAttachedAddress(t *testing.T) {
tc := controller.NewTestController(t, nil)
t.Cleanup(tc.Shutdown)
client := tc.Client()
at := tc.Token()
client.SetToken(at.Token)
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(at.UserId))
tClient := targets.NewClient(client)
tests := []struct {
name string
address string
}{
{
name: "target-ipv4-address",
address: "127.0.0.1",
},
{
name: "target-dns-address",
address: "null",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
createResp, err := tClient.Create(tc.Context(), "tcp", proj.PublicId,
targets.WithName(tt.name), targets.WithAddress(tt.address), targets.WithTcpTargetDefaultPort(22))
require.NoError(err)
require.NotNil(createResp)
assert.Equal(tt.address, createResp.GetItem().Address)
targetId := createResp.GetItem().Id
version := createResp.GetItem().Version
readResp, err := tClient.Read(tc.Context(), targetId)
require.NoError(err)
require.NotNil(readResp)
assert.Equal(tt.address, readResp.GetItem().Address)
updateResp, err := tClient.Update(tc.Context(), targetId, version, targets.DefaultAddress())
require.NoError(err)
require.NotNil(updateResp)
assert.Empty(updateResp.GetItem().Address)
readResp, err = tClient.Read(tc.Context(), targetId)
require.NoError(err)
require.NotNil(readResp)
assert.Empty(readResp.GetItem().Address)
})
}
}
func comparableSlice(in []*targets.Target) []targets.Target {
var filtered []targets.Target
for _, i := range in {
+26
View File
@@ -1,6 +1,7 @@
load _authorized_actions
export TGT_NAME='test'
export TGT_NAME_WITH_ADDR='test-address'
function create_tcp_target() {
local sid=$1
@@ -13,6 +14,19 @@ function create_tcp_target() {
-format json
}
function create_tcp_target_with_addr() {
local sid=$1
local addr=$2
local port=$3
local name=$4
boundary targets create tcp \
-address $addr \
-default-port $port \
-name $name \
-scope-id $sid \
-format json
}
function read_target() {
boundary targets read -id $1 -format json
}
@@ -25,12 +39,24 @@ function list_targets() {
boundary targets list -scope-id $1 -format json
}
function update_address() {
local id=$1
local addr=$2
boundary targets update tcp -id $id -address $2
}
function assoc_host_sources() {
local id=$1
local hst=$2
boundary targets add-host-sources -id $id -host-source $hst
}
function remove_host_sources() {
local id=$1
local hst=$2
boundary targets remove-host-sources -id $id -host-source $hst
}
function target_id_from_name() {
local sid=$1
local name=$2
+33
View File
@@ -100,3 +100,36 @@ load _helpers
run read_target $id
[ "$status" -eq 1 ]
}
@test "boundary/target: create target with a network address" {
run create_tcp_target_with_addr $DEFAULT_P_ID "localhost" 22 $TGT_NAME_WITH_ADDR
[ "$status" -eq 0 ]
}
@test "boundary/target: cannot assign an host source to a target with an address" {
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME_WITH_ADDR)
run assoc_host_sources $id $DEFAULT_HOST_SET
[ "$status" -eq 1 ]
}
@test "boundary/target: can assign an host source to a target after deleting address" {
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME_WITH_ADDR)
run update_address $id "null"
[ "$status" -eq 0 ]
run assoc_host_sources $id $DEFAULT_HOST_SET
[ "$status" -eq 0 ]
}
@test "boundary/target: cannot assign an address to a target with an host source" {
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME_WITH_ADDR)
run update_address $id "localhost"
[ "$status" -eq 1 ]
}
@test "boundary/target: can assign an an address to a target after deleting host source" {
local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME_WITH_ADDR)
run remove_host_sources $id $DEFAULT_HOST_SET
[ "$status" -eq 0 ]
run update_address $id "localhost"
[ "$status" -eq 0 ]
}
@@ -372,6 +372,8 @@ type Target struct {
Attrs isTarget_Attrs `protobuf_oneof:"attrs"`
// Output only. The available actions on this resource for this user.
AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty" class:"public"` // @gotags: `class:"public"`
// Optional string value that represents a network resource and is used when establishing a session.
Address *wrapperspb.StringValue `protobuf:"bytes,540,opt,name=address,proto3" json:"address,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *Target) Reset() {
@@ -598,6 +600,13 @@ func (x *Target) GetAuthorizedActions() []string {
return nil
}
func (x *Target) GetAddress() *wrapperspb.StringValue {
if x != nil {
return x.Address
}
return nil
}
type isTarget_Attrs interface {
isTarget_Attrs()
}
@@ -1234,7 +1243,7 @@ var file_controller_api_resources_targets_v1_target_proto_rawDesc = []byte{
0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x52, 0x12, 0x63, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79,
0x22, 0xe8, 0x13, 0x0a, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x22, 0xbd, 0x14, 0x0a, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73,
0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73,
0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65,
@@ -1379,122 +1388,127 @@ var file_controller_api_resources_targets_v1_target_proto_rawDesc = []byte{
0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a,
0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x07,
0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x4a, 0x06, 0x08, 0x96, 0x01, 0x10, 0x97, 0x01, 0x4a,
0x06, 0x08, 0xb4, 0x01, 0x10, 0xb5, 0x01, 0x4a, 0x06, 0x08, 0xf4, 0x03, 0x10, 0xf5, 0x03, 0x4a,
0x06, 0x08, 0xfe, 0x03, 0x10, 0xff, 0x03, 0x4a, 0x04, 0x08, 0x64, 0x10, 0x65, 0x4a, 0x04, 0x08,
0x6e, 0x10, 0x6f, 0x52, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72,
0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x52, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f,
0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73,
0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x52, 0x19, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f,
0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x73, 0x52, 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73,
0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x22, 0x87, 0x01, 0x0a, 0x13,
0x54, 0x63, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
0x74, 0x65, 0x73, 0x12, 0x70, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70,
0x6f, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74,
0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2e, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29,
0x26, 0x0a, 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x65,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x0b, 0x44, 0x65, 0x66, 0x61,
0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x87, 0x01, 0x0a, 0x13, 0x53, 0x73, 0x68, 0x54, 0x61, 0x72,
0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x70, 0x0a,
0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0a, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x42, 0x2e, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x26, 0x0a, 0x17, 0x61, 0x74, 0x74,
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f,
0x70, 0x6f, 0x72, 0x74, 0x12, 0x0b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72,
0x74, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22,
0x26, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a,
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xed, 0x03, 0x0a, 0x18, 0x53, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x44, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x5f, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69,
0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f,
0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f,
0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x63,
0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18,
0x5a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69,
0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x78, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x70, 0x72, 0x69,
0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07,
0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69,
0x6e, 0x74, 0x18, 0x8d, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x12, 0x52, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e,
0x66, 0x6f, 0x18, 0x96, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 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,
0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0xeb, 0x03, 0x0a, 0x14, 0x53, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64,
0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x43,
0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63,
0x6f, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x32,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x12, 0x20, 0x0a,
0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x3c, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12,
0x18, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70,
0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a,
0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x53,
0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x9c, 0x04, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x1a,
0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x12, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x4a, 0x06, 0x08, 0x96,
0x01, 0x10, 0x97, 0x01, 0x4a, 0x06, 0x08, 0xb4, 0x01, 0x10, 0xb5, 0x01, 0x4a, 0x06, 0x08, 0xf4,
0x03, 0x10, 0xf5, 0x03, 0x4a, 0x06, 0x08, 0xfe, 0x03, 0x10, 0xff, 0x03, 0x4a, 0x04, 0x08, 0x64,
0x10, 0x65, 0x4a, 0x04, 0x08, 0x6e, 0x10, 0x6f, 0x52, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x52, 0x20, 0x61, 0x70,
0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x1c,
0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x52, 0x19, 0x65, 0x67,
0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65,
0x74, 0x5f, 0x69, 0x64, 0x73, 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x73,
0x22, 0x87, 0x01, 0x0a, 0x13, 0x54, 0x63, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74,
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x70, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61,
0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2e, 0xa0, 0xda,
0x29, 0x01, 0xc2, 0xdd, 0x29, 0x26, 0x0a, 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
0x65, 0x73, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x12,
0x0b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x0c, 0x64, 0x65,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x87, 0x01, 0x0a, 0x13, 0x53,
0x73, 0x68, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
0x65, 0x73, 0x12, 0x70, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f,
0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33,
0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2e, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x26,
0x0a, 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x66,
0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x0b, 0x44, 0x65, 0x66, 0x61, 0x75,
0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f,
0x70, 0x6f, 0x72, 0x74, 0x22, 0x26, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e,
0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xed, 0x03, 0x0a,
0x18, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73,
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72,
0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61,
0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65,
0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70,
0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0c,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x74, 0x79, 0x70, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c,
0x69, 0x6d, 0x69, 0x74, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x6e,
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b,
0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x78, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x21,
0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x82, 0x01,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65,
0x79, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x8c, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x08, 0x65,
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x8d, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x52, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x96, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f,
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, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52,
0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0xeb, 0x03, 0x0a,
0x14, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69,
0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63,
0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66,
0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72,
0x5f, 0x69, 0x64, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f,
0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69,
0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65,
0x74, 0x5f, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18,
0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x12,
0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79,
0x70, 0x65, 0x12, 0x30, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52,
0x13, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x61, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12,
0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x64, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x58, 0x0a, 0x0b, 0x63,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x36, 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, 0x43, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x54, 0x0a, 0x1a, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d,
0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12,
0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x8c, 0x01, 0x0a, 0x17,
0x53, 0x73, 0x68, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b,
0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
0x65, 0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f,
0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79,
0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f,
0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61,
0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x61, 0x72,
0x67, 0x65, 0x74, 0x73, 0x3b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x12, 0x58, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18,
0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 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, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0b, 0x63,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x54, 0x0a, 0x1a, 0x55, 0x73,
0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x43, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
0x22, 0x8c, 0x01, 0x0a, 0x17, 0x53, 0x73, 0x68, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b,
0x65, 0x79, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08,
0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76,
0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70,
0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x72, 0x69,
0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72,
0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x70, 0x72, 0x69, 0x76, 0x61,
0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x42,
0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61,
0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79,
0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x73, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x3b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1552,19 +1566,20 @@ var file_controller_api_resources_targets_v1_target_proto_depIdxs = []int32{
12, // 18: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct
5, // 19: controller.api.resources.targets.v1.Target.tcp_target_attributes:type_name -> controller.api.resources.targets.v1.TcpTargetAttributes
6, // 20: controller.api.resources.targets.v1.Target.ssh_target_attributes:type_name -> controller.api.resources.targets.v1.SshTargetAttributes
16, // 21: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
16, // 22: controller.api.resources.targets.v1.SshTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
13, // 23: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
15, // 24: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp
7, // 25: controller.api.resources.targets.v1.SessionAuthorizationData.worker_info:type_name -> controller.api.resources.targets.v1.WorkerInfo
13, // 26: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
15, // 27: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp
3, // 28: controller.api.resources.targets.v1.SessionAuthorization.credentials:type_name -> controller.api.resources.targets.v1.SessionCredential
29, // [29:29] is the sub-list for method output_type
29, // [29:29] is the sub-list for method input_type
29, // [29:29] is the sub-list for extension type_name
29, // [29:29] is the sub-list for extension extendee
0, // [0:29] is the sub-list for field type_name
14, // 21: controller.api.resources.targets.v1.Target.address:type_name -> google.protobuf.StringValue
16, // 22: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
16, // 23: controller.api.resources.targets.v1.SshTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value
13, // 24: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
15, // 25: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp
7, // 26: controller.api.resources.targets.v1.SessionAuthorizationData.worker_info:type_name -> controller.api.resources.targets.v1.WorkerInfo
13, // 27: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo
15, // 28: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp
3, // 29: controller.api.resources.targets.v1.SessionAuthorization.credentials:type_name -> controller.api.resources.targets.v1.SessionCredential
30, // [30:30] is the sub-list for method output_type
30, // [30:30] is the sub-list for method input_type
30, // [30:30] is the sub-list for extension type_name
30, // [30:30] is the sub-list for extension extendee
0, // [0:30] is the sub-list for field type_name
}
func init() { file_controller_api_resources_targets_v1_target_proto_init() }
@@ -129,7 +129,8 @@ var (
// optional controller.custom_options.v1.MaskMapping mask_mapping = 85464;
E_MaskMapping = &file_controller_custom_options_v1_options_proto_extTypes[0]
// generate_sdk_option is a directive used when generating the SDK to
// indicate that an option should be created for the field
// indicate that an option should be created for the field. This also controls
// API options (DefaultX, WithX functions).
//
// optional bool generate_sdk_option = 85412;
E_GenerateSdkOption = &file_controller_custom_options_v1_options_proto_extTypes[1]
+52 -2
View File
@@ -61,14 +61,64 @@ func CreateNewTargetCli(t testing.TB, ctx context.Context, projectId string, def
return newTargetId
}
// AddHostSourceToTargetCli uses the cli to add a host source (host set or host) to a target
func AddHostSourceToTargetCli(t testing.TB, ctx context.Context, targetId string, hostSourceId string) {
// CreateNewAddressTargetCli uses the cli to create a new target using an
// address in boundary.
// Returns the id of the new target.
func CreateNewAddressTargetCli(t testing.TB, ctx context.Context, projectId string, defaultPort string, address string) string {
output := e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"targets", "create", "tcp",
"-scope-id", projectId,
"-default-port", defaultPort,
"-name", "e2e Target",
"-address", address,
"-format", "json",
),
)
require.NoError(t, output.Err, string(output.Stderr))
var newTargetResult targets.TargetCreateResult
err := json.Unmarshal(output.Stdout, &newTargetResult)
require.NoError(t, err)
newTargetId := newTargetResult.Item.Id
t.Logf("Created Target: %s", newTargetId)
return newTargetId
}
// AddHostSourceToTargetCli uses the cli to add a host source (host set or host)
// to a target.
// Boundary's `add-host-sources` functionality appends a new host source to the
// existing set of host sources in the target.
func AddHostSourceToTargetCli(t testing.TB, ctx context.Context, targetId, hostSourceId string) {
output := e2e.RunCommand(ctx, "boundary",
e2e.WithArgs("targets", "add-host-sources", "-id", targetId, "-host-source", hostSourceId),
)
require.NoError(t, output.Err, string(output.Stderr))
}
// SetHostSourceToTargetCli uses the cli to set a host source (host set or host)
// to a target.
// Boundary's `set-host-sources` functionality replaces all existing host sets
// on a target with the provided one.
func SetHostSourceToTargetCli(t testing.TB, ctx context.Context, targetId, hostSourceId string) {
output := e2e.RunCommand(ctx, "boundary",
e2e.WithArgs("targets", "set-host-sources", "-id", targetId, "-host-source", hostSourceId),
)
require.NoError(t, output.Err, string(output.Stderr))
}
// RemoveHostSourceFromTargetCli uses the cli to remove a host source (host set or host) to a target
func RemoveHostSourceFromTargetCli(t testing.TB, ctx context.Context, targetId, hostSourceId string) {
output := e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"targets", "remove-host-sources",
"-id", targetId,
"-host-source", hostSourceId,
),
)
require.NoError(t, output.Err, string(output.Stderr))
}
// AddCredentialSourceToTargetCli uses the cli to add a credential source (credential library or
// credential) to a target
func AddCredentialSourceToTargetCli(t testing.TB, ctx context.Context, targetId string, credentialSourceId string) {
@@ -0,0 +1,340 @@
package static_test
import (
"context"
"encoding/json"
"net/http"
"strings"
"testing"
"github.com/hashicorp/boundary/testing/internal/e2e"
"github.com/hashicorp/boundary/testing/internal/e2e/boundary"
"github.com/stretchr/testify/require"
)
// TestCliCreateUpdateTargetAddress asserts that creating and updating a target
// using a valid address is successful.
func TestCliCreateUpdateTargetAddress(t *testing.T) {
e2e.MaybeSkipTest(t)
c, err := loadConfig()
require.NoError(t, err)
ctx := context.Background()
boundary.AuthenticateAdminCli(t, ctx)
newOrgId := boundary.CreateNewOrgCli(t, ctx)
t.Cleanup(func() {
ctx := context.Background()
boundary.AuthenticateAdminCli(t, ctx)
output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs("scopes", "delete", "-id", newOrgId))
require.NoError(t, output.Err, string(output.Stderr))
})
newProjectId := boundary.CreateNewProjectCli(t, ctx, newOrgId)
newTargetId := boundary.CreateNewAddressTargetCli(t, ctx, newProjectId, c.TargetPort, c.TargetIp)
// Connect to target and print host's IP address
output := e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"connect",
"-target-id", newTargetId,
"-exec", "/usr/bin/ssh", "--",
"-l", c.TargetSshUser,
"-i", c.TargetSshKeyPath,
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
"-o", "IdentitiesOnly=yes", // forces the use of the provided key
"-p", "{{boundary.port}}", // this is provided by boundary
"{{boundary.ip}}",
"hostname", "-i",
),
)
require.NoError(t, output.Err, string(output.Stderr))
parts := strings.Fields(string(output.Stdout))
hostIp := parts[len(parts)-1]
require.Equal(t, c.TargetIp, hostIp, "SSH session did not return expected output")
t.Log("Successfully connected to target")
// Remove target address. We have now no address or host sources.
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"targets", "update", "tcp",
"-id", newTargetId,
"-address", "null",
"-format", "json",
),
)
require.NoError(t, output.Err, string(output.Stderr))
// Connect to target and print host's IP address - should error because
// there's no place to connect to.
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"connect",
"-target-id", newTargetId,
"-format", "json",
"-exec", "/usr/bin/ssh", "--",
"-l", c.TargetSshUser,
"-i", c.TargetSshKeyPath,
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
"-o", "IdentitiesOnly=yes", // forces the use of the provided key
"-p", "{{boundary.port}}", // this is provided by boundary
"{{boundary.ip}}",
"hostname", "-i",
),
)
require.Error(t, output.Err, string(output.Stderr))
var response boundary.CliError
require.NoError(t, json.Unmarshal(output.Stderr, &response))
require.Equal(t, http.StatusNotFound, response.Status, "Expected error when connecting to target with no host sources or address")
// Add a valid address back in.
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"targets", "update", "tcp",
"-id", newTargetId,
"-address", c.TargetIp,
"-format", "json",
),
)
require.NoError(t, output.Err, string(output.Stderr))
// Connect to target and print host's IP address.
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"connect",
"-target-id", newTargetId,
"-exec", "/usr/bin/ssh", "--",
"-l", c.TargetSshUser,
"-i", c.TargetSshKeyPath,
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
"-o", "IdentitiesOnly=yes", // forces the use of the provided key
"-p", "{{boundary.port}}", // this is provided by boundary
"{{boundary.ip}}",
"hostname", "-i",
),
)
require.NoError(t, output.Err, string(output.Stderr))
parts = strings.Fields(string(output.Stdout))
hostIp = parts[len(parts)-1]
require.Equal(t, c.TargetIp, hostIp, "SSH session did not return expected output")
t.Log("Successfully connected to target")
}
// TestCliTargetAddressToHostSource asserts that we can change a target using an
// address to a target using host sources only if we remove the address first.
// TestCliTargetAddressToHostSource and TestCliTargetHostSourceToAddress are separate,
// independent tests because switching between host sources and address is not a
// commutative operation (they use different codepaths).
func TestCliTargetAddressToHostSource(t *testing.T) {
e2e.MaybeSkipTest(t)
c, err := loadConfig()
require.NoError(t, err)
ctx := context.Background()
boundary.AuthenticateAdminCli(t, ctx)
newOrgId := boundary.CreateNewOrgCli(t, ctx)
t.Cleanup(func() {
ctx := context.Background()
boundary.AuthenticateAdminCli(t, ctx)
output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs("scopes", "delete", "-id", newOrgId))
require.NoError(t, output.Err, string(output.Stderr))
})
newProjectId := boundary.CreateNewProjectCli(t, ctx, newOrgId)
newHostCatalogId := boundary.CreateNewHostCatalogCli(t, ctx, newProjectId)
newHostSetId := boundary.CreateNewHostSetCli(t, ctx, newHostCatalogId)
newHostId := boundary.CreateNewHostCli(t, ctx, newHostCatalogId, c.TargetIp)
boundary.AddHostToHostSetCli(t, ctx, newHostSetId, newHostId)
newTargetId := boundary.CreateNewAddressTargetCli(t, ctx, newProjectId, c.TargetPort, c.TargetIp)
// Connect to target and print host's IP address
output := e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"connect",
"-target-id", newTargetId,
"-exec", "/usr/bin/ssh", "--",
"-l", c.TargetSshUser,
"-i", c.TargetSshKeyPath,
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
"-o", "IdentitiesOnly=yes", // forces the use of the provided key
"-p", "{{boundary.port}}", // this is provided by boundary
"{{boundary.ip}}",
"hostname", "-i",
),
)
require.NoError(t, output.Err, string(output.Stderr))
parts := strings.Fields(string(output.Stdout))
hostIp := parts[len(parts)-1]
require.Equal(t, c.TargetIp, hostIp, "SSH session did not return expected output")
t.Log("Successfully connected to target")
// Attempt add-host-sources. Should error because the target has an address.
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"targets", "add-host-sources",
"-id", newTargetId,
"-host-source", newHostSetId,
"-format", "json",
),
)
require.Error(t, output.Err, "Target add-host-sources did not return expected error")
var response boundary.CliError
require.NoError(t, json.Unmarshal(output.Stderr, &response))
require.Equal(t, http.StatusBadRequest, response.Status, "Expected error when adding host source to target with an address")
// Attempt set-host-sources. Should error because the target has an address.
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"targets", "set-host-sources",
"-id", newTargetId,
"-host-source", newHostSetId,
"-format", "json",
),
)
require.Error(t, output.Err, "Target set-host-sources did not return expected error")
require.NoError(t, json.Unmarshal(output.Stderr, &response))
require.Equal(t, http.StatusBadRequest, response.Status, "Expected error when setting host source to target with an address")
// Remove the target's address. We should now be able to add/set host
// sources.
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"targets", "update", "tcp",
"-id", newTargetId,
"-address", "null",
"-format", "json",
),
)
require.NoError(t, output.Err, string(output.Stderr))
boundary.AddHostSourceToTargetCli(t, ctx, newTargetId, newHostSetId)
boundary.SetHostSourceToTargetCli(t, ctx, newTargetId, newHostSetId)
// Connect to target and print host's IP address, now using the host source.
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"connect",
"-target-id", newTargetId,
"-exec", "/usr/bin/ssh", "--",
"-l", c.TargetSshUser,
"-i", c.TargetSshKeyPath,
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
"-o", "IdentitiesOnly=yes", // forces the use of the provided key
"-p", "{{boundary.port}}", // this is provided by boundary
"{{boundary.ip}}",
"hostname", "-i",
),
)
require.NoError(t, output.Err, string(output.Stderr))
parts = strings.Fields(string(output.Stdout))
hostIp = parts[len(parts)-1]
require.Equal(t, c.TargetIp, hostIp, "SSH session did not return expected output")
t.Log("Successfully connected to target")
}
// TestCliTargetHostSourceToAddress asserts that we can change a target using host
// source(s) to a target using an address only if we remove the host sources
// first.
// TestCliTargetAddressToHostSource and TestCliTargetHostSourceToAddress are separate,
// independent tests because switching between host sources and address is not a
// commutative operation (they use different codepaths).
func TestCliTargetHostSourceToAddress(t *testing.T) {
e2e.MaybeSkipTest(t)
c, err := loadConfig()
require.NoError(t, err)
ctx := context.Background()
boundary.AuthenticateAdminCli(t, ctx)
newOrgId := boundary.CreateNewOrgCli(t, ctx)
t.Cleanup(func() {
ctx := context.Background()
boundary.AuthenticateAdminCli(t, ctx)
output := e2e.RunCommand(ctx, "boundary", e2e.WithArgs("scopes", "delete", "-id", newOrgId))
require.NoError(t, output.Err, string(output.Stderr))
})
newProjectId := boundary.CreateNewProjectCli(t, ctx, newOrgId)
newHostCatalogId := boundary.CreateNewHostCatalogCli(t, ctx, newProjectId)
newHostSetId := boundary.CreateNewHostSetCli(t, ctx, newHostCatalogId)
newHostId := boundary.CreateNewHostCli(t, ctx, newHostCatalogId, c.TargetIp)
boundary.AddHostToHostSetCli(t, ctx, newHostSetId, newHostId)
newTargetId := boundary.CreateNewTargetCli(t, ctx, newProjectId, c.TargetPort)
boundary.AddHostSourceToTargetCli(t, ctx, newTargetId, newHostSetId)
// Connect to target and print host's IP address
output := e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"connect",
"-target-id", newTargetId,
"-exec", "/usr/bin/ssh", "--",
"-l", c.TargetSshUser,
"-i", c.TargetSshKeyPath,
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
"-o", "IdentitiesOnly=yes", // forces the use of the provided key
"-p", "{{boundary.port}}", // this is provided by boundary
"{{boundary.ip}}",
"hostname", "-i",
),
)
require.NoError(t, output.Err, string(output.Stderr))
parts := strings.Fields(string(output.Stdout))
hostIp := parts[len(parts)-1]
require.Equal(t, c.TargetIp, hostIp, "SSH session did not return expected output")
t.Log("Successfully connected to target")
// Attempt to add an address to the target - should error because we have
// host sources set.
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"targets", "update", "tcp",
"-id", newTargetId,
"-address", c.TargetIp,
"-format", "json",
),
)
require.Error(t, output.Err, "Target update did not return expected error")
var response boundary.CliError
require.NoError(t, json.Unmarshal(output.Stderr, &response))
require.Equal(t, http.StatusBadRequest, response.Status, "Expected error when setting address to target with a host source")
boundary.RemoveHostSourceFromTargetCli(t, ctx, newTargetId, newHostSetId)
// Attempt to add an address to the target again - should work
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"targets", "update", "tcp",
"-id", newTargetId,
"-address", c.TargetIp,
"-format", "json",
),
)
require.NoError(t, output.Err, string(output.Stderr))
// Connect to target and print host's IP address
output = e2e.RunCommand(ctx, "boundary",
e2e.WithArgs(
"connect",
"-target-id", newTargetId,
"-exec", "/usr/bin/ssh", "--",
"-l", c.TargetSshUser,
"-i", c.TargetSshKeyPath,
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
"-o", "IdentitiesOnly=yes", // forces the use of the provided key
"-p", "{{boundary.port}}", // this is provided by boundary
"{{boundary.ip}}",
"hostname", "-i",
),
)
require.NoError(t, output.Err, string(output.Stderr))
}
+2 -2
View File
@@ -130,8 +130,8 @@ However, a target can be one of many types of targets, and a concrete
implementation of a target is a `tcp` type of target. Therefore an extra
parameter is required when creating or updating a target:
- `boundary targets tcp create`
- `boundary targets tcp update`
- `boundary targets create tcp`
- `boundary targets update tcp`
This allows the CLI to perform proper presentation and validation of parameters
and functions for the given type.
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff. Show more