targets repo (#298)

This commit is contained in:
Jim authored and GitHub committed 2020-08-26 13:58:46 -04:00
1 parent 58dec98ea2
commit fcb61d4b67
24 files changed
+4403 -1

No files matched your search

+3 -1
View File
@@ -84,7 +84,9 @@ protobuild:
@protoc-go-inject-tag -input=./internal/auth/store/account.pb.go
@protoc-go-inject-tag -input=./internal/auth/password/store/password.pb.go
@protoc-go-inject-tag -input=./internal/auth/password/store/argon2.pb.go
@protoc-go-inject-tag -input=./internal/kms/store/root_key.pb.go
@protoc-go-inject-tag -input=./internal/kms/store/root_key.pb.go
@protoc-go-inject-tag -input=./internal/target/store/target.pb.go
@rm -R ${TMP_DIR}
protolint:
+295
View File
@@ -2877,6 +2877,301 @@ before insert on kms_session_key_version
('kms_root_key_version', 1);
commit;
`),
},
"migrations/40_targets.down.sql": {
name: "40_targets.down.sql",
bytes: []byte(`
begin;
drop function insert_target_subtype;
drop function delete_target_subtype;
drop function target_scope_valid;
drop function target_host_set_scope_valid
commit;
`),
},
"migrations/40_targets.up.sql": {
name: "40_targets.up.sql",
bytes: []byte(`
begin;
-- insert_target_subtype() is a before insert trigger
-- function for subtypes of target
create or replace function
insert_target_subtype()
returns trigger
as $$
begin
insert into target
(public_id, scope_id)
values
(new.public_id, new.scope_id);
return new;
end;
$$ language plpgsql;
-- delete_target_subtype() is an after delete trigger
-- function for subtypes of host
create or replace function delete_target_subtype()
returns trigger
as $$
begin
delete from target
where
public_id = old.public_id;
return null; -- result is ignored since this is an after trigger
end;
$$ language plpgsql;
-- target_scope_valid() is a before insert trigger function for target
create or replace function
target_scope_valid()
returns trigger
as $$
declare scope_type text;
begin
-- Fetch the type of scope
select isc.type from iam_scope isc where isc.public_id = new.scope_id into scope_type;
if scope_type = 'project' then
return new;
end if;
raise exception 'invalid target scope type % (must be project)', scope_type;
end;
$$ language plpgsql;
-- target_host_set_scope_valid() is a before insert trigger function for target_host_set
create or replace function
target_host_set_scope_valid()
returns trigger
as $$
begin
perform from
host_catalog hc,
host_set hs,
target t,
iam_scope s
where
hc.public_id = hs.catalog_id and
hc.scope_id = t.scope_id and
t.public_id = new.target_id;
if not found then
raise exception 'target scope and host set scope are not equal';
end if;
return new;
end;
$$ language plpgsql;
commit;
`),
},
"migrations/41_targets.down.sql": {
name: "41_targets.down.sql",
bytes: []byte(`
begin;
drop table target cascade;
drop table target_host_set cascade;
drop table target_tcp;
drop view target_all_subtypes;
drop view target_host_set_catalog;
delete
from oplog_ticket
where name in (
'target_tcp'
);
commit;
`),
},
"migrations/41_targets.up.sql": {
name: "41_targets.up.sql",
bytes: []byte(`
/*
┌─────────────────┐
┌─────────────────┐ │ target_tcp │
│ target │ ├─────────────────┤
├─────────────────┤ │public_id │
│public_id │┼─────────────○┼│scope_id │
│scope_id │ │default_port │
│ │ │name (not null) │
└─────────────────┘ │description │
┼ └─────────────────┘
│
○
╱│╲
┌─────────────────┐
│ target_host_set │
├─────────────────┤
│target_id │
│host_set_id │
│ │
└─────────────────┘
╲│╱
○
│
│
┼
┌─────────────────┐
│ host_set │
├─────────────────┤
│public_id │
│catalog_id │
│ │
└─────────────────┘
*/
begin;
create table target (
public_id wt_public_id primary key,
scope_id wt_scope_id not null
references iam_scope(public_id)
on delete cascade
on update cascade,
create_time wt_timestamp
);
create trigger
immutable_columns
before
update on target
for each row execute procedure immutable_columns('public_id', 'scope_id', 'create_time');
create trigger
default_create_time_column
before
insert on target
for each row execute procedure default_create_time();
create trigger
target_scope_valid
before insert on target
for each row execute procedure target_scope_valid();
create table target_host_set(
target_id wt_public_id
references target(public_id)
on delete cascade
on update cascade,
host_set_id wt_public_id
references host_set(public_id)
on delete cascade
on update cascade,
primary key(target_id, host_set_id),
create_time wt_timestamp
);
create trigger
immutable_columns
before
update on target_host_set
for each row execute procedure immutable_columns('target_id', 'host_set_id', 'create_time');
create trigger
target_host_set_scope_valid
before
insert on target_host_set
for each row execute procedure target_host_set_scope_valid();
create table target_tcp (
public_id wt_public_id primary key
references target(public_id)
on delete cascade
on update cascade,
scope_id wt_scope_id not null
references iam_scope(public_id)
on delete cascade
on update cascade,
name text not null, -- name is not optional for a target subtype
description text,
default_port int, -- default_port can be null
create_time wt_timestamp,
update_time wt_timestamp,
version wt_version,
unique(scope_id, name) -- name must be unique within a scope
);
create trigger
insert_target_subtype
before insert on target_tcp
for each row execute procedure insert_target_subtype();
create trigger
delete_target_subtype
after delete on target_tcp
for each row execute procedure delete_target_subtype();
-- define the immutable fields for target
create trigger
immutable_columns
before
update on target_tcp
for each row execute procedure immutable_columns('public_id', 'scope_id', 'create_time');
create trigger
update_version_column
after update on target_tcp
for each row execute procedure update_version_column();
create trigger
update_time_column
before update on target_tcp
for each row execute procedure update_time_column();
create trigger
default_create_time_column
before
insert on target_tcp
for each row execute procedure default_create_time();
create trigger
target_scope_valid
before insert on target_tcp
for each row execute procedure target_scope_valid();
-- target_all_subtypes is a union of all target subtypes
create view target_all_subtypes
as
select
public_id,
scope_id,
name,
description,
default_port,
version,
create_time,
update_time,
'tcp' as type
from target_tcp;
create view target_set
as
select
hs.public_id,
hs.catalog_id,
ths.target_id
from
target_host_set ths,
host_set hs
where
hs.public_id = ths.host_set_id;
insert into oplog_ticket
(name, version)
values
('target_tcp', 1);
commit;
`),
},
}
@@ -0,0 +1,8 @@
begin;
drop function insert_target_subtype;
drop function delete_target_subtype;
drop function target_scope_valid;
drop function target_host_set_scope_valid
commit;
@@ -0,0 +1,70 @@
begin;
-- insert_target_subtype() is a before insert trigger
-- function for subtypes of target
create or replace function
insert_target_subtype()
returns trigger
as $$
begin
insert into target
(public_id, scope_id)
values
(new.public_id, new.scope_id);
return new;
end;
$$ language plpgsql;
-- delete_target_subtype() is an after delete trigger
-- function for subtypes of host
create or replace function delete_target_subtype()
returns trigger
as $$
begin
delete from target
where
public_id = old.public_id;
return null; -- result is ignored since this is an after trigger
end;
$$ language plpgsql;
-- target_scope_valid() is a before insert trigger function for target
create or replace function
target_scope_valid()
returns trigger
as $$
declare scope_type text;
begin
-- Fetch the type of scope
select isc.type from iam_scope isc where isc.public_id = new.scope_id into scope_type;
if scope_type = 'project' then
return new;
end if;
raise exception 'invalid target scope type % (must be project)', scope_type;
end;
$$ language plpgsql;
-- target_host_set_scope_valid() is a before insert trigger function for target_host_set
create or replace function
target_host_set_scope_valid()
returns trigger
as $$
begin
perform from
host_catalog hc,
host_set hs,
target t,
iam_scope s
where
hc.public_id = hs.catalog_id and
hc.scope_id = t.scope_id and
t.public_id = new.target_id;
if not found then
raise exception 'target scope and host set scope are not equal';
end if;
return new;
end;
$$ language plpgsql;
commit;
@@ -0,0 +1,16 @@
begin;
drop table target cascade;
drop table target_host_set cascade;
drop table target_tcp;
drop view target_all_subtypes;
drop view target_host_set_catalog;
delete
from oplog_ticket
where name in (
'target_tcp'
);
commit;
@@ -0,0 +1,179 @@
/*
┌─────────────────┐
┌─────────────────┐ │ target_tcp │
│ target │ ├─────────────────┤
├─────────────────┤ │public_id │
│public_id │┼─────────────○┼│scope_id │
│scope_id │ │default_port │
│ │ │name (not null) │
└─────────────────┘ │description │
┼ └─────────────────┘
│
○
╱│╲
┌─────────────────┐
│ target_host_set │
├─────────────────┤
│target_id │
│host_set_id │
│ │
└─────────────────┘
╲│╱
○
│
│
┼
┌─────────────────┐
│ host_set │
├─────────────────┤
│public_id │
│catalog_id │
│ │
└─────────────────┘
*/
begin;
create table target (
public_id wt_public_id primary key,
scope_id wt_scope_id not null
references iam_scope(public_id)
on delete cascade
on update cascade,
create_time wt_timestamp
);
create trigger
immutable_columns
before
update on target
for each row execute procedure immutable_columns('public_id', 'scope_id', 'create_time');
create trigger
default_create_time_column
before
insert on target
for each row execute procedure default_create_time();
create trigger
target_scope_valid
before insert on target
for each row execute procedure target_scope_valid();
create table target_host_set(
target_id wt_public_id
references target(public_id)
on delete cascade
on update cascade,
host_set_id wt_public_id
references host_set(public_id)
on delete cascade
on update cascade,
primary key(target_id, host_set_id),
create_time wt_timestamp
);
create trigger
immutable_columns
before
update on target_host_set
for each row execute procedure immutable_columns('target_id', 'host_set_id', 'create_time');
create trigger
target_host_set_scope_valid
before
insert on target_host_set
for each row execute procedure target_host_set_scope_valid();
create table target_tcp (
public_id wt_public_id primary key
references target(public_id)
on delete cascade
on update cascade,
scope_id wt_scope_id not null
references iam_scope(public_id)
on delete cascade
on update cascade,
name text not null, -- name is not optional for a target subtype
description text,
default_port int, -- default_port can be null
create_time wt_timestamp,
update_time wt_timestamp,
version wt_version,
unique(scope_id, name) -- name must be unique within a scope
);
create trigger
insert_target_subtype
before insert on target_tcp
for each row execute procedure insert_target_subtype();
create trigger
delete_target_subtype
after delete on target_tcp
for each row execute procedure delete_target_subtype();
-- define the immutable fields for target
create trigger
immutable_columns
before
update on target_tcp
for each row execute procedure immutable_columns('public_id', 'scope_id', 'create_time');
create trigger
update_version_column
after update on target_tcp
for each row execute procedure update_version_column();
create trigger
update_time_column
before update on target_tcp
for each row execute procedure update_time_column();
create trigger
default_create_time_column
before
insert on target_tcp
for each row execute procedure default_create_time();
create trigger
target_scope_valid
before insert on target_tcp
for each row execute procedure target_scope_valid();
-- target_all_subtypes is a union of all target subtypes
create view target_all_subtypes
as
select
public_id,
scope_id,
name,
description,
default_port,
version,
create_time,
update_time,
'tcp' as type
from target_tcp;
create view target_set
as
select
hs.public_id,
hs.catalog_id,
ths.target_id
from
target_host_set ths,
host_set hs
where
hs.public_id = ths.host_set_id;
insert into oplog_ticket
(name, version)
values
('target_tcp', 1);
commit;
@@ -0,0 +1,106 @@
syntax = "proto3";
package controller.storage.target.store.v1;
option go_package = "github.com/hashicorp/boundary/internal/target/store;store";
import "controller/storage/timestamp/v1/timestamp.proto";
import "controller/custom_options/v1/options.proto";
// TargetView is a view that contains all the target subtypes
message TargetView {
// public_id is used to access the Target via an API
// @inject_tag: gorm:"primary_key"
string public_id = 10;
// scope id for the Target
// @inject_tag: `gorm:"default:null"`
string scope_id = 20;
// name is the optional friendly name used to
// access the Target via an API
// @inject_tag: `gorm:"default:null"`
string name = 30
[(custom_options.v1.mask_mapping) = { this: "name" that: "name" }];
// description of the Target
// @inject_tag: `gorm:"default:null"`
string description = 40 [(custom_options.v1.mask_mapping) = {
this: "description"
that: "description"
}];
// create_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp create_time = 50;
// update_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp update_time = 60;
// version allows optimistic locking of the Target when modifying the
// Target
// @inject_tag: `gorm:"default:null"`
uint32 version = 70;
// type represents the type of the Target
// @inject_tag: `gorm:"default:null"`
string type = 80;
// default port of the Target
// @inject_tag: `gorm:"default:null"`
uint32 default_port = 90;
}
message TargetHostSet {
// target_id of the TargetHostSet
// @inject_tag: gorm:"primary_key"
string target_id = 10;
// host_set_id of the TargetHostSet
// @inject_tag: gorm:"primary_key"
string host_set_id = 20;
// create_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp create_time = 30;
}
message TcpTarget {
// public_id is used to access the TargetTcp via an API
// @inject_tag: gorm:"primary_key"
string public_id = 10;
// scope id for the TargetTcp
// @inject_tag: `gorm:"default:null"`
string scope_id = 20;
// name is the optional friendly name used to
// access the TargetTcp via an API
// @inject_tag: `gorm:"default:null"`
string name = 30
[(custom_options.v1.mask_mapping) = { this: "name" that: "name" }];
// description of the TargetTcp
// @inject_tag: `gorm:"default:null"`
string description = 40 [(custom_options.v1.mask_mapping) = {
this: "description"
that: "description"
}];
// create_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp create_time = 50;
// update_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp update_time = 60;
// version allows optimistic locking of the TargetTcp when modifying the
// TargetTcp
// @inject_tag: `gorm:"default:null"`
uint32 version = 70;
// default port of the TargetTcp
// @inject_tag: `gorm:"default:null"`
uint32 default_port = 80;
}
+19
View File
@@ -0,0 +1,19 @@
package target
import (
"fmt"
"github.com/hashicorp/boundary/internal/db"
)
const (
TcpTargetPrefix = "ttcp"
)
func newTcpTargetId() (string, error) {
id, err := db.NewPublicId(TcpTargetPrefix)
if err != nil {
return "", fmt.Errorf("new tcp target id: %w", err)
}
return id, nil
}
+18
View File
@@ -0,0 +1,18 @@
package target
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Ids(t *testing.T) {
t.Parallel()
t.Run("tcp", func(t *testing.T) {
id, err := newTcpTargetId()
require.NoError(t, err)
assert.True(t, strings.HasPrefix(id, TcpTargetPrefix+"_"))
})
}
+233
View File
@@ -0,0 +1,233 @@
package target
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
)
func TestTarget_ImmutableFields(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
rw := db.New(conn)
ts := timestamp.Timestamp{Timestamp: &timestamppb.Timestamp{Seconds: 0, Nanos: 0}}
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
new := TestTcpTarget(t, conn, proj.PublicId, testId(t))
var tests = []struct {
name string
update *TcpTarget
fieldMask []string
}{
{
name: "public_id",
update: func() *TcpTarget {
target := new.Clone().(*TcpTarget)
target.PublicId = "p_thisIsNotAValidId"
return target
}(),
fieldMask: []string{"PublicId"},
},
{
name: "create time",
update: func() *TcpTarget {
target := new.Clone().(*TcpTarget)
target.CreateTime = &ts
return target
}(),
fieldMask: []string{"CreateTime"},
},
{
name: "scope_id",
update: func() *TcpTarget {
target := new.Clone().(*TcpTarget)
target.ScopeId = "o_thisIsNotAValidId"
return target
}(),
fieldMask: []string{"ScopeId"},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
orig := new.Clone()
orig.(*TcpTarget).SetTableName("target")
err := rw.LookupById(context.Background(), orig)
require.NoError(err)
tt.update.SetTableName("target")
fmt.Println(tt.update.TableName(), tt.update)
rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil)
require.Error(err)
assert.Equal(0, rowsUpdated)
after := new.Clone()
after.(*TcpTarget).SetTableName("target")
err = rw.LookupById(context.Background(), after)
require.NoError(err)
assert.True(proto.Equal(orig.(*TcpTarget), after.(*TcpTarget)))
})
}
}
func TestTcpTarget_ImmutableFields(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
rw := db.New(conn)
ts := timestamp.Timestamp{Timestamp: &timestamppb.Timestamp{Seconds: 0, Nanos: 0}}
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
new := TestTcpTarget(t, conn, proj.PublicId, testId(t))
var tests = []struct {
name string
update *TcpTarget
fieldMask []string
}{
{
name: "public_id",
update: func() *TcpTarget {
target := new.Clone().(*TcpTarget)
target.PublicId = "p_thisIsNotAValidId"
return target
}(),
fieldMask: []string{"PublicId"},
},
{
name: "create time",
update: func() *TcpTarget {
target := new.Clone().(*TcpTarget)
target.CreateTime = &ts
return target
}(),
fieldMask: []string{"CreateTime"},
},
{
name: "scope_id",
update: func() *TcpTarget {
target := new.Clone().(*TcpTarget)
target.ScopeId = "o_thisIsNotAValidId"
return target
}(),
fieldMask: []string{"ScopeId"},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
orig := new.Clone()
err := rw.LookupById(context.Background(), orig)
require.NoError(err)
rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil)
require.Error(err)
assert.Equal(0, rowsUpdated)
after := new.Clone()
err = rw.LookupById(context.Background(), after)
require.NoError(err)
assert.True(proto.Equal(orig.(*TcpTarget), after.(*TcpTarget)))
})
}
}
func TestTargetHostSet_ImmutableFields(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
rw := db.New(conn)
repo, err := NewRepository(rw, rw, testKms)
require.NoError(t, err)
ts := timestamp.Timestamp{Timestamp: &timestamppb.Timestamp{Seconds: 0, Nanos: 0}}
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
projTarget := TestTcpTarget(t, conn, proj.PublicId, testId(t))
testCats := static.TestCatalogs(t, conn, proj.PublicId, 1)
hsets := static.TestSets(t, conn, testCats[0].GetPublicId(), 2)
require.Equal(t, 2, len(hsets))
updateTarget := TestTcpTarget(t, conn, proj.PublicId, testId(t))
updateHset := hsets[1]
_, gotHostSets, err := repo.AddTargeHostSets(context.Background(), projTarget.PublicId, 1, []string{hsets[0].PublicId})
require.NoError(t, err)
require.Equal(t, 1, len(gotHostSets))
new, err := NewTargetHostSet(projTarget.PublicId, gotHostSets[0].PublicId)
require.NoError(t, err)
var tests = []struct {
name string
update *TargetHostSet
fieldMask []string
}{
{
name: "target_id",
update: func() *TargetHostSet {
target := new.Clone().(*TargetHostSet)
target.TargetId = updateTarget.PublicId
return target
}(),
fieldMask: []string{"TargetId"},
},
{
name: "create time",
update: func() *TargetHostSet {
target := new.Clone().(*TargetHostSet)
target.CreateTime = &ts
return target
}(),
fieldMask: []string{"CreateTime"},
},
{
name: "host_set_id",
update: func() *TargetHostSet {
target := new.Clone().(*TargetHostSet)
target.HostSetId = updateHset.PublicId
return target
}(),
fieldMask: []string{"HostSetId"},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
orig := new.Clone()
err := rw.LookupWhere(context.Background(), orig, "target_id = ? and host_set_id = ?", new.TargetId, new.HostSetId)
require.NoError(err)
rowsUpdated, err := rw.Update(context.Background(), tt.update, tt.fieldMask, nil)
require.Error(err)
assert.Equal(0, rowsUpdated)
after := new.Clone()
err = rw.LookupWhere(context.Background(), after, "target_id = ? and host_set_id = ?", new.TargetId, new.HostSetId)
require.NoError(err)
assert.True(proto.Equal(orig.(*TargetHostSet), after.(*TargetHostSet)))
})
}
}
+96
View File
@@ -0,0 +1,96 @@
package target
// getOpts - iterate the inbound Options and return a struct
func getOpts(opt ...Option) options {
opts := getDefaultOptions()
for _, o := range opt {
o(&opts)
}
return opts
}
// Option - how Options are passed as arguments
type Option func(*options)
// options = how options are represented
type options struct {
withName string
withDescription string
withDefaultPort uint32
withLimit int
withScopeId string
withUserId string
withTargetType *TargetType
withHostSets []string
}
func getDefaultOptions() options {
return options{
withName: "",
withDescription: "",
withLimit: 0,
withDefaultPort: 0,
withScopeId: "",
withUserId: "",
withTargetType: nil,
withHostSets: nil,
}
}
// WithDescription provides an optional description
func WithDescription(desc string) Option {
return func(o *options) {
o.withDescription = desc
}
}
// WithName provides an option to search by a friendly name
func WithName(name string) Option {
return func(o *options) {
o.withName = name
}
}
// WithLimit provides an option to provide a limit. Intentionally allowing
// negative integers. If WithLimit < 0, then unlimited results are returned.
// If WithLimit == 0, then default limits are used for results.
func WithLimit(limit int) Option {
return func(o *options) {
o.withLimit = limit
}
}
// WithDefaultPort provides an option to specify the default target port.
func WithDefaultPort(p uint32) Option {
return func(o *options) {
o.withDefaultPort = p
}
}
// WithScopeId provides an option to search by a scope id
func WithScopeId(scopeId string) Option {
return func(o *options) {
o.withScopeId = scopeId
}
}
// WithUserId provides an option to search by a user public id
func WithUserId(userId string) Option {
return func(o *options) {
o.withUserId = userId
}
}
// WithTargetType provides an option to search by a target type
func WithTargetType(t TargetType) Option {
return func(o *options) {
o.withTargetType = &t
}
}
// WithHostSets provides an option for providing a list of host set ids
func WithHostSets(hs []string) Option {
return func(o *options) {
o.withHostSets = hs
}
}
+86
View File
@@ -0,0 +1,86 @@
package target
import (
"testing"
"github.com/stretchr/testify/assert"
)
// Test_GetOpts provides unit tests for GetOpts and all the options
func Test_GetOpts(t *testing.T) {
t.Parallel()
t.Run("WithName", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithName("test"))
testOpts := getDefaultOptions()
testOpts.withName = "test"
assert.Equal(opts, testOpts)
})
t.Run("WithDescription", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithDescription("test desc"))
testOpts := getDefaultOptions()
testOpts.withDescription = "test desc"
assert.Equal(opts, testOpts)
})
t.Run("WithLimit", func(t *testing.T) {
assert := assert.New(t)
// test default of 0
opts := getOpts()
testOpts := getDefaultOptions()
testOpts.withLimit = 0
assert.Equal(opts, testOpts)
opts = getOpts(WithLimit(-1))
testOpts = getDefaultOptions()
testOpts.withLimit = -1
assert.Equal(opts, testOpts)
opts = getOpts(WithLimit(1))
testOpts = getDefaultOptions()
testOpts.withLimit = 1
assert.Equal(opts, testOpts)
})
t.Run("WithDefaultPort", func(t *testing.T) {
assert := assert.New(t)
// test default of 0
opts := getOpts()
testOpts := getDefaultOptions()
testOpts.withDefaultPort = 0
assert.Equal(opts, testOpts)
opts = getOpts(WithDefaultPort(22))
testOpts = getDefaultOptions()
testOpts.withDefaultPort = uint32(22)
assert.Equal(opts, testOpts)
})
t.Run("WithUserId", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithUserId("testId"))
testOpts := getDefaultOptions()
testOpts.withUserId = "testId"
assert.Equal(opts, testOpts)
})
t.Run("WithScopeId", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithScopeId("testId"))
testOpts := getDefaultOptions()
testOpts.withScopeId = "testId"
assert.Equal(opts, testOpts)
})
t.Run("WithTargetType", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithTargetType(TcpTargetType))
testOpts := getDefaultOptions()
target := TcpTargetType
testOpts.withTargetType = &target
assert.Equal(opts, testOpts)
})
t.Run("WithHostSets", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithHostSets([]string{"alice", "bob"}))
testOpts := getDefaultOptions()
testOpts.withHostSets = []string{"alice", "bob"}
assert.Equal(opts, testOpts)
})
}
+588
View File
@@ -0,0 +1,588 @@
package target
import (
"context"
"errors"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
)
var (
ErrMetadataScopeNotFound = errors.New("scope not found for metadata")
)
// Clonable provides a cloning interface
type Cloneable interface {
Clone() interface{}
}
// Repository is the target database repository
type Repository struct {
reader db.Reader
writer db.Writer
kms *kms.Kms
// defaultLimit provides a default for limiting the number of results returned from the repo
defaultLimit int
}
// NewRepository creates a new target Repository. Supports the options: WithLimit
// which sets a default limit on results returned by repo operations.
func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) {
if r == nil {
return nil, errors.New("error creating db repository with nil reader")
}
if w == nil {
return nil, errors.New("error creating db repository with nil writer")
}
if kms == nil {
return nil, errors.New("error creating db repository with nil kms")
}
opts := getOpts(opt...)
if opts.withLimit == 0 {
// zero signals the boundary defaults should be used.
opts.withLimit = db.DefaultLimit
}
return &Repository{
reader: r,
writer: w,
kms: kms,
defaultLimit: opts.withLimit,
}, nil
}
// LookupTarget will look up a target in the repository and return the target
// with its host set ids. If the target is not found, it will return nil, nil, nil.
// No options are currently supported.
func (r *Repository) LookupTarget(ctx context.Context, publicId string, opt ...Option) (Target, []*TargetSet, error) {
if publicId == "" {
return nil, nil, fmt.Errorf("lookup target: missing private id: %w", db.ErrInvalidParameter)
}
target := allocTargetView()
target.PublicId = publicId
var hostSets []*TargetSet
_, err := r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(read db.Reader, w db.Writer) error {
if err := read.LookupById(ctx, &target); err != nil {
return fmt.Errorf("lookup target: failed %w for %s", err, publicId)
}
var err error
if hostSets, err = fetchSets(ctx, read, target.PublicId); err != nil {
return err
}
return nil
},
)
if err != nil {
return nil, nil, fmt.Errorf("lookup target: %w", err)
}
subType, err := target.targetSubType()
if err != nil {
return nil, nil, fmt.Errorf("lookup target: %w", err)
}
return subType, hostSets, nil
}
func fetchSets(ctx context.Context, r db.Reader, targetId string) ([]*TargetSet, error) {
var hostSets []*TargetSet
if err := r.SearchWhere(ctx, &hostSets, "target_id = ?", []interface{}{targetId}); err != nil {
return nil, fmt.Errorf("fetch host sets: %w", err)
}
if len(hostSets) == 0 {
return nil, nil
}
return hostSets, nil
}
// ListTargets in targets in a scope. Supports the WithScopeId, WithLimit, WithTargetType options.
func (r *Repository) ListTargets(ctx context.Context, opt ...Option) ([]Target, error) {
opts := getOpts(opt...)
if opts.withScopeId == "" && opts.withUserId == "" {
return nil, fmt.Errorf("list targets: must specify either a scope id or user id: %w", db.ErrInvalidParameter)
}
// TODO (jimlambrt 8/2020) - implement WithUserId() optional filtering.
var where []string
var args []interface{}
if opts.withScopeId != "" {
where, args = append(where, "scope_id = ?"), append(args, opts.withScopeId)
}
if opts.withTargetType != nil {
where, args = append(where, "type = ?"), append(args, opts.withTargetType.String())
}
var foundTargets []*targetView
err := r.list(ctx, &foundTargets, strings.Join(where, " and "), args, opt...)
if err != nil {
return nil, fmt.Errorf("list targets: %w", err)
}
targets := make([]Target, 0, len(foundTargets))
for _, t := range foundTargets {
subType, err := t.targetSubType()
if err != nil {
return nil, fmt.Errorf("list targets: %w", err)
}
targets = append(targets, subType)
}
return targets, nil
}
// list will return a listing of resources and honor the WithLimit option or the
// repo defaultLimit
func (r *Repository) list(ctx context.Context, resources interface{}, where string, args []interface{}, opt ...Option) error {
opts := getOpts(opt...)
limit := r.defaultLimit
var dbOpts []db.Option
if opts.withLimit != 0 {
// non-zero signals an override of the default limit for the repo.
limit = opts.withLimit
}
dbOpts = append(dbOpts, db.WithLimit(limit))
return r.reader.SearchWhere(ctx, resources, where, args, dbOpts...)
}
// DeleteTarget will delete a target from the repository.
func (r *Repository) DeleteTarget(ctx context.Context, publicId string, opt ...Option) (int, error) {
if publicId == "" {
return db.NoRowsAffected, fmt.Errorf("delete target: missing public id %w", db.ErrInvalidParameter)
}
t := allocTargetView()
t.PublicId = publicId
if err := r.reader.LookupByPublicId(ctx, &t); err != nil {
return db.NoRowsAffected, fmt.Errorf("delete target: failed %w for %s", err, publicId)
}
var metadata oplog.Metadata
var deleteTarget interface{}
switch t.Type {
case TcpTargetType.String():
tcpT := allocTcpTarget()
tcpT.PublicId = publicId
deleteTarget = &tcpT
metadata = tcpT.oplog(oplog.OpType_OP_TYPE_DELETE)
default:
return db.NoRowsAffected, fmt.Errorf("delete target: %s is an unsupported target type %s", publicId, t.Type)
}
oplogWrapper, err := r.kms.GetWrapper(ctx, t.ScopeId, kms.KeyPurposeOplog)
if err != nil {
return db.NoRowsAffected, fmt.Errorf("delete target: unable to get oplog wrapper: %w", err)
}
var rowsDeleted int
var deleteResource interface{}
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(_ db.Reader, w db.Writer) error {
deleteResource = deleteTarget.(Cloneable).Clone()
rowsDeleted, err = w.Delete(
ctx,
deleteResource,
db.WithOplog(oplogWrapper, metadata),
)
if err == nil && rowsDeleted > 1 {
// return err, which will result in a rollback of the delete
return db.ErrMultipleRecords
}
return err
},
)
return rowsDeleted, err
}
// 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, opt ...Option) (Target, []*TargetSet, int, error) {
if version == 0 {
return nil, nil, db.NoRowsAffected, fmt.Errorf("update: version cannot be zero: %w", db.ErrInvalidParameter)
}
if target == nil {
return nil, nil, db.NoRowsAffected, fmt.Errorf("update: target is nil: %w", db.ErrInvalidParameter)
}
cloner, ok := target.(Cloneable)
if !ok {
return nil, nil, db.NoRowsAffected, fmt.Errorf("update: target is not Cloneable: %w", db.ErrInvalidParameter)
}
dbOpts := []db.Option{
db.WithVersion(&version),
}
scopeId := target.GetScopeId()
if scopeId == "" {
t := allocTargetView()
t.PublicId = target.GetPublicId()
if err := r.reader.LookupByPublicId(ctx, &t); err != nil {
return nil, nil, db.NoRowsAffected, fmt.Errorf("update: lookup failed %w for %s", err, t.PublicId)
}
scopeId = t.ScopeId
}
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, nil, db.NoRowsAffected, fmt.Errorf("unable to get oplog wrapper: %w", err)
}
metadata := target.oplog(oplog.OpType_OP_TYPE_UPDATE)
dbOpts = append(dbOpts, db.WithOplog(oplogWrapper, metadata))
var rowsUpdated int
var returnedTarget interface{}
var hostSets []*TargetSet
_, 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 err
}
if err == nil && rowsUpdated > 1 {
// return err, which will result in a rollback of the update
return fmt.Errorf("error more than 1 target would have been updated: %w", db.ErrMultipleRecords)
}
var err error
if hostSets, err = fetchSets(ctx, reader, target.GetPublicId()); err != nil {
return err
}
return err
},
)
return returnedTarget.(Target), hostSets, rowsUpdated, err
}
// AddTargeHostSets provides the ability to add host sets (hostSetIds) to a
// target (targetId). The target's current db version must match the
// targetVersion or an error will be returned. The target and a list of
// current host set ids will be returned on success. Zero is not a valid value
// for the WithVersion option and will return an error.
func (r *Repository) AddTargeHostSets(ctx context.Context, targetId string, targetVersion uint32, hostSetIds []string, opt ...Option) (Target, []*TargetSet, error) {
if targetId == "" {
return nil, nil, fmt.Errorf("add target host sets: missing target id: %w", db.ErrInvalidParameter)
}
if targetVersion == 0 {
return nil, nil, fmt.Errorf("add target host sets: version cannot be zero: %w", db.ErrInvalidParameter)
}
if len(hostSetIds) == 0 {
return nil, nil, fmt.Errorf("add target host sets: missing host set ids: %w", db.ErrInvalidParameter)
}
newHostSets := make([]interface{}, 0, len(hostSetIds))
for _, id := range hostSetIds {
ths, err := NewTargetHostSet(targetId, id)
if err != nil {
return nil, nil, fmt.Errorf("add target host sets: unable to create in memory target host set: %w", err)
}
newHostSets = append(newHostSets, ths)
}
t := allocTargetView()
t.PublicId = targetId
if err := r.reader.LookupByPublicId(ctx, &t); err != nil {
return nil, nil, fmt.Errorf("add target host sets: failed %w for %s", err, targetId)
}
var metadata oplog.Metadata
var target interface{}
switch t.Type {
case TcpTargetType.String():
tcpT := allocTcpTarget()
tcpT.PublicId = t.PublicId
tcpT.Version = targetVersion + 1
target = &tcpT
metadata = tcpT.oplog(oplog.OpType_OP_TYPE_UPDATE)
metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_CREATE.String())
default:
return nil, nil, fmt.Errorf("delete target host sets: %s is an unsupported target type %s", t.PublicId, t.Type)
}
oplogWrapper, err := r.kms.GetWrapper(ctx, t.GetScopeId(), kms.KeyPurposeOplog)
if err != nil {
return nil, nil, fmt.Errorf("add target host sets: unable to get oplog wrapper: %w", err)
}
var currentHostSets []*TargetSet
var updatedTarget interface{}
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(reader db.Reader, w db.Writer) error {
msgs := make([]*oplog.Message, 0, 2)
targetTicket, err := w.GetTicket(target)
if err != nil {
return fmt.Errorf("add target host sets: unable to get ticket: %w", err)
}
updatedTarget = target.(Cloneable).Clone()
var targetOplogMsg oplog.Message
rowsUpdated, err := w.Update(ctx, updatedTarget, []string{"Version"}, nil, db.NewOplogMsg(&targetOplogMsg), db.WithVersion(&targetVersion))
if err != nil {
return fmt.Errorf("add target host sets: unable to update target version: %w", err)
}
if rowsUpdated != 1 {
return fmt.Errorf("add target host sets: updated target and %d rows updated", rowsUpdated)
}
msgs = append(msgs, &targetOplogMsg)
hostSetsOplogMsgs := make([]*oplog.Message, 0, len(newHostSets))
if err := w.CreateItems(ctx, newHostSets, db.NewOplogMsgs(&hostSetsOplogMsgs)); err != nil {
return fmt.Errorf("add target host sets: unable to add target host sets: %w", err)
}
msgs = append(msgs, hostSetsOplogMsgs...)
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil {
return fmt.Errorf("add target host sets: unable to write oplog: %w", err)
}
currentHostSets, err = fetchSets(ctx, reader, targetId)
if err != nil {
return fmt.Errorf("add target host sets: unable to retrieve current host sets after adds: %w", err)
}
return nil
},
)
if err != nil {
return nil, nil, fmt.Errorf("add target host sets: error creating sets: %w", err)
}
return updatedTarget.(Target), currentHostSets, nil
}
// DeleteTargeHostSets deletes host sets from a target (targetId). The target's
// current db version must match the targetVersion or an error will be returned.
// Zero is not a valid value for the WithVersion option and will return an
// error.
func (r *Repository) DeleteTargeHostSets(ctx context.Context, targetId string, targetVersion uint32, hostSetIds []string, opt ...Option) (int, error) {
if targetId == "" {
return db.NoRowsAffected, fmt.Errorf("delete target host sets: missing target id: %w", db.ErrInvalidParameter)
}
if targetVersion == 0 {
return db.NoRowsAffected, fmt.Errorf("delete target host sets: version cannot be zero: %w", db.ErrInvalidParameter)
}
if len(hostSetIds) == 0 {
return db.NoRowsAffected, fmt.Errorf("delete target host sets: missing host set ids: %w", db.ErrInvalidParameter)
}
deleteTargeHostSets := make([]interface{}, 0, len(hostSetIds))
for _, id := range hostSetIds {
ths, err := NewTargetHostSet(targetId, id)
if err != nil {
return db.NoRowsAffected, fmt.Errorf("delete target host sets: unable to create in memory target host set: %w", err)
}
deleteTargeHostSets = append(deleteTargeHostSets, ths)
}
t := allocTargetView()
t.PublicId = targetId
if err := r.reader.LookupByPublicId(ctx, &t); err != nil {
return db.NoRowsAffected, fmt.Errorf("delete target host sets: failed %w for %s", err, targetId)
}
var metadata oplog.Metadata
var target interface{}
switch t.Type {
case TcpTargetType.String():
tcpT := allocTcpTarget()
tcpT.PublicId = t.PublicId
tcpT.Version = targetVersion + 1
target = &tcpT
metadata = tcpT.oplog(oplog.OpType_OP_TYPE_UPDATE)
metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String())
default:
return db.NoRowsAffected, fmt.Errorf("delete target host sets: %s is an unsupported target type %s", t.PublicId, t.Type)
}
oplogWrapper, err := r.kms.GetWrapper(ctx, t.GetScopeId(), kms.KeyPurposeOplog)
if err != nil {
return db.NoRowsAffected, fmt.Errorf("delete target host sets: unable to get oplog wrapper: %w", err)
}
var totalRowsDeleted int
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(reader db.Reader, w db.Writer) error {
msgs := make([]*oplog.Message, 0, 2)
targetTicket, err := w.GetTicket(target)
if err != nil {
return fmt.Errorf("delete target host sets: unable to get ticket: %w", err)
}
updatedTarget := target.(Cloneable).Clone()
var targetOplogMsg oplog.Message
rowsUpdated, err := w.Update(ctx, updatedTarget, []string{"Version"}, nil, db.NewOplogMsg(&targetOplogMsg), db.WithVersion(&targetVersion))
if err != nil {
return fmt.Errorf("delete target host sets: unable to update target version: %w", err)
}
if rowsUpdated != 1 {
return fmt.Errorf("delete target host sets: updated target and %d rows updated", rowsUpdated)
}
msgs = append(msgs, &targetOplogMsg)
hostSetsOplogMsgs := make([]*oplog.Message, 0, len(deleteTargeHostSets))
rowsDeleted, err := w.DeleteItems(ctx, deleteTargeHostSets, db.NewOplogMsgs(&hostSetsOplogMsgs))
if err != nil {
return fmt.Errorf("delete target host sets: unable to delete target host sets: %w", err)
}
if rowsDeleted != len(deleteTargeHostSets) {
return fmt.Errorf("delete target host sets: target host sets deleted %d did not match request for %d", rowsDeleted, len(deleteTargeHostSets))
}
totalRowsDeleted += rowsDeleted
msgs = append(msgs, hostSetsOplogMsgs...)
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil {
return fmt.Errorf("delete target host sets: unable to write oplog: %w", err)
}
return nil
},
)
if err != nil {
return db.NoRowsAffected, fmt.Errorf("delete target host sets: error deleting target host sets: %w", err)
}
return totalRowsDeleted, nil
}
// SetTargetHostSets will set the target's host sets. Set add and/or delete
// target host sets as need to reconcile the existing sets with the sets
// requested. If hostSetIds is empty, the target host sets will be cleared. Zero
// is not a valid value for the WithVersion option and will return an error.
func (r *Repository) SetTargetHostSets(ctx context.Context, targetId string, targetVersion uint32, hostSetIds []string, opt ...Option) ([]*TargetSet, int, error) {
if targetId == "" {
return nil, db.NoRowsAffected, fmt.Errorf("set target host sets: missing target id: %w", db.ErrInvalidParameter)
}
if targetVersion == 0 {
return nil, db.NoRowsAffected, fmt.Errorf("set target host sets: version cannot be zero: %w", db.ErrInvalidParameter)
}
t := allocTargetView()
t.PublicId = targetId
if err := r.reader.LookupByPublicId(ctx, &t); err != nil {
return nil, db.NoRowsAffected, fmt.Errorf("set target host sets: failed %w for %s", err, targetId)
}
// NOTE: calculating that to set can safely happen outside of the write
// transaction since we're using targetVersion to ensure that the only
// operate on the same set of data from these queries that calculate the
// set.
// TODO(mgaffney) 08/2020: Use SQL to calculate changes.
foundThs, err := fetchSets(ctx, r.reader, targetId)
if err != nil {
return nil, db.NoRowsAffected, fmt.Errorf("set target host sets: unable to search for existing target host sets: %w", err)
}
found := map[string]*TargetSet{}
for _, s := range foundThs {
found[s.PublicId] = s
}
addHostSets := make([]interface{}, 0, len(hostSetIds))
for _, id := range hostSetIds {
if _, ok := found[id]; ok {
// found a match, so do nothing (we want to keep it), but remove it
// from found
delete(found, id)
continue
}
hs, err := NewTargetHostSet(targetId, id)
if err != nil {
return nil, db.NoRowsAffected, fmt.Errorf("set target host set: unable to create in memory target host set: %w", err)
}
addHostSets = append(addHostSets, hs)
}
deleteHostSets := make([]interface{}, 0, len(hostSetIds))
if len(found) > 0 {
for _, s := range found {
hs, err := NewTargetHostSet(targetId, s.PublicId)
if err != nil {
return nil, db.NoRowsAffected, fmt.Errorf("set target host set: unable to create in memory target host set: %w", err)
}
deleteHostSets = append(deleteHostSets, hs)
}
}
if len(addHostSets) == 0 && len(deleteHostSets) == 0 {
return foundThs, db.NoRowsAffected, nil
}
var metadata oplog.Metadata
var target interface{}
switch t.Type {
case TcpTargetType.String():
tcpT := allocTcpTarget()
tcpT.PublicId = t.PublicId
tcpT.Version = targetVersion + 1
target = &tcpT
metadata = tcpT.oplog(oplog.OpType_OP_TYPE_UPDATE)
default:
return nil, db.NoRowsAffected, fmt.Errorf("set target host sets: %s is an unsupported target type %s", t.PublicId, t.Type)
}
oplogWrapper, err := r.kms.GetWrapper(ctx, t.GetScopeId(), kms.KeyPurposeOplog)
if err != nil {
return nil, db.NoRowsAffected, fmt.Errorf("set target host sets: unable to get oplog wrapper: %w", err)
}
var totalRowsAffected int
var currentHostSets []*TargetSet
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(reader db.Reader, w db.Writer) error {
msgs := make([]*oplog.Message, 0, 2)
targetTicket, err := w.GetTicket(target)
if err != nil {
return fmt.Errorf("set target host sets: unable to get ticket: %w", err)
}
updatedTarget := target.(Cloneable).Clone()
var targetOplogMsg oplog.Message
rowsUpdated, err := w.Update(ctx, updatedTarget, []string{"Version"}, nil, db.NewOplogMsg(&targetOplogMsg), db.WithVersion(&targetVersion))
if err != nil {
return fmt.Errorf("set target host sets: unable to update target version: %w", err)
}
if rowsUpdated != 1 {
return fmt.Errorf("set target host sets: updated target and %d rows updated", rowsUpdated)
}
msgs = append(msgs, &targetOplogMsg)
// Write the new ones in
if len(addHostSets) > 0 {
hostSetOplogMsgs := make([]*oplog.Message, 0, len(addHostSets))
if err := w.CreateItems(ctx, addHostSets, db.NewOplogMsgs(&hostSetOplogMsgs)); err != nil {
return fmt.Errorf("unable to add target host sets during set: %w", err)
}
totalRowsAffected += len(addHostSets)
msgs = append(msgs, hostSetOplogMsgs...)
metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_CREATE.String())
}
// Anything we didn't take out of found needs to be removed
if len(deleteHostSets) > 0 {
hostSetOplogMsgs := make([]*oplog.Message, 0, len(deleteHostSets))
rowsDeleted, err := w.DeleteItems(ctx, deleteHostSets, db.NewOplogMsgs(&hostSetOplogMsgs))
if err != nil {
return fmt.Errorf("set target host sets: unable to delete target host set: %w", err)
}
if rowsDeleted != len(deleteHostSets) {
return fmt.Errorf("set target host sets: target host sets deleted %d did not match request for %d", rowsDeleted, len(deleteHostSets))
}
totalRowsAffected += rowsDeleted
msgs = append(msgs, hostSetOplogMsgs...)
metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String())
}
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil {
return fmt.Errorf("set target host sets: unable to write oplog: %w", err)
}
currentHostSets, err = fetchSets(ctx, reader, targetId)
if err != nil {
return fmt.Errorf("set target host sets: unable to retrieve current target host sets after set: %w", err)
}
return nil
},
)
if err != nil {
return nil, db.NoRowsAffected, fmt.Errorf("target host sets: error setting target host sets: %w", err)
}
return currentHostSets, totalRowsAffected, nil
}
+158
View File
@@ -0,0 +1,158 @@
package target
import (
"context"
"fmt"
"strings"
dbcommon "github.com/hashicorp/boundary/internal/db/common"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
)
// CreateTcpTarget inserts into the repository and returns the new Target with
// its list of host sets. WithHostSets is currently the only supported option.
func (r *Repository) CreateTcpTarget(ctx context.Context, target *TcpTarget, opt ...Option) (Target, []*TargetSet, error) {
opts := getOpts(opt...)
if target == nil {
return nil, nil, fmt.Errorf("create tcp target: missing target: %w", db.ErrInvalidParameter)
}
if target.TcpTarget == nil {
return nil, nil, fmt.Errorf("create tcp target: missing target store: %w", db.ErrInvalidParameter)
}
if target.ScopeId == "" {
return nil, nil, fmt.Errorf("create tcp target: scope id empty: %w", db.ErrInvalidParameter)
}
if target.Name == "" {
return nil, nil, fmt.Errorf("create tcp target: name empty: %w", db.ErrInvalidParameter)
}
if target.PublicId != "" {
return nil, nil, fmt.Errorf("create tcp target: public id not empty: %w", db.ErrInvalidParameter)
}
id, err := newTcpTargetId()
if err != nil {
return nil, nil, fmt.Errorf("create tcp target: %w", err)
}
oplogWrapper, err := r.kms.GetWrapper(ctx, target.ScopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, nil, fmt.Errorf("create tcp target: unable to get oplog wrapper: %w", err)
}
t := target.Clone().(*TcpTarget)
t.PublicId = id
newHostSets := make([]interface{}, 0, len(opts.withHostSets))
for _, hsId := range opts.withHostSets {
hostSet, err := NewTargetHostSet(t.PublicId, hsId)
if err != nil {
return nil, nil, fmt.Errorf("create tcp target: unable to create in memory target host set: %w", err)
}
newHostSets = append(newHostSets, hostSet)
}
metadata := t.oplog(oplog.OpType_OP_TYPE_CREATE)
var returnedTarget interface{}
var returnedHostSet []*TargetSet
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(read db.Reader, w db.Writer) error {
targetTicket, err := w.GetTicket(t)
if err != nil {
return fmt.Errorf("create tcp target: unable to get ticket: %w", err)
}
msgs := make([]*oplog.Message, 0, 2)
var targetOplogMsg oplog.Message
returnedTarget = t.Clone()
if err := w.Create(ctx, returnedTarget, db.NewOplogMsg(&targetOplogMsg)); err != nil {
return err
}
msgs = append(msgs, &targetOplogMsg)
if len(newHostSets) > 0 {
hostSetOplogMsgs := make([]*oplog.Message, 0, len(newHostSets))
if err := w.CreateItems(ctx, newHostSets, db.NewOplogMsgs(&hostSetOplogMsgs)); err != nil {
return fmt.Errorf("create tcp target: unable to add host sets: %w", err)
}
if returnedHostSet, err = fetchSets(ctx, read, t.PublicId); err != nil {
return fmt.Errorf("create tcp target: unable to read host sets: %w", err)
}
msgs = append(msgs, hostSetOplogMsgs...)
}
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil {
return fmt.Errorf("create tcp target: unable to write oplog: %w", err)
}
return nil
},
)
if err != nil {
return nil, nil, fmt.Errorf("create tcp target: %w for %s target id id", err, t.PublicId)
}
return returnedTarget.(*TcpTarget), returnedHostSet, err
}
// UpdateTcpTarget will update a target in the repository and return the written
// target. fieldMaskPaths provides field_mask.proto paths for fields that should
// be updated. Fields will be set to NULL if the field is a zero value and
// included in fieldMask. Name and Description are the only updatable fields,
// If no updatable fields are included in the fieldMaskPaths, then an error is
// returned.
func (r *Repository) UpdateTcpTarget(ctx context.Context, target *TcpTarget, version uint32, fieldMaskPaths []string, opt ...Option) (Target, []*TargetSet, int, error) {
if target == nil {
return nil, nil, db.NoRowsAffected, fmt.Errorf("update tcp target: missing target %w", db.ErrInvalidParameter)
}
if target.TcpTarget == nil {
return nil, nil, db.NoRowsAffected, fmt.Errorf("update tcp target: missing target store %w", db.ErrInvalidParameter)
}
if target.PublicId == "" {
return nil, nil, db.NoRowsAffected, fmt.Errorf("update tcp target: missing target public id %w", db.ErrInvalidParameter)
}
for _, f := range fieldMaskPaths {
switch {
case strings.EqualFold("name", f):
case strings.EqualFold("description", f):
case strings.EqualFold("defaultport", f):
default:
return nil, nil, db.NoRowsAffected, fmt.Errorf("update tcp target: field: %s: %w", f, db.ErrInvalidFieldMask)
}
}
var dbMask, nullFields []string
dbMask, nullFields = dbcommon.BuildUpdatePaths(
map[string]interface{}{
"Name": target.Name,
"Description": target.Description,
"DefaultPort": target.DefaultPort,
},
fieldMaskPaths,
)
if len(dbMask) == 0 && len(nullFields) == 0 {
return nil, nil, db.NoRowsAffected, fmt.Errorf("update tcp target: %w", db.ErrEmptyFieldMask)
}
var returnedTarget Target
var rowsUpdated int
var targetSets []*TargetSet
_, err := r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(read db.Reader, w db.Writer) error {
var err error
t := target.Clone().(*TcpTarget)
returnedTarget, targetSets, rowsUpdated, err = r.update(ctx, t, version, dbMask, nullFields)
if err != nil {
return err
}
return nil
},
)
if err != nil {
if db.IsUniqueError(err) {
return nil, nil, db.NoRowsAffected, fmt.Errorf("update tcp target: target %s already exists in scope %s: %w", target.Name, target.ScopeId, db.ErrNotUnique)
}
return nil, nil, db.NoRowsAffected, fmt.Errorf("update tcp target: %w for %s", err, target.PublicId)
}
return returnedTarget.(Target), targetSets, rowsUpdated, err
}
@@ -0,0 +1,421 @@
package target
import (
"context"
"errors"
"testing"
"time"
"github.com/hashicorp/boundary/internal/db"
dbassert "github.com/hashicorp/boundary/internal/db/assert"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
func TestRepository_CreateTcpTarget(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(rw, rw, testKms)
require.NoError(t, err)
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cats := static.TestCatalogs(t, conn, proj.PublicId, 1)
hsets := static.TestSets(t, conn, cats[0].GetPublicId(), 2)
var sets []string
for _, s := range hsets {
sets = append(sets, s.PublicId)
}
type args struct {
target *TcpTarget
opt []Option
}
tests := []struct {
name string
args args
wantHostSets []string
wantErr bool
wantIsError error
}{
{
name: "valid-org",
args: args{
target: func() *TcpTarget {
target, err := NewTcpTarget(proj.PublicId, "valid-org",
WithDescription("valid-org"),
WithDefaultPort(uint32(22)))
require.NoError(t, err)
return target
}(),
opt: []Option{WithHostSets(sets)},
},
wantErr: false,
wantHostSets: sets,
},
{
name: "nil-target",
args: args{
target: nil,
},
wantErr: true,
wantIsError: db.ErrInvalidParameter,
},
{
name: "nil-target-store",
args: args{
target: func() *TcpTarget {
target := &TcpTarget{}
return target
}(),
},
wantErr: true,
wantIsError: db.ErrInvalidParameter,
},
{
name: "public-id-not-empty",
args: args{
target: func() *TcpTarget {
target, err := NewTcpTarget(proj.PublicId, "valid-org", WithDescription("valid-org"), WithDefaultPort(uint32(22)))
require.NoError(t, err)
id, err := newTcpTargetId()
require.NoError(t, err)
target.PublicId = id
return target
}(),
},
wantErr: true,
wantIsError: db.ErrInvalidParameter,
},
{
name: "empty-scope-id",
args: args{
target: func() *TcpTarget {
target := allocTcpTarget()
target.Name = "empty-scope-id"
require.NoError(t, err)
return &target
}(),
},
wantErr: true,
wantIsError: db.ErrInvalidParameter,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
target, hostSets, err := repo.CreateTcpTarget(context.Background(), tt.args.target, tt.args.opt...)
if tt.wantErr {
assert.Error(err)
assert.Nil(target)
if tt.wantIsError != nil {
assert.True(errors.Is(err, tt.wantIsError))
}
return
}
require.NoError(err)
assert.NotNil(target.GetPublicId())
gotIds := make([]string, 0, len(hostSets))
for _, s := range hostSets {
gotIds = append(gotIds, s.PublicId)
}
assert.Equal(tt.wantHostSets, gotIds)
foundTarget, foundHostSets, err := repo.LookupTarget(context.Background(), target.GetPublicId())
assert.NoError(err)
assert.True(proto.Equal(target.(*TcpTarget), foundTarget.(*TcpTarget)))
assert.Equal(hostSets, foundHostSets)
err = db.TestVerifyOplog(t, rw, target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))
assert.NoError(err)
// TODO (jimlambrt 9/2020) - unfortunately, we can currently
// test to make sure that the oplog entry for a target host sets
// create exist because the db.TestVerifyOplog doesn't really
// support that level of testing and the previous call to
// CreateTcpTarget would create an oplog entry for the
// create on the target even if no host sets were added. Once
// TestVerifyOplog supports the appropriate granularity, we should
// add an appropriate assert.
})
}
}
func TestRepository_UpdateTcpTarget(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(rw, rw, testKms)
require.NoError(t, err)
id := testId(t)
iamRepo := iam.TestRepo(t, conn, wrapper)
_, proj := iam.TestScopes(t, iamRepo)
pubId := func(s string) *string { return &s }
type args struct {
name string
description string
port uint32
fieldMaskPaths []string
opt []Option
ScopeId string
PublicId *string
}
tests := []struct {
name string
newScopeId string
newName string
newTargetOpts []Option
args args
wantRowsUpdate int
wantErr bool
wantErrMsg string
wantIsError error
wantDup bool
}{
{
name: "valid",
args: args{
name: "valid" + id,
fieldMaskPaths: []string{"Name"},
ScopeId: proj.PublicId,
},
newScopeId: proj.PublicId,
wantErr: false,
wantRowsUpdate: 1,
},
{
name: "valid-no-op",
args: args{
name: "valid-no-op" + id,
fieldMaskPaths: []string{"Name"},
ScopeId: proj.PublicId,
},
newScopeId: proj.PublicId,
newName: "valid-no-op" + id,
wantErr: false,
wantRowsUpdate: 1,
},
{
name: "not-found",
args: args{
name: "not-found" + id,
fieldMaskPaths: []string{"Name"},
ScopeId: proj.PublicId,
PublicId: func() *string { s := "1"; return &s }(),
},
newScopeId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "update tcp target: update: lookup after write: record not found for 1",
wantIsError: db.ErrRecordNotFound,
},
{
name: "null-name",
args: args{
name: "",
fieldMaskPaths: []string{"Name"},
ScopeId: proj.PublicId,
},
newScopeId: proj.PublicId,
newName: "null-name" + id,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "update tcp target: update: failed: pq: null value in column ",
},
{
name: "null-description",
args: args{
name: "null-description",
fieldMaskPaths: []string{"Description"},
ScopeId: proj.PublicId,
},
newScopeId: proj.PublicId,
newTargetOpts: []Option{WithDescription("null-description" + id)},
wantErr: false,
wantRowsUpdate: 1,
},
{
name: "empty-field-mask",
args: args{
name: "valid" + id,
fieldMaskPaths: []string{},
ScopeId: proj.PublicId,
},
newScopeId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "update tcp target: empty field mask",
wantIsError: db.ErrEmptyFieldMask,
},
{
name: "nil-fieldmask",
args: args{
name: "valid" + id,
fieldMaskPaths: nil,
ScopeId: proj.PublicId,
},
newScopeId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "update tcp target: empty field mask",
wantIsError: db.ErrEmptyFieldMask,
},
{
name: "read-only-fields",
args: args{
name: "valid" + id,
fieldMaskPaths: []string{"CreateTime"},
ScopeId: proj.PublicId,
},
newScopeId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "update tcp target: field: CreateTime: invalid field mask",
wantIsError: db.ErrInvalidFieldMask,
},
{
name: "unknown-fields",
args: args{
name: "valid" + id,
fieldMaskPaths: []string{"Alice"},
ScopeId: proj.PublicId,
},
newScopeId: proj.PublicId,
wantErr: true,
wantRowsUpdate: 0,
wantErrMsg: "update tcp target: field: Alice: invalid field mask",
wantIsError: db.ErrInvalidFieldMask,
},
{
name: "no-public-id",
args: args{
name: "valid" + id,
fieldMaskPaths: []string{"Name"},
ScopeId: proj.PublicId,
PublicId: pubId(""),
},
newScopeId: proj.PublicId,
wantErr: true,
wantErrMsg: "update tcp target: missing target public id invalid parameter",
wantIsError: db.ErrInvalidParameter,
wantRowsUpdate: 0,
},
{
name: "proj-scope-id-no-mask",
args: args{
name: "proj-scope-id" + id,
ScopeId: proj.PublicId,
},
newScopeId: proj.PublicId,
wantErr: true,
wantErrMsg: "update tcp target: empty field mask",
wantIsError: db.ErrEmptyFieldMask,
},
{
name: "empty-scope-id-with-name-mask",
args: args{
name: "empty-scope-id" + id,
fieldMaskPaths: []string{"Name"},
ScopeId: "",
},
newScopeId: proj.PublicId,
wantErr: false,
wantRowsUpdate: 1,
},
{
name: "dup-name",
args: args{
name: "dup-name" + id,
fieldMaskPaths: []string{"Name"},
ScopeId: proj.PublicId,
},
newScopeId: proj.PublicId,
wantErr: true,
wantDup: true,
wantErrMsg: " already exists in scope " + proj.PublicId,
wantIsError: db.ErrNotUnique,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require, assert := require.New(t), assert.New(t)
if tt.wantDup {
_ = TestTcpTarget(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)
}
tt.newTargetOpts = append(tt.newTargetOpts, WithHostSets(testHostSetIds))
name := tt.newName
if name == "" {
name = testId(t)
}
target := TestTcpTarget(t, conn, tt.newScopeId, name, tt.newTargetOpts...)
updateTarget := allocTcpTarget()
updateTarget.PublicId = target.PublicId
if tt.args.PublicId != nil {
updateTarget.PublicId = *tt.args.PublicId
}
updateTarget.ScopeId = tt.args.ScopeId
updateTarget.Name = tt.args.name
updateTarget.Description = tt.args.description
updateTarget.DefaultPort = tt.args.port
targetAfterUpdate, hostSets, updatedRows, err := repo.UpdateTcpTarget(context.Background(), &updateTarget, target.Version, tt.args.fieldMaskPaths, tt.args.opt...)
if tt.wantErr {
assert.Error(err)
if tt.wantIsError != nil {
assert.True(errors.Is(err, tt.wantIsError))
}
assert.Nil(targetAfterUpdate)
assert.Equal(0, updatedRows)
assert.Contains(err.Error(), tt.wantErrMsg)
err = db.TestVerifyOplog(t, rw, target.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
assert.Error(err)
assert.True(errors.Is(db.ErrRecordNotFound, err))
return
}
require.NoError(err)
require.NotNil(targetAfterUpdate)
assert.Equal(tt.wantRowsUpdate, updatedRows)
afterUpdateIds := make([]string, 0, len(hostSets))
for _, hs := range hostSets {
afterUpdateIds = append(afterUpdateIds, hs.PublicId)
}
assert.Equal(testHostSetIds, afterUpdateIds)
switch tt.name {
case "valid-no-op":
assert.Equal(target.UpdateTime, targetAfterUpdate.(*TcpTarget).UpdateTime)
default:
assert.NotEqual(target.UpdateTime, targetAfterUpdate.(*TcpTarget).UpdateTime)
}
foundTarget, _, err := repo.LookupTarget(context.Background(), target.PublicId)
assert.NoError(err)
assert.True(proto.Equal(targetAfterUpdate.((*TcpTarget)), foundTarget.((*TcpTarget))))
dbassert := dbassert.New(t, rw)
if tt.args.description == "" {
assert.Equal(foundTarget.GetDescription(), "")
dbassert.IsNull(foundTarget, "description")
}
err = db.TestVerifyOplog(t, rw, target.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
assert.NoError(err)
})
}
}
+764
View File
@@ -0,0 +1,764 @@
package target
import (
"context"
"errors"
"sort"
"strconv"
"testing"
"time"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
func TestNewRepository(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
type args struct {
r db.Reader
w db.Writer
kms *kms.Kms
}
tests := []struct {
name string
args args
want *Repository
wantErr bool
wantErrString string
}{
{
name: "valid",
args: args{
r: rw,
w: rw,
kms: testKms,
},
want: &Repository{
reader: rw,
writer: rw,
kms: testKms,
defaultLimit: db.DefaultLimit,
},
wantErr: false,
},
{
name: "nil-kms",
args: args{
r: rw,
w: rw,
kms: nil,
},
want: nil,
wantErr: true,
wantErrString: "error creating db repository with nil kms",
},
{
name: "nil-writer",
args: args{
r: rw,
w: nil,
kms: testKms,
},
want: nil,
wantErr: true,
wantErrString: "error creating db repository with nil writer",
},
{
name: "nil-reader",
args: args{
r: nil,
w: rw,
kms: testKms,
},
want: nil,
wantErr: true,
wantErrString: "error creating db repository with nil reader",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms)
if tt.wantErr {
require.Error(err)
assert.Equal(err.Error(), tt.wantErrString)
return
}
require.NoError(err)
assert.Equal(tt.want, got)
})
}
}
func TestRepository_ListTargets(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
const testLimit = 10
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
iamRepo := iam.TestRepo(t, conn, wrapper)
_, proj := iam.TestScopes(t, iamRepo)
rw := db.New(conn)
repo, err := NewRepository(rw, rw, testKms)
require.NoError(t, err)
repo.defaultLimit = testLimit
type args struct {
opt []Option
}
tests := []struct {
name string
createCnt int
createScopeId string
createScopeId2 string
grantUserId string
args args
wantCnt int
wantErr bool
}{
{
name: "tcp-target",
createCnt: 5,
createScopeId: proj.PublicId,
args: args{
opt: []Option{WithTargetType(TcpTargetType), WithScopeId(proj.PublicId)},
},
wantCnt: 5,
wantErr: false,
},
{
name: "no-limit",
createCnt: testLimit + 1,
createScopeId: proj.PublicId,
args: args{
opt: []Option{WithLimit(-1), WithScopeId(proj.PublicId)},
},
wantCnt: testLimit + 1,
wantErr: false,
},
{
name: "default-limit",
createCnt: testLimit + 1,
createScopeId: proj.PublicId,
args: args{
opt: []Option{WithScopeId(proj.PublicId)},
},
wantCnt: testLimit,
wantErr: false,
},
{
name: "custom-limit",
createCnt: testLimit + 1,
createScopeId: proj.PublicId,
args: args{
opt: []Option{WithLimit(3), WithScopeId(proj.PublicId)},
},
wantCnt: 3,
wantErr: false,
},
{
name: "bad-org",
createCnt: 1,
createScopeId: proj.PublicId,
args: args{
opt: []Option{WithScopeId("bad-id")},
},
wantCnt: 0,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
require.NoError(conn.Where("1=1").Delete(allocTcpTarget()).Error)
testGroups := []*TcpTarget{}
for i := 0; i < tt.createCnt; i++ {
switch {
case tt.createScopeId2 != "" && i%2 == 0:
testGroups = append(testGroups, TestTcpTarget(t, conn, tt.createScopeId2, strconv.Itoa(i)))
default:
testGroups = append(testGroups, TestTcpTarget(t, conn, tt.createScopeId, strconv.Itoa(i)))
}
}
assert.Equal(tt.createCnt, len(testGroups))
got, err := repo.ListTargets(context.Background(), tt.args.opt...)
if tt.wantErr {
require.Error(err)
return
}
require.NoError(err)
assert.Equal(tt.wantCnt, len(got))
})
}
}
func TestRepository_DeleteTarget(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
iamRepo := iam.TestRepo(t, conn, wrapper)
_, proj := iam.TestScopes(t, iamRepo)
repo, err := NewRepository(rw, rw, testKms)
require.NoError(t, err)
type args struct {
target Target
opt []Option
}
tests := []struct {
name string
args args
wantRowsDeleted int
wantErr bool
wantErrMsg string
}{
{
name: "valid",
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "valid"),
},
wantRowsDeleted: 1,
wantErr: false,
},
{
name: "no-public-id",
args: args{
target: func() Target {
target := allocTcpTarget()
return &target
}(),
},
wantRowsDeleted: 0,
wantErr: true,
wantErrMsg: "delete target: missing public id invalid parameter",
},
{
name: "not-found",
args: args{
target: func() Target {
id, err := newTcpTargetId()
require.NoError(t, err)
target := allocTcpTarget()
target.PublicId = id
return &target
}(),
},
wantRowsDeleted: 0,
wantErr: true,
wantErrMsg: "delete target: failed record not found for ",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
deletedRows, err := repo.DeleteTarget(context.Background(), tt.args.target.GetPublicId(), tt.args.opt...)
if tt.wantErr {
assert.Error(err)
assert.Equal(0, deletedRows)
assert.Contains(err.Error(), tt.wantErrMsg)
err = db.TestVerifyOplog(t, rw, tt.args.target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
assert.Error(err)
assert.True(errors.Is(db.ErrRecordNotFound, err))
return
}
assert.NoError(err)
assert.Equal(tt.wantRowsDeleted, deletedRows)
foundGroup, _, err := repo.LookupTarget(context.Background(), tt.args.target.GetPublicId())
assert.Error(err)
assert.Nil(foundGroup)
assert.True(errors.Is(err, db.ErrRecordNotFound))
err = db.TestVerifyOplog(t, rw, tt.args.target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
assert.NoError(err)
})
}
}
func TestRepository_AddTargetHostSets(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
iamRepo := iam.TestRepo(t, conn, wrapper)
staticOrg, staticProj := iam.TestScopes(t, iamRepo)
repo, err := NewRepository(rw, rw, testKms)
require.NoError(t, err)
createHostSetsFn := func(orgs, projects []string) []string {
results := []string{}
for _, publicId := range orgs {
cats := static.TestCatalogs(t, conn, publicId, 1)
hsets := static.TestSets(t, conn, cats[0].GetPublicId(), 1)
results = append(results, hsets[0].PublicId)
}
for _, publicId := range projects {
cats := static.TestCatalogs(t, conn, publicId, 1)
hsets := static.TestSets(t, conn, cats[0].GetPublicId(), 1)
results = append(results, hsets[0].PublicId)
}
return results
}
type args struct {
targetVersion uint32
wantTargetIds bool
opt []Option
}
tests := []struct {
name string
args args
wantErr bool
wantErrIs error
}{
{
name: "valid",
args: args{
targetVersion: 1,
wantTargetIds: true,
},
wantErr: false,
},
{
name: "bad-version",
args: args{
targetVersion: 1000,
wantTargetIds: true,
},
wantErr: true,
},
{
name: "zero-version",
args: args{
targetVersion: 0,
wantTargetIds: true,
},
wantErr: true,
},
{
name: "no-host-sets",
args: args{
targetVersion: 1,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
require.NoError(conn.Where("1=1").Delete(allocTargetHostSet()).Error)
require.NoError(conn.Where("1=1").Delete(allocTcpTarget()).Error)
projTarget := TestTcpTarget(t, conn, staticProj.PublicId, "static-proj")
var hostSetIds []string
origTarget, origHostSet, err := repo.LookupTarget(context.Background(), projTarget.PublicId)
require.NoError(err)
require.Equal(0, len(origHostSet))
if tt.args.wantTargetIds {
hostSetIds = createHostSetsFn([]string{staticOrg.PublicId}, []string{staticProj.PublicId})
}
gotTarget, gotHostSets, err := repo.AddTargeHostSets(context.Background(), projTarget.PublicId, tt.args.targetVersion, hostSetIds, tt.args.opt...)
if tt.wantErr {
require.Error(err)
if tt.wantErrIs != nil {
assert.Truef(errors.Is(err, tt.wantErrIs), "unexpected error %s", err.Error())
}
// test to see of the target version update oplog was not created
err = db.TestVerifyOplog(t, rw, projTarget.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
assert.Error(err)
// TODO (jimlambrt 9/2020) - unfortunately, we can currently
// test to make sure that the oplog entry for a target create
// doesn't exist because the db.TestVerifyOplog doesn't really
// support that level of testing and the previous call to
// TestTcpTarget would create an oplog entry for the
// create on the target. Once TestVerifyOplog supports the
// appropriate granularity, we should add an appropriate assert.
return
}
require.NoError(err)
gotHostSet := map[string]*TargetSet{}
for _, s := range gotHostSets {
gotHostSet[s.PublicId] = s
}
// TODO (jimlambrt 9/2020) - unfortunately, we can currently
// test to make sure that the oplog entry for a target create
// doesn't exist because the db.TestVerifyOplog doesn't really
// support that level of testing and the previous call to
// TestTcpTarget would create an oplog entry for the
// create on the target. Once TestVerifyOplog supports the
// appropriate granularity, we should add an appropriate assert.
// test to see of the target version update oplog was created
err = db.TestVerifyOplog(t, rw, projTarget.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
assert.NoError(err)
foundHostSets, err := fetchSets(context.Background(), rw, projTarget.PublicId)
require.NoError(err)
for _, s := range foundHostSets {
assert.NotEmpty(gotHostSet[s.PublicId])
}
target, ths, err := repo.LookupTarget(context.Background(), projTarget.PublicId)
require.NoError(err)
assert.Equal(tt.args.targetVersion+1, target.GetVersion())
assert.Equal(origTarget.GetVersion(), target.GetVersion()-1)
assert.Equal(gotHostSets, ths)
assert.True(proto.Equal(gotTarget.(*TcpTarget), target.(*TcpTarget)))
})
}
}
func TestRepository_DeleteTargetHosts(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
iamRepo := iam.TestRepo(t, conn, wrapper)
_, proj := iam.TestScopes(t, iamRepo)
repo, err := NewRepository(rw, rw, testKms)
require.NoError(t, err)
type args struct {
target Target
targetIdOverride *string
targetVersionOverride *uint32
createCnt int
deleteCnt int
opt []Option
}
tests := []struct {
name string
args args
wantRowsDeleted int
wantErr bool
wantIsErr error
}{
{
name: "valid",
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "valid"),
createCnt: 5,
deleteCnt: 5,
},
wantRowsDeleted: 5,
wantErr: false,
},
{
name: "valid-keeping-some",
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "valid-keeping-some"),
createCnt: 5,
deleteCnt: 2,
},
wantRowsDeleted: 2,
wantErr: false,
},
{
name: "no-deletes",
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "no-deletes"),
createCnt: 5,
},
wantRowsDeleted: 0,
wantErr: true,
wantIsErr: db.ErrInvalidParameter,
},
{
name: "not-found",
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "not-found"),
targetIdOverride: func() *string { id := testId(t); return &id }(),
createCnt: 5,
deleteCnt: 5,
},
wantRowsDeleted: 0,
wantErr: true,
},
{
name: "missing-target-id",
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "missing-target-id"),
targetIdOverride: func() *string { id := ""; return &id }(),
createCnt: 5,
deleteCnt: 5,
},
wantRowsDeleted: 0,
wantErr: true,
wantIsErr: db.ErrInvalidParameter,
},
{
name: "zero-version",
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "zero-version"),
targetVersionOverride: func() *uint32 { v := uint32(0); return &v }(),
createCnt: 5,
deleteCnt: 5,
},
wantRowsDeleted: 0,
wantErr: true,
wantIsErr: db.ErrInvalidParameter,
},
{
name: "bad-version",
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "bad-version"),
targetVersionOverride: func() *uint32 { v := uint32(1000); return &v }(),
createCnt: 5,
deleteCnt: 5,
},
wantRowsDeleted: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
hsIds := make([]string, 0, tt.args.createCnt)
if tt.args.createCnt > 0 {
for i := 0; i < tt.args.createCnt; i++ {
cats := static.TestCatalogs(t, conn, proj.PublicId, 1)
hsets := static.TestSets(t, conn, cats[0].GetPublicId(), 1)
hsIds = append(hsIds, hsets[0].PublicId)
}
}
_, addedHostSets, err := repo.AddTargeHostSets(context.Background(), tt.args.target.GetPublicId(), 1, hsIds, tt.args.opt...)
require.NoError(err)
assert.Equal(tt.args.createCnt, len(addedHostSets))
deleteHostSets := make([]string, 0, tt.args.deleteCnt)
for i := 0; i < tt.args.deleteCnt; i++ {
deleteHostSets = append(deleteHostSets, hsIds[i])
}
var targetId string
switch {
case tt.args.targetIdOverride != nil:
targetId = *tt.args.targetIdOverride
default:
targetId = tt.args.target.GetPublicId()
}
var targetVersion uint32
switch {
case tt.args.targetVersionOverride != nil:
targetVersion = *tt.args.targetVersionOverride
default:
targetVersion = 2
}
deletedRows, err := repo.DeleteTargeHostSets(context.Background(), targetId, targetVersion, deleteHostSets, tt.args.opt...)
if tt.wantErr {
assert.Error(err)
assert.Equal(0, deletedRows)
if tt.wantIsErr != nil {
assert.Truef(errors.Is(err, tt.wantIsErr), "unexpected error %s", err.Error())
}
// TODO (jimlambrt 9/2020) - unfortunately, we can currently
// test to make sure that the oplog entry for a target update
// doesn't exist because the db.TestVerifyOplog doesn't really
// support that level of testing and the previous call to
// repo.AddTargeHostSets() would create an oplog entry for the
// update to the target. Once TestVerifyOplog supports the
// appropriate granularity, we should add an appropriate assert.
err = db.TestVerifyOplog(t, rw, tt.args.target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
assert.Error(err)
assert.True(errors.Is(db.ErrRecordNotFound, err))
return
}
require.NoError(err)
assert.Equal(tt.wantRowsDeleted, deletedRows)
// TODO (jimlambrt 9/2020) - unfortunately, we can currently
// test to make sure that the oplog entry for a target update
// doesn't exist because the db.TestVerifyOplog doesn't really
// support that level of testing and the previous call to
// repo.AddTargeHostSets() would create an oplog entry for the
// update to the target. Once TestVerifyOplog supports the
// appropriate granularity,, we should add an appropriate assert.
// we should find the oplog for the delete of target host sets
err = db.TestVerifyOplog(t, rw, tt.args.target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
assert.NoError(err)
})
}
}
func TestRepository_SetTargetHostSets(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
testKms := kms.TestKms(t, conn, wrapper)
repo, err := NewRepository(rw, rw, testKms)
require.NoError(t, err)
iamRepo := iam.TestRepo(t, conn, wrapper)
_, proj := iam.TestScopes(t, iamRepo)
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)
}
createHostSetsFn := func() []string {
results := []string{}
for i := 0; i < 10; i++ {
cats := static.TestCatalogs(t, conn, proj.PublicId, 1)
hsets := static.TestSets(t, conn, cats[0].GetPublicId(), 1)
results = append(results, hsets[0].PublicId)
}
return results
}
setupFn := func(target Target) []*TargetSet {
hs := createHostSetsFn()
_, created, err := repo.AddTargeHostSets(context.Background(), target.GetPublicId(), 1, hs)
require.NoError(t, err)
require.Equal(t, 10, len(created))
return created
}
type args struct {
target Target
targetVersion uint32
hostSetIds []string
addToOrigHostSets bool
opt []Option
}
tests := []struct {
name string
setup func(Target) []*TargetSet
args args
wantAffectedRows int
wantErr bool
}{
{
name: "clear",
setup: setupFn,
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "clear"),
targetVersion: 2, // yep, since setupFn will increment it to 2
hostSetIds: []string{},
},
wantErr: false,
wantAffectedRows: 10,
},
{
name: "no-change",
setup: setupFn,
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "no-change"),
targetVersion: 2, // yep, since setupFn will increment it to 2
hostSetIds: []string{},
addToOrigHostSets: true,
},
wantErr: false,
wantAffectedRows: 0,
},
{
name: "add-sets",
setup: setupFn,
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "add-sets"),
targetVersion: 2, // yep, since setupFn will increment it to 2
hostSetIds: []string{testHostSetIds[0], testHostSetIds[1]},
addToOrigHostSets: true,
},
wantErr: false,
wantAffectedRows: 2,
},
{
name: "add host sets with zero version",
setup: setupFn,
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "add host sets with zero version"),
targetVersion: 0,
hostSetIds: []string{testHostSetIds[0], testHostSetIds[1]},
addToOrigHostSets: true,
},
wantErr: true,
},
{
name: "remove existing and add users and grps",
setup: setupFn,
args: args{
target: TestTcpTarget(t, conn, proj.PublicId, "remove existing and add users and grps"),
targetVersion: 2, // yep, since setupFn will increment it to 2
hostSetIds: []string{testHostSetIds[0], testHostSetIds[1]},
addToOrigHostSets: false,
},
wantErr: false,
wantAffectedRows: 12,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
var origHostSets []*TargetSet
if tt.setup != nil {
origHostSets = tt.setup(tt.args.target)
}
if tt.args.addToOrigHostSets {
origIds := make([]string, 0, len(origHostSets))
for _, s := range origHostSets {
origIds = append(origIds, s.PublicId)
}
tt.args.hostSetIds = append(tt.args.hostSetIds, origIds...)
}
origTarget, lookedUpHs, err := repo.LookupTarget(context.Background(), tt.args.target.GetPublicId())
require.NoError(err)
assert.Equal(len(origHostSets), len(lookedUpHs))
got, affectedRows, err := repo.SetTargetHostSets(context.Background(), tt.args.target.GetPublicId(), tt.args.targetVersion, tt.args.hostSetIds, tt.args.opt...)
if tt.wantErr {
require.Error(err)
t.Log(err)
return
}
t.Log(err)
require.NoError(err)
assert.Equal(tt.wantAffectedRows, affectedRows)
assert.Equal(len(tt.args.hostSetIds), len(got))
var wantIds []string
wantIds = append(wantIds, tt.args.hostSetIds...)
sort.Strings(wantIds)
var gotIds []string
if len(got) > 0 {
gotIds = make([]string, 0, len(got))
for _, s := range got {
gotIds = append(gotIds, s.PublicId)
}
}
sort.Strings(gotIds)
assert.Equal(wantIds, gotIds)
foundTarget, _, err := repo.LookupTarget(context.Background(), tt.args.target.GetPublicId())
require.NoError(err)
if tt.name != "no-change" {
assert.Equalf(tt.args.targetVersion+1, foundTarget.GetVersion(), "%s unexpected version: %d/%d", tt.name, tt.args.targetVersion+1, foundTarget.GetVersion())
assert.Equalf(origTarget.GetVersion(), foundTarget.GetVersion()-1, "%s unexpected version: %d/%d", tt.name, origTarget.GetVersion(), foundTarget.GetVersion()-1)
}
t.Logf("target: %v and origVersion/newVersion: %d/%d", foundTarget.GetPublicId(), origTarget.GetVersion(), foundTarget.GetVersion())
})
}
}
+526
View File
@@ -0,0 +1,526 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.12.4
// source: controller/storage/target/store/v1/target.proto
package store
import (
proto "github.com/golang/protobuf/proto"
timestamp "github.com/hashicorp/boundary/internal/db/timestamp"
_ "github.com/hashicorp/boundary/internal/gen/controller/protooptions"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// TargetView is a view that contains all the target subtypes
type TargetView struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// public_id is used to access the Target via an API
// @inject_tag: gorm:"primary_key"
PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
// scope id for the Target
// @inject_tag: `gorm:"default:null"`
ScopeId string `protobuf:"bytes,20,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"default:null"`
// name is the optional friendly name used to
// access the Target via an API
// @inject_tag: `gorm:"default:null"`
Name string `protobuf:"bytes,30,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
// description of the Target
// @inject_tag: `gorm:"default:null"`
Description string `protobuf:"bytes,40,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
// create_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
CreateTime *timestamp.Timestamp `protobuf:"bytes,50,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
// update_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
UpdateTime *timestamp.Timestamp `protobuf:"bytes,60,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
// version allows optimistic locking of the Target when modifying the
// Target
// @inject_tag: `gorm:"default:null"`
Version uint32 `protobuf:"varint,70,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
// type represents the type of the Target
// @inject_tag: `gorm:"default:null"`
Type string `protobuf:"bytes,80,opt,name=type,proto3" json:"type,omitempty" gorm:"default:null"`
// default port of the Target
// @inject_tag: `gorm:"default:null"`
DefaultPort uint32 `protobuf:"varint,90,opt,name=default_port,json=defaultPort,proto3" json:"default_port,omitempty" gorm:"default:null"`
}
func (x *TargetView) Reset() {
*x = TargetView{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TargetView) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TargetView) ProtoMessage() {}
func (x *TargetView) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TargetView.ProtoReflect.Descriptor instead.
func (*TargetView) Descriptor() ([]byte, []int) {
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{0}
}
func (x *TargetView) GetPublicId() string {
if x != nil {
return x.PublicId
}
return ""
}
func (x *TargetView) GetScopeId() string {
if x != nil {
return x.ScopeId
}
return ""
}
func (x *TargetView) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *TargetView) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *TargetView) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *TargetView) GetUpdateTime() *timestamp.Timestamp {
if x != nil {
return x.UpdateTime
}
return nil
}
func (x *TargetView) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
func (x *TargetView) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *TargetView) GetDefaultPort() uint32 {
if x != nil {
return x.DefaultPort
}
return 0
}
type TargetHostSet struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// target_id of the TargetHostSet
// @inject_tag: gorm:"primary_key"
TargetId string `protobuf:"bytes,10,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty" gorm:"primary_key"`
// host_set_id of the TargetHostSet
// @inject_tag: gorm:"primary_key"
HostSetId string `protobuf:"bytes,20,opt,name=host_set_id,json=hostSetId,proto3" json:"host_set_id,omitempty" gorm:"primary_key"`
// create_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
CreateTime *timestamp.Timestamp `protobuf:"bytes,30,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
}
func (x *TargetHostSet) Reset() {
*x = TargetHostSet{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TargetHostSet) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TargetHostSet) ProtoMessage() {}
func (x *TargetHostSet) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TargetHostSet.ProtoReflect.Descriptor instead.
func (*TargetHostSet) Descriptor() ([]byte, []int) {
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{1}
}
func (x *TargetHostSet) GetTargetId() string {
if x != nil {
return x.TargetId
}
return ""
}
func (x *TargetHostSet) GetHostSetId() string {
if x != nil {
return x.HostSetId
}
return ""
}
func (x *TargetHostSet) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
type TcpTarget struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// public_id is used to access the TargetTcp via an API
// @inject_tag: gorm:"primary_key"
PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
// scope id for the TargetTcp
// @inject_tag: `gorm:"default:null"`
ScopeId string `protobuf:"bytes,20,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"default:null"`
// name is the optional friendly name used to
// access the TargetTcp via an API
// @inject_tag: `gorm:"default:null"`
Name string `protobuf:"bytes,30,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
// description of the TargetTcp
// @inject_tag: `gorm:"default:null"`
Description string `protobuf:"bytes,40,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
// create_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
CreateTime *timestamp.Timestamp `protobuf:"bytes,50,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
// update_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
UpdateTime *timestamp.Timestamp `protobuf:"bytes,60,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
// version allows optimistic locking of the TargetTcp when modifying the
// TargetTcp
// @inject_tag: `gorm:"default:null"`
Version uint32 `protobuf:"varint,70,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
// default port of the TargetTcp
// @inject_tag: `gorm:"default:null"`
DefaultPort uint32 `protobuf:"varint,80,opt,name=default_port,json=defaultPort,proto3" json:"default_port,omitempty" gorm:"default:null"`
}
func (x *TcpTarget) Reset() {
*x = TcpTarget{}
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 *TcpTarget) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TcpTarget) ProtoMessage() {}
func (x *TcpTarget) 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 TcpTarget.ProtoReflect.Descriptor instead.
func (*TcpTarget) Descriptor() ([]byte, []int) {
return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{2}
}
func (x *TcpTarget) GetPublicId() string {
if x != nil {
return x.PublicId
}
return ""
}
func (x *TcpTarget) GetScopeId() string {
if x != nil {
return x.ScopeId
}
return ""
}
func (x *TcpTarget) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *TcpTarget) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *TcpTarget) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *TcpTarget) GetUpdateTime() *timestamp.Timestamp {
if x != nil {
return x.UpdateTime
}
return nil
}
func (x *TcpTarget) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
func (x *TcpTarget) GetDefaultPort() uint32 {
if x != nil {
return x.DefaultPort
}
return 0
}
var File_controller_storage_target_store_v1_target_proto protoreflect.FileDescriptor
var file_controller_storage_target_store_v1_target_proto_rawDesc = []byte{
0x0a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f,
0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72,
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, 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, 0x22, 0x97, 0x03, 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, 0x19,
0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x28,
0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x12, 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, 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, 0x82, 0x03, 0x0a, 0x09, 0x54, 0x63, 0x70,
0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 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, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18,
0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x24,
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd,
0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a,
0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 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, 0x21, 0x0a, 0x0c, 0x64, 0x65,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, 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 (
file_controller_storage_target_store_v1_target_proto_rawDescOnce sync.Once
file_controller_storage_target_store_v1_target_proto_rawDescData = file_controller_storage_target_store_v1_target_proto_rawDesc
)
func file_controller_storage_target_store_v1_target_proto_rawDescGZIP() []byte {
file_controller_storage_target_store_v1_target_proto_rawDescOnce.Do(func() {
file_controller_storage_target_store_v1_target_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_target_store_v1_target_proto_rawDescData)
})
return file_controller_storage_target_store_v1_target_proto_rawDescData
}
var file_controller_storage_target_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
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
(*TcpTarget)(nil), // 2: controller.storage.target.store.v1.TcpTarget
(*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp
}
var file_controller_storage_target_store_v1_target_proto_depIdxs = []int32{
3, // 0: controller.storage.target.store.v1.TargetView.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
3, // 1: controller.storage.target.store.v1.TargetView.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
3, // 2: controller.storage.target.store.v1.TargetHostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
3, // 3: controller.storage.target.store.v1.TcpTarget.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
3, // 4: controller.storage.target.store.v1.TcpTarget.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // [5:5] is the sub-list for method output_type
5, // [5:5] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
}
func init() { file_controller_storage_target_store_v1_target_proto_init() }
func file_controller_storage_target_store_v1_target_proto_init() {
if File_controller_storage_target_store_v1_target_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_controller_storage_target_store_v1_target_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TargetView); 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[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TargetHostSet); 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[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TcpTarget); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_storage_target_store_v1_target_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_controller_storage_target_store_v1_target_proto_goTypes,
DependencyIndexes: file_controller_storage_target_store_v1_target_proto_depIdxs,
MessageInfos: file_controller_storage_target_store_v1_target_proto_msgTypes,
}.Build()
File_controller_storage_target_store_v1_target_proto = out.File
file_controller_storage_target_store_v1_target_proto_rawDesc = nil
file_controller_storage_target_store_v1_target_proto_goTypes = nil
file_controller_storage_target_store_v1_target_proto_depIdxs = nil
}
+90
View File
@@ -0,0 +1,90 @@
package target
import (
"fmt"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/hashicorp/boundary/internal/target/store"
)
// Target is a commmon interface for all target subtypes
type Target interface {
GetPublicId() string
GetScopeId() string
GetDefaultPort() uint32
GetName() string
GetDescription() string
GetVersion() uint32
oplog(op oplog.OpType) oplog.Metadata
}
// TargetType defines the possible types for targets.
type TargetType uint32
const (
UnknownTargetType TargetType = 0
TcpTargetType TargetType = 1
)
// String returns a string representation of the target type.
func (t TargetType) String() string {
return [...]string{
"unknown",
"tcp",
}[t]
}
const (
targetsViewDefaultTable = "target_all_subtypes"
)
// targetView provides a common way to return targets regardless of their
// underlying type.
type targetView struct {
*store.TargetView
tableName string `gorm:"-"`
}
// allocTargetView will allocate a target view
func allocTargetView() targetView {
return targetView{
TargetView: &store.TargetView{},
}
}
// TableName provides an overridden gorm table name for targets.
func (t *targetView) TableName() string {
if t.tableName != "" {
return t.tableName
}
return targetsViewDefaultTable
}
// SetTableName sets the table name for the resource. If the caller attempts to
// set the name to "" the name will be reset to the default name.
func (t *targetView) SetTableName(n string) {
switch n {
case "":
t.tableName = targetsViewDefaultTable
default:
t.tableName = n
}
}
// targetSubType converts the target view to the concrete subtype
func (t *targetView) targetSubType() (Target, error) {
switch t.Type {
case TcpTargetType.String():
tcpTarget := allocTcpTarget()
tcpTarget.PublicId = t.PublicId
tcpTarget.ScopeId = t.ScopeId
tcpTarget.Name = t.Name
tcpTarget.Description = t.Description
tcpTarget.DefaultPort = t.DefaultPort
tcpTarget.CreateTime = t.CreateTime
tcpTarget.UpdateTime = t.UpdateTime
tcpTarget.Version = t.Version
return &tcpTarget, nil
}
return nil, fmt.Errorf("%s is an unknown target subtype of %s", t.PublicId, t.Type)
}
+104
View File
@@ -0,0 +1,104 @@
package target
import (
"context"
"fmt"
"github.com/hashicorp/boundary/internal/db"
hostStore "github.com/hashicorp/boundary/internal/host/store"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/hashicorp/boundary/internal/target/store"
"google.golang.org/protobuf/proto"
)
const (
DefaultTargetHostSetTableName = "target_host_set"
)
type TargetHostSet struct {
*store.TargetHostSet
tableName string `gorm:"-"`
}
var _ db.VetForWriter = (*TargetHostSet)(nil)
// TargetHostSet creates a new in memory target host set. No options are
// currently supported.
func NewTargetHostSet(targetId, hostSetId string, opt ...Option) (*TargetHostSet, error) {
if targetId == "" {
return nil, fmt.Errorf("new target host set: missing target id: %w", db.ErrInvalidParameter)
}
if hostSetId == "" {
return nil, fmt.Errorf("new target host set: missing hostSetId id: %w", db.ErrInvalidParameter)
}
t := &TargetHostSet{
TargetHostSet: &store.TargetHostSet{
TargetId: targetId,
HostSetId: hostSetId,
},
}
return t, nil
}
// allocTargetHostSet will allocate a target host set
func allocTargetHostSet() TargetHostSet {
return TargetHostSet{
TargetHostSet: &store.TargetHostSet{},
}
}
// Clone creates a clone of the target host set
func (t *TargetHostSet) Clone() interface{} {
cp := proto.Clone(t.TargetHostSet)
return &TargetHostSet{
TargetHostSet: cp.(*store.TargetHostSet),
}
}
// VetForWrite implements db.VetForWrite() interface and validates the target
// host set before it's written.
func (t *TargetHostSet) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
if opType == db.CreateOp {
if t.TargetId == "" {
return fmt.Errorf("target host set: vet for write: missing target id: %w", db.ErrInvalidParameter)
}
if t.HostSetId == "" {
return fmt.Errorf("target host set: vet for write: missing host set id: %w", db.ErrInvalidParameter)
}
}
return nil
}
// TableName returns the tablename to override the default gorm table name
func (t *TargetHostSet) TableName() string {
if t.tableName != "" {
return t.tableName
}
return DefaultTargetHostSetTableName
}
// 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 *TargetHostSet) SetTableName(n string) {
t.tableName = n
}
func (t *TargetHostSet) oplog(op oplog.OpType) oplog.Metadata {
metadata := oplog.Metadata{
"resource-public-id": []string{fmt.Sprintf("%s:%s", t.TargetId, t.HostSetId)},
"resource-type": []string{"target host set"},
"op-type": []string{op.String()},
}
return metadata
}
// TargetSet is returned from most repo operations as the target's host set.
type TargetSet struct {
*hostStore.Set
}
// TableName returns the tablename to override the default gorm table name
func (ts *TargetSet) TableName() string {
return "target_set"
}
+39
View File
@@ -0,0 +1,39 @@
package target
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestTarget_SetTableName(t *testing.T) {
t.Parallel()
defaultTableName := targetsViewDefaultTable
tests := []struct {
name string
setNameTo string
want string
}{
{
name: "new-name",
setNameTo: "new-name",
want: "new-name",
},
{
name: "reset to default",
setNameTo: "",
want: defaultTableName,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
def := allocTargetView()
require.Equal(defaultTableName, def.TableName())
s := allocTargetView()
s.SetTableName(tt.setNameTo)
assert.Equal(tt.want, s.TableName())
})
}
}
+102
View File
@@ -0,0 +1,102 @@
package target
import (
"context"
"fmt"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/hashicorp/boundary/internal/target/store"
"google.golang.org/protobuf/proto"
)
const (
DefaultTcpTableName = "target_tcp"
)
type TcpTarget struct {
*store.TcpTarget
tableName string `gorm:"-"`
}
var _ Target = (*TcpTarget)(nil)
var _ db.VetForWriter = (*TcpTarget)(nil)
var _ oplog.ReplayableMessage = (*TcpTarget)(nil)
// NewTcpTarget creates a new in memory tcp target. WithName, WithDescription and
// WithDefaultPort options are supported
func NewTcpTarget(scopeId, name string, opt ...Option) (*TcpTarget, error) {
opts := getOpts(opt...)
if scopeId == "" {
return nil, fmt.Errorf("new tcp target: missing scope id: %w", db.ErrInvalidParameter)
}
if name == "" {
return nil, fmt.Errorf("new tcp target: missing name: %w", db.ErrInvalidParameter)
}
t := &TcpTarget{
TcpTarget: &store.TcpTarget{
ScopeId: scopeId,
Name: name,
Description: opts.withDescription,
DefaultPort: opts.withDefaultPort,
},
}
return t, nil
}
// allocTcpTarget will allocate a tcp target
func allocTcpTarget() TcpTarget {
return TcpTarget{
TcpTarget: &store.TcpTarget{},
}
}
// Clone creates a clone of the TcpTarget
func (t *TcpTarget) Clone() interface{} {
cp := proto.Clone(t.TcpTarget)
return &TcpTarget{
TcpTarget: cp.(*store.TcpTarget),
}
}
// VetForWrite implements db.VetForWrite() interface and validates the tcp target
// before it's written.
func (t *TcpTarget) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
if t.PublicId == "" {
return fmt.Errorf("tcp target vet for write: missing public id: %w", db.ErrInvalidParameter)
}
if opType == db.CreateOp {
if t.ScopeId == "" {
return fmt.Errorf("tcp target vet for write: missing scope id: %w", db.ErrInvalidParameter)
}
if t.Name == "" {
return fmt.Errorf("tcp target vet for write: missing name id: %w", db.ErrInvalidParameter)
}
}
return nil
}
// TableName returns the tablename to override the default gorm table name
func (t *TcpTarget) TableName() string {
if t.tableName != "" {
return t.tableName
}
return DefaultTcpTableName
}
// 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 *TcpTarget) SetTableName(n string) {
t.tableName = n
}
func (t *TcpTarget) oplog(op oplog.OpType) oplog.Metadata {
metadata := oplog.Metadata{
"resource-public-id": []string{t.PublicId},
"resource-type": []string{"tcp target"},
"op-type": []string{op.String()},
"scope-id": []string{t.ScopeId},
}
return metadata
}
+393
View File
@@ -0,0 +1,393 @@
package target
import (
"context"
"errors"
"testing"
"time"
"github.com/hashicorp/boundary/internal/db"
dbassert "github.com/hashicorp/boundary/internal/db/assert"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
func TestTcpTarget_Create(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
type args struct {
scopeId string
name string
opt []Option
}
tests := []struct {
name string
args args
want *TcpTarget
wantErr bool
wantIsErr error
create bool
wantCreateErr bool
}{
{
name: "empty-scopeId",
args: args{},
wantErr: true,
wantIsErr: db.ErrInvalidParameter,
},
{
name: "valid-proj-scope",
args: args{
scopeId: prj.PublicId,
name: "valid-proj-scope",
},
want: func() *TcpTarget {
t := allocTcpTarget()
t.ScopeId = prj.PublicId
t.Name = "valid-proj-scope"
return &t
}(),
create: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := NewTcpTarget(tt.args.scopeId, tt.args.name, tt.args.opt...)
if tt.wantErr {
require.Error(err)
assert.True(errors.Is(err, tt.wantIsErr))
return
}
require.NoError(err)
assert.Equal(tt.want, got)
if tt.create {
id, err := newTcpTargetId()
require.NoError(err)
got.PublicId = id
err = db.New(conn).Create(context.Background(), got)
if tt.wantCreateErr {
assert.Error(err)
return
} else {
assert.NoError(err)
}
}
})
}
}
func TestTcpTarget_Delete(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
tests := []struct {
name string
target *TcpTarget
wantRowsDeleted int
wantErr bool
wantErrMsg string
}{
{
name: "valid",
target: TestTcpTarget(t, conn, proj.PublicId, testTargetName(t, proj.PublicId)),
wantErr: false,
wantRowsDeleted: 1,
},
{
name: "bad-id",
target: func() *TcpTarget {
target := allocTcpTarget()
id, err := newTcpTargetId()
require.NoError(t, err)
target.PublicId = id
target.ScopeId = proj.PublicId
target.Name = testTargetName(t, proj.PublicId)
return &target
}(),
wantErr: false,
wantRowsDeleted: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
deleteTarget := allocTcpTarget()
deleteTarget.PublicId = tt.target.PublicId
deletedRows, err := rw.Delete(context.Background(), &deleteTarget)
if tt.wantErr {
require.Error(err)
return
}
require.NoError(err)
if tt.wantRowsDeleted == 0 {
assert.Equal(tt.wantRowsDeleted, deletedRows)
return
}
assert.Equal(tt.wantRowsDeleted, deletedRows)
foundTarget := allocTcpTarget()
foundTarget.PublicId = tt.target.PublicId
err = rw.LookupById(context.Background(), &foundTarget)
require.Error(err)
assert.True(errors.Is(db.ErrRecordNotFound, err))
})
}
}
func TestTcpTarget_Update(t *testing.T) {
t.Parallel()
id := testId(t)
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
type args struct {
name string
description string
fieldMaskPaths []string
nullPaths []string
ScopeId string
}
tests := []struct {
name string
args args
wantRowsUpdate int
wantErr bool
wantErrMsg string
wantDup bool
}{
{
name: "valid",
args: args{
name: "valid" + id,
fieldMaskPaths: []string{"Name"},
ScopeId: proj.PublicId,
},
wantErr: false,
wantRowsUpdate: 1,
},
{
name: "proj-scope-id-not-in-mask",
args: args{
name: "proj-scope-id" + id,
fieldMaskPaths: []string{"Name"},
ScopeId: proj.PublicId,
},
wantErr: false,
wantRowsUpdate: 1,
},
{
name: "empty-scope-id",
args: args{
name: "empty-scope-id" + id,
fieldMaskPaths: []string{"Name"},
ScopeId: "",
},
wantErr: false,
wantRowsUpdate: 1,
},
{
name: "dup-name",
args: args{
name: "dup-name" + id,
fieldMaskPaths: []string{"Name"},
ScopeId: proj.PublicId,
},
wantErr: true,
wantDup: true,
wantErrMsg: `update: failed: pq: duplicate key value violates unique constraint "target_tcp_scope_id_name_key"`,
},
{
name: "set description null",
args: args{
name: "set description null" + id,
fieldMaskPaths: []string{"Name"},
nullPaths: []string{"Description"},
ScopeId: proj.PublicId,
},
wantErr: false,
wantRowsUpdate: 1,
},
{
name: "set name null",
args: args{
description: "set description null" + id,
fieldMaskPaths: []string{"Description"},
nullPaths: []string{"Name"},
ScopeId: proj.PublicId,
},
wantErr: true,
wantErrMsg: `update: failed: pq: null value in column "name" violates not-null constraint`,
},
{
name: "set description null",
args: args{
name: "set name null" + id,
fieldMaskPaths: []string{"Name"},
nullPaths: []string{"Description"},
ScopeId: proj.PublicId,
},
wantErr: false,
wantRowsUpdate: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
if tt.wantDup {
target := TestTcpTarget(t, conn, proj.PublicId, testTargetName(t, proj.PublicId))
target.Name = tt.args.name
_, err := rw.Update(context.Background(), target, tt.args.fieldMaskPaths, tt.args.nullPaths)
require.NoError(err)
}
id := testId(t)
target := TestTcpTarget(t, conn, proj.PublicId, id, WithDescription(id))
updateTarget := allocTcpTarget()
updateTarget.PublicId = target.PublicId
updateTarget.ScopeId = tt.args.ScopeId
updateTarget.Name = tt.args.name
updateTarget.Description = tt.args.description
updatedRows, err := rw.Update(context.Background(), &updateTarget, tt.args.fieldMaskPaths, tt.args.nullPaths)
if tt.wantErr {
require.Error(err)
assert.Equal(0, updatedRows)
assert.Equal(tt.wantErrMsg, err.Error())
err = db.TestVerifyOplog(t, rw, target.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
require.Error(err)
assert.Equal("record not found", err.Error())
return
}
require.NoError(err)
assert.Equal(tt.wantRowsUpdate, updatedRows)
assert.NotEqual(target.UpdateTime, updateTarget.UpdateTime)
foundTarget := allocTcpTarget()
foundTarget.PublicId = target.GetPublicId()
err = rw.LookupByPublicId(context.Background(), &foundTarget)
require.NoError(err)
assert.True(proto.Equal(updateTarget, foundTarget))
if len(tt.args.nullPaths) != 0 {
dbassert := dbassert.New(t, rw)
for _, f := range tt.args.nullPaths {
dbassert.IsNull(&foundTarget, f)
}
}
})
}
t.Run("update dup names in diff scopes", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
id := testId(t)
_, proj2 := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
_ = TestTcpTarget(t, conn, proj2.PublicId, id, WithDescription(id))
projTarget := TestTcpTarget(t, conn, proj.PublicId, id)
projTarget.Name = id
updatedRows, err := rw.Update(context.Background(), projTarget, []string{"Name"}, nil)
require.NoError(err)
assert.Equal(1, updatedRows)
foundTarget := allocTcpTarget()
foundTarget.PublicId = projTarget.GetPublicId()
err = rw.LookupByPublicId(context.Background(), &foundTarget)
require.NoError(err)
assert.Equal(id, projTarget.Name)
})
}
func TestTcpTarget_Clone(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
t.Run("valid", func(t *testing.T) {
assert := assert.New(t)
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
target := TestTcpTarget(t, conn, proj.PublicId, testTargetName(t, proj.PublicId))
cp := target.Clone()
assert.True(proto.Equal(cp.(*TcpTarget).TcpTarget, target.TcpTarget))
})
t.Run("not-equal", func(t *testing.T) {
assert := assert.New(t)
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
_, proj2 := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
target := TestTcpTarget(t, conn, proj.PublicId, testTargetName(t, proj.PublicId))
target2 := TestTcpTarget(t, conn, proj2.PublicId, testTargetName(t, proj2.PublicId))
cp := target.Clone()
assert.True(!proto.Equal(cp.(*TcpTarget).TcpTarget, target2.TcpTarget))
})
}
func TestTcpTable_SetTableName(t *testing.T) {
t.Parallel()
defaultTableName := DefaultTcpTableName
tests := []struct {
name string
setNameTo string
want string
}{
{
name: "new-name",
setNameTo: "new-name",
want: "new-name",
},
{
name: "reset to default",
setNameTo: "",
want: defaultTableName,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
def := allocTcpTarget()
require.Equal(defaultTableName, def.TableName())
s := allocTcpTarget()
s.SetTableName(tt.setNameTo)
assert.Equal(tt.want, s.TableName())
})
}
}
func TestTcpTarget_oplog(t *testing.T) {
id := testId(t)
tests := []struct {
name string
target *TcpTarget
op oplog.OpType
want oplog.Metadata
}{
{
name: "simple",
target: func() *TcpTarget {
t := allocTcpTarget()
t.PublicId = id
t.ScopeId = id
return &t
}(),
op: oplog.OpType_OP_TYPE_CREATE,
want: oplog.Metadata{
"resource-public-id": []string{id},
"resource-type": []string{"tcp target"},
"op-type": []string{oplog.OpType_OP_TYPE_CREATE.String()},
"scope-id": []string{id},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
got := tt.target.oplog(tt.op)
assert.Equal(got, tt.want)
})
}
}
+50
View File
@@ -0,0 +1,50 @@
package target
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/go-uuid"
"github.com/jinzhu/gorm"
"github.com/stretchr/testify/require"
)
func TestTcpTarget(t *testing.T, conn *gorm.DB, scopeId, name string, opt ...Option) *TcpTarget {
t.Helper()
opts := getOpts(opt...)
require := require.New(t)
rw := db.New(conn)
target, err := NewTcpTarget(scopeId, name, opt...)
require.NoError(err)
id, err := newTcpTargetId()
require.NoError(err)
target.PublicId = id
err = rw.Create(context.Background(), target)
require.NoError(err)
if len(opts.withHostSets) > 0 {
newHostSets := make([]interface{}, 0, len(opts.withHostSets))
for _, s := range opts.withHostSets {
hostSet, err := NewTargetHostSet(target.PublicId, s)
require.NoError(err)
newHostSets = append(newHostSets, hostSet)
}
err := rw.CreateItems(context.Background(), newHostSets)
require.NoError(err)
}
return target
}
func testTargetName(t *testing.T, scopeId string) string {
t.Helper()
return fmt.Sprintf("%s-%s", scopeId, testId(t))
}
func testId(t *testing.T) string {
t.Helper()
id, err := uuid.GenerateUUID()
require.NoError(t, err)
return id
}
+39
View File
@@ -0,0 +1,39 @@
package target
import (
"context"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/iam"
"github.com/stretchr/testify/require"
)
func Test_TestTcpTarget(t *testing.T) {
t.Helper()
require := require.New(t)
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
cats := static.TestCatalogs(t, conn, proj.PublicId, 1)
hsets := static.TestSets(t, conn, cats[0].GetPublicId(), 2)
var sets []string
for _, s := range hsets {
sets = append(sets, s.PublicId)
}
name := testTargetName(t, proj.PublicId)
target := TestTcpTarget(t, conn, proj.PublicId, name, WithHostSets(sets))
require.NotNil(t)
require.NotEmpty(target.PublicId)
require.Equal(name, target.Name)
rw := db.New(conn)
foundSets, err := fetchSets(context.Background(), rw, target.PublicId)
require.NoError(err)
foundIds := make([]string, 0, len(foundSets))
for _, s := range foundSets {
foundIds = append(foundIds, s.PublicId)
}
require.Equal(sets, foundIds)
}