Reference PreferredEndpoints by top level struct name (#1688)

* Reference PreferredEndpoints by top level struct name instead of embedded proto GetPreferredEndpoint method.

Reorganize the sql files a little.

* Make PreferredEndpoints replayable. Test refactoring to remove variables that persist across test calls. DeleteItems instead of trying to use WithWhere.
This commit is contained in:
Todd authored and GitHub committed 2021-11-11 13:51:52 -07:00
1 parent 30e9f944a2
commit 29b4537d3b
7 files changed
+382 -389

No files matched your search

@@ -335,63 +335,55 @@ begin;
create trigger insert_host_plugin_set_member before insert on host_plugin_set_member
for each row execute procedure insert_host_plugin_set_member();
create table host_set_preferred_endpoint (
create_time wt_timestamp,
host_set_id wt_public_id not null
constraint host_set_fkey
references host_set(public_id)
on delete cascade
on update cascade,
priority wt_priority,
condition text not null
constraint condition_must_not_be_too_short
check(length(trim(condition)) > 4) -- minimum is 'dns:*'
constraint condition_must_not_be_too_long
check(length(trim(condition)) < 255)
constraint condition_has_valid_prefix
check(
left(trim(condition), 4) = 'dns:'
or
left(trim(condition), 5) = 'cidr:'
)
constraint condition_does_not_contain_invalid_chars
check(
position('|' in trim(condition)) = 0
and
position('=' in trim(condition)) = 0
),
primary key(host_set_id, priority),
constraint host_set_preferred_endpoint_host_set_id_condition_uq
unique(host_set_id, condition)
);
-- host_set_immutable_preferred_endpoint() ensures that endpoint conditions
-- assigned to host sets are immutable.
create function
host_set_immutable_preferred_endpoint()
returns trigger
as $$
begin
raise exception 'preferred endpoints are immutable';
end;
$$ language plpgsql;
create trigger immutable_preferred_endpoint
before update on host_set_preferred_endpoint
for each row execute procedure host_set_immutable_preferred_endpoint();
insert into oplog_ticket (name, version)
values
('host_plugin_catalog', 1),
('host_plugin_catalog_secret', 1),
('host_plugin_set', 1),
('host_plugin_host', 1);
-- host_plugin_catalog_with_secret is useful for reading a plugin catalog with
-- its associated persisted data.
create view host_plugin_catalog_with_secret as
select
hc.public_id,
hc.scope_id,
hc.plugin_id,
hc.name,
hc.description,
hc.create_time,
hc.update_time,
hc.version,
hc.attributes,
hcs.secret,
hcs.key_id,
hcs.create_time as persisted_create_time,
hcs.update_time as persisted_update_time
from
host_plugin_catalog hc
left outer join host_plugin_catalog_secret hcs on hc.public_id = hcs.catalog_id;
comment on view host_plugin_catalog_with_secret is
'host plugin catalog with its associated persisted data';
-- host_plugin_host_with_value_obj_and_set_memberships is useful for reading a
-- plugin host with its associated value objects (ip addresses, dns names) and
-- 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_and_set_memberships as
select
h.public_id,
h.catalog_id,
h.external_id,
hc.scope_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 host(hip.address), '|') as ip_addresses,
string_agg(distinct hdns.name, '|') as dns_names,
string_agg(distinct hpsm.set_id, '|') as set_ids
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
left outer join host_plugin_set_member hpsm on h.public_id = hpsm.host_id
group by h.public_id, hc.plugin_id, hc.scope_id;
comment on view host_plugin_host_with_value_obj_and_set_memberships is
'host plugin host with its associated value objects';
commit;
@@ -0,0 +1,85 @@
begin;
-- host_plugin_host_set_with_value_obj is useful for reading a plugin host set with its
-- associated value objects (preferred endpoints) as columns with delimited
-- values. The delimiter depends on the value objects (e.g. if they need
-- ordering).
create view host_plugin_host_set_with_value_obj as
select
hs.public_id,
hs.catalog_id,
hc.plugin_id,
hs.name,
hs.description,
hs.create_time,
hs.update_time,
hs.last_sync_time,
hs.need_sync,
hs.sync_interval_seconds,
hs.version,
hs.attributes,
-- the string_agg(..) column will be null if there are no associated value objects
string_agg(distinct concat_ws('=', hspe.priority, hspe.condition), '|') as preferred_endpoints,
string_agg(distinct hpsm.host_id, '|') as host_ids
from
host_plugin_set hs
join host_plugin_catalog hc on hs.catalog_id = hc.public_id
left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id
left outer join host_plugin_set_member hpsm on hs.public_id = hpsm.set_id
group by hs.public_id, hc.plugin_id;
comment on view host_plugin_host_set_with_value_obj is
'host plugin host set with its associated value objects';
-- host_plugin_catalog_with_secret is useful for reading a plugin catalog with
-- its associated persisted data.
create view host_plugin_catalog_with_secret as
select
hc.public_id,
hc.scope_id,
hc.plugin_id,
hc.name,
hc.description,
hc.create_time,
hc.update_time,
hc.version,
hc.attributes,
hcs.secret,
hcs.key_id,
hcs.create_time as persisted_create_time,
hcs.update_time as persisted_update_time
from
host_plugin_catalog hc
left outer join host_plugin_catalog_secret hcs on hc.public_id = hcs.catalog_id;
comment on view host_plugin_catalog_with_secret is
'host plugin catalog with its associated persisted data';
-- host_plugin_host_with_value_obj_and_set_memberships is useful for reading a
-- plugin host with its associated value objects (ip addresses, dns names) and
-- 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_and_set_memberships as
select
h.public_id,
h.catalog_id,
h.external_id,
hc.scope_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 host(hip.address), '|') as ip_addresses,
string_agg(distinct hdns.name, '|') as dns_names,
string_agg(distinct hpsm.set_id, '|') as set_ids
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
left outer join host_plugin_set_member hpsm on h.public_id = hpsm.host_id
group by h.public_id, hc.plugin_id, hc.scope_id;
comment on view host_plugin_host_with_value_obj_and_set_memberships is
'host plugin host with its associated value objects';
commit;
@@ -1,78 +0,0 @@
begin;
create table host_set_preferred_endpoint (
create_time wt_timestamp,
host_set_id wt_public_id not null
constraint host_set_fkey
references host_set(public_id)
on delete cascade
on update cascade,
priority wt_priority,
condition text not null
constraint condition_must_not_be_too_short
check(length(trim(condition)) > 4) -- minimum is 'dns:*'
constraint condition_must_not_be_too_long
check(length(trim(condition)) < 255)
constraint condition_has_valid_prefix
check(
left(trim(condition), 4) = 'dns:'
or
left(trim(condition), 5) = 'cidr:'
)
constraint condition_does_not_contain_invalid_chars
check(
position('|' in trim(condition)) = 0
and
position('=' in trim(condition)) = 0
),
primary key(host_set_id, priority),
constraint host_set_preferred_endpoint_host_set_id_condition_uq
unique(host_set_id, condition)
);
-- host_set_immutable_preferred_endpoint() ensures that endpoint conditions
-- assigned to host sets are immutable.
create function
host_set_immutable_preferred_endpoint()
returns trigger
as $$
begin
raise exception 'preferred endpoints are immutable';
end;
$$ language plpgsql;
create trigger immutable_preferred_endpoint
before update on host_set_preferred_endpoint
for each row execute procedure host_set_immutable_preferred_endpoint();
-- host_plugin_host_set_with_value_obj is useful for reading a plugin host set with its
-- associated value objects (preferred endpoints) as columns with delimited
-- values. The delimiter depends on the value objects (e.g. if they need
-- ordering).
create view host_plugin_host_set_with_value_obj as
select
hs.public_id,
hs.catalog_id,
hc.plugin_id,
hs.name,
hs.description,
hs.create_time,
hs.update_time,
hs.last_sync_time,
hs.need_sync,
hs.sync_interval_seconds,
hs.version,
hs.attributes,
-- the string_agg(..) column will be null if there are no associated value objects
string_agg(distinct concat_ws('=', hspe.priority, hspe.condition), '|') as preferred_endpoints,
string_agg(distinct hpsm.host_id, '|') as host_ids
from
host_plugin_set hs
join host_plugin_catalog hc on hs.catalog_id = hc.public_id
left outer join host_set_preferred_endpoint hspe on hs.public_id = hspe.host_set_id
left outer join host_plugin_set_member hpsm on hs.public_id = hpsm.set_id
group by hs.public_id, hc.plugin_id;
comment on view host_plugin_host_set_with_value_obj is
'host plugin host set with its associated value objects';
commit;
+30 -22
View File
@@ -10,7 +10,6 @@ import (
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/host"
"github.com/hashicorp/boundary/internal/host/store"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/endpoint"
"github.com/hashicorp/boundary/internal/libs/patchstruct"
@@ -358,9 +357,9 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet,
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(r db.Reader, w db.Writer) error {
func(reader db.Reader, w db.Writer) error {
returnedSet = newSet.clone()
msgs := make([]*oplog.Message, 0, len(preferredEndpoints)+2)
msgs := make([]*oplog.Message, 0, len(preferredEndpoints) + len(currentSet.PreferredEndpoints) + 2)
ticket, err := w.GetTicket(s)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
@@ -391,43 +390,43 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet,
switch endpointOp {
case endpointOpDelete:
// Delete all old endpoint entries.
var peDeleteOplogMsg oplog.Message
pep := &host.PreferredEndpoint{
PreferredEndpoint: &store.PreferredEndpoint{
HostSetId: newSet.PublicId,
Priority: 1,
},
var peps []interface{}
for i := 1; i <= len(currentSet.PreferredEndpoints); i++ {
p := host.AllocPreferredEndpoint()
p.HostSetId, p.Priority = currentSet.GetPublicId(), uint32(i)
peps = append(peps, p)
}
_, err := w.Delete(ctx, pep, db.WithWhere("host_set_id = ?", newSet.PublicId), db.NewOplogMsg(&peDeleteOplogMsg))
deleteOplogMsgs := make([]*oplog.Message, 0, len(peps))
_, err := w.DeleteItems(ctx, peps, db.NewOplogMsgs(&deleteOplogMsgs))
if err != nil {
return errors.Wrap(ctx, err, op)
}
// Only append the oplog message if an operation was actually
// performed.
if peDeleteOplogMsg.Message != nil {
if len(deleteOplogMsgs) > 0 {
preferredEndpointsUpdated = true
msgs = append(msgs, &peDeleteOplogMsg)
msgs = append(msgs, deleteOplogMsgs...)
}
case endpointOpUpdate:
// Delete all old endpoint entries.
var peDeleteOplogMsg oplog.Message
pep := &host.PreferredEndpoint{
PreferredEndpoint: &store.PreferredEndpoint{
HostSetId: newSet.PublicId,
Priority: 1,
},
var peps []interface{}
for i := 1; i <= len(currentSet.PreferredEndpoints); i++ {
p := host.AllocPreferredEndpoint()
p.HostSetId, p.Priority = currentSet.GetPublicId(), uint32(i)
peps = append(peps, p)
}
_, err := w.Delete(ctx, pep, db.WithWhere("host_set_id = ?", newSet.PublicId), db.NewOplogMsg(&peDeleteOplogMsg))
deleteOplogMsgs := make([]*oplog.Message, 0, len(peps))
_, err := w.DeleteItems(ctx, peps, db.WithDebug(true), db.NewOplogMsgs(&deleteOplogMsgs))
if err != nil {
return errors.Wrap(ctx, err, op)
}
// Only append the oplog message if an operation was actually
// performed.
if peDeleteOplogMsg.Message != nil {
msgs = append(msgs, &peDeleteOplogMsg)
if len(deleteOplogMsgs) > 0 {
msgs = append(msgs, deleteOplogMsgs...)
}
// Create the new entries.
@@ -469,6 +468,15 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet,
}
}
hsAgg := &hostSetAgg{PublicId: currentSet.GetPublicId()}
if err := reader.LookupByPublicId(ctx, hsAgg); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("looking up host after update"))
}
returnedSet, err = hsAgg.toHostSet(ctx)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("converting from host aggregate to host after update"))
}
if !pluginCalledSuccessfully {
_, err = plgClient.OnUpdateSet(ctx, &plgpb.OnUpdateSetRequest{
CurrentSet: currentPlgSet,
@@ -714,7 +722,7 @@ func toPluginSet(ctx context.Context, in *HostSet) (*pb.HostSet, error) {
Id: in.GetPublicId(),
Name: name,
Description: description,
PreferredEndpoints: in.GetPreferredEndpoints(),
PreferredEndpoints: in.PreferredEndpoints,
}
if in.GetAttributes() != nil {
attrs := &structpb.Struct{}
+216 -230
View File
@@ -19,7 +19,6 @@ import (
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
hostplg "github.com/hashicorp/boundary/internal/plugin/host"
hostplugin "github.com/hashicorp/boundary/internal/plugin/host"
"github.com/hashicorp/boundary/internal/scheduler"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/assert"
@@ -411,26 +410,9 @@ func TestRepository_UpdateSet(t *testing.T) {
dbKmsCache := kms.TestKms(t, dbConn, dbWrapper)
_, projectScope := iam.TestScopes(t, iam.TestRepo(t, dbConn, dbWrapper))
// Define a plugin "manager", basically just a map with a mock
// plugin in it. This also includes functionality to capture the
// state, and set an error and the returned secrets to nil. Note
// that the way that this is set up means that the tests cannot run
// in parallel, but there could be other factors affecting that as
// well.
var gotOnUpdateCallCount int
var gotOnUpdateSetRequest *plgpb.OnUpdateSetRequest
var pluginError error
testPlugin := hostplg.TestPlugin(t, dbConn, "test")
testPluginMap := map[string]plgpb.HostPluginServiceClient{
testPlugin.GetPublicId(): &WrappingPluginClient{
Server: &TestPluginServer{
OnUpdateSetFn: func(_ context.Context, req *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error) {
gotOnUpdateCallCount++
gotOnUpdateSetRequest = req
return &plgpb.OnUpdateSetResponse{}, pluginError
},
},
},
dummyPluginMap := map[string]plgpb.HostPluginServiceClient{
testPlugin.GetPublicId(): &WrappingPluginClient{Server: &plgpb.UnimplementedHostPluginServiceServer{}},
}
// Set up a test catalog and the secrets for it
@@ -451,7 +433,7 @@ func TestRepository_UpdateSet(t *testing.T) {
// exists to ensure that we can test for conflicts when setting
// the name.
const testDuplicateSetName = "duplicate-set-name"
testDuplicateSet := TestSet(t, dbConn, dbKmsCache, sched, testCatalog, testPluginMap)
testDuplicateSet := TestSet(t, dbConn, dbKmsCache, sched, testCatalog, dummyPluginMap)
testDuplicateSet.Name = testDuplicateSetName
setsUpdated, err := dbRW.Update(ctx, testDuplicateSet, []string{"name"}, []string{})
require.NoError(t, err)
@@ -525,176 +507,60 @@ func TestRepository_UpdateSet(t *testing.T) {
// Note that we define some state here, similar to how we
// previously defined gotOnUpdateCatalogRequest above next the
// plugin map.
type checkFunc func(t *testing.T, ctx context.Context)
var (
gotSet *HostSet
gotNumUpdated int
)
type checkHostSetFunc func(t *testing.T, got *HostSet)
checkVersion := func(want uint32) checkFunc {
return func(t *testing.T, ctx context.Context) {
checkVersion := func(want uint32) checkHostSetFunc {
return func(t *testing.T, got *HostSet) {
t.Helper()
assert := assert.New(t)
assert.Equal(want, gotSet.Version)
assert.Equal(t, want, got.Version, "checkVersion")
}
}
checkName := func(want string) checkFunc {
return func(t *testing.T, ctx context.Context) {
checkName := func(want string) checkHostSetFunc {
return func(t *testing.T, got *HostSet) {
t.Helper()
assert := assert.New(t)
assert.Equal(want, gotSet.Name)
assert.Equal(t, want, got.Name, "checkName")
}
}
checkDescription := func(want string) checkFunc {
return func(t *testing.T, ctx context.Context) {
checkDescription := func(want string) checkHostSetFunc {
return func(t *testing.T, got *HostSet) {
t.Helper()
assert := assert.New(t)
assert.Equal(want, gotSet.Description)
assert.Equal(t, want, got.Description, "checkDescription")
}
}
checkPreferredEndpoints := func(want []string) checkFunc {
return func(t *testing.T, ctx context.Context) {
checkPreferredEndpoints := func(want []string) checkHostSetFunc {
return func(t *testing.T, got *HostSet) {
t.Helper()
assert := assert.New(t)
assert.Equal(want, gotSet.PreferredEndpoints)
assert.Equal(t, want, got.PreferredEndpoints, "checkPreferredEndpoints")
}
}
checkAttributes := func(want map[string]interface{}) checkFunc {
return func(t *testing.T, ctx context.Context) {
checkAttributes := func(want map[string]interface{}) checkHostSetFunc {
return func(t *testing.T, got *HostSet) {
t.Helper()
assert := assert.New(t)
require := require.New(t)
st := &structpb.Struct{}
require.NoError(proto.Unmarshal(gotSet.Attributes, st))
assert.Empty(cmp.Diff(mustStruct(want), st, protocmp.Transform()))
require.NoError(t, proto.Unmarshal(got.Attributes, st))
assert.Empty(t, cmp.Diff(mustStruct(want), st, protocmp.Transform()))
}
}
checkNeedSync := func(want bool) checkFunc {
return func(t *testing.T, ctx context.Context) {
checkNeedSync := func(want bool) checkHostSetFunc {
return func(t *testing.T, got *HostSet) {
t.Helper()
assert := assert.New(t)
assert.Equal(want, gotSet.NeedSync)
assert.Equal(t, want, got.NeedSync, "checkNeedSync")
}
}
checkUpdateSetRequestCurrentNameNil := func() checkFunc {
return func(t *testing.T, ctx context.Context) {
checkVerifySetOplog := func(op oplog.OpType) checkHostSetFunc {
return func(t *testing.T, got *HostSet) {
t.Helper()
assert := assert.New(t)
assert.Nil(gotOnUpdateSetRequest.CurrentSet.Name)
}
}
checkUpdateSetRequestNewName := func(want string) checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Equal(wrapperspb.String(want), gotOnUpdateSetRequest.NewSet.Name)
}
}
checkUpdateSetRequestNewNameNil := func() checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Nil(gotOnUpdateSetRequest.NewSet.Name)
}
}
checkUpdateSetRequestCurrentDescriptionNil := func() checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Nil(gotOnUpdateSetRequest.CurrentSet.Description)
}
}
checkUpdateSetRequestNewDescription := func(want string) checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Equal(wrapperspb.String(want), gotOnUpdateSetRequest.NewSet.Description)
}
}
checkUpdateSetRequestNewDescriptionNil := func() checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Nil(gotOnUpdateSetRequest.NewSet.Description)
}
}
checkUpdateSetRequestCurrentPreferredEndpoints := func(want []string) checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Equal(want, gotOnUpdateSetRequest.CurrentSet.PreferredEndpoints)
}
}
checkUpdateSetRequestNewPreferredEndpoints := func(want []string) checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Equal(want, gotOnUpdateSetRequest.NewSet.PreferredEndpoints)
}
}
checkUpdateSetRequestNewPreferredEndpointsNil := func() checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Nil(gotOnUpdateSetRequest.NewSet.PreferredEndpoints)
}
}
checkUpdateSetRequestCurrentAttributes := func(want map[string]interface{}) checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateSetRequest.CurrentSet.Attributes, protocmp.Transform()))
}
}
checkUpdateSetRequestNewAttributes := func(want map[string]interface{}) checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateSetRequest.NewSet.Attributes, protocmp.Transform()))
}
}
checkUpdateSetRequestPersistedSecrets := func(want map[string]interface{}) checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Empty(cmp.Diff(mustStruct(want), gotOnUpdateSetRequest.Persisted.Secrets, protocmp.Transform()))
}
}
checkNumUpdated := func(want int) checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.Equal(want, gotNumUpdated)
}
}
checkVerifySetOplog := func(op oplog.OpType) checkFunc {
return func(t *testing.T, ctx context.Context) {
t.Helper()
assert := assert.New(t)
assert.NoError(
assert.NoError(t,
db.TestVerifyOplog(
t,
dbRW,
gotSet.PublicId,
got.PublicId,
db.WithOperation(op),
db.WithCreateNotBefore(10*time.Second),
),
@@ -702,6 +568,92 @@ func TestRepository_UpdateSet(t *testing.T) {
}
}
type checkPluginReqFunc func(t *testing.T, got *plgpb.OnUpdateSetRequest)
checkUpdateSetRequestCurrentNameNil := func() checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Nil(t, got.CurrentSet.Name, "checkUpdateSetRequestCurrentNameNil")
}
}
checkUpdateSetRequestNewName := func(want string) checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Equal(t, wrapperspb.String(want), got.NewSet.Name, "checkUpdateSetRequestNewName")
}
}
checkUpdateSetRequestNewNameNil := func() checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Nil(t, got.NewSet.Name, "checkUpdateSetRequestNewNameNil")
}
}
checkUpdateSetRequestCurrentDescriptionNil := func() checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Nil(t, got.CurrentSet.Description, "checkUpdateSetRequestCurrentDescriptionNil")
}
}
checkUpdateSetRequestNewDescription := func(want string) checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Equal(t, wrapperspb.String(want), got.NewSet.Description, "checkUpdateSetRequestNewDescription")
}
}
checkUpdateSetRequestNewDescriptionNil := func() checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Nil(t, got.NewSet.Description, "checkUpdateSetRequestNewDescriptionNil")
}
}
checkUpdateSetRequestCurrentPreferredEndpoints := func(want []string) checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Equal(t, want, got.CurrentSet.PreferredEndpoints, "checkUpdateSetRequestCurrentPreferredEndpoints")
}
}
checkUpdateSetRequestNewPreferredEndpoints := func(want []string) checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Equal(t, want, got.NewSet.PreferredEndpoints, "checkUpdateSetRequestNewPreferredEndpoints")
}
}
checkUpdateSetRequestNewPreferredEndpointsNil := func() checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Nil(t, got.NewSet.PreferredEndpoints, "checkUpdateSetRequestNewPreferredEndpointsNil")
}
}
checkUpdateSetRequestCurrentAttributes := func(want map[string]interface{}) checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Empty(t, cmp.Diff(mustStruct(want), got.CurrentSet.Attributes, protocmp.Transform()), "checkUpdateSetRequestCurrentAttributes")
}
}
checkUpdateSetRequestNewAttributes := func(want map[string]interface{}) checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Empty(t, cmp.Diff(mustStruct(want), got.NewSet.Attributes, protocmp.Transform()), "checkUpdateSetRequestNewAttributes")
}
}
checkUpdateSetRequestPersistedSecrets := func(want map[string]interface{}) checkPluginReqFunc {
return func(t *testing.T, got *plgpb.OnUpdateSetRequest) {
t.Helper()
assert.Empty(t, cmp.Diff(mustStruct(want), got.Persisted.Secrets, protocmp.Transform()), "checkUpdateSetRequestPersistedSecrets")
}
}
tests := []struct {
name string
withScopeId *string
@@ -709,9 +661,10 @@ func TestRepository_UpdateSet(t *testing.T) {
withPluginError error
changeFuncs []changeHostSetFunc
version uint32
fieldMask []string
wantCheckFuncs []checkFunc
wantIsErr errors.Code
fieldMask []string
wantCheckSetFuncs []checkHostSetFunc
wantCheckPluginReqFuncs []checkPluginReqFunc
wantIsErr errors.Code
}{
{
name: "nil set",
@@ -784,16 +737,17 @@ func TestRepository_UpdateSet(t *testing.T) {
changeFuncs: []changeHostSetFunc{changeName("foo")},
version: 2,
fieldMask: []string{"name"},
wantCheckFuncs: []checkFunc{
checkVersion(3),
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentNameNil(),
checkUpdateSetRequestNewName("foo"),
checkName("foo"),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(3),
checkName("foo"),
checkNeedSync(false),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -802,16 +756,17 @@ func TestRepository_UpdateSet(t *testing.T) {
changeFuncs: []changeHostSetFunc{changeName("")},
version: 2,
fieldMask: []string{"name"},
wantCheckFuncs: []checkFunc{
checkVersion(2), // Version remains same even though row is updated
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentNameNil(),
checkUpdateSetRequestNewNameNil(),
checkName(""),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(2), // Version remains same even though row is updated
checkName(""),
checkNeedSync(false),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -820,16 +775,17 @@ func TestRepository_UpdateSet(t *testing.T) {
changeFuncs: []changeHostSetFunc{changeDescription("foo")},
version: 2,
fieldMask: []string{"description"},
wantCheckFuncs: []checkFunc{
checkVersion(3),
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentDescriptionNil(),
checkUpdateSetRequestNewDescription("foo"),
checkDescription("foo"),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(3),
checkDescription("foo"),
checkNeedSync(false),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -838,16 +794,17 @@ func TestRepository_UpdateSet(t *testing.T) {
changeFuncs: []changeHostSetFunc{changeDescription("")},
version: 2,
fieldMask: []string{"description"},
wantCheckFuncs: []checkFunc{
checkVersion(2), // Version remains same even though row is updated
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentDescriptionNil(),
checkUpdateSetRequestNewDescriptionNil(),
checkDescription(""),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(2), // Version remains same even though row is updated
checkDescription(""),
checkNeedSync(false),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -856,16 +813,17 @@ func TestRepository_UpdateSet(t *testing.T) {
changeFuncs: []changeHostSetFunc{changePreferredEndpoints([]string{"cidr:10.0.0.0/24"})},
version: 2,
fieldMask: []string{"PreferredEndpoints"},
wantCheckFuncs: []checkFunc{
checkVersion(3),
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}),
checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}),
checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(3),
checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}),
checkNeedSync(false),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -874,16 +832,17 @@ func TestRepository_UpdateSet(t *testing.T) {
changeFuncs: []changeHostSetFunc{changePreferredEndpoints(nil)},
version: 2,
fieldMask: []string{"PreferredEndpoints"},
wantCheckFuncs: []checkFunc{
checkVersion(3),
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}),
checkUpdateSetRequestNewPreferredEndpointsNil(),
checkPreferredEndpoints(nil),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(3),
checkPreferredEndpoints(nil),
checkNeedSync(false),
checkNumUpdated(1),
},
},
{
@@ -893,8 +852,7 @@ func TestRepository_UpdateSet(t *testing.T) {
})},
version: 2,
fieldMask: []string{"attributes"},
wantCheckFuncs: []checkFunc{
checkVersion(3),
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentAttributes(map[string]interface{}{
"foo": "bar",
}),
@@ -902,15 +860,17 @@ func TestRepository_UpdateSet(t *testing.T) {
"foo": "bar",
"baz": "qux",
}),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(3),
checkAttributes(map[string]interface{}{
"foo": "bar",
"baz": "qux",
}),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
checkNeedSync(true),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -921,22 +881,23 @@ func TestRepository_UpdateSet(t *testing.T) {
})},
version: 2,
fieldMask: []string{"attributes"},
wantCheckFuncs: []checkFunc{
checkVersion(3),
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentAttributes(map[string]interface{}{
"foo": "bar",
}),
checkUpdateSetRequestNewAttributes(map[string]interface{}{
"foo": "baz",
}),
checkAttributes(map[string]interface{}{
"foo": "baz",
}),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(3),
checkAttributes(map[string]interface{}{
"foo": "baz",
}),
checkNeedSync(true),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -947,18 +908,19 @@ func TestRepository_UpdateSet(t *testing.T) {
})},
version: 2,
fieldMask: []string{"attributes"},
wantCheckFuncs: []checkFunc{
checkVersion(3),
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentAttributes(map[string]interface{}{
"foo": "bar",
}),
checkUpdateSetRequestNewAttributes(map[string]interface{}{}),
checkAttributes(map[string]interface{}{}),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(3),
checkAttributes(map[string]interface{}{}),
checkNeedSync(true),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -967,18 +929,19 @@ func TestRepository_UpdateSet(t *testing.T) {
changeFuncs: []changeHostSetFunc{changeAttributesNil()},
version: 2,
fieldMask: []string{"attributes"},
wantCheckFuncs: []checkFunc{
checkVersion(3),
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentAttributes(map[string]interface{}{
"foo": "bar",
}),
checkUpdateSetRequestNewAttributes(map[string]interface{}{}),
checkAttributes(map[string]interface{}{}),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(3),
checkAttributes(map[string]interface{}{}),
checkNeedSync(true),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -990,8 +953,7 @@ func TestRepository_UpdateSet(t *testing.T) {
})},
version: 2,
fieldMask: []string{"attributes.a", "attributes.foo"},
wantCheckFuncs: []checkFunc{
checkVersion(3),
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentAttributes(map[string]interface{}{
"foo": "bar",
}),
@@ -999,15 +961,17 @@ func TestRepository_UpdateSet(t *testing.T) {
"a": "b",
"foo": "baz",
}),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(3),
checkAttributes(map[string]interface{}{
"a": "b",
"foo": "baz",
}),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
checkNeedSync(true),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -1019,19 +983,20 @@ func TestRepository_UpdateSet(t *testing.T) {
},
version: 2,
fieldMask: []string{"name", "PreferredEndpoints"},
wantCheckFuncs: []checkFunc{
checkVersion(3),
wantCheckPluginReqFuncs: []checkPluginReqFunc{
checkUpdateSetRequestCurrentNameNil(),
checkUpdateSetRequestNewName("foo"),
checkName("foo"),
checkUpdateSetRequestCurrentPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}),
checkUpdateSetRequestNewPreferredEndpoints([]string{"cidr:10.0.0.0/24"}),
checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}),
checkUpdateSetRequestPersistedSecrets(map[string]interface{}{
"one": "two",
}),
},
wantCheckSetFuncs: []checkHostSetFunc{
checkVersion(3),
checkName("foo"),
checkPreferredEndpoints([]string{"cidr:10.0.0.0/24"}),
checkNeedSync(false),
checkNumUpdated(1),
checkVerifySetOplog(oplog.OpType_OP_TYPE_UPDATE),
},
},
@@ -1056,7 +1021,7 @@ func TestRepository_UpdateSet(t *testing.T) {
dbKmsCache,
sched,
testCatalog,
testPluginMap,
dummyPluginMap,
WithPreferredEndpoints([]string{"cidr:192.168.0.0/24", "cidr:192.168.1.0/24", "cidr:172.16.0.0/12"}),
)
// Set some (default) attributes on our test set
@@ -1091,14 +1056,34 @@ func TestRepository_UpdateSet(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
require := require.New(t)
assert := assert.New(t)
// Define a plugin "manager", basically just a map with a mock
// plugin in it. This also includes functionality to capture the
// state, and set an error and the returned secrets to nil. Note
// that the way that this is set up means that the tests cannot run
// in parallel, but there could be other factors affecting that as
// well.
var gotOnUpdateCallCount int
testPluginMap := map[string]plgpb.HostPluginServiceClient{
testPlugin.GetPublicId(): &WrappingPluginClient{
Server: &TestPluginServer{
OnUpdateSetFn: func(_ context.Context, req *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error) {
gotOnUpdateCallCount++
for _, check := range tt.wantCheckPluginReqFuncs {
check(t, req)
}
return &plgpb.OnUpdateSetResponse{}, tt.withPluginError
},
},
},
}
origSet := setupHostSet(t, ctx)
pluginMap := testPluginMap
if tt.withEmptyPluginMap {
pluginMap = make(map[string]plgpb.HostPluginServiceClient)
}
pluginError = tt.withPluginError
t.Cleanup(func() { pluginError = nil })
repo, err := NewRepository(dbRW, dbRW, dbKmsCache, sched, pluginMap)
require.NoError(err)
require.NotNil(repo)
@@ -1113,9 +1098,7 @@ func TestRepository_UpdateSet(t *testing.T) {
scopeId = *tt.withScopeId
}
var gotHosts []*Host
var gotPlugin *hostplugin.Plugin
gotSet, gotHosts, gotPlugin, gotNumUpdated, err = repo.UpdateSet(ctx, scopeId, workingSet, tt.version, tt.fieldMask)
gotUpdatedSet, gotHosts, gotPlugin, gotNumUpdated, err := repo.UpdateSet(ctx, scopeId, workingSet, tt.version, tt.fieldMask)
t.Cleanup(func() { gotOnUpdateCallCount = 0 })
if tt.wantIsErr != 0 {
require.Equal(db.NoRowsAffected, gotNumUpdated)
@@ -1124,14 +1107,14 @@ func TestRepository_UpdateSet(t *testing.T) {
}
require.NoError(err)
require.Equal(1, gotOnUpdateCallCount)
t.Cleanup(func() { gotOnUpdateSetRequest = nil })
assert.Equal(1, gotNumUpdated)
// Quick assertion that the set is not nil and that the plugin
// ID in the catalog referenced by the set matches the plugin
// ID in the returned plugin.
require.NotNil(gotSet)
require.NotNil(gotUpdatedSet)
require.NotNil(gotPlugin)
assert.Equal(testCatalog.PublicId, gotSet.CatalogId)
assert.Equal(testCatalog.PublicId, gotUpdatedSet.CatalogId)
assert.Equal(testCatalog.PluginId, gotPlugin.PublicId)
// Also assert that the hosts returned by the request are the ones that belong to the set
@@ -1146,9 +1129,12 @@ func TestRepository_UpdateSet(t *testing.T) {
assert.Equal(wantHostMap, gotHostMap)
// Perform checks
for _, check := range tt.wantCheckFuncs {
check(t, ctx)
for _, check := range tt.wantCheckSetFuncs {
check(t, gotUpdatedSet)
}
gotLookupSet, gotPlugin, err := repo.LookupSet(ctx, workingSet.GetPublicId())
assert.Empty(cmp.Diff(gotUpdatedSet, gotLookupSet, protocmp.Transform()))
})
}
}
+2 -2
View File
@@ -59,8 +59,8 @@ func (pe *PreferredEndpoint) validate(ctx context.Context, caller errors.Op) err
}
// AllocPreferredEndpoint makes an empty one in-memory
func AllocPreferredEndpoint() PreferredEndpoint {
return PreferredEndpoint{
func AllocPreferredEndpoint() *PreferredEndpoint {
return &PreferredEndpoint{
PreferredEndpoint: &store.PreferredEndpoint{},
}
}
+4 -4
View File
@@ -61,7 +61,7 @@ func TestPreferredEndpoint_Create(t *testing.T) {
want.HostSetId = set.PublicId
want.Condition = "cidr:1.2.3.4"
want.Priority = 1
return &want
return want
}(),
},
{
@@ -77,7 +77,7 @@ func TestPreferredEndpoint_Create(t *testing.T) {
want.HostSetId = set.PublicId
want.Condition = "cidr:1.2.3.4"
want.Priority = 1
return &want
return want
}(),
wantCreateErr: true,
wantCreateIsErr: errors.NotUnique,
@@ -141,8 +141,8 @@ func TestPreferredEndpoint_Create(t *testing.T) {
assert.NoError(err)
}
found := host.AllocPreferredEndpoint()
require.NoError(rw.LookupWhere(ctx, &found, "host_set_id = ? and priority = ?", tt.args.hostSetId, tt.args.priority))
assert.Equal(got, &found)
require.NoError(rw.LookupWhere(ctx, found, "host_set_id = ? and priority = ?", tt.args.hostSetId, tt.args.priority))
assert.Equal(got, found)
}
})
}