Add DB changes for host, set members, and host addresses for plugins. (#1584)

* Add DB changes for host, set members, and host addresses for plugins.

* Add priority, address review feedback, update tests, naming, protos

Co-authored-by: Jeff Mitchell <jeffrey.mitchell@gmail.com>
Co-authored-by: Michael Gaffney <mgaffney@users.noreply.github.com>
pull/1633/head
Todd 5 years ago committed by GitHub
parent e8313146f6
commit 23baaac36b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,18 @@
begin;
create domain wt_priority as int not null
constraint priority_must_be_greater_than_zero
check(value > 0);
comment on domain wt_priority is
'Represents a priority value which must not be null and must be greater than zero';
-- wt_dns_name defines a type for dns names
create domain wt_dns_name as text not null
constraint wt_dns_name_too_short
check (length(trim(value)) > 0)
constraint wt_dns_name_too_long
check (length(trim(value)) < 256);
comment on domain wt_dns_name is
'standard column for dns names';
commit;

@ -56,10 +56,10 @@ begin;
version wt_version,
attributes bytea not null,
constraint host_catalog_fkey
foreign key (scope_id, public_id)
references host_catalog (scope_id, public_id)
on delete cascade
on update cascade,
foreign key (scope_id, public_id)
references host_catalog (scope_id, public_id)
on delete cascade
on update cascade,
constraint host_plugin_catalog_scope_id_name_uq
unique(scope_id, name)
);
@ -128,10 +128,10 @@ begin;
constraint host_plugin_set_catalog_id_name_uq
unique(catalog_id, name),
constraint host_set_fkey
foreign key (catalog_id, public_id)
references host_set (catalog_id, public_id)
on delete cascade
on update cascade,
foreign key (catalog_id, public_id)
references host_set (catalog_id, public_id)
on delete cascade
on update cascade,
constraint host_plugin_set_catalog_id_public_id_uq
unique(catalog_id, public_id)
);
@ -154,10 +154,7 @@ 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
-- 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 (
@ -167,37 +164,28 @@ begin;
references host_plugin_catalog (public_id)
on delete cascade
on update cascade,
external_id text not null
constraint external_id_must_not_be_empty
check(length(trim(external_id)) > 0),
name wt_name,
description text,
create_time wt_timestamp,
-- update_time is the last time the data was synced with what is provided
-- from the plugin.
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_external_id_uq
unique(catalog_id, external_id),
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();
@ -205,7 +193,7 @@ begin;
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');
for each row execute procedure immutable_columns('public_id', 'catalog_id', 'external_id', 'create_time');
create trigger insert_host_subtype before insert on host_plugin_host
for each row execute procedure insert_host_subtype();
@ -213,6 +201,141 @@ begin;
create trigger delete_host_subtype after delete on host_plugin_host
for each row execute procedure delete_host_subtype();
-- host_ip_address contains the IP addresses associated with
-- a host, one per row.
create table host_ip_address (
host_id wt_public_id
constraint host_fkey
references host(public_id)
on delete cascade
on update cascade,
priority wt_priority,
address inet not null,
create_time wt_timestamp,
primary key (host_id, priority),
constraint host_ip_address_host_id_address_uq
unique(host_id, address)
);
comment on table host_ip_address is
'host_ip_address entries are ip addresses set on a host with a preserved order.';
create trigger default_create_time_column before insert on host_ip_address
for each row execute procedure default_create_time();
-- host_immutable_ip_address() ensures that ip addresses assigned to hosts are
-- immutable.
create function
host_immutable_ip_address()
returns trigger
as $$
begin
raise exception 'host ip addresses are immutable';
end;
$$ language plpgsql;
create trigger immutable_ip_address
before update on host_ip_address
for each row execute procedure host_immutable_ip_address();
-- host_dns_name contains the DNS names associated with a host, one per row.
create table host_dns_name (
host_id wt_public_id
constraint host_fkey
references host(public_id)
on delete cascade
on update cascade,
priority wt_priority,
name wt_dns_name,
create_time wt_timestamp,
primary key (host_id, priority),
constraint host_dns_name_host_id_name_uq
unique(host_id, name)
);
comment on table host_dns_name is
'host_dns_name entries are dns names set on a host with a preserved order.';
create trigger default_create_time_column before insert on host_dns_name
for each row execute procedure default_create_time();
-- host_immutable_dns_name() ensures that dns names assigned to hosts are
-- immutable.
create function
host_immutable_dns_name()
returns trigger
as $$
begin
raise exception 'host dns names are immutable';
end;
$$ language plpgsql;
create trigger immutable_dns_name
before update on host_dns_name
for each row execute procedure host_immutable_dns_name();
create table host_plugin_set_member (
host_id wt_public_id not null,
set_id wt_public_id not null,
catalog_id wt_public_id not null,
create_time wt_timestamp,
primary key(host_id, set_id),
constraint host_plugin_host_fkey
foreign key (catalog_id, host_id)
references host_plugin_host (catalog_id, public_id)
on delete cascade
on update cascade,
constraint host_plugin_set_fkey
foreign key (catalog_id, set_id)
references host_plugin_set (catalog_id, public_id)
on delete cascade
on update cascade
);
comment on table host_plugin_set_member is
'host_plugin_set_member entries are the membership relationships from plugin hosts in plugin sets.';
create trigger default_create_time_column before insert on host_plugin_set_member
for each row execute procedure default_create_time();
create trigger immutable_columns before update on host_plugin_set_member
for each row execute procedure immutable_columns('host_id', 'set_id', 'catalog_id', 'create_time');
create function insert_host_plugin_set_member()
returns trigger
as $$
begin
select host_plugin_set.catalog_id
into new.catalog_id
from host_plugin_set
where host_plugin_set.public_id = new.set_id;
return new;
end;
$$ language plpgsql;
comment on function insert_host_plugin_set_member is
'insert_host_plugin_set_member entries are the membership relationships from plugin hosts in plugin sets.';
create trigger insert_host_plugin_set_member before insert on host_plugin_set_member
for each row execute procedure insert_host_plugin_set_member();
-- delete_orphaned_host_plugin_host is an after delete trigger
-- function to delete plugin hosts when no more set members
-- reference it.
create function delete_orphaned_host_plugin_host()
returns trigger
as $$
begin
delete from host_plugin_host
where not exists (select host_id
from host_plugin_set_member
where host_id = old.host_id)
and
public_id = old.host_id;
return null;
end;
$$ language plpgsql;
comment on function delete_orphaned_host_plugin_host is
'delete_orphaned_host_plugin_host deletes a host when all set members with that host are deleted.';
create trigger delete_orphaned_host_plugin_host after delete on host_plugin_set_member
for each row execute procedure delete_orphaned_host_plugin_host();
insert into oplog_ticket (name, version)
values
@ -220,4 +343,32 @@ begin;
('host_plugin_catalog_secret', 1),
('host_plugin_set', 1);
-- host_plugin_host_with_value_obj is useful for reading a plugin host with
-- its associated value objects (ip addresses, dns names, set membership) as
-- columns with delimited values. The delimiter depends on the value objects
-- (e.g. if they need ordering).
create view host_plugin_host_with_value_obj as
select
h.public_id,
h.catalog_id,
h.external_id,
hc.plugin_id,
h.name,
h.description,
h.create_time,
h.update_time,
-- the string_agg(..) column will be null if there are no associated value objects
string_agg(distinct concat_ws('=', hip.priority, hip.address), '|') as ip_addresses,
string_agg(distinct concat_ws('=', hdns.priority, hdns.name), '|') as dns_names
from
host_plugin_host h
join host_plugin_catalog hc on h.catalog_id = hc.public_id
left outer join host_ip_address hip on h.public_id = hip.host_id
left outer join host_dns_name hdns on h.public_id = hdns.host_id
-- FIXME: add set membership once that's shaken out
group by h.public_id, hc.plugin_id;
comment on view host_plugin_host_with_value_obj is
'host plugin host with its associated value objects';
commit;

@ -7,9 +7,7 @@ create table host_set_preferred_endpoint (
references host_set(public_id)
on delete cascade
on update cascade,
priority int not null
constraint priority_must_be_greater_than_zero
check(priority > 0),
priority wt_priority,
condition text not null
constraint condition_must_not_be_too_short
check(length(trim(condition)) > 4) -- minimum is 'dns:*'

@ -20,4 +20,6 @@ type Host interface {
boundary.Resource
GetCatalogId() string
GetAddress() string
GetIpAddresses() []string
GetDnsNames() []string
}

@ -0,0 +1,76 @@
package host
import (
"context"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/host/store"
"google.golang.org/protobuf/proto"
)
// NOTE: Tests for this are in the plugin subtype directory in host_address_test.go
const defaultDnsNameTableName = "host_dns_name"
type DnsName struct {
*store.DnsName
tableName string `gorm:"-"`
}
func NewDnsName(ctx context.Context, hostId string, priority uint32, name string) (*DnsName, error) {
const op = "host.NewDnsName"
dn := &DnsName{
DnsName: &store.DnsName{
HostId: hostId,
Name: name,
Priority: priority,
},
}
if err := dn.validate(ctx, op); err != nil {
return nil, err
}
return dn, nil
}
// validate the host dns name. On success, it will return nil.
func (dn *DnsName) validate(ctx context.Context, caller errors.Op) error {
if dn.HostId == "" {
return errors.New(ctx, errors.InvalidParameter, caller, "missing host id")
}
if dn.Priority < 1 {
return errors.New(ctx, errors.InvalidParameter, caller, "invalid priority value")
}
if dn.Name == "" {
return errors.New(ctx, errors.InvalidParameter, caller, "missing dns name")
}
return nil
}
// allocDnsName make an empty one in memory.
func allocDnsName() DnsName {
return DnsName{
DnsName: &store.DnsName{},
}
}
// Clone an DnsName
func (c *DnsName) Clone() *DnsName {
cp := proto.Clone(c.DnsName)
return &DnsName{
DnsName: cp.(*store.DnsName),
}
}
// TableName returns the table name.
func (c *DnsName) TableName() string {
if c.tableName != "" {
return c.tableName
}
return defaultDnsNameTableName
}
// SetTableName sets the table name.
func (c *DnsName) SetTableName(n string) {
c.tableName = n
}

@ -0,0 +1,81 @@
package host
import (
"context"
"net"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/host/store"
"google.golang.org/protobuf/proto"
)
// NOTE: Tests for this are in the plugin subtype directory in host_address_test.go
const defaultIpAddressTableName = "host_ip_address"
type IpAddress struct {
*store.IpAddress
tableName string `gorm:"-"`
}
func NewIpAddress(ctx context.Context, hostId string, priority uint32, address string) (*IpAddress, error) {
const op = "host.NewIpAddress"
ia := &IpAddress{
IpAddress: &store.IpAddress{
HostId: hostId,
Address: address,
Priority: priority,
},
}
if err := ia.validate(ctx, op); err != nil {
return nil, err
}
return ia, nil
}
// validate the host ip address. On success, it will return nil.
func (ia *IpAddress) validate(ctx context.Context, caller errors.Op) error {
if ia.HostId == "" {
return errors.New(ctx, errors.InvalidParameter, caller, "missing host id")
}
if ia.Priority < 1 {
return errors.New(ctx, errors.InvalidParameter, caller, "invalid priority value")
}
if ia.Address == "" {
return errors.New(ctx, errors.InvalidParameter, caller, "missing ip address")
}
if ip := net.ParseIP(ia.Address); ip == nil {
return errors.New(ctx, errors.InvalidParameter, caller, "given address is not an ip address")
}
return nil
}
// allocIpAddress make an empty one in memory.
func allocIpAddress() IpAddress {
return IpAddress{
IpAddress: &store.IpAddress{},
}
}
// Clone an IpAddress
func (c *IpAddress) Clone() *IpAddress {
cp := proto.Clone(c.IpAddress)
return &IpAddress{
IpAddress: cp.(*store.IpAddress),
}
}
// TableName returns the table name.
func (c *IpAddress) TableName() string {
if c.tableName != "" {
return c.tableName
}
return defaultIpAddressTableName
}
// SetTableName sets the table name.
func (c *IpAddress) SetTableName(n string) {
c.tableName = n
}

@ -2,9 +2,14 @@ package plugin
import (
"context"
"fmt"
"sort"
"strconv"
"strings"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/host/plugin/store"
"github.com/hashicorp/boundary/internal/oplog"
"google.golang.org/protobuf/proto"
)
@ -14,27 +19,42 @@ import (
// field for this host's host catalog.
type Host struct {
*store.Host
PluginId string `gorm:"-"`
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 {
// Supported options: WithName, WithDescription, WithIpAddresses, WithDnsNames,
// WithPluginId. Others ignored.
func newHost(ctx context.Context, catalogId, externalId string, opt ...Option) *Host {
opts := getOpts(opt...)
h := &Host{
PluginId: opts.withPluginId,
Host: &store.Host{
CatalogId: catalogId,
ExternalId: externalId,
Name: opts.withName,
Description: opts.withDescription,
Address: address,
},
}
if len(opts.withIpAddresses) > 0 {
h.IpAddresses = make([]string, 0, len(opts.withIpAddresses))
h.IpAddresses = append(h.IpAddresses, opts.withIpAddresses...)
}
if len(opts.withDnsNames) > 0 {
h.DnsNames = make([]string, 0, len(opts.withDnsNames))
h.DnsNames = append(h.DnsNames, opts.withDnsNames...)
}
return h
}
// For compatibility with the general Host type
func (h *Host) GetAddress() string {
return ""
}
// TableName returns the table name for the host set.
func (s *Host) TableName() string {
if s.tableName != "" {
@ -55,22 +75,111 @@ func allocHost() *Host {
}
}
func (s *Host) clone() *Host {
cp := proto.Clone(s.Host)
hs := &Host{
Host: cp.(*store.Host),
func (h *Host) clone() *Host {
cp := proto.Clone(h.Host)
return &Host{
PluginId: h.PluginId,
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()},
// hostAgg is a view that aggregates the host's value objects in to
// string fields delimited with the aggregateDelimiter of "|"
type hostAgg struct {
PublicId string `gorm:"primary_key"`
CatalogId string
ExternalId string
PluginId string
Name string
Description string
CreateTime *timestamp.Timestamp
UpdateTime *timestamp.Timestamp
IpAddresses string
DnsNames string
}
func (agg *hostAgg) toHost(ctx context.Context) (*Host, error) {
const op = "plugin.(hostAgg).toHost"
const aggregateDelimiter = "|"
const priorityDelimiter = "="
h := allocHost()
h.PublicId = agg.PublicId
h.CatalogId = agg.CatalogId
h.ExternalId = agg.ExternalId
h.PluginId = agg.PluginId
h.Name = agg.Name
h.Description = agg.Description
h.CreateTime = agg.CreateTime
h.UpdateTime = agg.UpdateTime
// This function is used to protect against someone messing with the order
// in the DB by doing some validation
prioritySortFunc := func(in []string) error {
var sortErr error
sort.Slice(in, func(i, j int) bool {
ini := strings.Split(in[i], priorityDelimiter)
if len(ini) != 2 {
sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("value %s had unexpected fields", in[i]))
return false
}
inj := strings.Split(in[j], priorityDelimiter)
if len(inj) != 2 {
sortErr = errors.New(ctx, errors.NotSpecificIntegrity, op, fmt.Sprintf("value %s had unexpected fields", in[j]))
return false
}
indexi, err := strconv.Atoi(ini[0])
if err != nil {
sortErr = errors.Wrap(ctx, err, op)
return false
}
indexj, err := strconv.Atoi(inj[0])
if err != nil {
sortErr = errors.Wrap(ctx, err, op)
return false
}
return indexi < indexj
})
return sortErr
}
if s.CatalogId != "" {
metadata["catalog-id"] = []string{s.CatalogId}
if agg.IpAddresses != "" {
ips := strings.Split(agg.IpAddresses, aggregateDelimiter)
if len(ips) > 0 {
if err := prioritySortFunc(ips); err != nil {
return nil, err
}
for i, ip := range ips {
// At this point they're in the correct order, but we still
// have to strip off the priority
ips[i] = strings.Split(ip, priorityDelimiter)[1]
}
h.IpAddresses = ips
}
}
if agg.DnsNames != "" {
names := strings.Split(agg.DnsNames, aggregateDelimiter)
if len(names) > 0 {
if err := prioritySortFunc(names); err != nil {
return nil, err
}
for i, name := range names {
// At this point they're in the correct order, but we still
// have to strip off the priority
names[i] = strings.Split(name, priorityDelimiter)[1]
}
h.DnsNames = names
}
}
return metadata
return h, nil
}
// TableName returns the table name for gorm
func (agg *hostAgg) TableName() string {
return "host_plugin_host_with_value_obj"
}
func (agg *hostAgg) GetPublicId() string {
return agg.PublicId
}

@ -0,0 +1,548 @@
package plugin
import (
"context"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/host"
"github.com/hashicorp/boundary/internal/host/store"
"github.com/hashicorp/boundary/internal/iam"
hostplugin "github.com/hashicorp/boundary/internal/plugin/host"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestHostDnsName_Create(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
w := db.New(conn)
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
plg := hostplugin.TestPlugin(t, conn, "test")
cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
host1 := testHost(t, conn, cat.GetPublicId(), "external")
type args struct {
hostId string
name string
priority uint32
}
tests := []struct {
name string
args args
want *host.DnsName
wantNewErr bool
skipNewFunc bool
wantDbErr bool
}{
{
name: "blank-host-id-validate",
args: args{
hostId: "",
name: "foo.bar.com",
priority: 1,
},
wantNewErr: true,
},
{
name: "blank-name-validate",
args: args{
hostId: host1.GetPublicId(),
name: "",
priority: 1,
},
wantNewErr: true,
},
{
name: "blank-priority-validate",
args: args{
hostId: host1.GetPublicId(),
name: "foo.bar.com",
},
wantNewErr: true,
},
{
name: "blank-host-id-db",
args: args{
name: "foo.bar.com",
priority: 1,
},
skipNewFunc: true,
wantDbErr: true,
},
{
name: "blank-name-db",
args: args{
hostId: host1.GetPublicId(),
priority: 1,
},
skipNewFunc: true,
wantDbErr: true,
},
{
name: "blank-priority-db",
args: args{
hostId: host1.GetPublicId(),
name: "foo.bar.com",
},
skipNewFunc: true,
wantDbErr: true,
},
{
name: "valid",
args: args{
hostId: host1.GetPublicId(),
name: "foo.bar.com",
priority: 1,
},
want: &host.DnsName{
DnsName: &store.DnsName{
HostId: host1.GetPublicId(),
Name: "foo.bar.com",
Priority: 1,
},
},
},
{
name: "duplicate-name",
args: args{
hostId: host1.GetPublicId(),
name: "foo.bar.com",
priority: 2,
},
want: &host.DnsName{
DnsName: &store.DnsName{
HostId: host1.GetPublicId(),
Name: "foo.bar.com",
Priority: 2,
},
},
wantDbErr: true,
},
{
name: "duplicate-priority",
args: args{
hostId: host1.GetPublicId(),
name: "baz.bar.com",
priority: 1,
},
want: &host.DnsName{
DnsName: &store.DnsName{
HostId: host1.GetPublicId(),
Name: "baz.bar.com",
Priority: 1,
},
},
wantDbErr: true,
},
{
name: "valid-second",
args: args{
hostId: host1.GetPublicId(),
name: "baz.bar.com",
priority: 2,
},
want: &host.DnsName{
DnsName: &store.DnsName{
HostId: host1.GetPublicId(),
Name: "baz.bar.com",
Priority: 2,
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
require := require.New(t)
var got *host.DnsName
var err error
if !tt.skipNewFunc {
got, err = host.NewDnsName(ctx, tt.args.hostId, tt.args.priority, tt.args.name)
if tt.wantNewErr {
require.Error(err)
return
}
require.NoError(err)
require.Equal(tt.want, got)
} else {
got = &host.DnsName{
DnsName: &store.DnsName{
HostId: tt.args.hostId,
Name: tt.args.name,
Priority: tt.args.priority,
},
}
}
require.NotNil(got)
err = w.Create(ctx, got)
if tt.wantDbErr {
require.Error(err)
return
}
require.NoError(err)
})
}
}
func TestHostIpAddress_Create(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
w := db.New(conn)
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
plg := hostplugin.TestPlugin(t, conn, "test")
cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
host1 := testHost(t, conn, cat.GetPublicId(), "external")
type args struct {
hostId string
address string
priority uint32
}
tests := []struct {
name string
args args
want *host.IpAddress
wantNewErr bool
skipNewFunc bool
wantDbErr bool
}{
{
name: "blank-host-id-validate",
args: args{
hostId: "",
address: "1.2.3.4",
priority: 1,
},
wantNewErr: true,
},
{
name: "blank-name-validate",
args: args{
hostId: host1.GetPublicId(),
address: "",
priority: 1,
},
wantNewErr: true,
},
{
name: "blank-priority-validate",
args: args{
hostId: host1.GetPublicId(),
address: "1.2.3.4",
},
wantNewErr: true,
},
{
name: "bad-address-validate",
args: args{
hostId: host1.GetPublicId(),
address: "foo.bar.com",
},
wantNewErr: true,
},
{
name: "blank-host-id-db",
args: args{
address: "1.2.3.4",
priority: 1,
},
skipNewFunc: true,
wantDbErr: true,
},
{
name: "blank-address-db",
args: args{
hostId: host1.GetPublicId(),
priority: 1,
},
skipNewFunc: true,
wantDbErr: true,
},
{
name: "blank-priority-db",
args: args{
hostId: host1.GetPublicId(),
address: "1.2.3.4",
},
skipNewFunc: true,
wantDbErr: true,
},
{
name: "valid",
args: args{
hostId: host1.GetPublicId(),
address: "1.2.3.4",
priority: 1,
},
want: &host.IpAddress{
IpAddress: &store.IpAddress{
HostId: host1.GetPublicId(),
Address: "1.2.3.4",
Priority: 1,
},
},
},
{
name: "duplicate-name",
args: args{
hostId: host1.GetPublicId(),
address: "1.2.3.4",
priority: 2,
},
want: &host.IpAddress{
IpAddress: &store.IpAddress{
HostId: host1.GetPublicId(),
Address: "1.2.3.4",
Priority: 2,
},
},
wantDbErr: true,
},
{
name: "duplicate-priority",
args: args{
hostId: host1.GetPublicId(),
address: "2.3.4.5",
priority: 1,
},
want: &host.IpAddress{
IpAddress: &store.IpAddress{
HostId: host1.GetPublicId(),
Address: "2.3.4.5",
Priority: 1,
},
},
wantDbErr: true,
},
{
name: "valid-second",
args: args{
hostId: host1.GetPublicId(),
address: "2.3.4.5",
priority: 2,
},
want: &host.IpAddress{
IpAddress: &store.IpAddress{
HostId: host1.GetPublicId(),
Address: "2.3.4.5",
Priority: 2,
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
require := require.New(t)
var got *host.IpAddress
var err error
if !tt.skipNewFunc {
got, err = host.NewIpAddress(ctx, tt.args.hostId, tt.args.priority, tt.args.address)
if tt.wantNewErr {
require.Error(err)
return
}
require.NoError(err)
require.Equal(tt.want, got)
} else {
got = &host.IpAddress{
IpAddress: &store.IpAddress{
HostId: tt.args.hostId,
Address: tt.args.address,
Priority: tt.args.priority,
},
}
}
require.NotNil(got)
err = w.Create(ctx, got)
if tt.wantDbErr {
require.Error(err)
return
}
require.NoError(err)
})
}
}
func TestHostDnsName_Delete(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
w := db.New(conn)
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
plg := hostplugin.TestPlugin(t, conn, "test")
cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
host1 := testHost(t, conn, cat.GetPublicId(), "external")
addr1, err := host.NewDnsName(ctx, host1.GetPublicId(), 1, "addr1.foo.com")
require.NoError(t, err)
require.NoError(t, w.Create(ctx, addr1))
type args struct {
hostId string
name string
priority uint32
}
tests := []struct {
name string
args args
wantDelete bool
wantError bool
}{
{
name: "wrong_host_id",
args: args{
hostId: "something",
name: addr1.GetName(),
priority: 1,
},
},
{
name: "missing_priority",
args: args{
hostId: addr1.GetHostId(),
name: addr1.GetName(),
},
wantError: true,
},
{
name: "wrong_priority",
args: args{
hostId: addr1.GetHostId(),
name: addr1.GetName(),
priority: 2,
},
},
{
name: "valid",
args: args{
hostId: addr1.GetHostId(),
name: addr1.GetName(),
priority: 1,
},
wantDelete: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
got := &host.DnsName{
DnsName: &store.DnsName{
HostId: tt.args.hostId,
Name: tt.args.name,
Priority: tt.args.priority,
},
}
require.NoError(t, err)
require.NotNil(t, got)
k, err := w.Delete(ctx, got)
if tt.wantError {
assert.Error(t, err)
return
}
require.NoError(t, err)
if tt.wantDelete {
assert.Equal(t, 1, k)
} else {
assert.Equal(t, 0, k)
}
})
}
}
func TestHostIpAddress_Delete(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
w := db.New(conn)
wrapper := db.TestWrapper(t)
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
plg := hostplugin.TestPlugin(t, conn, "test")
cat := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
host1 := testHost(t, conn, cat.GetPublicId(), "external")
addr1, err := host.NewIpAddress(ctx, host1.GetPublicId(), 1, "1.2.3.4")
require.NoError(t, err)
require.NoError(t, w.Create(ctx, addr1))
type args struct {
hostId string
address string
priority uint32
}
tests := []struct {
name string
args args
wantDelete bool
wantError bool
}{
{
name: "wrong_host_id",
args: args{
hostId: "something",
address: addr1.GetAddress(),
priority: 1,
},
},
{
name: "missing_priority",
args: args{
hostId: addr1.GetHostId(),
address: addr1.GetAddress(),
},
wantError: true,
},
{
name: "wrong_priority",
args: args{
hostId: addr1.GetHostId(),
address: addr1.GetAddress(),
priority: 2,
},
},
{
name: "valid",
args: args{
hostId: addr1.GetHostId(),
address: addr1.GetAddress(),
priority: 1,
},
wantDelete: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
got := &host.IpAddress{
IpAddress: &store.IpAddress{
HostId: tt.args.hostId,
Address: tt.args.address,
Priority: tt.args.priority,
},
}
require.NoError(t, err)
require.NotNil(t, got)
k, err := w.Delete(ctx, got)
if tt.wantError {
assert.Error(t, err)
return
}
require.NoError(t, err)
if tt.wantDelete {
assert.Equal(t, 1, k)
} else {
assert.Equal(t, 0, k)
}
})
}
}

@ -21,9 +21,9 @@ func TestHost_Create(t *testing.T) {
cat2 := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
type args struct {
catalogId string
address string
opts []Option
catalogId string
externalId string
opts []Option
}
tests := []struct {
@ -35,16 +35,16 @@ func TestHost_Create(t *testing.T) {
{
name: "blank-catalogId",
args: args{
catalogId: "",
address: "foo.bar.com",
catalogId: "",
externalId: "external_id",
},
want: &Host{Host: &store.Host{
Address: "foo.bar.com",
ExternalId: "external_id",
}},
wantErr: true,
},
{
name: "blank-address",
name: "blank-external-id",
args: args{
catalogId: cat.GetPublicId(),
},
@ -56,47 +56,47 @@ func TestHost_Create(t *testing.T) {
{
name: "valid-no-options",
args: args{
catalogId: cat.GetPublicId(),
address: "foo.bar.com",
catalogId: cat.GetPublicId(),
externalId: "valid-no-options",
},
want: &Host{
Host: &store.Host{
CatalogId: cat.GetPublicId(),
Address: "foo.bar.com",
CatalogId: cat.GetPublicId(),
ExternalId: "valid-no-options",
},
},
},
{
name: "valid-with-name",
args: args{
catalogId: cat.GetPublicId(),
address: "foo.bar.com",
catalogId: cat.GetPublicId(),
externalId: "valid-with-name",
opts: []Option{
WithName("test-name"),
},
},
want: &Host{
Host: &store.Host{
CatalogId: cat.GetPublicId(),
Address: "foo.bar.com",
Name: "test-name",
CatalogId: cat.GetPublicId(),
ExternalId: "valid-with-name",
Name: "test-name",
},
},
},
{
name: "duplicate-name",
args: args{
catalogId: cat.GetPublicId(),
address: "foo.bar.com",
catalogId: cat.GetPublicId(),
externalId: "duplicate-name",
opts: []Option{
WithName("test-name"),
},
},
want: &Host{
Host: &store.Host{
CatalogId: cat.GetPublicId(),
Address: "foo.bar.com",
Name: "test-name",
CatalogId: cat.GetPublicId(),
ExternalId: "duplicate-name",
Name: "test-name",
},
},
wantErr: true,
@ -104,25 +104,25 @@ func TestHost_Create(t *testing.T) {
{
name: "valid-duplicate-name-different-catalog",
args: args{
catalogId: cat2.GetPublicId(),
address: "foo.bar.com",
catalogId: cat2.GetPublicId(),
externalId: "valid-duplicate-name-different-catalog",
opts: []Option{
WithName("test-name"),
},
},
want: &Host{
Host: &store.Host{
CatalogId: cat2.GetPublicId(),
Address: "foo.bar.com",
Name: "test-name",
CatalogId: cat2.GetPublicId(),
ExternalId: "valid-duplicate-name-different-catalog",
Name: "test-name",
},
},
},
{
name: "valid-with-description",
args: args{
catalogId: cat.GetPublicId(),
address: "foo.bar.com",
catalogId: cat.GetPublicId(),
externalId: "valid-with-description",
opts: []Option{
WithDescription("test-description"),
},
@ -130,7 +130,7 @@ func TestHost_Create(t *testing.T) {
want: &Host{
Host: &store.Host{
CatalogId: cat.GetPublicId(),
Address: "foo.bar.com",
ExternalId: "valid-with-description",
Description: "test-description",
},
},
@ -141,7 +141,7 @@ func TestHost_Create(t *testing.T) {
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...)
got := newHost(ctx, tt.args.catalogId, tt.args.externalId, tt.args.opts...)
require.NotNil(t, got)
assert.Emptyf(t, got.PublicId, "PublicId set")
assert.Equal(t, tt.want, got)
@ -163,6 +163,9 @@ func TestHost_Create(t *testing.T) {
}
}
// TODO: Test Deletion directly of host, of set membership, and of cascading
// from the set
func TestHost_SetTableName(t *testing.T) {
defaultTableName := "host_plugin_host"
tests := []struct {
@ -200,3 +203,15 @@ func TestHost_SetTableName(t *testing.T) {
})
}
}
func testHost(t *testing.T, conn *db.DB, catId, externId string) *Host {
t.Helper()
w := db.New(conn)
ctx := context.Background()
host1 := newHost(ctx, catId, externId)
var err error
host1.PublicId, err = newHostId(ctx, catId, externId)
require.NoError(t, err)
require.NoError(t, w.Create(ctx, host1))
return host1
}

@ -16,11 +16,15 @@ type Option func(*options)
// options = how options are represented
type options struct {
withPluginId string
withName string
withDescription string
withAttributes *structpb.Struct
withSecrets *structpb.Struct
withPreferredEndpoints []string
withIpAddresses []string
withDnsNames []string
withLimit int
}
func getDefaultOptions() options {
@ -29,6 +33,13 @@ func getDefaultOptions() options {
}
}
// WithPluginId provides an optional plugin id.
func withPluginId(with string) Option {
return func(o *options) {
o.withPluginId = with
}
}
// WithDescription provides an optional description.
func WithDescription(desc string) Option {
return func(o *options) {
@ -63,3 +74,26 @@ func WithPreferredEndpoints(with []string) Option {
o.withPreferredEndpoints = with
}
}
// withIpAddresses provides an optional list of ip addresses.
func withIpAddresses(with []string) Option {
return func(o *options) {
o.withIpAddresses = with
}
}
// withDnsNames provides an optional list of dns names.
func withDnsNames(with []string) Option {
return func(o *options) {
o.withDnsNames = with
}
}
// WithLimit provides an option to provide a limit. Intentionally allowing
// negative integers. If WithLimit < 0, then unlimited results are
// returned. If WithLimit == 0, then default limits are used for results.
func WithLimit(l int) Option {
return func(o *options) {
o.withLimit = l
}
}

@ -14,16 +14,40 @@ func Test_GetOpts(t *testing.T) {
testOpts.withName = "test"
assert.Equal(t, opts, testOpts)
})
t.Run("WithPluginId", func(t *testing.T) {
opts := getOpts(withPluginId("test"))
testOpts := getDefaultOptions()
testOpts.withPluginId = "test"
assert.Equal(t, opts, testOpts)
})
t.Run("WithDescription", func(t *testing.T) {
opts := getOpts(WithDescription("test desc"))
testOpts := getDefaultOptions()
testOpts.withDescription = "test desc"
assert.Equal(t, opts, testOpts)
})
t.Run("WithLimit", func(t *testing.T) {
opts := getOpts(WithLimit(5))
testOpts := getDefaultOptions()
testOpts.withLimit = 5
assert.Equal(t, opts, testOpts)
})
t.Run("WithPreferredEndpoints", func(t *testing.T) {
opts := getOpts(WithPreferredEndpoints([]string{"foo"}))
testOpts := getDefaultOptions()
testOpts.withPreferredEndpoints = []string{"foo"}
assert.EqualValues(t, opts, testOpts)
})
t.Run("withDnsNames", func(t *testing.T) {
opts := getOpts(withDnsNames([]string{"foo"}))
testOpts := getDefaultOptions()
testOpts.withDnsNames = []string{"foo"}
assert.EqualValues(t, opts, testOpts)
})
t.Run("withIpAddresses", func(t *testing.T) {
opts := getOpts(withIpAddresses([]string{"foo"}))
testOpts := getDefaultOptions()
testOpts.withIpAddresses = []string{"foo"}
assert.EqualValues(t, opts, testOpts)
})
}

@ -0,0 +1,284 @@
package plugin
import (
"context"
"fmt"
"sort"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/host"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
)
// UpsertHost inserts phs into the repository or updates its current
// attributes/set memberships and returns Hosts. h is not changed. hc must
// contain a valid public ID and scope ID. Each ph in phs must not contain a
// PublicId but must contain an external ID. The PublicId is generated and
// assigned by this method.
//
// NOTE: If phs is empty, this assumes that there are simply no hosts that
// matched the given sets! Which means it will remove all hosts from the given
// sets.
func (r *Repository) UpsertHosts(
ctx context.Context,
hc *HostCatalog,
sets []string,
phs []*plgpb.ListHostsResponseHost,
_ ...Option) ([]*Host, error) {
const op = "plugin.(Repository).UpsertHosts"
if phs == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil plugin hosts")
}
for _, ph := range phs {
if ph.GetExternalId() == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing host external id")
}
}
if hc == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil host catalog")
}
if hc.GetPublicId() == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id")
}
if hc.GetScopeId() == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "no scope id")
}
if sets == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil sets")
}
if len(sets) == 0 { // At least one must have been given to the plugin
return nil, errors.New(ctx, errors.InvalidParameter, op, "empty sets")
}
// hostInfo stores the info we need for the transaction below as well as
// which sets they matched
type hostInfo struct {
h *Host
ips []interface{}
dnsNames []interface{}
sets map[string]struct{}
}
hostMapping := make(map[string]hostInfo, len(phs))
var err error
var totalMsgs uint32
for _, ph := range phs {
totalMsgs += 2 // delete and create
h := newHost(ctx,
hc.GetPublicId(),
ph.GetExternalId(),
withIpAddresses(ph.GetIpAddresses()),
withDnsNames(ph.GetDnsNames()),
withPluginId(hc.GetPluginId()))
h.PublicId, err = newHostId(ctx, hc.GetPublicId(), ph.GetExternalId())
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
var ipAddresses []interface{}
if len(h.GetIpAddresses()) > 0 {
sort.Strings(h.IpAddresses)
ipAddresses = make([]interface{}, 0, len(h.GetIpAddresses()))
for i, a := range h.GetIpAddresses() {
obj, err := host.NewIpAddress(ctx, h.PublicId, uint32(i+1), a)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
ipAddresses = append(ipAddresses, obj)
}
}
var dnsNames []interface{}
if len(h.GetDnsNames()) > 0 {
sort.Strings(h.DnsNames)
dnsNames = make([]interface{}, 0, len(h.GetDnsNames()))
for i, n := range h.GetDnsNames() {
obj, err := host.NewDnsName(ctx, h.PublicId, uint32(i+1), n)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
dnsNames = append(dnsNames, obj)
}
}
hi := hostInfo{
h: h,
ips: ipAddresses,
dnsNames: dnsNames,
}
for _, id := range ph.GetSetIds() {
if hi.sets == nil {
hi.sets = make(map[string]struct{})
}
hi.sets[id] = struct{}{}
}
hostMapping[h.PublicId] = hi
totalMsgs += uint32(len(ipAddresses) + len(dnsNames))
}
oplogWrapper, err := r.kms.GetWrapper(ctx, hc.GetScopeId(), kms.KeyPurposeOplog)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var returnedHosts []*Host
// Here's what this function does: first it deletes a host, which causes
// cascading deletes on ip addresses and dns names (but all within the DB).
// Then the host is recreated, which uses the same public ID (since it's
// based on the host catalog/external ID), along with new/updated
// information.
//
// If we ever do start allowing hosts directly in targets this may cause a
// problem because the host deletion would probably cascade to the target
// and remove it from the target host sources, but at this point there are
// several issues with that scenario so it's not soon...
//
// The main reason I'm doing it this way is because I'm not sure how to do
// oplogs with custom queries. Otherwise a custom insert/on conflict query
// could replace the initial deletion. @jimlambrt @mgaffney help? :-D
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(r db.Reader, w db.Writer) error {
// FIXME: We need to ensure hosts no longer found have their
// memberships removed. To do this, first list all set memberships.
// This is waiting on membership to be plumbed through to gorm,
// which in turn is waiting on figuring out whether or not we can
// share that between static and plugin.
//
// Next, for each set, check whether a given host ID is in the
// hostMapping and if so, if the set ID is in the host's sets. If
// not, queue that membership entry for deletion.
msgs := make([]*oplog.Message, 0, totalMsgs+3)
ticket, err := w.GetTicket(hc)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
}
for _, hi := range hostMapping {
ret := hi.h.clone()
var hOplogMsg oplog.Message
// We don't need to check whether something was deleted because
// it's okay if it fails
if _, err := w.Delete(ctx, ret, db.NewOplogMsg(&hOplogMsg)); err != nil {
return errors.Wrap(ctx, err, op)
}
msgs = append(msgs, &hOplogMsg)
hOplogMsg = oplog.Message{}
if err := w.Create(ctx, ret, db.NewOplogMsg(&hOplogMsg)); err != nil {
return errors.Wrap(ctx, err, op)
}
msgs = append(msgs, &hOplogMsg)
if len(hi.ips) > 0 {
ipOplogMsgs := make([]*oplog.Message, 0, len(hi.ips))
if err := w.CreateItems(ctx, hi.ips, db.NewOplogMsgs(&ipOplogMsgs)); err != nil {
return err
}
msgs = append(msgs, ipOplogMsgs...)
}
if len(hi.dnsNames) > 0 {
dnsOplogMsgs := make([]*oplog.Message, 0, len(hi.dnsNames))
if err := w.CreateItems(ctx, hi.dnsNames, db.NewOplogMsgs(&dnsOplogMsgs)); err != nil {
return err
}
msgs = append(msgs, dnsOplogMsgs...)
}
// FIXME: Add set memberships here
returnedHosts = append(returnedHosts, ret)
}
metadata := hc.oplog(oplog.OpType_OP_TYPE_CREATE)
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog"))
}
return nil
},
)
if err != nil {
if errors.IsCheckConstraintError(err) || errors.IsNotNullError(err) {
return nil, errors.New(ctx,
errors.InvalidAddress,
op,
fmt.Sprintf("in catalog: %s", hc.GetPublicId()),
errors.WithWrap(err),
)
}
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in catalog: %s", hc.PublicId)))
}
return returnedHosts, nil
}
// LookupHost will look up a host in the repository. If the host is not
// found, it will return nil, nil. All options are ignored.
func (r *Repository) LookupHost(ctx context.Context, publicId string, opt ...Option) (*Host, error) {
const op = "plugin.(Repository).LookupHost"
if publicId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
}
ha := &hostAgg{
PublicId: publicId,
}
if err := r.reader.LookupByPublicId(ctx, ha); err != nil {
if errors.IsNotFoundError(err) {
return nil, nil
}
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId)))
}
h, err := ha.toHost(ctx)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed to convert host agg for %s", publicId)))
}
return h, nil
}
// ListHosts returns a slice of Hosts for the catalogId.
// WithLimit is the only option supported.
func (r *Repository) ListHosts(ctx context.Context, catalogId string, opt ...Option) ([]*Host, error) {
const op = "plugin.(Repository).ListHosts"
if catalogId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "no catalog id")
}
opts := getOpts(opt...)
limit := r.defaultLimit
if opts.withLimit != 0 {
// non-zero signals an override of the default limit for the repo.
limit = opts.withLimit
}
var hostAggs []*hostAgg
err := r.reader.SearchWhere(ctx, &hostAggs, "catalog_id = ?", []interface{}{catalogId}, db.WithLimit(limit))
switch {
case err != nil:
return nil, errors.Wrap(ctx, err, op)
case hostAggs == nil:
return nil, nil
}
hosts := make([]*Host, 0, len(hostAggs))
for _, ha := range hostAggs {
host, err := ha.toHost(ctx)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
hosts = append(hosts, host)
}
return hosts, nil
}

