targets repo (#298)
This commit is contained in:
24 files changed
+4403
-1
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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+"_"))
|
||||
})
|
||||
}
|
||||
@@ -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: ×tamppb.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: ×tamppb.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: ×tamppb.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)))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Reference in new issue
Block a user