Add authorize session for plugin based host sets. (#1559)
Added a new Endpoint struct at the top level host package level to capture host, set, and address chosen. Temporarily add a host_plugin_host table to capture the hosts returned by the plugins. This ensures the datawarehouse is able to query the table to decide what host was chosen. The view used to update the datawarehouse dimension table provides rows with host set ids and hosts which are not in that host set. This is ok since the function which updates the dimension table provides the host set and host id chosen so that relationship doesn't need to be captured in the view itself. Co-authored-by: Jeff Mitchell <jeffrey.mitchell@gmail.com>
This commit is contained in:
1 parent
8b8f5de120
commit
14cc97b645
18 files changed
+1536
-332
No files matched your search
@@ -154,6 +154,66 @@ begin;
|
||||
create trigger delete_host_set_subtype after delete on host_plugin_set
|
||||
for each row execute procedure delete_host_set_subtype();
|
||||
|
||||
|
||||
-- TODO: Rebuild this table to appropriately cache or sync data from the
|
||||
-- plugin'ed service.
|
||||
-- host_plugin_host captures plugin based host data. This is only written to
|
||||
-- from the controller and is not mutable directly by actions from the end
|
||||
-- user.
|
||||
create table host_plugin_host (
|
||||
public_id wt_public_id primary key,
|
||||
catalog_id wt_public_id not null
|
||||
constraint host_plugin_catalog_fkey
|
||||
references host_plugin_catalog (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
name wt_name,
|
||||
description text,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
|
||||
-- TODO: Break out the address to match the domain model where a host
|
||||
-- can have multiple addresses and relies on the set to select the
|
||||
-- prefered one.
|
||||
address text not null
|
||||
constraint address_must_be_more_than_2_characters
|
||||
check(length(trim(address)) > 2)
|
||||
constraint address_must_be_less_than_256_characters
|
||||
check(length(trim(address)) < 256),
|
||||
|
||||
constraint host_fkey
|
||||
foreign key (catalog_id, public_id)
|
||||
references host (catalog_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
|
||||
constraint host_plugin_host_catalog_id_name_uq
|
||||
unique(catalog_id, name),
|
||||
|
||||
constraint host_plugin_host_catalog_id_public_id_uq
|
||||
unique(catalog_id, public_id)
|
||||
);
|
||||
|
||||
create trigger update_version_column after update on host_plugin_host
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger update_time_column before update on host_plugin_host
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on host_plugin_host
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on host_plugin_host
|
||||
for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time');
|
||||
|
||||
create trigger insert_host_subtype before insert on host_plugin_host
|
||||
for each row execute procedure insert_host_subtype();
|
||||
|
||||
create trigger delete_host_subtype after delete on host_plugin_host
|
||||
for each row execute procedure delete_host_subtype();
|
||||
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('host_plugin_catalog', 1),
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
begin;
|
||||
|
||||
-- replaces view from 15/02_wh_rename_host_dimension_org.up.sql
|
||||
|
||||
-- Updates whx_host_dimension_source to support plugin based host and related
|
||||
-- resources. While this will return rows where there are host_sets where hosts
|
||||
-- are not in the host sets, this view is only used when looking up a row by
|
||||
-- host, set, and target so the records where hosts and host sets aren't
|
||||
-- related beyond the shared catalog will be ignored.
|
||||
-- TODO: Support a host having multiple addresses.
|
||||
drop view whx_host_dimension_source;
|
||||
create view whx_host_dimension_source as
|
||||
select -- id is the first column in the target view
|
||||
h.public_id as host_id,
|
||||
case when sh.public_id is not null then 'static host'
|
||||
when ph.public_id is not null then 'plugin host'
|
||||
else 'Unknown' end as host_type,
|
||||
case when sh.public_id is not null then coalesce(sh.name, 'None')
|
||||
when ph.public_id is not null then coalesce(ph.name, 'None')
|
||||
else 'Unknown' end as host_name,
|
||||
case when sh.public_id is not null then coalesce(sh.description, 'None')
|
||||
when ph.public_id is not null then coalesce(ph.description, 'None')
|
||||
else 'Unknown' end as host_description,
|
||||
|
||||
-- TODO: update this to be endpoint address...
|
||||
coalesce(sh.address, 'Unknown') as host_address,
|
||||
|
||||
hs.public_id as host_set_id,
|
||||
case when shs.public_id is not null then 'static host set'
|
||||
when phs.public_id is not null then 'plugin host set'
|
||||
else 'Unknown' end as host_set_type,
|
||||
case
|
||||
when shs.public_id is not null then coalesce(shs.name, 'None')
|
||||
when phs.public_id is not null then coalesce(phs.name, 'None')
|
||||
else 'None'
|
||||
end as host_set_name,
|
||||
case
|
||||
when shs.public_id is not null then coalesce(shs.description, 'None')
|
||||
when phs.public_id is not null then coalesce(phs.description, 'None')
|
||||
else 'None'
|
||||
end as host_set_description,
|
||||
hc.public_id as host_catalog_id,
|
||||
case when shc.public_id is not null then 'static host catalog'
|
||||
when phc.public_id is not null then 'plugin host catalog'
|
||||
else 'Unknown' end as host_catalog_type,
|
||||
case
|
||||
when shc.public_id is not null then coalesce(shc.name, 'None')
|
||||
when phc.public_id is not null then coalesce(phc.name, 'None')
|
||||
else 'None'
|
||||
end as host_catalog_name,
|
||||
case
|
||||
when shc.public_id is not null then coalesce(shc.description, 'None')
|
||||
when phc.public_id is not null then coalesce(phc.description, 'None')
|
||||
else 'None'
|
||||
end as host_catalog_description,
|
||||
t.public_id as target_id,
|
||||
'tcp target' as target_type,
|
||||
coalesce(t.name, 'None') as target_name,
|
||||
coalesce(t.description, 'None') as target_description,
|
||||
coalesce(t.default_port, 0) as target_default_port_number,
|
||||
t.session_max_seconds as target_session_max_seconds,
|
||||
t.session_connection_limit as target_session_connection_limit,
|
||||
p.public_id as project_id,
|
||||
coalesce(p.name, 'None') as project_name,
|
||||
coalesce(p.description, 'None') as project_description,
|
||||
o.public_id as organization_id,
|
||||
coalesce(o.name, 'None') as organization_name,
|
||||
coalesce(o.description, 'None') as organization_description
|
||||
from host as h
|
||||
join host_catalog as hc on h.catalog_id = hc.public_id
|
||||
join host_set as hs on h.catalog_id = hs.catalog_id
|
||||
join target_host_set as ts on hs.public_id = ts.host_set_id
|
||||
join target_tcp as t on ts.target_id = t.public_id
|
||||
join iam_scope as p on t.scope_id = p.public_id and p.type = 'project'
|
||||
join iam_scope as o on p.parent_id = o.public_id and o.type = 'org'
|
||||
|
||||
left join static_host as sh on sh.public_id = h.public_id
|
||||
left join host_plugin_host as ph on ph.public_id = h.public_id
|
||||
left join static_host_catalog as shc on shc.public_id = hc.public_id
|
||||
left join host_plugin_catalog as phc on phc.public_id = hc.public_id
|
||||
left join static_host_set as shs on shs.public_id = hs.public_id
|
||||
left join host_plugin_set as phs on phs.public_id = hs.public_id
|
||||
;
|
||||
|
||||
commit;
|
||||
@@ -4,7 +4,7 @@ package schema
|
||||
|
||||
func init() {
|
||||
migrationStates["postgres"] = migrationState{
|
||||
binarySchemaVersion: 17005,
|
||||
binarySchemaVersion: 17006,
|
||||
upMigrations: map[int][]byte{
|
||||
1: []byte(`
|
||||
create domain wt_public_id as text
|
||||
@@ -7828,6 +7828,66 @@ alter table wh_session_connection_accumulating_fact
|
||||
create trigger delete_host_set_subtype after delete on host_plugin_set
|
||||
for each row execute procedure delete_host_set_subtype();
|
||||
|
||||
|
||||
-- TODO: Rebuild this table to appropriately cache or sync data from the
|
||||
-- plugin'ed service.
|
||||
-- host_plugin_host captures plugin based host data. This is only written to
|
||||
-- from the controller and is not mutable directly by actions from the end
|
||||
-- user.
|
||||
create table host_plugin_host (
|
||||
public_id wt_public_id primary key,
|
||||
catalog_id wt_public_id not null
|
||||
constraint host_plugin_catalog_fkey
|
||||
references host_plugin_catalog (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
name wt_name,
|
||||
description text,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
|
||||
-- TODO: Break out the address to match the domain model where a host
|
||||
-- can have multiple addresses and relies on the set to select the
|
||||
-- prefered one.
|
||||
address text not null
|
||||
constraint address_must_be_more_than_2_characters
|
||||
check(length(trim(address)) > 2)
|
||||
constraint address_must_be_less_than_256_characters
|
||||
check(length(trim(address)) < 256),
|
||||
|
||||
constraint host_fkey
|
||||
foreign key (catalog_id, public_id)
|
||||
references host (catalog_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
|
||||
constraint host_plugin_host_catalog_id_name_uq
|
||||
unique(catalog_id, name),
|
||||
|
||||
constraint host_plugin_host_catalog_id_public_id_uq
|
||||
unique(catalog_id, public_id)
|
||||
);
|
||||
|
||||
create trigger update_version_column after update on host_plugin_host
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger update_time_column before update on host_plugin_host
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on host_plugin_host
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on host_plugin_host
|
||||
for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time');
|
||||
|
||||
create trigger insert_host_subtype before insert on host_plugin_host
|
||||
for each row execute procedure insert_host_subtype();
|
||||
|
||||
create trigger delete_host_subtype after delete on host_plugin_host
|
||||
for each row execute procedure delete_host_subtype();
|
||||
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('host_plugin_catalog', 1),
|
||||
@@ -7906,6 +7966,89 @@ from
|
||||
group by hs.public_id, hc.plugin_id;
|
||||
comment on view host_plugin_host_set_with_value_obj is
|
||||
'host plugin host set with its associated value objects';
|
||||
`),
|
||||
17006: []byte(`
|
||||
-- replaces view from 15/02_wh_rename_host_dimension_org.up.sql
|
||||
|
||||
-- Updates whx_host_dimension_source to support plugin based host and related
|
||||
-- resources. While this will return rows where there are host_sets where hosts
|
||||
-- are not in the host sets, this view is only used when looking up a row by
|
||||
-- host, set, and target so the records where hosts and host sets aren't
|
||||
-- related beyond the shared catalog will be ignored.
|
||||
-- TODO: Support a host having multiple addresses.
|
||||
drop view whx_host_dimension_source;
|
||||
create view whx_host_dimension_source as
|
||||
select -- id is the first column in the target view
|
||||
h.public_id as host_id,
|
||||
case when sh.public_id is not null then 'static host'
|
||||
when ph.public_id is not null then 'plugin host'
|
||||
else 'Unknown' end as host_type,
|
||||
case when sh.public_id is not null then coalesce(sh.name, 'None')
|
||||
when ph.public_id is not null then coalesce(ph.name, 'None')
|
||||
else 'Unknown' end as host_name,
|
||||
case when sh.public_id is not null then coalesce(sh.description, 'None')
|
||||
when ph.public_id is not null then coalesce(ph.description, 'None')
|
||||
else 'Unknown' end as host_description,
|
||||
|
||||
-- TODO: update this to be endpoint address...
|
||||
coalesce(sh.address, 'Unknown') as host_address,
|
||||
|
||||
hs.public_id as host_set_id,
|
||||
case when shs.public_id is not null then 'static host set'
|
||||
when phs.public_id is not null then 'plugin host set'
|
||||
else 'Unknown' end as host_set_type,
|
||||
case
|
||||
when shs.public_id is not null then coalesce(shs.name, 'None')
|
||||
when phs.public_id is not null then coalesce(phs.name, 'None')
|
||||
else 'None'
|
||||
end as host_set_name,
|
||||
case
|
||||
when shs.public_id is not null then coalesce(shs.description, 'None')
|
||||
when phs.public_id is not null then coalesce(phs.description, 'None')
|
||||
else 'None'
|
||||
end as host_set_description,
|
||||
hc.public_id as host_catalog_id,
|
||||
case when shc.public_id is not null then 'static host catalog'
|
||||
when phc.public_id is not null then 'plugin host catalog'
|
||||
else 'Unknown' end as host_catalog_type,
|
||||
case
|
||||
when shc.public_id is not null then coalesce(shc.name, 'None')
|
||||
when phc.public_id is not null then coalesce(phc.name, 'None')
|
||||
else 'None'
|
||||
end as host_catalog_name,
|
||||
case
|
||||
when shc.public_id is not null then coalesce(shc.description, 'None')
|
||||
when phc.public_id is not null then coalesce(phc.description, 'None')
|
||||
else 'None'
|
||||
end as host_catalog_description,
|
||||
t.public_id as target_id,
|
||||
'tcp target' as target_type,
|
||||
coalesce(t.name, 'None') as target_name,
|
||||
coalesce(t.description, 'None') as target_description,
|
||||
coalesce(t.default_port, 0) as target_default_port_number,
|
||||
t.session_max_seconds as target_session_max_seconds,
|
||||
t.session_connection_limit as target_session_connection_limit,
|
||||
p.public_id as project_id,
|
||||
coalesce(p.name, 'None') as project_name,
|
||||
coalesce(p.description, 'None') as project_description,
|
||||
o.public_id as organization_id,
|
||||
coalesce(o.name, 'None') as organization_name,
|
||||
coalesce(o.description, 'None') as organization_description
|
||||
from host as h
|
||||
join host_catalog as hc on h.catalog_id = hc.public_id
|
||||
join host_set as hs on h.catalog_id = hs.catalog_id
|
||||
join target_host_set as ts on hs.public_id = ts.host_set_id
|
||||
join target_tcp as t on ts.target_id = t.public_id
|
||||
join iam_scope as p on t.scope_id = p.public_id and p.type = 'project'
|
||||
join iam_scope as o on p.parent_id = o.public_id and o.type = 'org'
|
||||
|
||||
left join static_host as sh on sh.public_id = h.public_id
|
||||
left join host_plugin_host as ph on ph.public_id = h.public_id
|
||||
left join static_host_catalog as shc on shc.public_id = hc.public_id
|
||||
left join host_plugin_catalog as phc on phc.public_id = hc.public_id
|
||||
left join static_host_set as shs on shs.public_id = hs.public_id
|
||||
left join host_plugin_set as phs on phs.public_id = hs.public_id
|
||||
;
|
||||
`),
|
||||
2001: []byte(`
|
||||
-- log_migration entries represent logs generated during migrations
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package host
|
||||
|
||||
// Endpoint is a struct which identifies an address provided by a host and
|
||||
// selected as the priority address by the specified host set.
|
||||
type Endpoint struct {
|
||||
HostId string
|
||||
SetId string
|
||||
Address string
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package host
|
||||
|
||||
import "github.com/hashicorp/boundary/internal/boundary"
|
||||
|
||||
// Catalog contains the common methods across all the different types of host
|
||||
// catalogs.
|
||||
type Catalog interface {
|
||||
boundary.Resource
|
||||
GetScopeId() string
|
||||
}
|
||||
|
||||
// Set contains the common methods across all the different types of host sets.
|
||||
type Set interface {
|
||||
boundary.Resource
|
||||
GetCatalogId() string
|
||||
}
|
||||
|
||||
// Host contains the common methods across all the different types of hosts.
|
||||
type Host interface {
|
||||
boundary.Resource
|
||||
GetCatalogId() string
|
||||
GetAddress() string
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/host/plugin/store"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// A Host is a temporarily cached plugin based resource. The source of truth
|
||||
// of the data contained here is provided by the service backing the plugin for
|
||||
// this host. The plugin for this host can be found by looking at the plugin
|
||||
// field for this host's host catalog.
|
||||
type Host struct {
|
||||
*store.Host
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// newHost creates a new in memory Host assigned to catalogId with an address.
|
||||
// Name and description are the only valid options. All other options are
|
||||
// ignored.
|
||||
func newHost(ctx context.Context, catalogId, address string, opt ...Option) *Host {
|
||||
opts := getOpts(opt...)
|
||||
|
||||
h := &Host{
|
||||
Host: &store.Host{
|
||||
CatalogId: catalogId,
|
||||
Name: opts.withName,
|
||||
Description: opts.withDescription,
|
||||
Address: address,
|
||||
},
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// TableName returns the table name for the host set.
|
||||
func (s *Host) TableName() string {
|
||||
if s.tableName != "" {
|
||||
return s.tableName
|
||||
}
|
||||
return "host_plugin_host"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name. If the caller attempts to
|
||||
// set the name to "" the name will be reset to the default name.
|
||||
func (s *Host) SetTableName(n string) {
|
||||
s.tableName = n
|
||||
}
|
||||
|
||||
func allocHost() *Host {
|
||||
return &Host{
|
||||
Host: &store.Host{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Host) clone() *Host {
|
||||
cp := proto.Clone(s.Host)
|
||||
hs := &Host{
|
||||
Host: cp.(*store.Host),
|
||||
}
|
||||
return hs
|
||||
}
|
||||
|
||||
func (s *Host) oplog(op oplog.OpType) oplog.Metadata {
|
||||
metadata := oplog.Metadata{
|
||||
"resource-public-id": []string{s.PublicId},
|
||||
"resource-type": []string{"plugin-host"},
|
||||
"op-type": []string{op.String()},
|
||||
}
|
||||
if s.CatalogId != "" {
|
||||
metadata["catalog-id"] = []string{s.CatalogId}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin/store"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/plugin/host"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHost_Create(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "test")
|
||||
cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
|
||||
cat2 := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
|
||||
|
||||
type args struct {
|
||||
catalogId string
|
||||
address string
|
||||
opts []Option
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *Host
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "blank-catalogId",
|
||||
args: args{
|
||||
catalogId: "",
|
||||
address: "foo.bar.com",
|
||||
},
|
||||
want: &Host{Host: &store.Host{
|
||||
Address: "foo.bar.com",
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "blank-address",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
},
|
||||
want: &Host{Host: &store.Host{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid-no-options",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
address: "foo.bar.com",
|
||||
},
|
||||
want: &Host{
|
||||
Host: &store.Host{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
Address: "foo.bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-name",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
address: "foo.bar.com",
|
||||
opts: []Option{
|
||||
WithName("test-name"),
|
||||
},
|
||||
},
|
||||
want: &Host{
|
||||
Host: &store.Host{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
Address: "foo.bar.com",
|
||||
Name: "test-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate-name",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
address: "foo.bar.com",
|
||||
opts: []Option{
|
||||
WithName("test-name"),
|
||||
},
|
||||
},
|
||||
want: &Host{
|
||||
Host: &store.Host{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
Address: "foo.bar.com",
|
||||
Name: "test-name",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid-duplicate-name-different-catalog",
|
||||
args: args{
|
||||
catalogId: cat2.GetPublicId(),
|
||||
address: "foo.bar.com",
|
||||
opts: []Option{
|
||||
WithName("test-name"),
|
||||
},
|
||||
},
|
||||
want: &Host{
|
||||
Host: &store.Host{
|
||||
CatalogId: cat2.GetPublicId(),
|
||||
Address: "foo.bar.com",
|
||||
Name: "test-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-description",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
address: "foo.bar.com",
|
||||
opts: []Option{
|
||||
WithDescription("test-description"),
|
||||
},
|
||||
},
|
||||
want: &Host{
|
||||
Host: &store.Host{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
Address: "foo.bar.com",
|
||||
Description: "test-description",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
got := newHost(ctx, tt.args.catalogId, tt.args.address, tt.args.opts...)
|
||||
require.NotNil(t, got)
|
||||
assert.Emptyf(t, got.PublicId, "PublicId set")
|
||||
assert.Equal(t, tt.want, got)
|
||||
|
||||
id, err := newHostId(ctx, tt.name, tt.name)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tt.want.PublicId = id
|
||||
got.PublicId = id
|
||||
|
||||
w := db.New(conn)
|
||||
err = w.Create(context.Background(), got)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHost_SetTableName(t *testing.T) {
|
||||
defaultTableName := "host_plugin_host"
|
||||
tests := []struct {
|
||||
name string
|
||||
initialName string
|
||||
setNameTo string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "new-name",
|
||||
initialName: "",
|
||||
setNameTo: "new-name",
|
||||
want: "new-name",
|
||||
},
|
||||
{
|
||||
name: "reset to default",
|
||||
initialName: "initial",
|
||||
setNameTo: "",
|
||||
want: defaultTableName,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
def := &Host{
|
||||
Host: &store.Host{},
|
||||
}
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := &Host{
|
||||
Host: &store.Host{},
|
||||
tableName: tt.initialName,
|
||||
}
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@ func init() {
|
||||
|
||||
// PublicId prefixes for the resources in the plugin package.
|
||||
const (
|
||||
// TODO: Pull these out of being constants and have them derivable at run time.
|
||||
HostCatalogPrefix = "hc"
|
||||
HostSetPrefix = "hs"
|
||||
HostPrefix = "h"
|
||||
|
||||
Subtype = subtypes.Subtype("plugin")
|
||||
)
|
||||
@@ -39,3 +39,18 @@ func newHostSetId(ctx context.Context) (string, error) {
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newHostId(ctx context.Context, catalogId, externalId string) (string, error) {
|
||||
const op = "plugin.newHostId"
|
||||
if catalogId == "" {
|
||||
return "", errors.New(ctx, errors.InvalidParameter, op, "missing catalog id")
|
||||
}
|
||||
if externalId == "" {
|
||||
return "", errors.New(ctx, errors.InvalidParameter, op, "missing external id")
|
||||
}
|
||||
id, err := db.NewPublicId(HostPrefix, db.WithPrngValues([]string{catalogId, externalId}))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(ctx, err, op)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
@@ -151,10 +151,11 @@ func (r *Repository) LookupCatalog(ctx context.Context, id string, _ ...Option)
|
||||
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
||||
}
|
||||
c, err := r.getCatalog(ctx, id)
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id)))
|
||||
}
|
||||
plg, err := r.getPlugin(ctx, c.GetPluginId())
|
||||
@@ -206,16 +207,6 @@ func (r *Repository) getCatalog(ctx context.Context, id string) (*HostCatalog, e
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (r *Repository) getPlugin(ctx context.Context, id string) (*hostplugin.Plugin, error) {
|
||||
const op = "plugin.(Repository).getPlugin"
|
||||
c := hostplugin.NewPlugin()
|
||||
c.PublicId = id
|
||||
if err := r.reader.LookupByPublicId(ctx, c); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", id)))
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// getPersistedDataForCatalog returns the persisted data for a catalog if
|
||||
// present. c must have a valid Public Id and Scope Id set.
|
||||
// TODO: consider merging the functions for getting catalog and persisted data into a view.
|
||||
@@ -252,6 +243,19 @@ func (r *Repository) getPersistedDataForCatalog(ctx context.Context, c *HostCata
|
||||
return &plgpb.HostCatalogPersisted{Secrets: secrets}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) getPlugin(ctx context.Context, plgId string) (*hostplugin.Plugin, error) {
|
||||
const op = "plugin.(Repository).getPlugin"
|
||||
if plgId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no plugin id")
|
||||
}
|
||||
plg := hostplugin.NewPlugin()
|
||||
plg.PublicId = plgId
|
||||
if err := r.reader.LookupByPublicId(ctx, plg); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("unable to get host plugin with id %q", plgId)))
|
||||
}
|
||||
return plg, nil
|
||||
}
|
||||
|
||||
// toPluginCatalog returns a host catalog, with it's secret if available, in the format expected
|
||||
// by the host plugin system.
|
||||
func toPluginCatalog(ctx context.Context, in *HostCatalog) (*pb.HostCatalog, error) {
|
||||
|
||||
@@ -12,8 +12,10 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/host"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/libs/endpoint"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
hostplugin "github.com/hashicorp/boundary/internal/plugin/host"
|
||||
hcpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets"
|
||||
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -192,9 +194,6 @@ func (r *Repository) ListSets(ctx context.Context, catalogId string, opt ...host
|
||||
|
||||
func (r *Repository) getSets(ctx context.Context, publicId string, catalogId string, opt ...host.Option) ([]*HostSet, *hostplugin.Plugin, error) {
|
||||
const op = "plugin.(Repository).getSets"
|
||||
const aggregateDelimiter = "|"
|
||||
const priorityDelimiter = "="
|
||||
|
||||
if publicId == "" && catalogId == "" {
|
||||
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing search criteria: both host set id and catalog id are empty")
|
||||
}
|
||||
@@ -245,55 +244,9 @@ func (r *Repository) getSets(ctx context.Context, publicId string, catalogId str
|
||||
|
||||
sets := make([]*HostSet, 0, len(aggHostSets))
|
||||
for _, agg := range aggHostSets {
|
||||
hs := allocHostSet()
|
||||
hs.PublicId = agg.PublicId
|
||||
hs.CatalogId = agg.CatalogId
|
||||
hs.Name = agg.Name
|
||||
hs.Description = agg.Description
|
||||
hs.CreateTime = agg.CreateTime
|
||||
hs.UpdateTime = agg.UpdateTime
|
||||
hs.Version = agg.Version
|
||||
hs.Attributes = agg.Attributes
|
||||
if agg.PreferredEndpoints != "" {
|
||||
eps := strings.Split(agg.PreferredEndpoints, aggregateDelimiter)
|
||||
if len(eps) > 0 {
|
||||
// We want to protect against someone messing with the DB
|
||||
// and not panic, so we do a bit of a dance here
|
||||
var sortErr error
|
||||
sort.Slice(eps, func(i, j int) bool {
|
||||
epi := strings.Split(eps[i], priorityDelimiter)
|
||||
if len(epi) != 2 {
|
||||
sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[i]))
|
||||
return false
|
||||
}
|
||||
epj := strings.Split(eps[j], priorityDelimiter)
|
||||
if len(epj) != 2 {
|
||||
sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[j]))
|
||||
return false
|
||||
}
|
||||
indexi, err := strconv.Atoi(epi[0])
|
||||
if err != nil {
|
||||
sortErr = errors.Wrap(ctx, err, op)
|
||||
return false
|
||||
}
|
||||
indexj, err := strconv.Atoi(epj[0])
|
||||
if err != nil {
|
||||
sortErr = errors.Wrap(ctx, err, op)
|
||||
return false
|
||||
}
|
||||
return indexi < indexj
|
||||
})
|
||||
if sortErr != nil {
|
||||
return nil, nil, sortErr
|
||||
}
|
||||
for i, ep := range eps {
|
||||
// At this point they're in the correct order, but we still
|
||||
// have to strip off the priority
|
||||
eps[i] = strings.Split(ep, priorityDelimiter)[1]
|
||||
}
|
||||
|
||||
hs.PreferredEndpoints = eps
|
||||
}
|
||||
hs, err := agg.toHostSet(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
sets = append(sets, hs)
|
||||
}
|
||||
@@ -323,6 +276,62 @@ type hostSetAgg struct {
|
||||
PreferredEndpoints string
|
||||
}
|
||||
|
||||
func (agg *hostSetAgg) toHostSet(ctx context.Context) (*HostSet, error) {
|
||||
const op = "plugin.(hostSetAgg).toHostSet"
|
||||
const aggregateDelimiter = "|"
|
||||
const priorityDelimiter = "="
|
||||
hs := allocHostSet()
|
||||
hs.PublicId = agg.PublicId
|
||||
hs.CatalogId = agg.CatalogId
|
||||
hs.Name = agg.Name
|
||||
hs.Description = agg.Description
|
||||
hs.CreateTime = agg.CreateTime
|
||||
hs.UpdateTime = agg.UpdateTime
|
||||
hs.Version = agg.Version
|
||||
hs.Attributes = agg.Attributes
|
||||
if agg.PreferredEndpoints != "" {
|
||||
eps := strings.Split(agg.PreferredEndpoints, aggregateDelimiter)
|
||||
if len(eps) > 0 {
|
||||
// We want to protect against someone messing with the DB
|
||||
// and not panic, so we do a bit of a dance here
|
||||
var sortErr error
|
||||
sort.Slice(eps, func(i, j int) bool {
|
||||
epi := strings.Split(eps[i], priorityDelimiter)
|
||||
if len(epi) != 2 {
|
||||
sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[i]))
|
||||
return false
|
||||
}
|
||||
epj := strings.Split(eps[j], priorityDelimiter)
|
||||
if len(epj) != 2 {
|
||||
sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("preferred endpoint %s had unexpected fields", eps[j]))
|
||||
return false
|
||||
}
|
||||
indexi, err := strconv.Atoi(epi[0])
|
||||
if err != nil {
|
||||
sortErr = errors.Wrap(ctx, err, op)
|
||||
return false
|
||||
}
|
||||
indexj, err := strconv.Atoi(epj[0])
|
||||
if err != nil {
|
||||
sortErr = errors.Wrap(ctx, err, op)
|
||||
return false
|
||||
}
|
||||
return indexi < indexj
|
||||
})
|
||||
if sortErr != nil {
|
||||
return nil, sortErr
|
||||
}
|
||||
for i, ep := range eps {
|
||||
// At this point they're in the correct order, but we still
|
||||
// have to strip off the priority
|
||||
eps[i] = strings.Split(ep, priorityDelimiter)[1]
|
||||
}
|
||||
hs.PreferredEndpoints = eps
|
||||
}
|
||||
}
|
||||
return hs, nil
|
||||
}
|
||||
|
||||
// TableName returns the table name for gorm
|
||||
func (agg *hostSetAgg) TableName() string { return "host_plugin_host_set_with_value_obj" }
|
||||
|
||||
@@ -344,3 +353,183 @@ func toPluginSet(ctx context.Context, in *HostSet) (*pb.HostSet, error) {
|
||||
}
|
||||
return hs, nil
|
||||
}
|
||||
|
||||
// Endpoints provides all the endpoints available for a given set id.
|
||||
// An error is returned if the set, related catalog, or related plugin are
|
||||
// unable to be retrieved. If a host does not contain an addressible endpoint
|
||||
// it is not included in the resulting slice of endpoints.
|
||||
func (r *Repository) Endpoints(ctx context.Context, setIds []string) ([]*host.Endpoint, error) {
|
||||
const op = "plugin.(Repository).Endpoints"
|
||||
if len(setIds) == 0 {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no set ids")
|
||||
}
|
||||
var setAggs []*hostSetAgg
|
||||
if err := r.reader.SearchWhere(ctx, &setAggs, "public_id in (?)", []interface{}{setIds}); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve sets %v", setIds)))
|
||||
}
|
||||
if len(setAggs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type setInfo struct {
|
||||
preferredEndpoint endpoint.Option
|
||||
plgSet *pb.HostSet
|
||||
}
|
||||
|
||||
type catalogInfo struct{
|
||||
publicId string
|
||||
plg plgpb.HostPluginServiceServer
|
||||
setInfos map[string]*setInfo
|
||||
plgCat *hcpb.HostCatalog
|
||||
persisted *plgpb.HostCatalogPersisted
|
||||
}
|
||||
|
||||
catalogInfos := make(map[string]*catalogInfo)
|
||||
for _, ag := range setAggs {
|
||||
ci, ok := catalogInfos[ag.CatalogId]
|
||||
if !ok {
|
||||
ci = &catalogInfo{
|
||||
publicId: ag.CatalogId,
|
||||
setInfos: make(map[string]*setInfo),
|
||||
}
|
||||
}
|
||||
ci.plg, ok = r.plugins[ag.PluginId]
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.Internal, op, fmt.Sprintf("expected plugin %q not available", ag.PluginId))
|
||||
}
|
||||
|
||||
s, err := ag.toHostSet(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
si, ok := ci.setInfos[s.GetPublicId()]
|
||||
if !ok {
|
||||
si = &setInfo{}
|
||||
}
|
||||
si.preferredEndpoint = endpoint.WithPreferenceOrder(s.GetPreferredEndpoints())
|
||||
si.plgSet, err = toPluginSet(ctx, s)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("converting set %q to plugin set", s.GetPublicId())))
|
||||
}
|
||||
|
||||
ci.setInfos[s.GetPublicId()] = si
|
||||
catalogInfos[ag.CatalogId] = ci
|
||||
}
|
||||
|
||||
catIds := make([]string, 0, len(catalogInfos))
|
||||
for k := range catalogInfos {
|
||||
catIds = append(catIds, k)
|
||||
}
|
||||
var cats []*HostCatalog
|
||||
if err := r.reader.SearchWhere(ctx, &cats, "public_id in (?)", []interface{}{catIds}); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("can't retrieve catalogs %v", catIds)))
|
||||
}
|
||||
if len(cats) == 0 {
|
||||
return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "no catalogs returned for retrieved sets")
|
||||
}
|
||||
for _, c := range cats {
|
||||
ci, ok := catalogInfos[c.GetPublicId()]
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "catalog returned when no set requested it")
|
||||
}
|
||||
plgCat, err := toPluginCatalog(ctx, c)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("storage to plugin catalog conversion"))
|
||||
}
|
||||
ci.plgCat = plgCat
|
||||
|
||||
// TODO: Do these looksups from the DB in bulk instead of individually.
|
||||
per, err := r.getPersistedDataForCatalog(ctx, c)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("persisted catalog lookup failed"))
|
||||
}
|
||||
ci.persisted = per
|
||||
catalogInfos[c.GetPublicId()] = ci
|
||||
}
|
||||
|
||||
// For writing the hosts to the db so data warehouse doesn't complain
|
||||
var hosts []interface{}
|
||||
hostIds := map[string]bool{}
|
||||
var es []*host.Endpoint
|
||||
for _, ci := range catalogInfos {
|
||||
|
||||
var sets []*pb.HostSet
|
||||
for _, si := range ci.setInfos {
|
||||
sets = append(sets, si.plgSet)
|
||||
}
|
||||
|
||||
resp, err := ci.plg.ListHosts(ctx, &plgpb.ListHostsRequest{
|
||||
Catalog: ci.plgCat,
|
||||
Sets: sets,
|
||||
//Persisted: ci.persisted,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
for _, h := range resp.GetHosts() {
|
||||
hostId, err := newHostId(ctx, ci.publicId, h.GetExternalId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
for _, sId := range h.GetSetIds() {
|
||||
var opts []endpoint.Option
|
||||
if len(h.GetIpAddresses()) > 0 {
|
||||
opts = append(opts, endpoint.WithIpAddrs(h.GetIpAddresses()))
|
||||
}
|
||||
if len(h.GetDnsNames()) > 0 {
|
||||
opts = append(opts, endpoint.WithIpAddrs(h.GetDnsNames()))
|
||||
}
|
||||
|
||||
si, ok := ci.setInfos[sId]
|
||||
if !ok {
|
||||
return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, "host is reporting it's part of a set we didn't query for")
|
||||
}
|
||||
pref, err := endpoint.NewPreferencer(ctx, si.preferredEndpoint)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("getting preferencer for set %q", sId)))
|
||||
}
|
||||
addr, err := pref.Choose(ctx, opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
es = append(es, &host.Endpoint{
|
||||
HostId: hostId,
|
||||
SetId: sId,
|
||||
Address: addr,
|
||||
})
|
||||
|
||||
if _, ok := hostIds[hostId]; !ok {
|
||||
hostIds[hostId] = true
|
||||
host := newHost(ctx, ci.publicId, addr)
|
||||
host.PublicId = hostId
|
||||
hosts = append(hosts, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(hosts) > 0 {
|
||||
_, err := r.writer.DoTx(
|
||||
ctx,
|
||||
db.StdRetryCnt,
|
||||
db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
if _, err := r.writer.DeleteItems(ctx, hosts); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("couldn't delete existing hosts"))
|
||||
}
|
||||
if err := r.writer.CreateItems(ctx, hosts); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("can't persist hosts"))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return es, nil
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -365,6 +366,139 @@ func TestRepository_LookupSet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_Endpoints(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, prj := iam.TestScopes(t, iamRepo)
|
||||
plg := hostplg.TestPlugin(t, conn, "endpoints")
|
||||
|
||||
hostlessCatalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
|
||||
plgm := map[string]plgpb.HostPluginServiceServer{
|
||||
plg.GetPublicId(): &TestPluginServer{
|
||||
ListHostsFn: func(_ context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) {
|
||||
if req.Catalog.GetId() == hostlessCatalog.GetPublicId() {
|
||||
return &plgpb.ListHostsResponse{}, nil
|
||||
}
|
||||
var setIds []string
|
||||
for _, set := range req.GetSets() {
|
||||
setIds = append(setIds, set.GetId())
|
||||
}
|
||||
return &plgpb.ListHostsResponse{Hosts: []*plgpb.ListHostsResponseHost{
|
||||
{
|
||||
SetIds: setIds,
|
||||
ExternalId: "test",
|
||||
IpAddresses: []string{"10.0.0.5", "192.168.0.5"},
|
||||
DnsNames: nil,
|
||||
},
|
||||
}}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
|
||||
hostSet10 := TestSet(t, conn, kms, catalog, plgm, WithPreferredEndpoints([]string{"cidr:10.0.0.1/24"}))
|
||||
hostSet192 := TestSet(t, conn, kms, catalog, plgm, WithPreferredEndpoints([]string{"cidr:192.168.0.1/24"}))
|
||||
hostSet100 := TestSet(t, conn, kms, catalog, plgm, WithPreferredEndpoints([]string{"cidr:100.100.100.100/24"}))
|
||||
hostlessSet := TestSet(t, conn, kms, hostlessCatalog, plgm)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
setIds []string
|
||||
want []*host.Endpoint
|
||||
wantIsErr errors.Code
|
||||
}{
|
||||
{
|
||||
name: "with-no-set-id",
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
},
|
||||
{
|
||||
name: "with-set10",
|
||||
setIds: []string{hostSet10.GetPublicId()},
|
||||
want: []*host.Endpoint{
|
||||
{
|
||||
HostId: func() string {
|
||||
s, err := newHostId(ctx, catalog.GetPublicId(), "test")
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
SetId: hostSet10.GetPublicId(),
|
||||
Address: "10.0.0.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with-different-set",
|
||||
setIds: []string{hostSet192.GetPublicId()},
|
||||
want: []*host.Endpoint{
|
||||
{
|
||||
HostId: func() string {
|
||||
s, err := newHostId(ctx, catalog.GetPublicId(), "test")
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
SetId: hostSet192.GetPublicId(),
|
||||
Address: "192.168.0.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with-all-addresses-filtered-set",
|
||||
setIds: []string{hostSet100.GetPublicId()},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "with-no-hosts-from-plugin",
|
||||
setIds: []string{hostlessSet.GetPublicId()},
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
repo, err := NewRepository(rw, rw, kms, plgm)
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.Endpoints(ctx, tt.setIds)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
if tt.want == nil {
|
||||
return
|
||||
}
|
||||
|
||||
sort.Slice(tt.want, func(i, j int) bool {
|
||||
return tt.want[i].HostId < tt.want[j].HostId
|
||||
})
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
return got[i].HostId < got[j].HostId
|
||||
})
|
||||
assert.Empty(cmp.Diff(got, tt.want, protocmp.Transform()))
|
||||
|
||||
// TODO: Remove this once we no longer persist all host lookup calls
|
||||
// when retrieving the endpoints.
|
||||
for _, ep := range got {
|
||||
h := allocHost()
|
||||
h.PublicId = ep.HostId
|
||||
require.NoError(rw.LookupByPublicId(ctx, h))
|
||||
|
||||
assert.Equal(uint32(1), h.Version)
|
||||
assert.Equal(ep.HostId, h.PublicId)
|
||||
assert.Equal(ep.Address, h.Address)
|
||||
assert.Equal(catalog.GetPublicId(), h.GetCatalogId())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_ListSets(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
|
||||
@@ -155,6 +155,127 @@ func (x *HostCatalog) GetAttributes() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Host struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
|
||||
// The create_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// The update_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// name is optional. If set, it must be unique within
|
||||
// catalog_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
|
||||
// catalog_id is the public_id of the owning
|
||||
// plugin_host_catalog and must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
CatalogId string `protobuf:"bytes,6,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"not_null"`
|
||||
// version allows optimistic locking of the resource
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
|
||||
// address is the IP Address or DNS name of the host.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Address string `protobuf:"bytes,8,opt,name=address,proto3" json:"address,omitempty" gorm:"default:null"`
|
||||
}
|
||||
|
||||
func (x *Host) Reset() {
|
||||
*x = Host{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Host) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Host) ProtoMessage() {}
|
||||
|
||||
func (x *Host) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_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 Host.ProtoReflect.Descriptor instead.
|
||||
func (*Host) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Host) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Host) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Host) GetUpdateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Host) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Host) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Host) GetCatalogId() string {
|
||||
if x != nil {
|
||||
return x.CatalogId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Host) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Host) GetAddress() string {
|
||||
if x != nil {
|
||||
return x.Address
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type HostSet struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -194,7 +315,7 @@ type HostSet struct {
|
||||
func (x *HostSet) Reset() {
|
||||
*x = HostSet{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1]
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -207,7 +328,7 @@ func (x *HostSet) String() string {
|
||||
func (*HostSet) ProtoMessage() {}
|
||||
|
||||
func (x *HostSet) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1]
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -220,7 +341,7 @@ func (x *HostSet) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use HostSet.ProtoReflect.Descriptor instead.
|
||||
func (*HostSet) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{1}
|
||||
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *HostSet) GetPublicId() string {
|
||||
@@ -316,7 +437,7 @@ type HostCatalogSecret struct {
|
||||
func (x *HostCatalogSecret) Reset() {
|
||||
*x = HostCatalogSecret{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2]
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -329,7 +450,7 @@ func (x *HostCatalogSecret) String() string {
|
||||
func (*HostCatalogSecret) ProtoMessage() {}
|
||||
|
||||
func (x *HostCatalogSecret) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2]
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -342,7 +463,7 @@ func (x *HostCatalogSecret) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use HostCatalogSecret.ProtoReflect.Descriptor instead.
|
||||
func (*HostCatalogSecret) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{2}
|
||||
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *HostCatalogSecret) GetCatalogId() string {
|
||||
@@ -429,57 +550,77 @@ var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{
|
||||
0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74,
|
||||
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61,
|
||||
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xb2, 0x03, 0x0a, 0x07, 0x48, 0x6f,
|
||||
0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x18, 0x02, 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, 0x03,
|
||||
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xc6, 0x02, 0x0a, 0x04, 0x48, 0x6f,
|
||||
0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12,
|
||||
0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02,
|
||||
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, 0x24, 0x0a, 0x04,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 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, 0x05, 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, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f,
|
||||
0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a,
|
||||
0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a,
|
||||
0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f,
|
||||
0x69, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66,
|
||||
0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98,
|
||||
0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65,
|
||||
0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f,
|
||||
0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x18, 0x02, 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,
|
||||
0x03, 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, 0x16, 0x0a,
|
||||
0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73,
|
||||
0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72,
|
||||
0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72,
|
||||
0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 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, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
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, 0x03, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75,
|
||||
0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a,
|
||||
0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
||||
0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18,
|
||||
0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x22, 0xb2, 0x03, 0x0a, 0x07, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b,
|
||||
0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63,
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 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, 0x03, 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, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 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, 0x05, 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, 0x1d, 0x0a,
|
||||
0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
|
||||
0x75, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72,
|
||||
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72,
|
||||
0x72, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20,
|
||||
0x03, 0x28, 0x09, 0x52, 0x12, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x45, 0x6e,
|
||||
0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x98, 0x02, 0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74,
|
||||
0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a,
|
||||
0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b,
|
||||
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 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, 0x03, 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, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1b,
|
||||
0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b,
|
||||
0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79,
|
||||
0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 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, 0x68, 0x6f, 0x73,
|
||||
0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -494,25 +635,28 @@ func file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP() []byt
|
||||
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_host_plugin_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_controller_storage_host_plugin_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_controller_storage_host_plugin_store_v1_host_proto_goTypes = []interface{}{
|
||||
(*HostCatalog)(nil), // 0: controller.storage.host.plugin.store.v1.HostCatalog
|
||||
(*HostSet)(nil), // 1: controller.storage.host.plugin.store.v1.HostSet
|
||||
(*HostCatalogSecret)(nil), // 2: controller.storage.host.plugin.store.v1.HostCatalogSecret
|
||||
(*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp
|
||||
(*Host)(nil), // 1: controller.storage.host.plugin.store.v1.Host
|
||||
(*HostSet)(nil), // 2: controller.storage.host.plugin.store.v1.HostSet
|
||||
(*HostCatalogSecret)(nil), // 3: controller.storage.host.plugin.store.v1.HostCatalogSecret
|
||||
(*timestamp.Timestamp)(nil), // 4: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_host_plugin_store_v1_host_proto_depIdxs = []int32{
|
||||
3, // 0: controller.storage.host.plugin.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
3, // 1: controller.storage.host.plugin.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
3, // 2: controller.storage.host.plugin.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
3, // 3: controller.storage.host.plugin.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
3, // 4: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
3, // 5: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // [6:6] is the sub-list for method output_type
|
||||
6, // [6:6] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
4, // 0: controller.storage.host.plugin.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
4, // 1: controller.storage.host.plugin.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
4, // 2: controller.storage.host.plugin.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
4, // 3: controller.storage.host.plugin.store.v1.Host.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
4, // 4: controller.storage.host.plugin.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
4, // 5: controller.storage.host.plugin.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
4, // 6: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
4, // 7: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
8, // [8:8] is the sub-list for method output_type
|
||||
8, // [8:8] is the sub-list for method input_type
|
||||
8, // [8:8] is the sub-list for extension type_name
|
||||
8, // [8:8] is the sub-list for extension extendee
|
||||
0, // [0:8] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_host_plugin_store_v1_host_proto_init() }
|
||||
@@ -534,7 +678,7 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() {
|
||||
}
|
||||
}
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HostSet); i {
|
||||
switch v := v.(*Host); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -546,6 +690,18 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() {
|
||||
}
|
||||
}
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HostSet); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HostCatalogSecret); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -564,7 +720,7 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_storage_host_plugin_store_v1_host_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
dbcommon "github.com/hashicorp/boundary/internal/db/common"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/host"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
)
|
||||
@@ -182,6 +183,26 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet,
|
||||
return returnedHostSet, hosts, rowsUpdated, nil
|
||||
}
|
||||
|
||||
// Endpoints returns a slice of host.Endpoint for the provided set id.
|
||||
// If there are no hosts in the provided set id the slice is empty.
|
||||
// If the set does not exist an error is returned.
|
||||
func (r *Repository) Endpoints(ctx context.Context, setId string) ([]*host.Endpoint, error) {
|
||||
const op = "static.(Repository).Endpoints"
|
||||
_, hs, err := r.lookupSet(ctx, setId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
var es []*host.Endpoint
|
||||
for _, h := range hs {
|
||||
es = append(es, &host.Endpoint{
|
||||
HostId: h.GetPublicId(),
|
||||
SetId: setId,
|
||||
Address: h.GetAddress(),
|
||||
})
|
||||
}
|
||||
return es, err
|
||||
}
|
||||
|
||||
// LookupSet will look up a host set in the repository and return the host
|
||||
// set and the hosts assigned to the host set. If the host set is not
|
||||
// found, it will return nil, nil, nil. The WithLimit option can be used to
|
||||
@@ -191,6 +212,11 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...Opti
|
||||
if publicId == "" {
|
||||
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
||||
}
|
||||
return r.lookupSet(ctx, publicId, opt...)
|
||||
}
|
||||
|
||||
func (r *Repository) lookupSet(ctx context.Context, publicId string, opt ...Option) (*HostSet, []*Host, error) {
|
||||
const op = "static.(Repository).lookupSet"
|
||||
opts := getOpts(opt...)
|
||||
limit := r.defaultLimit
|
||||
if opts.withLimit != 0 {
|
||||
@@ -201,29 +227,20 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...Opti
|
||||
s := allocHostSet()
|
||||
s.PublicId = publicId
|
||||
|
||||
if err := r.reader.LookupByPublicId(ctx, s); err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
return nil, nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
var hosts []*Host
|
||||
_, err := r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(reader db.Reader, _ db.Writer) error {
|
||||
if err := reader.LookupByPublicId(ctx, s); err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
s = nil
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
hosts, err = getHosts(ctx, reader, s.PublicId, limit)
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
var err error
|
||||
if hosts, err = getHosts(ctx, r.reader, s.PublicId, limit); err != nil {
|
||||
return nil, nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in %s", s.PublicId)))
|
||||
}
|
||||
|
||||
return s, hosts, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/boundary"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
)
|
||||
|
||||
@@ -25,22 +24,3 @@ func SubtypeFromId(id string) subtypes.Subtype {
|
||||
func Register(subtype subtypes.Subtype, prefixes ...string) error {
|
||||
return registry.Register(subtype, prefixes...)
|
||||
}
|
||||
|
||||
// Catalog contains the common methods across all the different types of host catalogs.
|
||||
type Catalog interface {
|
||||
boundary.Resource
|
||||
GetScopeId() string
|
||||
}
|
||||
|
||||
// Set contains the common methods across all the different types of host sets.
|
||||
type Set interface {
|
||||
boundary.Resource
|
||||
GetCatalogId() string
|
||||
}
|
||||
|
||||
// Host contains the common methods across all the different types of hosts.
|
||||
type Host interface {
|
||||
boundary.Resource
|
||||
GetCatalogId() string
|
||||
GetAddress() string
|
||||
}
|
||||
@@ -47,6 +47,42 @@ message HostCatalog {
|
||||
bytes attributes = 9;
|
||||
}
|
||||
|
||||
message Host {
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
string public_id = 1;
|
||||
|
||||
// The create_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
timestamp.v1.Timestamp create_time = 2;
|
||||
|
||||
// The update_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
timestamp.v1.Timestamp update_time = 3;
|
||||
|
||||
// name is optional. If set, it must be unique within
|
||||
// catalog_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string name = 4;
|
||||
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string description = 5;
|
||||
|
||||
// catalog_id is the public_id of the owning
|
||||
// plugin_host_catalog and must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string catalog_id = 6;
|
||||
|
||||
// version allows optimistic locking of the resource
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
uint32 version = 7;
|
||||
|
||||
// address is the IP Address or DNS name of the host.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string address = 8;
|
||||
}
|
||||
|
||||
message HostSet {
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
|
||||
@@ -142,11 +142,13 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er
|
||||
return nil, fmt.Errorf("failed to register user service handler: %w", err)
|
||||
}
|
||||
ts, err := targets.NewService(
|
||||
ctx,
|
||||
c.kms,
|
||||
c.TargetRepoFn,
|
||||
c.IamRepoFn,
|
||||
c.ServersRepoFn,
|
||||
c.SessionRepoFn,
|
||||
c.PluginHostRepoFn,
|
||||
c.StaticHostRepoFn,
|
||||
c.VaultCredentialRepoFn)
|
||||
if err != nil {
|
||||
|
||||
@@ -91,6 +91,7 @@ type Service struct {
|
||||
iamRepoFn common.IamRepoFactory
|
||||
serversRepoFn common.ServersRepoFactory
|
||||
sessionRepoFn common.SessionRepoFactory
|
||||
pluginHostRepoFn common.PluginHostRepoFactory
|
||||
staticHostRepoFn common.StaticRepoFactory
|
||||
vaultCredRepoFn common.VaultCredentialRepoFactory
|
||||
kmsCache *kms.Kms
|
||||
@@ -98,37 +99,43 @@ type Service struct {
|
||||
|
||||
// NewService returns a target service which handles target related requests to boundary.
|
||||
func NewService(
|
||||
ctx context.Context,
|
||||
kmsCache *kms.Kms,
|
||||
repoFn common.TargetRepoFactory,
|
||||
iamRepoFn common.IamRepoFactory,
|
||||
serversRepoFn common.ServersRepoFactory,
|
||||
sessionRepoFn common.SessionRepoFactory,
|
||||
pluginHostRepoFn common.PluginHostRepoFactory,
|
||||
staticHostRepoFn common.StaticRepoFactory,
|
||||
vaultCredRepoFn common.VaultCredentialRepoFactory) (Service, error) {
|
||||
const op = "targets.NewService"
|
||||
if repoFn == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing target repository")
|
||||
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing target repository")
|
||||
}
|
||||
if iamRepoFn == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing iam repository")
|
||||
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing iam repository")
|
||||
}
|
||||
if serversRepoFn == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing servers repository")
|
||||
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing servers repository")
|
||||
}
|
||||
if sessionRepoFn == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing session repository")
|
||||
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing session repository")
|
||||
}
|
||||
if pluginHostRepoFn == nil {
|
||||
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing plugin host repository")
|
||||
}
|
||||
if staticHostRepoFn == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static host repository")
|
||||
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static host repository")
|
||||
}
|
||||
if vaultCredRepoFn == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing vault credential repository")
|
||||
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing vault credential repository")
|
||||
}
|
||||
return Service{
|
||||
repoFn: repoFn,
|
||||
iamRepoFn: iamRepoFn,
|
||||
serversRepoFn: serversRepoFn,
|
||||
sessionRepoFn: sessionRepoFn,
|
||||
pluginHostRepoFn: pluginHostRepoFn,
|
||||
staticHostRepoFn: staticHostRepoFn,
|
||||
vaultCredRepoFn: vaultCredRepoFn,
|
||||
kmsCache: kmsCache,
|
||||
@@ -940,55 +947,65 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
|
||||
"No workers are available to handle this session, or all have been filtered.")
|
||||
}
|
||||
|
||||
// First, fetch all available hosts. Unless one was chosen in the request,
|
||||
// we will pick one at random.
|
||||
type compoundHost struct {
|
||||
hostSetId string
|
||||
hostId string
|
||||
}
|
||||
|
||||
var chosenId *compoundHost
|
||||
requestedId := req.GetHostId()
|
||||
staticHostRepo, err := s.staticHostRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pluginHostRepo, err := s.pluginHostRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostIds := make([]compoundHost, 0, len(hostSources)*10)
|
||||
|
||||
HostSourceIterationLoop:
|
||||
var pluginHostSetIds []string
|
||||
var endpoints []*host.Endpoint
|
||||
for _, hSource := range hostSources {
|
||||
hsId := hSource.Id()
|
||||
// FIXME: read in type from DB rather than rely on prefix
|
||||
switch host.SubtypeFromId(hsId) {
|
||||
case static.Subtype:
|
||||
_, hosts, err := staticHostRepo.LookupSet(ctx, hsId)
|
||||
eps, err := staticHostRepo.Endpoints(ctx, hsId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, host := range hosts {
|
||||
compoundId := compoundHost{hostSetId: hsId, hostId: host.PublicId}
|
||||
hostIds = append(hostIds, compoundId)
|
||||
if host.PublicId == requestedId {
|
||||
chosenId = &compoundId
|
||||
break HostSourceIterationLoop
|
||||
}
|
||||
endpoints = append(endpoints, eps...)
|
||||
default:
|
||||
// Batch the plugin host set ids since each round trip to the plugin
|
||||
// has the potential to be expensive.
|
||||
pluginHostSetIds = append(pluginHostSetIds, hsId)
|
||||
}
|
||||
}
|
||||
if len(pluginHostSetIds) > 0 {
|
||||
eps, err := pluginHostRepo.Endpoints(ctx, pluginHostSetIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endpoints = append(endpoints, eps...)
|
||||
}
|
||||
|
||||
var chosenEndpoint *host.Endpoint
|
||||
if requestedId != "" {
|
||||
for _, ep := range endpoints {
|
||||
if ep.HostId == requestedId {
|
||||
chosenEndpoint = ep
|
||||
}
|
||||
}
|
||||
}
|
||||
if requestedId != "" && chosenId == nil {
|
||||
// We didn't find it
|
||||
return nil, handlers.InvalidArgumentErrorf(
|
||||
"Errors in provided fields.",
|
||||
map[string]string{
|
||||
"host_id": "The requested host id is not available.",
|
||||
})
|
||||
}
|
||||
if chosenId == nil {
|
||||
if len(hostIds) == 0 {
|
||||
// No hosts were found, error
|
||||
return nil, handlers.NotFoundErrorf("No hosts found from available target host sources.")
|
||||
if chosenEndpoint == nil {
|
||||
// We didn't find it
|
||||
return nil, handlers.InvalidArgumentErrorf(
|
||||
"Errors in provided fields.",
|
||||
map[string]string{
|
||||
"host_id": "The requested host id is not available.",
|
||||
})
|
||||
}
|
||||
chosenId = &hostIds[rand.Intn(len(hostIds))]
|
||||
}
|
||||
|
||||
if chosenEndpoint == nil {
|
||||
if len(endpoints) == 0 {
|
||||
// No hosts were found, error
|
||||
return nil, handlers.NotFoundErrorf("No endpoint found from available target host sources.")
|
||||
}
|
||||
chosenEndpoint = endpoints[rand.Intn(len(endpoints))]
|
||||
}
|
||||
|
||||
// Generate the endpoint URL
|
||||
@@ -996,22 +1013,10 @@ HostSourceIterationLoop:
|
||||
Scheme: t.GetType(),
|
||||
}
|
||||
defaultPort := t.GetDefaultPort()
|
||||
var endpointHost string
|
||||
switch host.SubtypeFromId(chosenId.hostId) {
|
||||
case static.Subtype:
|
||||
h, err := staticHostRepo.LookupHost(ctx, chosenId.hostId)
|
||||
if err != nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "errors looking up host")
|
||||
}
|
||||
endpointHost = h.Address
|
||||
if endpointHost == "" {
|
||||
return nil, stderrors.New("host had empty address")
|
||||
}
|
||||
}
|
||||
if defaultPort != 0 {
|
||||
endpointUrl.Host = fmt.Sprintf("%s:%d", endpointHost, defaultPort)
|
||||
endpointUrl.Host = fmt.Sprintf("%s:%d", chosenEndpoint.Address, defaultPort)
|
||||
} else {
|
||||
endpointUrl.Host = endpointHost
|
||||
endpointUrl.Host = chosenEndpoint.Address
|
||||
}
|
||||
|
||||
var reqs []credential.Request
|
||||
@@ -1028,9 +1033,9 @@ HostSourceIterationLoop:
|
||||
expTime.Seconds += int64(t.GetSessionMaxSeconds())
|
||||
sessionComposition := session.ComposedOf{
|
||||
UserId: authResults.UserId,
|
||||
HostId: chosenId.hostId,
|
||||
HostId: chosenEndpoint.HostId,
|
||||
TargetId: t.GetPublicId(),
|
||||
HostSetId: chosenId.hostSetId,
|
||||
HostSetId: chosenEndpoint.SetId,
|
||||
AuthTokenId: authResults.AuthTokenId,
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Endpoint: endpointUrl.String(),
|
||||
@@ -1122,7 +1127,7 @@ HostSourceIterationLoop:
|
||||
Type: t.GetType(),
|
||||
Certificate: sess.Certificate,
|
||||
PrivateKey: privKey,
|
||||
HostId: chosenId.hostId,
|
||||
HostId: chosenEndpoint.HostId,
|
||||
Endpoint: endpointUrl.String(),
|
||||
WorkerInfo: workers,
|
||||
ConnectionLimit: t.GetSessionConnectionLimit(),
|
||||
@@ -1141,8 +1146,8 @@ HostSourceIterationLoop:
|
||||
Type: t.GetType(),
|
||||
AuthorizationToken: string(encodedMarshaledSad),
|
||||
UserId: authResults.UserId,
|
||||
HostId: chosenId.hostId,
|
||||
HostSetId: chosenId.hostSetId,
|
||||
HostId: chosenEndpoint.HostId,
|
||||
HostSetId: chosenEndpoint.SetId,
|
||||
Endpoint: endpointUrl.String(),
|
||||
Credentials: creds,
|
||||
}
|
||||
|
||||
@@ -88,10 +88,13 @@ func testService(t *testing.T, conn *gorm.DB, kms *kms.Kms, wrapper wrapping.Wra
|
||||
staticHostRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
pluginHostRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
|
||||
}
|
||||
credentialRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
return targets.NewService(kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, staticHostRepoFn, credentialRepoFn)
|
||||
return targets.NewService(context.Background(), kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
@@ -2586,6 +2589,33 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
plg := host.TestPlugin(t, conn, "test")
|
||||
plgm := map[string]plgpb.HostPluginServiceServer{
|
||||
plg.GetPublicId(): plugin.TestPluginServer{
|
||||
ListHostsFn: func(_ context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) {
|
||||
var setIds []string
|
||||
for _, set := range req.GetSets() {
|
||||
setIds = append(setIds, set.GetId())
|
||||
}
|
||||
return &plgpb.ListHostsResponse{Hosts: []*plgpb.ListHostsResponseHost{
|
||||
{
|
||||
SetIds: setIds,
|
||||
ExternalId: "test",
|
||||
IpAddresses: []string{"10.0.0.1", "192.168.0.1"},
|
||||
},
|
||||
{
|
||||
SetIds: setIds,
|
||||
ExternalId: "test2",
|
||||
IpAddresses: []string{"10.1.1.1", "192.168.1.1"},
|
||||
},
|
||||
}}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
pluginHostRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, plgm)
|
||||
}
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
ctx := auth.NewVerifierContext(requests.NewRequestContext(context.Background()),
|
||||
@@ -2603,21 +2633,16 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := targets.NewService(kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, staticHostRepoFn, credentialRepoFn)
|
||||
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test")
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
||||
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
_ = static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
|
||||
shs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
_ = static.TestSetMembers(t, conn, shs.GetPublicId(), []*static.Host{h})
|
||||
|
||||
apiTar, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{
|
||||
Id: tar.GetPublicId(),
|
||||
Version: tar.GetVersion(),
|
||||
HostSetIds: []string{hs.GetPublicId()},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
phc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
||||
phs := plugin.TestSet(t, conn, kms, phc, plgm, plugin.WithPreferredEndpoints([]string{"cidr:10.0.0.0/24"}))
|
||||
|
||||
v := vault.NewTestVaultServer(t)
|
||||
v.MountPKI(t)
|
||||
@@ -2638,95 +2663,135 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = s.AddTargetCredentialSources(ctx,
|
||||
&pbs.AddTargetCredentialSourcesRequest{
|
||||
Id: tar.GetPublicId(),
|
||||
ApplicationCredentialSourceIds: []string{clsResp.GetItem().GetId()},
|
||||
Version: apiTar.GetItem().GetVersion(),
|
||||
cases := []struct {
|
||||
name string
|
||||
hostSourceId string
|
||||
credSourceId string
|
||||
wantedHostId string
|
||||
wantedEndpoint string
|
||||
}{
|
||||
{
|
||||
name: "static host",
|
||||
hostSourceId: shs.GetPublicId(),
|
||||
credSourceId: clsResp.GetItem().GetId(),
|
||||
wantedHostId: h.GetPublicId(),
|
||||
wantedEndpoint: h.GetAddress(),
|
||||
},
|
||||
{
|
||||
name: "plugin host",
|
||||
hostSourceId: phs.GetPublicId(),
|
||||
credSourceId: clsResp.GetItem().GetId(),
|
||||
wantedHostId: "?",
|
||||
wantedEndpoint: "10.0.0.1",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), tc.name)
|
||||
apiTar, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{
|
||||
Id: tar.GetPublicId(),
|
||||
Version: tar.GetVersion(),
|
||||
HostSetIds: []string{tc.hostSourceId},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = s.AddTargetCredentialSources(ctx,
|
||||
&pbs.AddTargetCredentialSourcesRequest{
|
||||
Id: tar.GetPublicId(),
|
||||
ApplicationCredentialSourceIds: []string{clsResp.GetItem().GetId()},
|
||||
Version: apiTar.GetItem().GetVersion(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Tell our DB that there is a worker ready to serve the data
|
||||
workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, &sync.Map{}, kms)
|
||||
_, err = workerService.Status(ctx, &spbs.StatusRequest{
|
||||
Worker: &spb.Server{
|
||||
PrivateId: "testworker",
|
||||
Address: "localhost:8457",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
asRes1, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
|
||||
Id: tar.GetPublicId(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
asRes2, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
|
||||
Id: tar.GetPublicId(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, cmp.Diff(asRes1.GetItem().GetCredentials(), asRes2.GetItem().GetCredentials(), protocmp.Transform()),
|
||||
"the credentials aren't unique per request authorized session")
|
||||
|
||||
wantedHostId := tc.wantedHostId
|
||||
if tc.wantedHostId == "?" {
|
||||
wantedHostId = asRes2.GetItem().GetHostId()
|
||||
}
|
||||
|
||||
want := &pb.SessionAuthorization{
|
||||
Scope: &scopes.ScopeInfo{
|
||||
Id: proj.GetPublicId(),
|
||||
Type: proj.GetType(),
|
||||
Name: proj.GetName(),
|
||||
Description: proj.GetDescription(),
|
||||
ParentScopeId: proj.GetParentId(),
|
||||
},
|
||||
TargetId: tar.GetPublicId(),
|
||||
UserId: at.GetIamUserId(),
|
||||
HostSetId: tc.hostSourceId,
|
||||
HostId: wantedHostId,
|
||||
Type: "tcp",
|
||||
Endpoint: fmt.Sprintf("tcp://%s", tc.wantedEndpoint),
|
||||
Credentials: []*pb.SessionCredential{{
|
||||
CredentialLibrary: &pb.CredentialLibrary{
|
||||
Id: clsResp.GetItem().GetId(),
|
||||
Name: clsResp.GetItem().GetName().GetValue(),
|
||||
Description: clsResp.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: clsResp.GetItem().GetId(),
|
||||
Name: clsResp.GetItem().GetName().GetValue(),
|
||||
Description: clsResp.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
}},
|
||||
// TODO: validate the contents of the authorization token is what is expected
|
||||
}
|
||||
wantSecret := map[string]interface{}{
|
||||
"certificate": "-----BEGIN CERTIFICATE-----\n",
|
||||
"issuing_ca": "-----BEGIN CERTIFICATE-----\n",
|
||||
"private_key": "-----BEGIN RSA PRIVATE KEY-----\n",
|
||||
"private_key_type": "rsa",
|
||||
}
|
||||
got := asRes1.GetItem()
|
||||
|
||||
require.Len(t, got.GetCredentials(), 1)
|
||||
|
||||
gotCred := got.Credentials[0]
|
||||
require.NotNil(t, gotCred.Secret)
|
||||
assert.NotEmpty(t, gotCred.Secret.Raw)
|
||||
dSec := decodeJsonSecret(t, gotCred.Secret.Raw)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dSec, gotCred.Secret.Decoded.AsMap())
|
||||
for k, v := range wantSecret {
|
||||
gotV, ok := dSec[k]
|
||||
require.True(t, ok)
|
||||
assert.Truef(t, strings.HasPrefix(gotV.(string), v.(string)), "%q:%q doesn't have prefix %q", k, gotV, v)
|
||||
}
|
||||
gotCred.Secret = nil
|
||||
|
||||
got.AuthorizationToken, got.SessionId, got.CreatedTime = "", "", nil
|
||||
assert.Empty(t, cmp.Diff(got, want, protocmp.Transform()))
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Tell our DB that there is a worker ready to serve the data
|
||||
workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, &sync.Map{}, kms)
|
||||
_, err = workerService.Status(ctx, &spbs.StatusRequest{
|
||||
Worker: &spb.Server{
|
||||
PrivateId: "testworker",
|
||||
Address: "localhost:8457",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
asRes1, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
|
||||
Id: tar.GetPublicId(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
asRes2, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
|
||||
Id: tar.GetPublicId(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, cmp.Diff(asRes1.GetItem().GetCredentials(), asRes2.GetItem().GetCredentials(), protocmp.Transform()),
|
||||
"the credentials aren't unique per request authorized session")
|
||||
|
||||
want := &pb.SessionAuthorization{
|
||||
Scope: &scopes.ScopeInfo{
|
||||
Id: proj.GetPublicId(),
|
||||
Type: proj.GetType(),
|
||||
Name: proj.GetName(),
|
||||
Description: proj.GetDescription(),
|
||||
ParentScopeId: proj.GetParentId(),
|
||||
},
|
||||
TargetId: tar.GetPublicId(),
|
||||
UserId: at.GetIamUserId(),
|
||||
HostSetId: hs.GetPublicId(),
|
||||
HostId: h.GetPublicId(),
|
||||
Type: "tcp",
|
||||
Endpoint: fmt.Sprintf("tcp://%s", h.GetAddress()),
|
||||
Credentials: []*pb.SessionCredential{{
|
||||
CredentialLibrary: &pb.CredentialLibrary{
|
||||
Id: clsResp.GetItem().GetId(),
|
||||
Name: clsResp.GetItem().GetName().GetValue(),
|
||||
Description: clsResp.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
CredentialSource: &pb.CredentialSource{
|
||||
Id: clsResp.GetItem().GetId(),
|
||||
Name: clsResp.GetItem().GetName().GetValue(),
|
||||
Description: clsResp.GetItem().GetDescription().GetValue(),
|
||||
CredentialStoreId: store.GetPublicId(),
|
||||
Type: vault.Subtype.String(),
|
||||
},
|
||||
}},
|
||||
// TODO: validate the contents of the authorization token is what is expected
|
||||
}
|
||||
wantSecret := map[string]interface{}{
|
||||
"certificate": "-----BEGIN CERTIFICATE-----\n",
|
||||
"issuing_ca": "-----BEGIN CERTIFICATE-----\n",
|
||||
"private_key": "-----BEGIN RSA PRIVATE KEY-----\n",
|
||||
"private_key_type": "rsa",
|
||||
}
|
||||
got := asRes1.GetItem()
|
||||
|
||||
require.Len(t, got.GetCredentials(), 1)
|
||||
|
||||
gotCred := got.Credentials[0]
|
||||
require.NotNil(t, gotCred.Secret)
|
||||
assert.NotEmpty(t, gotCred.Secret.Raw)
|
||||
dSec := decodeJsonSecret(t, gotCred.Secret.Raw)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dSec, gotCred.Secret.Decoded.AsMap())
|
||||
for k, v := range wantSecret {
|
||||
gotV, ok := dSec[k]
|
||||
require.True(t, ok)
|
||||
assert.Truef(t, strings.HasPrefix(gotV.(string), v.(string)), "%q:%q doesn't have prefix %q", k, gotV, v)
|
||||
}
|
||||
gotCred.Secret = nil
|
||||
|
||||
got.AuthorizationToken, got.SessionId, got.CreatedTime = "", "", nil
|
||||
assert.Empty(t, cmp.Diff(got, want, protocmp.Transform()))
|
||||
}
|
||||
|
||||
func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
@@ -2749,6 +2814,9 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
staticHostRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
}
|
||||
pluginHostRepoFn := func() (*plugin.Repository, error) {
|
||||
return plugin.NewRepository(rw, rw, kms, map[string]plgpb.HostPluginServiceServer{})
|
||||
}
|
||||
credentialRepoFn := func() (*vault.Repository, error) {
|
||||
return vault.NewRepository(rw, rw, kms, sche)
|
||||
}
|
||||
@@ -2757,12 +2825,12 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
}
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
|
||||
s, err := targets.NewService(kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, staticHostRepoFn, credentialRepoFn)
|
||||
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Authorized user gets full permissions
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
ctx := auth.NewVerifierContext(requests.NewRequestContext(context.Background()),
|
||||
ctx = auth.NewVerifierContext(requests.NewRequestContext(context.Background()),
|
||||
iamRepoFn,
|
||||
atRepoFn,
|
||||
serversRepoFn,
|
||||
|
||||
Reference in new issue
Block a user