@ -87,11 +87,6 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet,
}
}
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var preferredEndpoints []interface{}
if s.PreferredEndpoints != nil {
preferredEndpoints = make([]interface{}, 0, len(s.PreferredEndpoints))
@ -104,6 +99,11 @@ func (r *Repository) CreateSet(ctx context.Context, scopeId string, s *HostSet,
}
}
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
if err != nil {
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var returnedHostSet *HostSet
_, err = r.writer.DoTx(
ctx,
@ -185,6 +185,7 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...host
setToReturn := sets[0]
var hostIdsToReturn []string
// FIXME: change to use the database
if plg != nil && opts.WithSetMembers {
cat, err := r.getCatalog(ctx, setToReturn.GetCatalogId())
if err != nil {

@ -489,16 +489,15 @@ func TestRepository_Endpoints(t *testing.T) {
})
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)
// TODO: Uncomment when we have a better way to lookup host
// with it's address
// assert.Equal(ep.Address, h.Address)
assert.Equal(catalog.GetPublicId(), h.GetCatalogId())
}
})

@ -0,0 +1,245 @@
package plugin
import (
"context"
"fmt"
"sort"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/host/plugin/store"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
hostplg "github.com/hashicorp/boundary/internal/plugin/host"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepository_UpsertHosts(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, "create")
plgm := map[string]plgpb.HostPluginServiceClient{
plg.GetPublicId(): NewWrappingPluginClient(&plgpb.UnimplementedHostPluginServiceServer{}),
}
catalog := TestCatalog(t, conn, prj.PublicId, plg.GetPublicId())
const setCount int = 3
setIds := make([]string, 0, setCount)
for i := 0; i < setCount; i++ {
set := TestSet(t, conn, kms, catalog, plgm)
setIds = append(setIds, set.GetPublicId())
}
phs, exp := TestExternalHosts(t, catalog, setIds, setCount)
type input struct {
catalog *HostCatalog
sets []string
phs []*plgpb.ListHostsResponseHost
exp []*Host
}
tests := []struct {
name string
in func() *input
opts []Option
wantIsErr errors.Code
}{
{
name: "nil-hosts",
in: func() *input {
return &input{
catalog: catalog,
sets: setIds,
}
},
wantIsErr: errors.InvalidParameter,
},
{
name: "no-external-id-hosts",
in: func() *input {
testPhs, _ := TestExternalHosts(t, catalog, setIds, setCount)
testPhs[1].ExternalId = ""
return &input{
catalog: catalog,
sets: setIds,
phs: testPhs,
}
},
wantIsErr: errors.InvalidParameter,
},
{
name: "nil-catalog",
in: func() *input {
return &input{
sets: setIds,
phs: phs,
}
},
wantIsErr: errors.InvalidParameter,
},
{
name: "no-catalog-id",
in: func() *input {
cat := catalog.clone()
cat.PublicId = ""
return &input{
catalog: cat,
sets: setIds,
phs: phs,
}
},
wantIsErr: errors.InvalidParameter,
},
{
name: "no-scope-id",
in: func() *input {
cat := catalog.clone()
cat.ScopeId = ""
return &input{
catalog: cat,
sets: setIds,
phs: phs,
}
},
wantIsErr: errors.InvalidParameter,
},
{
name: "nil-sets",
in: func() *input {
return &input{
catalog: catalog,
phs: phs,
}
},
wantIsErr: errors.InvalidParameter,
},
{
name: "no-sets",
in: func() *input {
return &input{
catalog: catalog,
sets: make([]string, 0),
phs: phs,
}
},
wantIsErr: errors.InvalidParameter,
},
{
name: "valid",
in: func() *input {
return &input{
catalog: catalog,
sets: setIds,
phs: phs,
exp: exp,
}
},
},
{
name: "valid-changed-values",
in: func() *input {
ph := phs[1]
e := exp[1]
newIp := testGetIpAddress(t)
newName := testGetDnsName(t)
ph.IpAddresses = append(ph.IpAddresses, newIp)
e.IpAddresses = append(e.IpAddresses, newIp)
ph.DnsNames = append(ph.DnsNames, newName)
e.DnsNames = append(e.DnsNames, newName)
// These are sorted by the repo function, so we need to match
sort.Strings(e.IpAddresses)
sort.Strings(e.DnsNames)
ph.SetIds = ph.SetIds[0 : len(ph.SetIds)-1]
return &input{
catalog: catalog,
sets: setIds,
phs: phs,
exp: exp,
}
},
},
}
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)
require.NoError(err)
require.NotNil(repo)
in := tt.in()
got, err := repo.UpsertHosts(ctx, in.catalog, in.sets, in.phs, tt.opts...)
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, fmt.Sprintf("%v", in.catalog))
require.NotNil(got)
// Basic tests
assert.Len(got, len(in.phs))
assert.NoError(db.TestVerifyOplog(t, rw, in.catalog.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)))
// Make sure outputs match. Ignore timestamps.
assert.Empty(
cmp.Diff(
in.exp,
got,
cmpopts.IgnoreUnexported(Host{}, store.Host{}),
cmpopts.IgnoreTypes(&timestamp.Timestamp{}),
cmpopts.SortSlices(func(x, y *Host) bool {
return x.GetPublicId() < y.GetPublicId()
}),
),
)
// Check again, but via performing an explicit list
got, err = repo.ListHosts(ctx, in.catalog.GetPublicId())
require.NoError(err)
require.NotNil(got)
assert.Len(got, len(in.phs))
assert.Empty(
cmp.Diff(
in.exp,
got,
cmpopts.IgnoreUnexported(Host{}, store.Host{}),
cmpopts.IgnoreTypes(&timestamp.Timestamp{}),
cmpopts.SortSlices(func(x, y *Host) bool {
return x.GetPublicId() < y.GetPublicId()
}),
),
)
// Now individually call read on each host
for _, exp := range in.exp {
got, err := repo.LookupHost(ctx, exp.GetPublicId())
require.NoError(err)
require.NotNil(got)
assert.Empty(
cmp.Diff(
exp,
got,
cmpopts.IgnoreUnexported(Host{}, store.Host{}),
cmpopts.IgnoreTypes(&timestamp.Timestamp{}),
),
)
}
})
}
}

@ -155,7 +155,7 @@ func (x *HostCatalog) GetAttributes() []byte {
return nil
}
type Host struct {
type HostSet struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
@ -183,13 +183,16 @@ type Host struct {
// 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"`
// attributes is a byte field containing marshaled JSON data
// @inject_tag: `gorm:"not_null"`
Attributes []byte `protobuf:"bytes,8,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"not_null"`
// preferred_endpoints stores string preference values
// @inject_tag: `gorm:"-"`
PreferredEndpoints []string `protobuf:"bytes,9,rep,name=preferred_endpoints,json=preferredEndpoints,proto3" json:"preferred_endpoints,omitempty" gorm:"-"`
}
func (x *Host) Reset() {
*x = Host{}
func (x *HostSet) Reset() {
*x = HostSet{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -197,13 +200,13 @@ func (x *Host) Reset() {
}
}
func (x *Host) String() string {
func (x *HostSet) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Host) ProtoMessage() {}
func (*HostSet) ProtoMessage() {}
func (x *Host) ProtoReflect() protoreflect.Message {
func (x *HostSet) 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))
@ -215,68 +218,178 @@ func (x *Host) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use Host.ProtoReflect.Descriptor instead.
func (*Host) Descriptor() ([]byte, []int) {
// Deprecated: Use HostSet.ProtoReflect.Descriptor instead.
func (*HostSet) Descriptor() ([]byte, []int) {
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{1}
}
func (x *Host) GetPublicId() string {
func (x *HostSet) GetPublicId() string {
if x != nil {
return x.PublicId
}
return ""
}
func (x *Host) GetCreateTime() *timestamp.Timestamp {
func (x *HostSet) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *Host) GetUpdateTime() *timestamp.Timestamp {
func (x *HostSet) GetUpdateTime() *timestamp.Timestamp {
if x != nil {
return x.UpdateTime
}
return nil
}
func (x *Host) GetName() string {
func (x *HostSet) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Host) GetDescription() string {
func (x *HostSet) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *Host) GetCatalogId() string {
func (x *HostSet) GetCatalogId() string {
if x != nil {
return x.CatalogId
}
return ""
}
func (x *Host) GetVersion() uint32 {
func (x *HostSet) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
func (x *Host) GetAddress() string {
func (x *HostSet) GetAttributes() []byte {
if x != nil {
return x.Attributes
}
return nil
}
func (x *HostSet) GetPreferredEndpoints() []string {
if x != nil {
return x.Address
return x.PreferredEndpoints
}
return nil
}
type HostCatalogSecret struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// catalog_id is the public id of the catalog containing this secret.
// @inject_tag: `gorm:"primary_key"`
CatalogId string `protobuf:"bytes,1,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_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"`
// attributes is the plain-text of the attribute data. We are not storing
// this plain-text value in the database.
// @inject_tag: `gorm:"-" wrapping:"pt,secret_data"`
Secret []byte `protobuf:"bytes,4,opt,name=secret,proto3" json:"secret,omitempty" gorm:"-" wrapping:"pt,secret_data"`
// ct_attributes is the ciphertext of the attribute data stored in the db.
// @inject_tag: `gorm:"column:secret;not_null" wrapping:"ct,secret_data"`
CtSecret []byte `protobuf:"bytes,5,opt,name=ct_secret,json=ctSecret,proto3" json:"ct_secret,omitempty" gorm:"column:secret;not_null" wrapping:"ct,secret_data"`
// The key_id of the kms database key used for encrypting this entry.
// It must be set.
// @inject_tag: `gorm:"not_null"`
KeyId string `protobuf:"bytes,6,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"`
}
func (x *HostCatalogSecret) Reset() {
*x = HostCatalogSecret{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HostCatalogSecret) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HostCatalogSecret) ProtoMessage() {}
func (x *HostCatalogSecret) ProtoReflect() protoreflect.Message {
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 {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HostCatalogSecret.ProtoReflect.Descriptor instead.
func (*HostCatalogSecret) Descriptor() ([]byte, []int) {
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{2}
}
func (x *HostCatalogSecret) GetCatalogId() string {
if x != nil {
return x.CatalogId
}
return ""
}
type HostSet struct {
func (x *HostCatalogSecret) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *HostCatalogSecret) GetUpdateTime() *timestamp.Timestamp {
if x != nil {
return x.UpdateTime
}
return nil
}
func (x *HostCatalogSecret) GetSecret() []byte {
if x != nil {
return x.Secret
}
return nil
}
func (x *HostCatalogSecret) GetCtSecret() []byte {
if x != nil {
return x.CtSecret
}
return nil
}
func (x *HostCatalogSecret) GetKeyId() string {
if x != nil {
return x.KeyId
}
return ""
}
// TODO: Add a field which tracks if the host in cache should be considered
// invalid and fall back to the plugin provided data.
type Host struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
@ -284,51 +397,55 @@ type HostSet struct {
// 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"`
// external_id is an id provided by the plugin.
// @inject_tag: `gorm:"not_null"`
ExternalId string `protobuf:"bytes,2,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty" gorm:"not_null"`
// 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"`
CreateTime *timestamp.Timestamp `protobuf:"bytes,3,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"`
UpdateTime *timestamp.Timestamp `protobuf:"bytes,4,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"`
Name string `protobuf:"bytes,5,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"`
Description string `protobuf:"bytes,6,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"`
// attributes is a byte field containing marshaled JSON data
// @inject_tag: `gorm:"not_null"`
Attributes []byte `protobuf:"bytes,8,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"not_null"`
// preferred_endpoints stores string preference values
CatalogId string `protobuf:"bytes,7,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"not_null"`
// @inject_tag: `gorm:"-"`
PreferredEndpoints []string `protobuf:"bytes,9,rep,name=preferred_endpoints,json=preferredEndpoints,proto3" json:"preferred_endpoints,omitempty" gorm:"-"`
Version uint32 `protobuf:"varint,8,opt,name=version,proto3" json:"version,omitempty" gorm:"-"`
// ip_addresses are the ip addresses associated with this host and will
// be persisted in the db through the HostAddress message.
// @inject_tag: `gorm:"-"`
IpAddresses []string `protobuf:"bytes,9,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty" gorm:"-"`
// dns_names are the dns names associated with this host and will
// be persisted in the db through the HostAddress message.
// @inject_tag: `gorm:"-"`
DnsNames []string `protobuf:"bytes,10,rep,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty" gorm:"-"`
}
func (x *HostSet) Reset() {
*x = HostSet{}
func (x *Host) Reset() {
*x = Host{}
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)
}
}
func (x *HostSet) String() string {
func (x *Host) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HostSet) ProtoMessage() {}
func (*Host) ProtoMessage() {}
func (x *HostSet) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2]
func (x *Host) ProtoReflect() protoreflect.Message {
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 {
@ -339,118 +456,111 @@ func (x *HostSet) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use HostSet.ProtoReflect.Descriptor instead.
func (*HostSet) Descriptor() ([]byte, []int) {
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{2}
// Deprecated: Use Host.ProtoReflect.Descriptor instead.
func (*Host) Descriptor() ([]byte, []int) {
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{3}
}
func (x *HostSet) GetPublicId() string {
func (x *Host) GetPublicId() string {
if x != nil {
return x.PublicId
}
return ""
}
func (x *HostSet) GetCreateTime() *timestamp.Timestamp {
func (x *Host) GetExternalId() string {
if x != nil {
return x.ExternalId
}
return ""
}
func (x *Host) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *HostSet) GetUpdateTime() *timestamp.Timestamp {
func (x *Host) GetUpdateTime() *timestamp.Timestamp {
if x != nil {
return x.UpdateTime
}
return nil
}
func (x *HostSet) GetName() string {
func (x *Host) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *HostSet) GetDescription() string {
func (x *Host) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *HostSet) GetCatalogId() string {
func (x *Host) GetCatalogId() string {
if x != nil {
return x.CatalogId
}
return ""
}
func (x *HostSet) GetVersion() uint32 {
func (x *Host) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
func (x *HostSet) GetAttributes() []byte {
func (x *Host) GetIpAddresses() []string {
if x != nil {
return x.Attributes
return x.IpAddresses
}
return nil
}
func (x *HostSet) GetPreferredEndpoints() []string {
func (x *Host) GetDnsNames() []string {
if x != nil {
return x.PreferredEndpoints
return x.DnsNames
}
return nil
}
type HostCatalogSecret struct {
type HostSetMember struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// catalog_id is the public id of the catalog containing this secret.
// @inject_tag: `gorm:"primary_key"`
CatalogId string `protobuf:"bytes,1,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_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"`
// attributes is the plain-text of the attribute data. We are not storing
// this plain-text value in the database.
// @inject_tag: `gorm:"-" wrapping:"pt,secret_data"`
Secret []byte `protobuf:"bytes,4,opt,name=secret,proto3" json:"secret,omitempty" gorm:"-" wrapping:"pt,secret_data"`
// ct_attributes is the ciphertext of the attribute data stored in the db.
// @inject_tag: `gorm:"column:secret;not_null" wrapping:"ct,secret_data"`
CtSecret []byte `protobuf:"bytes,5,opt,name=ct_secret,json=ctSecret,proto3" json:"ct_secret,omitempty" gorm:"column:secret;not_null" wrapping:"ct,secret_data"`
// The key_id of the kms database key used for encrypting this entry.
// It must be set.
// @inject_tag: `gorm:"not_null"`
KeyId string `protobuf:"bytes,6,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"`
HostId string `protobuf:"bytes,1,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty" gorm:"primary_key"`
// @inject_tag: `gorm:"primary_key"`
SetId string `protobuf:"bytes,2,opt,name=set_id,json=setId,proto3" json:"set_id,omitempty" gorm:"primary_key"`
// @inject_tag: `gorm:"default:null"`
CatalogId string `protobuf:"bytes,3,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"default:null"`
}
func (x *HostCatalogSecret) Reset() {
*x = HostCatalogSecret{}
func (x *HostSetMember) Reset() {
*x = HostSetMember{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3]
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HostCatalogSecret) String() string {
func (x *HostSetMember) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HostCatalogSecret) ProtoMessage() {}
func (*HostSetMember) ProtoMessage() {}
func (x *HostCatalogSecret) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3]
func (x *HostSetMember) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -461,49 +571,28 @@ func (x *HostCatalogSecret) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use HostCatalogSecret.ProtoReflect.Descriptor instead.
func (*HostCatalogSecret) Descriptor() ([]byte, []int) {
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{3}
// Deprecated: Use HostSetMember.ProtoReflect.Descriptor instead.
func (*HostSetMember) Descriptor() ([]byte, []int) {
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{4}
}
func (x *HostCatalogSecret) GetCatalogId() string {
func (x *HostSetMember) GetHostId() string {
if x != nil {
return x.CatalogId
return x.HostId
}
return ""
}
func (x *HostCatalogSecret) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *HostCatalogSecret) GetUpdateTime() *timestamp.Timestamp {
func (x *HostSetMember) GetSetId() string {
if x != nil {
return x.UpdateTime
return x.SetId
}
return nil
}
func (x *HostCatalogSecret) GetSecret() []byte {
if x != nil {
return x.Secret
}
return nil
}
func (x *HostCatalogSecret) GetCtSecret() []byte {
if x != nil {
return x.CtSecret
}
return nil
return ""
}
func (x *HostCatalogSecret) GetKeyId() string {
func (x *HostSetMember) GetCatalogId() string {
if x != nil {
return x.KeyId
return x.CatalogId
}
return ""
}
@ -550,77 +639,88 @@ 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, 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,
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,
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, 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,
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, 0x22, 0x8d, 0x03, 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,
0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64,
0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 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, 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, 0x04, 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, 0x05, 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, 0x06, 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, 0x07,
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, 0x08, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x70, 0x5f,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52,
0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09,
0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52,
0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5e, 0x0a, 0x0d, 0x48, 0x6f, 0x73,
0x74, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f,
0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73,
0x74, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61,
0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 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 (
@ -635,23 +735,24 @@ 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, 4)
var file_controller_storage_host_plugin_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_controller_storage_host_plugin_store_v1_host_proto_goTypes = []interface{}{
(*HostCatalog)(nil), // 0: controller.storage.host.plugin.store.v1.HostCatalog
(*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
(*HostSet)(nil), // 1: controller.storage.host.plugin.store.v1.HostSet
(*HostCatalogSecret)(nil), // 2: controller.storage.host.plugin.store.v1.HostCatalogSecret
(*Host)(nil), // 3: controller.storage.host.plugin.store.v1.Host
(*HostSetMember)(nil), // 4: controller.storage.host.plugin.store.v1.HostSetMember
(*timestamp.Timestamp)(nil), // 5: controller.storage.timestamp.v1.Timestamp
}
var file_controller_storage_host_plugin_store_v1_host_proto_depIdxs = []int32{
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
5, // 0: controller.storage.host.plugin.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 1: controller.storage.host.plugin.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 2: controller.storage.host.plugin.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 3: controller.storage.host.plugin.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 4: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 5: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 6: controller.storage.host.plugin.store.v1.Host.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
5, // 7: controller.storage.host.plugin.store.v1.Host.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
@ -678,7 +779,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.(*Host); i {
switch v := v.(*HostSet); i {
case 0:
return &v.state
case 1:
@ -690,7 +791,7 @@ 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 {
switch v := v.(*HostCatalogSecret); i {
case 0:
return &v.state
case 1:
@ -702,7 +803,19 @@ func file_controller_storage_host_plugin_store_v1_host_proto_init() {
}
}
file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HostCatalogSecret); i {
switch v := v.(*Host); 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[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HostSetMember); i {
case 0:
return &v.state
case 1:
@ -720,7 +833,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: 4,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},

@ -2,12 +2,18 @@ package plugin
import (
"context"
"crypto/rand"
"fmt"
"io"
"net"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/host/plugin/store"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/plugin/host"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/go-secure-stdlib/base62"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -67,6 +73,65 @@ func TestSet(t *testing.T, conn *db.DB, kmsCache *kms.Kms, hc *HostCatalog, plgm
return set
}
func TestExternalHosts(t *testing.T, catalog *HostCatalog, setIds []string, count int) ([]*plgpb.ListHostsResponseHost, []*Host) {
t.Helper()
require := require.New(t)
retRH := make([]*plgpb.ListHostsResponseHost, 0, count)
retH := make([]*Host, 0, count)
for i := 0; i < count; i++ {
externalId, err := base62.Random(10)
require.NoError(err)
ipStr := testGetIpAddress(t)
dnsName := testGetDnsName(t)
retRH = append(retRH, &plgpb.ListHostsResponseHost{
ExternalId: externalId,
SetIds: setIds,
IpAddresses: []string{ipStr},
DnsNames: []string{dnsName},
})
publicId, err := newHostId(context.Background(), catalog.PublicId, externalId)
require.NoError(err)
retH = append(retH, &Host{
PluginId: catalog.PluginId,
Host: &store.Host{
CatalogId: catalog.PublicId,
PublicId: publicId,
ExternalId: externalId,
IpAddresses: []string{ipStr},
DnsNames: []string{dnsName},
},
})
}
return retRH, retH
}
func testGetDnsName(t *testing.T) string {
dnsName, err := base62.Random(10)
require.NoError(t, err)
return fmt.Sprintf("%s.example.com", dnsName)
}
func testGetIpAddress(t *testing.T) string {
ipBytes := make([]byte, 4)
for {
lr := io.LimitReader(rand.Reader, 4)
n, err := lr.Read(ipBytes)
require.NoError(t, err)
require.Equal(t, n, 4)
ip := net.IP(ipBytes)
v4 := ip.To4()
if v4 != nil {
return v4.String()
}
}
}
var _ plgpb.HostPluginServiceServer = (*TestPluginServer)(nil)
// TestPluginServer provides a host plugin service server where each method can be overwritten for tests.

@ -38,6 +38,16 @@ func NewHost(catalogId string, opt ...Option) (*Host, error) {
return host, nil
}
// For compatibility with the general Host type
func (h *Host) GetIpAddresses() []string {
return nil
}
// For compatibility with the general Host type
func (h *Host) GetDnsNames() []string {
return nil
}
// TableName returns the table name for the host.
func (h *Host) TableName() string {
if h.tableName != "" {

@ -100,8 +100,11 @@ func updateVersion(ctx context.Context, w db.Writer, wrapper wrapping.Wrapper, m
switch {
case err != nil:
return errors.Wrap(ctx, err, op)
case rowsUpdated == 0:
return errors.New(ctx, errors.RecordNotFound, op, "no matching version for host set found")
case rowsUpdated > 1:
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated")
}
msgs = append(msgs, setMsg)

@ -268,6 +268,138 @@ func (x *PreferredEndpoint) GetCondition() string {
return ""
}
type IpAddress struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// @inject_tag: `gorm:"primary_key"`
HostId string `protobuf:"bytes,1,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty" gorm:"primary_key"`
// @inject_tag: `gorm:"primary_key"`
Priority uint32 `protobuf:"varint,2,opt,name=priority,proto3" json:"priority,omitempty" gorm:"primary_key"`
// @inject_tag: `gorm:"not_null`
Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"`
}
func (x *IpAddress) Reset() {
*x = IpAddress{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_host_store_v1_host_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *IpAddress) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*IpAddress) ProtoMessage() {}
func (x *IpAddress) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_host_store_v1_host_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use IpAddress.ProtoReflect.Descriptor instead.
func (*IpAddress) Descriptor() ([]byte, []int) {
return file_controller_storage_host_store_v1_host_proto_rawDescGZIP(), []int{4}
}
func (x *IpAddress) GetHostId() string {
if x != nil {
return x.HostId
}
return ""
}
func (x *IpAddress) GetPriority() uint32 {
if x != nil {
return x.Priority
}
return 0
}
func (x *IpAddress) GetAddress() string {
if x != nil {
return x.Address
}
return ""
}
type DnsName struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// @inject_tag: `gorm:"primary_key"`
HostId string `protobuf:"bytes,1,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty" gorm:"primary_key"`
// @inject_tag: `gorm:"primary_key"`
Priority uint32 `protobuf:"varint,2,opt,name=priority,proto3" json:"priority,omitempty" gorm:"primary_key"`
// @inject_tag: `gorm:"not_null`
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *DnsName) Reset() {
*x = DnsName{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_host_store_v1_host_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DnsName) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DnsName) ProtoMessage() {}
func (x *DnsName) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_host_store_v1_host_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DnsName.ProtoReflect.Descriptor instead.
func (*DnsName) Descriptor() ([]byte, []int) {
return file_controller_storage_host_store_v1_host_proto_rawDescGZIP(), []int{5}
}
func (x *DnsName) GetHostId() string {
if x != nil {
return x.HostId
}
return ""
}
func (x *DnsName) GetPriority() uint32 {
if x != nil {
return x.Priority
}
return 0
}
func (x *DnsName) GetName() string {
if x != nil {
return x.Name
}
return ""
}
var File_controller_storage_host_store_v1_host_proto protoreflect.FileDescriptor
var file_controller_storage_host_store_v1_host_proto_rawDesc = []byte{
@ -295,7 +427,18 @@ var file_controller_storage_host_store_v1_host_proto_rawDesc = []byte{
0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f,
0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63,
0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68,
0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5a, 0x0a, 0x09, 0x49, 0x70, 0x41, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a,
0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x0a, 0x07, 0x44, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12,
0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f,
0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f,
0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x39, 0x5a, 0x37, 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, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74,
@ -314,12 +457,14 @@ func file_controller_storage_host_store_v1_host_proto_rawDescGZIP() []byte {
return file_controller_storage_host_store_v1_host_proto_rawDescData
}
var file_controller_storage_host_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_controller_storage_host_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_controller_storage_host_store_v1_host_proto_goTypes = []interface{}{
(*Catalog)(nil), // 0: controller.storage.host.store.v1.Catalog
(*Host)(nil), // 1: controller.storage.host.store.v1.Host
(*Set)(nil), // 2: controller.storage.host.store.v1.Set
(*PreferredEndpoint)(nil), // 3: controller.storage.host.store.v1.PreferredEndpoint
(*IpAddress)(nil), // 4: controller.storage.host.store.v1.IpAddress
(*DnsName)(nil), // 5: controller.storage.host.store.v1.DnsName
}
var file_controller_storage_host_store_v1_host_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
@ -383,6 +528,30 @@ func file_controller_storage_host_store_v1_host_proto_init() {
return nil
}
}
file_controller_storage_host_store_v1_host_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*IpAddress); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_storage_host_store_v1_host_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DnsName); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@ -390,7 +559,7 @@ func file_controller_storage_host_store_v1_host_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_storage_host_store_v1_host_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumMessages: 6,
NumExtensions: 0,
NumServices: 0,
},

@ -47,42 +47,6 @@ 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"`
@ -149,4 +113,62 @@ message HostCatalogSecret {
// It must be set.
// @inject_tag: `gorm:"not_null"`
string key_id = 6;
}
// TODO: Add a field which tracks if the host in cache should be considered
// invalid and fall back to the plugin provided data.
message Host {
// public_id is a surrogate key suitable for use in a public API.
// @inject_tag: `gorm:"primary_key"`
string public_id = 1;
// external_id is an id provided by the plugin.
// @inject_tag: `gorm:"not_null"`
string external_id = 2;
// The create_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp create_time = 3;
// The update_time is set by the database.
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp update_time = 4;
// name is optional. If set, it must be unique within
// catalog_id.
// @inject_tag: `gorm:"default:null"`
string name = 5;
// description is optional.
// @inject_tag: `gorm:"default:null"`
string description = 6;
// catalog_id is the public_id of the owning
// plugin_host_catalog and must be set.
// @inject_tag: `gorm:"not_null"`
string catalog_id = 7;
// @inject_tag: `gorm:"-"`
uint32 version = 8;
// ip_addresses are the ip addresses associated with this host and will
// be persisted in the db through the HostAddress message.
// @inject_tag: `gorm:"-"`
repeated string ip_addresses = 9;
// dns_names are the dns names associated with this host and will
// be persisted in the db through the HostAddress message.
// @inject_tag: `gorm:"-"`
repeated string dns_names = 10;
}
message HostSetMember {
// @inject_tag: `gorm:"primary_key"`
string host_id = 1;
// @inject_tag: `gorm:"primary_key"`
string set_id = 2;
// @inject_tag: `gorm:"default:null"`
string catalog_id = 3;
}

@ -46,4 +46,26 @@ message PreferredEndpoint {
// The string text of the preference condition
// @inject_tag: `gorm:"not_null"`
string condition = 3;
}
message IpAddress {
// @inject_tag: `gorm:"primary_key"`
string host_id = 1;
// @inject_tag: `gorm:"primary_key"`
uint32 priority = 2;
// @inject_tag: `gorm:"not_null`
string address = 3;
}
message DnsName {
// @inject_tag: `gorm:"primary_key"`
string host_id = 1;
// @inject_tag: `gorm:"primary_key"`
uint32 priority = 2;
// @inject_tag: `gorm:"not_null`
string name = 3;
}
Loading…
Cancel
Save