feat(host): Add host plugin related schemas and tests (#1475)
* feat(host): Add host plugin related schemas and tests This adds the schema files for host plugins, plugin based catalogs and plugin based sets. This also adds the protos and tests which utilize gorm to ensure the schemas enforce the constraints we expect. Along with this change was a migration which moved catalog names to the base type table so we can ensure all catalogs in a single scope, regardless of subtype, have a unique name. * Updated plugin schemas to do the following: - Add plugin_executable table - plugin table now also has scope_id and name w/ uniqueness constraints - host_plugin no longer has id_prefix column - host_plugin separates name and plugin_name - plugin_version table renamed version to symantic_version column. * Fix typo in delete_plugin_subtype trigger. Add update test for host plugins. * Update id prefixes. Fix public_id typos in protos and plugin_executable name in sql diagrams. * Created internal/plugin and moved base plugin structs to it and moved host plugin subdirectory to it. * Created internal/plugin and moved base plugin structs to it and moved host plugin subdirectory to it. * Add first pass at enums for OS and Arch supported by plugin executables. * Capitalizing the const enums for OS and Architectures. * Add comments to sql files. * Reviewer comments. Remove unsupported OSes and Archs from SQL and go constants. Add missing tests. * Be more permissive in the values the db accepts. The go logic can restrict which values it will allow getting populated into the db. * Removing constraints on which values can be placed inside operating_system and architecture columns. * Updating test helper functions to only create a single catalog or set. * Move files to new migration version. * Fixing job/plugin test. * Update table names, named a number of constraints, remove plugin version and executable tables. * Add constraints for plugin name and id prefix. * Rename constraint, remove unhelpful comment block. * Adding semantic version to plugin_host and set attributes to not null. * Remove semantic versioning from all locations. * Remove semantic_version from plugin_host table comment. * Fix op copy/paste error. Don't use deprecated errors methods. Update constraint names. Add docs. * Apply suggestions from code review Fixing constraint names. Co-authored-by: Michael Gaffney <mgaffney@users.noreply.github.com>
This commit is contained in:
1 parent
71cba858e3
commit
db9e1106a9
39 files changed
+3987
-4
No files matched your search
@@ -121,6 +121,9 @@ protobuild:
|
||||
@protoc-go-inject-tag -input=./internal/db/db_test/db_test.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/host/store/host.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/host/static/store/static.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/host/plugin/store/host.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/plugin/host/store/plugin.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/plugin/store/plugin.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/authtoken/store/authtoken.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/auth/store/account.pb.go
|
||||
@protoc-go-inject-tag -input=./internal/auth/password/store/password.pb.go
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
begin;
|
||||
|
||||
-- We are updating the plugin table here to have a scope id, since
|
||||
-- subtypes of plugin should be scoped. We use 'global' as the default
|
||||
-- just for the purpose of the migration since the scope cannot be null
|
||||
-- and the one plugin that already exists (pi_system) can be globally
|
||||
-- scoped. The immediately following statement removes this default.
|
||||
-- We add a name so we can enforce that all plugins have a unique name
|
||||
-- in a specific scope across all plugin subtypes.
|
||||
alter table plugin
|
||||
add column name wt_name,
|
||||
add column scope_id wt_scope_id
|
||||
not null
|
||||
default 'global'
|
||||
constraint iam_scope_global_fkey
|
||||
references iam_scope_global(scope_id)
|
||||
on delete cascade
|
||||
on update cascade;
|
||||
|
||||
alter table plugin
|
||||
alter column scope_id drop default;
|
||||
|
||||
-- Add constraints that enforce names are unique across all plugin subtypes
|
||||
-- in a specific scope.
|
||||
alter table plugin
|
||||
add constraint plugin_scope_id_public_id_uq
|
||||
unique (scope_id, public_id),
|
||||
add constraint plugin_scope_id_name_uq
|
||||
unique (scope_id, name);
|
||||
|
||||
|
||||
-- insert, update, and delete plugin_subtypes are created since we are adding
|
||||
-- subtyped plugins and we need to keep the base table plugin in sync with all
|
||||
-- subtype tables.
|
||||
create function insert_plugin_subtype()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
insert into plugin
|
||||
(public_id, scope_id, name)
|
||||
values
|
||||
(new.public_id, new.scope_id, new.name);
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function insert_plugin_subtype() is
|
||||
'insert_plugin_subtype() inserts sub type name into the base type plugin table';
|
||||
|
||||
create function update_plugin_subtype()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
update plugin set name = new.name where public_id = new.public_id and new.name != name;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function update_plugin_subtype() is
|
||||
'update_plugin_subtype() will update base plugin type name column with new values from sub type';
|
||||
|
||||
-- delete_plugin_subtype() is an after delete trigger function
|
||||
-- for subtypes of plugin
|
||||
create function delete_plugin_subtype()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
delete from plugin
|
||||
where public_id = old.public_id;
|
||||
return null; -- result is ignored since this is an after trigger
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function delete_plugin_subtype is
|
||||
'delete_plugin_subtype() is an after trigger function for subytypes of plugin';
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('plugin', 1);
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,89 @@
|
||||
begin;
|
||||
|
||||
/*
|
||||
┌──────────────────┐
|
||||
│ plugin │
|
||||
├──────────────────┤
|
||||
│public_id (pk) │
|
||||
│scope_id (fk) │
|
||||
└──────────────────┘
|
||||
┼
|
||||
┼
|
||||
│
|
||||
┼
|
||||
┼
|
||||
┌──────────────────┐
|
||||
│ plugin_host │
|
||||
├──────────────────┤
|
||||
│public_id (pk) │
|
||||
│scope_id (fk) │
|
||||
│name │
|
||||
│description │
|
||||
│version │
|
||||
│plugin_name │
|
||||
│id_prefix │
|
||||
└──────────────────┘
|
||||
*/
|
||||
create table plugin_host (
|
||||
public_id wt_plugin_id primary key,
|
||||
scope_id wt_scope_id not null
|
||||
-- TODO: Allow plugins to be created in different scopes and
|
||||
-- constrain the host-catalog's plugin reference accordingly.
|
||||
constraint iam_scope_global_fkey
|
||||
references iam_scope_global(scope_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
name wt_name,
|
||||
description text,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
plugin_name text not null
|
||||
constraint plugin_name_must_be_not_empty
|
||||
check(length(trim(plugin_name)) > 0)
|
||||
constraint plugin_name_must_be_lowercase
|
||||
check(lower(trim(plugin_name)) = plugin_name)
|
||||
constraint plugin_host_plugin_name_uq
|
||||
unique,
|
||||
id_prefix text not null
|
||||
constraint plugin_id_prefix_must_be_not_empty
|
||||
check(length(trim(id_prefix)) > 0)
|
||||
constraint plugin_id_prefix_must_fit_format
|
||||
check (id_prefix ~ '^[a-z0-9]*$')
|
||||
constraint plugin_host_id_prefix_uq
|
||||
unique,
|
||||
constraint plugin_fkey
|
||||
foreign key (scope_id, public_id)
|
||||
references plugin(scope_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
constraint plugin_host_scope_id_name_uq
|
||||
unique(scope_id, name)
|
||||
);
|
||||
|
||||
create trigger update_version_column after update on plugin_host
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger update_time_column before update on plugin_host
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on plugin_host
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on plugin_host
|
||||
for each row execute procedure immutable_columns('public_id', 'create_time', 'plugin_name');
|
||||
|
||||
create trigger insert_plugin_subtype before insert on plugin_host
|
||||
for each row execute procedure insert_plugin_subtype();
|
||||
|
||||
create trigger update_plugin_subtype before update on plugin_host
|
||||
for each row execute procedure update_plugin_subtype();
|
||||
|
||||
create trigger delete_plugin_subtype after delete on plugin_host
|
||||
for each row execute procedure delete_plugin_subtype();
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('plugin_host', 1);
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,61 @@
|
||||
begin;
|
||||
|
||||
-- We are adding the name to the base host catalog type. This allows the db
|
||||
-- to ensure that catalog names are unique in a scope across all subtypes.
|
||||
alter table host_catalog
|
||||
add column name wt_name;
|
||||
|
||||
alter table host_catalog
|
||||
add constraint host_catalog_scope_id_name_uq
|
||||
unique (scope_id, name);
|
||||
|
||||
-- Now that we've added the name column to the base type, we copy
|
||||
-- the name from the static host catalog table into the base table.
|
||||
update host_catalog
|
||||
set name = st.name
|
||||
from
|
||||
static_host_catalog st
|
||||
where
|
||||
host_catalog.public_id = st.public_id and
|
||||
st.name is not null;
|
||||
|
||||
-- Replace the insert_host_catalog_subtype function defined in 0/20_host.up.sql
|
||||
-- to include the name.
|
||||
-- insert_host_catalog_subtype() is a before insert trigger
|
||||
-- function for subtypes of host_catalog
|
||||
create or replace function insert_host_catalog_subtype()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
insert into host_catalog
|
||||
(public_id, scope_id, name)
|
||||
values
|
||||
(new.public_id, new.scope_id, new.name);
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function insert_host_catalog_subtype() is
|
||||
'insert_host_catalog_subtype() inserts sub type name into the base type host catalog table';
|
||||
|
||||
-- Now that we are tracking the name, which is mutable, we need to also
|
||||
-- update the base table when the subtype tables are updated.
|
||||
-- update_host_catalog_subtype() is intended to be used as a before update
|
||||
-- trigger for all host catalog sub types. The purpose is to ensure that the
|
||||
-- base table for host catalog to contain the updated names for each host catalog
|
||||
-- in order to enforce uniqueness across all host catalogs, regardless of subtype,
|
||||
-- in a given scope.
|
||||
create function update_host_catalog_subtype()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
update host_catalog set name = new.name where public_id = new.public_id and new.name != name;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function update_host_catalog_subtype() is
|
||||
'update_host_catalog_subtype() will update base host catalog type name column with new values from sub type';
|
||||
|
||||
create trigger update_host_catalog_subtype before update on static_host_catalog
|
||||
for each row execute procedure update_host_catalog_subtype();
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,167 @@
|
||||
begin;
|
||||
|
||||
/*
|
||||
┌──────────────────┐
|
||||
│ plugin_host │
|
||||
├──────────────────┤
|
||||
│public_id (pk) │
|
||||
│... │
|
||||
└──────────────────┘
|
||||
┼
|
||||
┼
|
||||
○
|
||||
╱│╲
|
||||
┌─────────────────────┐
|
||||
│ host_plugin_catalog │
|
||||
├─────────────────────┤ ┌───────────────────────────┐
|
||||
┌────────────────┐ │public_id (pk) │ │host_plugin_catalog_secret │
|
||||
│host_catalog │ │plugin_id (fk) │ ├───────────────────────────┤
|
||||
├────────────────┤ │scope_id (fk) │ │host_catalog_id (pk, fk) │
|
||||
│public_id │┼┼──○┼│name │┼┼──○┼│secret │
|
||||
│scope_id │ │description │ │key_id (fk) │
|
||||
└────────────────┘ │attributes │ └───────────────────────────┘
|
||||
┼ └─────────────────────┘
|
||||
┼ ┼
|
||||
│ ┼
|
||||
│ ○
|
||||
○ ╱│╲
|
||||
╱│╲ ┌────────────────────┐
|
||||
┌────────────────┐ │ host_plugin_set │
|
||||
│ host_set │ ├────────────────────┤
|
||||
├────────────────┤ │public_id (pk) │
|
||||
│public_id │ │host_catalog_id (fk)│
|
||||
│host_catalog_id │┼┼──○┼│name │
|
||||
└────────────────┘ │description │
|
||||
│attributes │
|
||||
└────────────────────┘
|
||||
|
||||
*/
|
||||
|
||||
create table host_plugin_catalog (
|
||||
public_id wt_public_id primary key,
|
||||
scope_id wt_scope_id not null
|
||||
constraint iam_scope_fkey
|
||||
references iam_scope (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
plugin_id wt_plugin_id not null
|
||||
constraint plugin_host_fkey
|
||||
references plugin_host (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
name wt_name,
|
||||
description text,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
attributes bytea not null
|
||||
constraint attributes_must_not_be_empty
|
||||
check(length(attributes) > 0),
|
||||
constraint host_catalog_fkey
|
||||
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)
|
||||
);
|
||||
|
||||
create trigger update_version_column after update on host_plugin_catalog
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger update_time_column before update on host_plugin_catalog
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on host_plugin_catalog
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on host_plugin_catalog
|
||||
for each row execute procedure immutable_columns('public_id', 'scope_id', 'plugin_id', 'create_time');
|
||||
|
||||
create trigger insert_host_catalog_subtype before insert on host_plugin_catalog
|
||||
for each row execute procedure insert_host_catalog_subtype();
|
||||
|
||||
create trigger update_host_catalog_subtype before update on host_plugin_catalog
|
||||
for each row execute procedure update_host_catalog_subtype();
|
||||
|
||||
create trigger delete_host_catalog_subtype after delete on host_plugin_catalog
|
||||
for each row execute procedure delete_host_catalog_subtype();
|
||||
|
||||
create table host_plugin_catalog_secret (
|
||||
catalog_id wt_public_id primary key
|
||||
constraint host_plugin_catalog_fkey
|
||||
references host_plugin_catalog (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
secret bytea not null -- encrypted value
|
||||
constraint secret_must_not_be_empty
|
||||
check(length(secret) > 0),
|
||||
key_id wt_private_id not null
|
||||
constraint kms_database_key_version_fkey
|
||||
references kms_database_key_version (private_id)
|
||||
on delete restrict
|
||||
on update cascade
|
||||
);
|
||||
|
||||
create trigger update_time_column before update on host_plugin_catalog_secret
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on host_plugin_catalog_secret
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on host_plugin_catalog_secret
|
||||
for each row execute procedure immutable_columns('catalog_id', 'create_time');
|
||||
|
||||
create table host_plugin_set (
|
||||
public_id wt_public_id primary key,
|
||||
catalog_id wt_public_id not null
|
||||
constraint host_plugin_catalog_fkey
|
||||
references host_plugin_catalog (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
name wt_name,
|
||||
description text,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
attributes bytea not null
|
||||
constraint attributes_must_not_be_empty
|
||||
check(length(attributes) > 0),
|
||||
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,
|
||||
constraint host_plugin_set_catalog_id_public_id_uq
|
||||
unique(catalog_id, public_id)
|
||||
);
|
||||
|
||||
create trigger update_version_column after update on host_plugin_set
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger update_time_column before update on host_plugin_set
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on host_plugin_set
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on host_plugin_set
|
||||
for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time');
|
||||
|
||||
create trigger insert_host_set_subtype before insert on host_plugin_set
|
||||
for each row execute procedure insert_host_set_subtype();
|
||||
|
||||
create trigger delete_host_set_subtype after delete on host_plugin_set
|
||||
for each row execute procedure delete_host_set_subtype();
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('host_plugin_catalog', 1),
|
||||
('host_plugin_catalog_secret', 1),
|
||||
('host_plugin_set', 1);
|
||||
|
||||
commit;
|
||||
@@ -4,7 +4,7 @@ package schema
|
||||
|
||||
func init() {
|
||||
migrationStates["postgres"] = migrationState{
|
||||
binarySchemaVersion: 15002,
|
||||
binarySchemaVersion: 16004,
|
||||
upMigrations: map[int][]byte{
|
||||
1: []byte(`
|
||||
create domain wt_public_id as text
|
||||
@@ -6938,6 +6938,393 @@ alter table wh_host_dimension
|
||||
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
`),
|
||||
16001: []byte(`
|
||||
-- We are updating the plugin table here to have a scope id, since
|
||||
-- subtypes of plugin should be scoped. We use 'global' as the default
|
||||
-- just for the purpose of the migration since the scope cannot be null
|
||||
-- and the one plugin that already exists (pi_system) can be globally
|
||||
-- scoped. The immediately following statement removes this default.
|
||||
-- We add a name so we can enforce that all plugins have a unique name
|
||||
-- in a specific scope across all plugin subtypes.
|
||||
alter table plugin
|
||||
add column name wt_name,
|
||||
add column scope_id wt_scope_id
|
||||
not null
|
||||
default 'global'
|
||||
constraint iam_scope_global_fkey
|
||||
references iam_scope_global(scope_id)
|
||||
on delete cascade
|
||||
on update cascade;
|
||||
|
||||
alter table plugin
|
||||
alter column scope_id drop default;
|
||||
|
||||
-- Add constraints that enforce names are unique across all plugin subtypes
|
||||
-- in a specific scope.
|
||||
alter table plugin
|
||||
add constraint plugin_scope_id_public_id_uq
|
||||
unique (scope_id, public_id),
|
||||
add constraint plugin_scope_id_name_uq
|
||||
unique (scope_id, name);
|
||||
|
||||
|
||||
-- insert, update, and delete plugin_subtypes are created since we are adding
|
||||
-- subtyped plugins and we need to keep the base table plugin in sync with all
|
||||
-- subtype tables.
|
||||
create function insert_plugin_subtype()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
insert into plugin
|
||||
(public_id, scope_id, name)
|
||||
values
|
||||
(new.public_id, new.scope_id, new.name);
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function insert_plugin_subtype() is
|
||||
'insert_plugin_subtype() inserts sub type name into the base type plugin table';
|
||||
|
||||
create function update_plugin_subtype()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
update plugin set name = new.name where public_id = new.public_id and new.name != name;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function update_plugin_subtype() is
|
||||
'update_plugin_subtype() will update base plugin type name column with new values from sub type';
|
||||
|
||||
-- delete_plugin_subtype() is an after delete trigger function
|
||||
-- for subtypes of plugin
|
||||
create function delete_plugin_subtype()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
delete from plugin
|
||||
where public_id = old.public_id;
|
||||
return null; -- result is ignored since this is an after trigger
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function delete_plugin_subtype is
|
||||
'delete_plugin_subtype() is an after trigger function for subytypes of plugin';
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('plugin', 1);
|
||||
`),
|
||||
16002: []byte(`
|
||||
/*
|
||||
┌──────────────────┐
|
||||
│ plugin │
|
||||
├──────────────────┤
|
||||
│public_id (pk) │
|
||||
│scope_id (fk) │
|
||||
└──────────────────┘
|
||||
┼
|
||||
┼
|
||||
│
|
||||
┼
|
||||
┼
|
||||
┌──────────────────┐
|
||||
│ plugin_host │
|
||||
├──────────────────┤
|
||||
│public_id (pk) │
|
||||
│scope_id (fk) │
|
||||
│name │
|
||||
│description │
|
||||
│version │
|
||||
│plugin_name │
|
||||
│id_prefix │
|
||||
└──────────────────┘
|
||||
*/
|
||||
create table plugin_host (
|
||||
public_id wt_plugin_id primary key,
|
||||
scope_id wt_scope_id not null
|
||||
-- TODO: Allow plugins to be created in different scopes and
|
||||
-- constrain the host-catalog's plugin reference accordingly.
|
||||
constraint iam_scope_global_fkey
|
||||
references iam_scope_global(scope_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
name wt_name,
|
||||
description text,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
plugin_name text not null
|
||||
constraint plugin_name_must_be_not_empty
|
||||
check(length(trim(plugin_name)) > 0)
|
||||
constraint plugin_name_must_be_lowercase
|
||||
check(lower(trim(plugin_name)) = plugin_name)
|
||||
constraint plugin_host_name_uq
|
||||
unique,
|
||||
id_prefix text not null
|
||||
constraint plugin_id_prefix_must_be_not_empty
|
||||
check(length(trim(id_prefix)) > 0)
|
||||
constraint plugin_id_prefix_must_fit_format
|
||||
check (id_prefix ~ '^[a-z0-9]*$')
|
||||
constraint plugin_host_id_prefix_uq
|
||||
unique,
|
||||
constraint plugin_fkey
|
||||
foreign key (scope_id, public_id)
|
||||
references plugin(scope_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
constraint plugin_host_scope_id_name_uq
|
||||
unique(scope_id, name)
|
||||
);
|
||||
|
||||
create trigger update_version_column after update on plugin_host
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger update_time_column before update on plugin_host
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on plugin_host
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on plugin_host
|
||||
for each row execute procedure immutable_columns('public_id', 'create_time', 'plugin_name');
|
||||
|
||||
create trigger insert_plugin_subtype before insert on plugin_host
|
||||
for each row execute procedure insert_plugin_subtype();
|
||||
|
||||
create trigger update_plugin_subtype before update on plugin_host
|
||||
for each row execute procedure update_plugin_subtype();
|
||||
|
||||
create trigger delete_plugin_subtype after delete on plugin_host
|
||||
for each row execute procedure delete_plugin_subtype();
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('plugin_host', 1);
|
||||
`),
|
||||
16003: []byte(`
|
||||
-- We are adding the name to the base host catalog type. This allows the db
|
||||
-- to ensure that catalog names are unique in a scope across all subtypes.
|
||||
alter table host_catalog
|
||||
add column name wt_name;
|
||||
|
||||
alter table host_catalog
|
||||
add constraint host_catalog_scope_id_name_uq
|
||||
unique (scope_id, name);
|
||||
|
||||
-- Now that we've added the name column to the base type, we copy
|
||||
-- the name from the static host catalog table into the base table.
|
||||
update host_catalog
|
||||
set name = st.name
|
||||
from
|
||||
static_host_catalog st
|
||||
where
|
||||
host_catalog.public_id = st.public_id and
|
||||
st.name is not null;
|
||||
|
||||
-- Replace the insert_host_catalog_subtype function defined in 0/20_host.up.sql
|
||||
-- to include the name.
|
||||
-- insert_host_catalog_subtype() is a before insert trigger
|
||||
-- function for subtypes of host_catalog
|
||||
create or replace function insert_host_catalog_subtype()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
insert into host_catalog
|
||||
(public_id, scope_id, name)
|
||||
values
|
||||
(new.public_id, new.scope_id, new.name);
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function insert_host_catalog_subtype() is
|
||||
'insert_host_catalog_subtype() inserts sub type name into the base type host catalog table';
|
||||
|
||||
-- Now that we are tracking the name, which is mutable, we need to also
|
||||
-- update the base table when the subtype tables are updated.
|
||||
-- update_host_catalog_subtype() is intended to be used as a before update
|
||||
-- trigger for all host catalog sub types. The purpose is to ensure that the
|
||||
-- base table for host catalog to contain the updated names for each host catalog
|
||||
-- in order to enforce uniqueness across all host catalogs, regardless of subtype,
|
||||
-- in a given scope.
|
||||
create function update_host_catalog_subtype()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
update host_catalog set name = new.name where public_id = new.public_id and new.name != name;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function update_host_catalog_subtype() is
|
||||
'update_host_catalog_subtype() will update base host catalog type name column with new values from sub type';
|
||||
|
||||
create trigger update_host_catalog_subtype before update on static_host_catalog
|
||||
for each row execute procedure update_host_catalog_subtype();
|
||||
`),
|
||||
16004: []byte(`
|
||||
/*
|
||||
┌──────────────────┐
|
||||
│ plugin_host │
|
||||
├──────────────────┤
|
||||
│public_id (pk) │
|
||||
│... │
|
||||
└──────────────────┘
|
||||
┼
|
||||
┼
|
||||
○
|
||||
╱│╲
|
||||
┌─────────────────────┐
|
||||
│ host_plugin_catalog │
|
||||
├─────────────────────┤ ┌───────────────────────────┐
|
||||
┌────────────────┐ │public_id (pk) │ │host_plugin_catalog_secret │
|
||||
│host_catalog │ │plugin_id (fk) │ ├───────────────────────────┤
|
||||
├────────────────┤ │scope_id (fk) │ │host_catalog_id (pk, fk) │
|
||||
│public_id │┼┼──○┼│name │┼┼──○┼│secret │
|
||||
│scope_id │ │description │ │key_id (fk) │
|
||||
└────────────────┘ │attributes │ └───────────────────────────┘
|
||||
┼ └─────────────────────┘
|
||||
┼ ┼
|
||||
│ ┼
|
||||
│ ○
|
||||
○ ╱│╲
|
||||
╱│╲ ┌────────────────────┐
|
||||
┌────────────────┐ │ host_plugin_set │
|
||||
│ host_set │ ├────────────────────┤
|
||||
├────────────────┤ │public_id (pk) │
|
||||
│public_id │ │host_catalog_id (fk)│
|
||||
│host_catalog_id │┼┼──○┼│name │
|
||||
└────────────────┘ │description │
|
||||
│attributes │
|
||||
└────────────────────┘
|
||||
|
||||
*/
|
||||
|
||||
create table host_plugin_catalog (
|
||||
public_id wt_public_id primary key,
|
||||
scope_id wt_scope_id not null
|
||||
constraint iam_scope_fkey
|
||||
references iam_scope (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
plugin_id wt_plugin_id not null
|
||||
constraint plugin_host_fkey
|
||||
references plugin_host (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
name wt_name,
|
||||
description text,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
attributes bytea not null
|
||||
constraint attributes_must_not_empty
|
||||
check(length(attributes) > 0),
|
||||
constraint host_catalog_fkey
|
||||
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)
|
||||
);
|
||||
|
||||
create trigger update_version_column after update on host_plugin_catalog
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger update_time_column before update on host_plugin_catalog
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on host_plugin_catalog
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on host_plugin_catalog
|
||||
for each row execute procedure immutable_columns('public_id', 'scope_id', 'plugin_id', 'create_time');
|
||||
|
||||
create trigger insert_host_catalog_subtype before insert on host_plugin_catalog
|
||||
for each row execute procedure insert_host_catalog_subtype();
|
||||
|
||||
create trigger update_host_catalog_subtype before update on host_plugin_catalog
|
||||
for each row execute procedure update_host_catalog_subtype();
|
||||
|
||||
create trigger delete_host_catalog_subtype after delete on host_plugin_catalog
|
||||
for each row execute procedure delete_host_catalog_subtype();
|
||||
|
||||
create table host_plugin_catalog_secret (
|
||||
catalog_id wt_public_id primary key
|
||||
constraint host_plugin_catalog_fkey
|
||||
references host_plugin_catalog (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
secret bytea not null -- encrypted value
|
||||
constraint secret_must_not_be_empty
|
||||
check(length(secret) > 0),
|
||||
key_id wt_private_id not null
|
||||
constraint kms_database_key_version_fkey
|
||||
references kms_database_key_version (private_id)
|
||||
on delete restrict
|
||||
on update cascade
|
||||
);
|
||||
|
||||
create trigger update_time_column before update on host_plugin_catalog_secret
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on host_plugin_catalog_secret
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on host_plugin_catalog_secret
|
||||
for each row execute procedure immutable_columns('catalog_id', 'create_time');
|
||||
|
||||
create table host_plugin_set (
|
||||
public_id wt_public_id primary key,
|
||||
catalog_id wt_public_id not null
|
||||
constraint host_plugin_catalog_fkey
|
||||
references host_plugin_catalog (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
name wt_name,
|
||||
description text,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
attributes bytea not null
|
||||
constraint attributes_must_not_empty
|
||||
check(length(attributes) > 0),
|
||||
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,
|
||||
constraint host_plugin_set_catalog_id_public_id_uq
|
||||
unique(catalog_id, public_id)
|
||||
);
|
||||
|
||||
create trigger update_version_column after update on host_plugin_set
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger update_time_column before update on host_plugin_set
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on host_plugin_set
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on host_plugin_set
|
||||
for each row execute procedure immutable_columns('public_id', 'catalog_id','create_time');
|
||||
|
||||
create trigger insert_host_set_subtype before insert on host_plugin_set
|
||||
for each row execute procedure insert_host_set_subtype();
|
||||
|
||||
create trigger delete_host_set_subtype after delete on host_plugin_set
|
||||
for each row execute procedure delete_host_set_subtype();
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('host_plugin_catalog', 1),
|
||||
('host_plugin_catalog_secret', 1),
|
||||
('host_plugin_set', 1);
|
||||
`),
|
||||
2001: []byte(`
|
||||
-- log_migration entries represent logs generated during migrations
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Package plugin provides a plugin host catalog, and plugin host set resource
|
||||
// which are used to interact with a host plugin as well as a repository to
|
||||
// perform CRUDL and custom actions on these resource types.
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin/store"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// A HostCatalog contains plugin host sets. It is owned by
|
||||
// a scope.
|
||||
type HostCatalog struct {
|
||||
*store.HostCatalog
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewHostCatalog creates a new in memory HostCatalog assigned to a scopeId
|
||||
// and pluginId. Name and description are the only valid options. All other
|
||||
// options are ignored.
|
||||
func NewHostCatalog(ctx context.Context, pluginId, scopeId string, opt ...Option) (*HostCatalog, error) {
|
||||
const op = "plugin.NewHostCatalog"
|
||||
opts := getOpts(opt...)
|
||||
hc := &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{
|
||||
PluginId: pluginId,
|
||||
ScopeId: scopeId,
|
||||
Name: opts.withName,
|
||||
Description: opts.withDescription,
|
||||
},
|
||||
}
|
||||
|
||||
if opts.withAttributes != nil {
|
||||
attrs, err := json.Marshal(opts.withAttributes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter))
|
||||
}
|
||||
hc.Attributes = attrs
|
||||
}
|
||||
return hc, nil
|
||||
}
|
||||
|
||||
func (c *HostCatalog) clone() *HostCatalog {
|
||||
cp := proto.Clone(c.HostCatalog)
|
||||
return &HostCatalog{
|
||||
HostCatalog: cp.(*store.HostCatalog),
|
||||
}
|
||||
}
|
||||
|
||||
// TableName returns the table name for the host catalog.
|
||||
func (c *HostCatalog) TableName() string {
|
||||
if c.tableName != "" {
|
||||
return c.tableName
|
||||
}
|
||||
return "host_plugin_catalog"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name. If the caller attempts to
|
||||
// set the name to "" the name will be reset to the default name.
|
||||
func (c *HostCatalog) SetTableName(n string) {
|
||||
c.tableName = n
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin/store"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-kms-wrapping/structwrapping"
|
||||
)
|
||||
|
||||
// HostCatalogSecret contains the encrypted secret for a host catalog.
|
||||
// It is owned by a HostCatalog.
|
||||
type HostCatalogSecret struct {
|
||||
*store.HostCatalogSecret
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// newHostCatalogSecret creates an in memory host catalog secret.
|
||||
// All options are ignored.
|
||||
func newHostCatalogSecret(ctx context.Context, catalogId string, attributes map[string]interface{}, _ ...Option) (*HostCatalogSecret, error) {
|
||||
const op = "plugin.newHostCatlogSecret"
|
||||
hcs := &HostCatalogSecret{
|
||||
HostCatalogSecret: &store.HostCatalogSecret{
|
||||
CatalogId: catalogId,
|
||||
},
|
||||
}
|
||||
|
||||
if attributes != nil {
|
||||
attrs, err := json.Marshal(attributes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter))
|
||||
}
|
||||
hcs.Secret = attrs
|
||||
}
|
||||
return hcs, nil
|
||||
}
|
||||
|
||||
// TableName returns the table name for the host catalog.
|
||||
func (c *HostCatalogSecret) TableName() string {
|
||||
if c.tableName != "" {
|
||||
return c.tableName
|
||||
}
|
||||
return "host_plugin_catalog_secret"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name. If the caller attempts to
|
||||
// set the name to "" the name will be reset to the default name.
|
||||
func (c *HostCatalogSecret) SetTableName(n string) {
|
||||
c.tableName = n
|
||||
}
|
||||
|
||||
func (c *HostCatalogSecret) encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "plugin.(HostCatalogSecret).encrypt"
|
||||
if len(c.Secret) == 0 {
|
||||
errors.New(ctx, errors.InvalidParameter, op, "no attributes defined")
|
||||
}
|
||||
if err := structwrapping.WrapStruct(ctx, cipher, c.HostCatalogSecret, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt))
|
||||
}
|
||||
c.KeyId = cipher.KeyID()
|
||||
c.Secret = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HostCatalogSecret) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "plugin.(HostCatalogSecret).decrypt"
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, c.HostCatalogSecret, nil); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt))
|
||||
}
|
||||
c.CtSecret = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HostCatalogSecret) insertQuery() (query string, queryValues []interface{}) {
|
||||
query = upsertHostCatalogSecretQuery
|
||||
queryValues = []interface{}{
|
||||
c.CatalogId,
|
||||
c.CtSecret,
|
||||
c.KeyId,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *HostCatalogSecret) deleteQuery() (query string, queryValues []interface{}) {
|
||||
query = deleteHostCatalogSecretQuery
|
||||
queryValues = []interface{}{
|
||||
c.CatalogId,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"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/plugin/host"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
)
|
||||
|
||||
func TestHostCatalogSecret_New(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
kkms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "test", "test")
|
||||
cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId())
|
||||
|
||||
type args struct {
|
||||
catalogId string
|
||||
attrs map[string]interface{}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *HostCatalogSecret
|
||||
wantErr bool
|
||||
wantEncryptErr bool
|
||||
}{
|
||||
{
|
||||
name: "blank-catalog-id",
|
||||
args: args{
|
||||
catalogId: "",
|
||||
},
|
||||
want: &HostCatalogSecret{
|
||||
HostCatalogSecret: &store.HostCatalogSecret{},
|
||||
},
|
||||
wantEncryptErr: true,
|
||||
},
|
||||
{
|
||||
name: "no-attributes",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
},
|
||||
want: &HostCatalogSecret{
|
||||
HostCatalogSecret: &store.HostCatalogSecret{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
},
|
||||
},
|
||||
wantEncryptErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
attrs: map[string]interface{}{"foo": "bar"},
|
||||
},
|
||||
want: &HostCatalogSecret{
|
||||
HostCatalogSecret: &store.HostCatalogSecret{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
Secret: func() []byte {
|
||||
b, err := json.Marshal(map[string]interface{}{"foo": "bar"})
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
databaseWrapper, err := kkms.GetWrapper(ctx, prj.PublicId, kms.KeyPurposeDatabase)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, databaseWrapper)
|
||||
|
||||
got, err := newHostCatalogSecret(context.Background(), tt.args.catalogId, tt.args.attrs)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
require.Nil(t, got)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
|
||||
assert.Empty(t, got.CtSecret)
|
||||
assert.Equal(t, tt.want, got)
|
||||
|
||||
err = got.encrypt(ctx, databaseWrapper)
|
||||
if tt.wantEncryptErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, got.decrypt(ctx, databaseWrapper))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostCatalogSecret_Custom_Queries(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kkms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "test", "prefix")
|
||||
cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId())
|
||||
databaseWrapper, err := kkms.GetWrapper(ctx, prj.PublicId, kms.KeyPurposeDatabase)
|
||||
|
||||
hcs, err := newHostCatalogSecret(ctx, cat.GetPublicId(), map[string]interface{}{"foo": "bar"})
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, hcs.encrypt(ctx, databaseWrapper))
|
||||
q, v := hcs.insertQuery()
|
||||
_, err = rw.Exec(ctx, q, v)
|
||||
assert.NoError(t, err)
|
||||
|
||||
found, err := newHostCatalogSecret(ctx, cat.GetPublicId(), nil)
|
||||
assert.NoError(t, rw.LookupWhere(ctx, found, "catalog_id=?", found.GetCatalogId()))
|
||||
require.NoError(t, found.decrypt(ctx, databaseWrapper))
|
||||
require.NoError(t, hcs.decrypt(ctx, databaseWrapper))
|
||||
// update the created/updated time from the original
|
||||
hcs.CreateTime, hcs.UpdateTime = found.CreateTime, found.UpdateTime
|
||||
assert.Empty(t, cmp.Diff(hcs, found, protocmp.Transform()))
|
||||
|
||||
// Update the secret and see the value updated.
|
||||
updated, err := newHostCatalogSecret(ctx, cat.GetPublicId(), map[string]interface{}{"updated": "value"})
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, updated.encrypt(ctx, databaseWrapper))
|
||||
q, v = updated.insertQuery()
|
||||
_, err = rw.Exec(ctx, q, v)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NoError(t, rw.LookupWhere(ctx, found, "catalog_id=?", found.GetCatalogId()))
|
||||
require.NoError(t, found.decrypt(ctx, databaseWrapper))
|
||||
require.NoError(t, updated.decrypt(ctx, databaseWrapper))
|
||||
// set the created time to the first and update time from the newly found
|
||||
updated.CreateTime, updated.UpdateTime = hcs.CreateTime, found.UpdateTime
|
||||
assert.Empty(t, cmp.Diff(updated, found, protocmp.Transform()))
|
||||
|
||||
// Try to delete this secret.
|
||||
q, v = updated.deleteQuery()
|
||||
_, err = rw.Exec(ctx, q, v)
|
||||
assert.NoError(t, err)
|
||||
err = rw.LookupWhere(ctx, found, "catalog_id=?", found.GetCatalogId())
|
||||
assert.Error(t, err)
|
||||
assert.True(t, errors.IsNotFoundError(err))
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"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/plugin/host"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
)
|
||||
|
||||
func TestHostCatalog_Create(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "test", "prefix")
|
||||
|
||||
type args struct {
|
||||
pluginId string
|
||||
scopeId string
|
||||
opts []Option
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *HostCatalog
|
||||
wantErr bool
|
||||
wantCreateErr bool
|
||||
}{
|
||||
{
|
||||
name: "blank-scopeId",
|
||||
args: args{
|
||||
pluginId: plg.GetPublicId(),
|
||||
scopeId: "",
|
||||
},
|
||||
want: &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{
|
||||
PluginId: plg.GetPublicId(),
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
},
|
||||
wantCreateErr: true,
|
||||
},
|
||||
{
|
||||
name: "blank-pluginId",
|
||||
args: args{
|
||||
pluginId: "",
|
||||
scopeId: prj.GetPublicId(),
|
||||
},
|
||||
want: &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
},
|
||||
wantCreateErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid-no-options",
|
||||
args: args{
|
||||
pluginId: plg.GetPublicId(),
|
||||
scopeId: prj.GetPublicId(),
|
||||
},
|
||||
want: &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{
|
||||
PluginId: plg.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-name",
|
||||
args: args{
|
||||
pluginId: plg.GetPublicId(),
|
||||
scopeId: prj.GetPublicId(),
|
||||
opts: []Option{
|
||||
WithName("test-name"),
|
||||
},
|
||||
},
|
||||
want: &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{
|
||||
PluginId: plg.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Name: "test-name",
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-description",
|
||||
args: args{
|
||||
pluginId: plg.GetPublicId(),
|
||||
scopeId: prj.GetPublicId(),
|
||||
opts: []Option{
|
||||
WithDescription("test-description"),
|
||||
},
|
||||
},
|
||||
want: &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{
|
||||
PluginId: plg.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Description: "test-description",
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-attributes",
|
||||
args: args{
|
||||
pluginId: plg.GetPublicId(),
|
||||
scopeId: prj.GetPublicId(),
|
||||
opts: []Option{
|
||||
WithAttributes(map[string]interface{}{"foo": "bar"}),
|
||||
},
|
||||
},
|
||||
want: func() *HostCatalog {
|
||||
hc := &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{
|
||||
PluginId: plg.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
},
|
||||
}
|
||||
var err error
|
||||
hc.Attributes, err = json.Marshal(map[string]interface{}{"foo": "bar"})
|
||||
require.NoError(t, err)
|
||||
return hc
|
||||
}(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewHostCatalog(context.Background(), tt.args.pluginId, tt.args.scopeId, tt.args.opts...)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
|
||||
assert.Emptyf(t, got.PublicId, "PublicId set")
|
||||
assert.Equal(t, tt.want, got)
|
||||
|
||||
id, err := newHostCatalogId()
|
||||
assert.NoError(t, err)
|
||||
|
||||
tt.want.PublicId = id
|
||||
got.PublicId = id
|
||||
|
||||
w := db.New(conn)
|
||||
err = w.Create(context.Background(), got)
|
||||
if tt.wantCreateErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
found := &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{
|
||||
PublicId: id,
|
||||
},
|
||||
}
|
||||
require.NoError(t, w.LookupById(context.Background(), found))
|
||||
assert.Empty(t, cmp.Diff(got.HostCatalog, found.HostCatalog, protocmp.Transform()), "%q compared to %q", got.Attributes, found.Attributes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostCatalog_Create_DuplicateNames(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))
|
||||
_, prj2 := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "test1", "prefix1")
|
||||
plg2 := host.TestPlugin(t, conn, "test2", "prefix2")
|
||||
|
||||
got, err := NewHostCatalog(context.Background(), plg.GetPublicId(), prj.GetPublicId(), WithName("duplicate"))
|
||||
require.NoError(t, err)
|
||||
got.PublicId, err = newHostCatalogId()
|
||||
require.NoError(t, err)
|
||||
w.Create(ctx, got)
|
||||
|
||||
// Can't create another resource with the same pluginName in the same scope
|
||||
got.PublicId, err = newHostCatalogId()
|
||||
require.NoError(t, err)
|
||||
assert.Error(t, w.Create(ctx, got))
|
||||
|
||||
// Can't create another resource with same pluginName in same scope even for different plugin
|
||||
got, err = NewHostCatalog(context.Background(), plg2.GetPublicId(), prj.GetPublicId(), WithName("duplicate"))
|
||||
require.NoError(t, err)
|
||||
got.PublicId, err = newHostCatalogId()
|
||||
require.NoError(t, err)
|
||||
assert.Error(t, w.Create(ctx, got))
|
||||
|
||||
// Can create another resource with same pluginName in different scope even for same plugin
|
||||
got, err = NewHostCatalog(context.Background(), plg.GetPublicId(), prj2.GetPublicId(), WithName("duplicate"))
|
||||
require.NoError(t, err)
|
||||
got.PublicId, err = newHostCatalogId()
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, w.Create(ctx, got))
|
||||
}
|
||||
|
||||
func TestHostCatalog_Delete(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "test", "prefix")
|
||||
cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId())
|
||||
ignoredCat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId())
|
||||
_ = ignoredCat
|
||||
tests := []struct {
|
||||
name string
|
||||
cat *HostCatalog
|
||||
rowsDeleted int
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
cat: &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{
|
||||
PublicId: cat.GetPublicId(),
|
||||
},
|
||||
},
|
||||
rowsDeleted: 1,
|
||||
},
|
||||
{
|
||||
name: "bad-id",
|
||||
cat: &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{
|
||||
PublicId: "bad-id",
|
||||
},
|
||||
},
|
||||
rowsDeleted: 0,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
w := db.New(conn)
|
||||
deletedRows, err := w.Delete(ctx, tt.cat)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.rowsDeleted, deletedRows)
|
||||
|
||||
err = w.LookupById(ctx, tt.cat)
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.IsNotFoundError(err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostCatalog_Delete_Cascading(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
w := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("delete-scope", func(t *testing.T) {
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "deletescope", "deletescope")
|
||||
cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId())
|
||||
|
||||
deleted, err := w.Delete(ctx, prj)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, deleted)
|
||||
|
||||
err = w.LookupById(ctx, cat)
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.IsNotFoundError(err))
|
||||
})
|
||||
|
||||
t.Run("delete-plugin", func(t *testing.T) {
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "deleteplugin", "deleteplugin")
|
||||
cat := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId())
|
||||
|
||||
deleted, err := w.Delete(ctx, plg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, deleted)
|
||||
|
||||
err = w.LookupById(ctx, cat)
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.IsNotFoundError(err))
|
||||
})
|
||||
}
|
||||
|
||||
func TestHostCatalog_SetTableName(t *testing.T) {
|
||||
defaultTableName := "host_plugin_catalog"
|
||||
tests := []struct {
|
||||
name string
|
||||
initialName string
|
||||
setNameTo string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "new-name",
|
||||
initialName: "",
|
||||
setNameTo: "new-name",
|
||||
want: "new-name",
|
||||
},
|
||||
{
|
||||
name: "reset to default",
|
||||
initialName: "initial",
|
||||
setNameTo: "",
|
||||
want: defaultTableName,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
def, err := NewHostCatalog(context.Background(), "", "")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, defaultTableName, def.TableName())
|
||||
s := &HostCatalog{
|
||||
HostCatalog: &store.HostCatalog{},
|
||||
tableName: tt.initialName,
|
||||
}
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(t, tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin/store"
|
||||
)
|
||||
|
||||
// A HostSet is a collection of hosts from the set's catalog.
|
||||
type HostSet struct {
|
||||
*store.HostSet
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewHostSet creates a new in memory HostSet assigned to catalogId.
|
||||
// Attributes, name, and description are the only valid options. All other
|
||||
// options are ignored.
|
||||
func NewHostSet(ctx context.Context, catalogId string, opt ...Option) (*HostSet, error) {
|
||||
const op = "plugin.NewHostSet"
|
||||
opts := getOpts(opt...)
|
||||
set := &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: catalogId,
|
||||
Name: opts.withName,
|
||||
Description: opts.withDescription,
|
||||
},
|
||||
}
|
||||
|
||||
if opts.withAttributes != nil {
|
||||
attrs, err := json.Marshal(opts.withAttributes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter))
|
||||
}
|
||||
set.Attributes = attrs
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
|
||||
// TableName returns the table name for the host set.
|
||||
func (s *HostSet) TableName() string {
|
||||
if s.tableName != "" {
|
||||
return s.tableName
|
||||
}
|
||||
return "host_plugin_set"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name. If the caller attempts to
|
||||
// set the name to "" the name will be reset to the default name.
|
||||
func (s *HostSet) SetTableName(n string) {
|
||||
s.tableName = n
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin/store"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/plugin/host"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHostSet_Create(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "test", "prefix")
|
||||
cat := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
cat2 := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
|
||||
type args struct {
|
||||
catalogId string
|
||||
opts []Option
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *HostSet
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "blank-catalogId",
|
||||
args: args{
|
||||
catalogId: "",
|
||||
},
|
||||
want: &HostSet{HostSet: &store.HostSet{
|
||||
Attributes: []byte("{}"),
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid-no-options",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
},
|
||||
want: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-name",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
opts: []Option{
|
||||
WithName("test-name"),
|
||||
},
|
||||
},
|
||||
want: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
Name: "test-name",
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate-name",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
opts: []Option{
|
||||
WithName("test-name"),
|
||||
},
|
||||
},
|
||||
want: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
Name: "test-name",
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid-duplicate-name-different-catalog",
|
||||
args: args{
|
||||
catalogId: cat2.GetPublicId(),
|
||||
opts: []Option{
|
||||
WithName("test-name"),
|
||||
},
|
||||
},
|
||||
want: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: cat2.GetPublicId(),
|
||||
Name: "test-name",
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-description",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
opts: []Option{
|
||||
WithDescription("test-description"),
|
||||
},
|
||||
},
|
||||
want: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
Description: "test-description",
|
||||
Attributes: []byte("{}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-attributes",
|
||||
args: args{
|
||||
catalogId: cat.GetPublicId(),
|
||||
opts: []Option{
|
||||
WithAttributes(map[string]interface{}{"foo": "bar"}),
|
||||
},
|
||||
},
|
||||
want: &HostSet{
|
||||
HostSet: &store.HostSet{
|
||||
CatalogId: cat.GetPublicId(),
|
||||
Attributes: []byte(`{"foo":"bar"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
got, err := NewHostSet(ctx, tt.args.catalogId, tt.args.opts...)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
assert.Emptyf(t, got.PublicId, "PublicId set")
|
||||
assert.Equal(t, tt.want, got)
|
||||
|
||||
id, err := newHostSetId()
|
||||
assert.NoError(t, err)
|
||||
|
||||
tt.want.PublicId = id
|
||||
got.PublicId = id
|
||||
|
||||
w := db.New(conn)
|
||||
err = w.Create(context.Background(), got)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostSet_SetTableName(t *testing.T) {
|
||||
defaultTableName := "host_plugin_set"
|
||||
tests := []struct {
|
||||
name string
|
||||
initialName string
|
||||
setNameTo string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "new-name",
|
||||
initialName: "",
|
||||
setNameTo: "new-name",
|
||||
want: "new-name",
|
||||
},
|
||||
{
|
||||
name: "reset to default",
|
||||
initialName: "initial",
|
||||
setNameTo: "",
|
||||
want: defaultTableName,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
def := &HostSet{
|
||||
HostSet: &store.HostSet{},
|
||||
}
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := &HostSet{
|
||||
HostSet: &store.HostSet{},
|
||||
tableName: tt.initialName,
|
||||
}
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/host"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := host.Register(Subtype, HostCatalogPrefix, HostSetPrefix); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// PublicId prefixes for the resources in the plugin package.
|
||||
const (
|
||||
// TODO: Pull these out of being constants and have them derivable at run time.
|
||||
HostCatalogPrefix = "hcplg"
|
||||
HostSetPrefix = "hsplg"
|
||||
|
||||
Subtype = subtypes.Subtype("plugin")
|
||||
)
|
||||
|
||||
func newHostCatalogId() (string, error) {
|
||||
// TODO: Take in a prefix portion as an argument.
|
||||
id, err := db.NewPublicId(HostCatalogPrefix)
|
||||
if err != nil {
|
||||
return "", errors.WrapDeprecated(err, "plugin.newHostCatalogId")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func newHostSetId() (string, error) {
|
||||
// TODO: Take in a prefix portion as an argument.
|
||||
id, err := db.NewPublicId(HostSetPrefix)
|
||||
if err != nil {
|
||||
return "", errors.WrapDeprecated(err, "plugin.newHostSetId")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/host/plugin/store"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/plugin/host"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func TestPluginCatalog_ImmutableFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
w := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}}
|
||||
o, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "test", "prefix")
|
||||
plg2 := host.TestPlugin(t, conn, "test2", "prefix2")
|
||||
new := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
update *HostCatalog
|
||||
fieldMask []string
|
||||
}{
|
||||
{
|
||||
name: "public_id",
|
||||
update: func() *HostCatalog {
|
||||
c := new.clone()
|
||||
c.PublicId = "hc_thisIsNotAValidId"
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"PublicId"},
|
||||
},
|
||||
{
|
||||
name: "create time",
|
||||
update: func() *HostCatalog {
|
||||
c := new.clone()
|
||||
c.CreateTime = &ts
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"CreateTime"},
|
||||
},
|
||||
{
|
||||
name: "scope id",
|
||||
update: func() *HostCatalog {
|
||||
c := new.clone()
|
||||
c.ScopeId = o.PublicId
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"ScopeId"},
|
||||
},
|
||||
{
|
||||
name: "plugin id",
|
||||
update: func() *HostCatalog {
|
||||
c := new.clone()
|
||||
c.PluginId = plg2.GetPublicId()
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"PluginId"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
orig := new.clone()
|
||||
err := w.LookupById(context.Background(), orig)
|
||||
require.NoError(err)
|
||||
|
||||
rowsUpdated, err := w.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true))
|
||||
require.Error(err)
|
||||
assert.Equal(0, rowsUpdated)
|
||||
|
||||
after := new.clone()
|
||||
err = w.LookupById(context.Background(), after)
|
||||
require.NoError(err)
|
||||
|
||||
assert.True(proto.Equal(orig, after))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginSet_ImmutableFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
w := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}}
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
plg := host.TestPlugin(t, conn, "test", "prefix")
|
||||
cat := TestCatalog(t, conn, plg.GetPublicId(), prj.PublicId)
|
||||
new := TestSet(t, conn, cat.GetPublicId())
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
update *HostSet
|
||||
fieldMask []string
|
||||
}{
|
||||
{
|
||||
name: "public_id",
|
||||
update: func() *HostSet {
|
||||
c := new.testCloneHostSet()
|
||||
c.PublicId = "hc_thisIsNotAValidId"
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"PublicId"},
|
||||
},
|
||||
{
|
||||
name: "create time",
|
||||
update: func() *HostSet {
|
||||
c := new.testCloneHostSet()
|
||||
c.CreateTime = &ts
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"CreateTime"},
|
||||
},
|
||||
{
|
||||
name: "plugin_host_catalog_id",
|
||||
update: func() *HostSet {
|
||||
c := new.testCloneHostSet()
|
||||
c.CatalogId = "stc_01234567890"
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"CatalogId"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
orig := new.testCloneHostSet()
|
||||
err := w.LookupById(context.Background(), orig)
|
||||
require.NoError(err)
|
||||
|
||||
rowsUpdated, err := w.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true))
|
||||
require.Error(err)
|
||||
assert.Equal(0, rowsUpdated)
|
||||
|
||||
after := new.testCloneHostSet()
|
||||
err = w.LookupById(context.Background(), after)
|
||||
require.NoError(err)
|
||||
|
||||
assert.True(proto.Equal(orig, after))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HostSet) testCloneHostSet() *HostSet {
|
||||
cp := proto.Clone(c.HostSet)
|
||||
return &HostSet{
|
||||
HostSet: cp.(*store.HostSet),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package plugin
|
||||
|
||||
// getOpts - iterate the inbound Options and return a struct
|
||||
func getOpts(opt ...Option) options {
|
||||
opts := getDefaultOptions()
|
||||
for _, o := range opt {
|
||||
o(&opts)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// Option - how Options are passed as arguments.
|
||||
type Option func(*options)
|
||||
|
||||
// options = how options are represented
|
||||
type options struct {
|
||||
withName string
|
||||
withDescription string
|
||||
withLimit int
|
||||
withAttributes map[string]interface{}
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
return options{
|
||||
withDescription: "",
|
||||
withName: "",
|
||||
withAttributes: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// WithDescription provides an optional description.
|
||||
func WithDescription(desc string) Option {
|
||||
return func(o *options) {
|
||||
o.withDescription = desc
|
||||
}
|
||||
}
|
||||
|
||||
// WithName provides an optional name.
|
||||
func WithName(name string) Option {
|
||||
return func(o *options) {
|
||||
o.withName = name
|
||||
}
|
||||
}
|
||||
|
||||
// WithAttributes provides an optional attributes field.
|
||||
func WithAttributes(attrs map[string]interface{}) Option {
|
||||
return func(o *options) {
|
||||
o.withAttributes = attrs
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_GetOpts(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("WithName", func(t *testing.T) {
|
||||
opts := getOpts(WithName("test"))
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withName = "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)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package plugin
|
||||
|
||||
const (
|
||||
upsertHostCatalogSecretQuery = `
|
||||
insert into host_plugin_catalog_secret
|
||||
(catalog_id, secret, key_id)
|
||||
values
|
||||
($1, $2, $3)
|
||||
on conflict (catalog_id) do update
|
||||
set secret = excluded.secret,
|
||||
key_id = excluded.key_id
|
||||
returning *;
|
||||
`
|
||||
|
||||
deleteHostCatalogSecretQuery = `
|
||||
delete from host_plugin_catalog_secret
|
||||
where catalog_id = $1;
|
||||
`
|
||||
)
|
||||
@@ -0,0 +1,566 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.15.8
|
||||
// source: controller/storage/host/plugin/store/v1/host.proto
|
||||
|
||||
// Package store provides protobufs for storing types in the plugin host
|
||||
// package.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
timestamp "github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
_ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
_ "google.golang.org/protobuf/types/known/structpb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type HostCatalog struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
|
||||
// The create_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// The update_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// name is optional. If set, it must be unique within scope_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
|
||||
// The scope_id of the owning scope and must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
ScopeId string `protobuf:"bytes,6,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"not_null"`
|
||||
// The public id of the plugin this catalog uses.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
PluginId string `protobuf:"bytes,7,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty" gorm:"not_null"`
|
||||
// version allows optimistic locking of the resource
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Version uint32 `protobuf:"varint,8,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
|
||||
// attributes is a jsonb formatted field.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Attributes []byte `protobuf:"bytes,9,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"default:null"`
|
||||
}
|
||||
|
||||
func (x *HostCatalog) Reset() {
|
||||
*x = HostCatalog{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HostCatalog) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HostCatalog) ProtoMessage() {}
|
||||
|
||||
func (x *HostCatalog) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HostCatalog.ProtoReflect.Descriptor instead.
|
||||
func (*HostCatalog) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *HostCatalog) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HostCatalog) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HostCatalog) GetUpdateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HostCatalog) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HostCatalog) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HostCatalog) GetScopeId() string {
|
||||
if x != nil {
|
||||
return x.ScopeId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HostCatalog) GetPluginId() string {
|
||||
if x != nil {
|
||||
return x.PluginId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HostCatalog) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HostCatalog) GetAttributes() []byte {
|
||||
if x != nil {
|
||||
return x.Attributes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type HostSet struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
|
||||
// The create_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// The update_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// name is optional. If set, it must be unique within
|
||||
// catalog_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
|
||||
// catalog_id is the public_id of the owning
|
||||
// plugin_host_catalog and must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
CatalogId string `protobuf:"bytes,6,opt,name=catalog_id,json=catalogId,proto3" json:"catalog_id,omitempty" gorm:"not_null"`
|
||||
// version allows optimistic locking of the resource
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
|
||||
// attributes is a jsonb formatted field.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Attributes []byte `protobuf:"bytes,8,opt,name=attributes,proto3" json:"attributes,omitempty" gorm:"default:null"`
|
||||
}
|
||||
|
||||
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))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HostSet) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HostSet) ProtoMessage() {}
|
||||
|
||||
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))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
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{1}
|
||||
}
|
||||
|
||||
func (x *HostSet) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HostSet) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HostSet) GetUpdateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HostSet) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HostSet) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HostSet) GetCatalogId() string {
|
||||
if x != nil {
|
||||
return x.CatalogId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HostSet) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HostSet) GetAttributes() []byte {
|
||||
if x != nil {
|
||||
return x.Attributes
|
||||
}
|
||||
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 ""
|
||||
}
|
||||
|
||||
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 ""
|
||||
}
|
||||
|
||||
var File_controller_storage_host_plugin_store_v1_host_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = []byte{
|
||||
0x0a, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
|
||||
0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
|
||||
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x2e, 0x70, 0x6c,
|
||||
0x75, 0x67, 0x69, 0x6e, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1c, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73,
|
||||
0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2f, 0x63, 0x6f, 0x6e,
|
||||
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f,
|
||||
0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9e, 0x03, 0x0a, 0x0b, 0x48, 0x6f, 0x73,
|
||||
0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 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, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65,
|
||||
0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 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, 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, 0x81, 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, 0x22, 0x98, 0x02,
|
||||
0x0a, 0x11, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x53, 0x65, 0x63,
|
||||
0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67,
|
||||
0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
|
||||
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12,
|
||||
0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
|
||||
0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06,
|
||||
0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65,
|
||||
0x63, 0x72, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65,
|
||||
0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65,
|
||||
0x74, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70,
|
||||
0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_rawDescOnce sync.Once
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_rawDescData = file_controller_storage_host_plugin_store_v1_host_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_controller_storage_host_plugin_store_v1_host_proto_rawDescGZIP() []byte {
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_rawDescOnce.Do(func() {
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_host_plugin_store_v1_host_proto_rawDescData)
|
||||
})
|
||||
return file_controller_storage_host_plugin_store_v1_host_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_host_plugin_store_v1_host_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_controller_storage_host_plugin_store_v1_host_proto_goTypes = []interface{}{
|
||||
(*HostCatalog)(nil), // 0: controller.storage.host.plugin.store.v1.HostCatalog
|
||||
(*HostSet)(nil), // 1: controller.storage.host.plugin.store.v1.HostSet
|
||||
(*HostCatalogSecret)(nil), // 2: controller.storage.host.plugin.store.v1.HostCatalogSecret
|
||||
(*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_host_plugin_store_v1_host_proto_depIdxs = []int32{
|
||||
3, // 0: controller.storage.host.plugin.store.v1.HostCatalog.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
3, // 1: controller.storage.host.plugin.store.v1.HostCatalog.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
3, // 2: controller.storage.host.plugin.store.v1.HostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
3, // 3: controller.storage.host.plugin.store.v1.HostSet.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
3, // 4: controller.storage.host.plugin.store.v1.HostCatalogSecret.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
3, // 5: controller.storage.host.plugin.store.v1.HostCatalogSecret.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
6, // [6:6] is the sub-list for method output_type
|
||||
6, // [6:6] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_host_plugin_store_v1_host_proto_init() }
|
||||
func file_controller_storage_host_plugin_store_v1_host_proto_init() {
|
||||
if File_controller_storage_host_plugin_store_v1_host_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HostCatalog); 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[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HostSet); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HostCatalogSecret); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_storage_host_plugin_store_v1_host_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_controller_storage_host_plugin_store_v1_host_proto_goTypes,
|
||||
DependencyIndexes: file_controller_storage_host_plugin_store_v1_host_proto_depIdxs,
|
||||
MessageInfos: file_controller_storage_host_plugin_store_v1_host_proto_msgTypes,
|
||||
}.Build()
|
||||
File_controller_storage_host_plugin_store_v1_host_proto = out.File
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_rawDesc = nil
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_goTypes = nil
|
||||
file_controller_storage_host_plugin_store_v1_host_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestCatalog creates count number of plugin host catalogs to the provided DB
|
||||
// with the provided scope id. If any errors are encountered during the creation of
|
||||
// the host catalog, the test will fail.
|
||||
func TestCatalog(t *testing.T, conn *gorm.DB, pluginId, scopeId string, opt ...Option) *HostCatalog {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
cat, err := NewHostCatalog(ctx, pluginId, scopeId, opt...)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, cat)
|
||||
id, err := newHostCatalogId()
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, id)
|
||||
cat.PublicId = id
|
||||
|
||||
w := db.New(conn)
|
||||
require.NoError(t, w.Create(ctx, cat))
|
||||
return cat
|
||||
}
|
||||
|
||||
// TestSet creates count number of plugin host sets in the provided DB
|
||||
// with the provided catalog id. The catalog must have been created
|
||||
// previously. The test will fail if any errors are encountered.
|
||||
func TestSet(t *testing.T, conn *gorm.DB, catalogId string, opt ...Option) *HostSet {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
assert := assert.New(t)
|
||||
set, err := NewHostSet(ctx, catalogId, opt...)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(set)
|
||||
id, err := newHostSetId()
|
||||
assert.NoError(err)
|
||||
assert.NotEmpty(id)
|
||||
set.PublicId = id
|
||||
|
||||
w := db.New(conn)
|
||||
require.NoError(t, w.Create(ctx, set))
|
||||
return set
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/plugin/host"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_TestCatalogs(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NotNil(proj)
|
||||
assert.NotEmpty(proj.GetPublicId())
|
||||
|
||||
plg := host.TestPlugin(t, conn, "test", "prefix")
|
||||
require.NotNil(plg)
|
||||
assert.NotEmpty(plg.GetPublicId())
|
||||
|
||||
cs := TestCatalog(t, conn, plg.GetPublicId(), proj.GetPublicId(), WithName("foo"), WithDescription("bar"))
|
||||
assert.NotEmpty(cs.GetPublicId())
|
||||
assert.Equal("foo", cs.GetName())
|
||||
assert.Equal("bar", cs.GetDescription())
|
||||
}
|
||||
|
||||
func Test_TestSet(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NotNil(prj)
|
||||
assert.NotEmpty(prj.GetPublicId())
|
||||
|
||||
plg := host.TestPlugin(t, conn, "test", "prefix")
|
||||
require.NotNil(plg)
|
||||
assert.NotEmpty(plg.GetPublicId())
|
||||
|
||||
c := TestCatalog(t, conn, plg.GetPublicId(), prj.GetPublicId())
|
||||
set := TestSet(t, conn, c.GetPublicId(), WithName("foo"), WithDescription("bar"))
|
||||
assert.NotEmpty(set.GetPublicId())
|
||||
assert.Equal("foo", set.GetName())
|
||||
assert.Equal("bar", set.GetDescription())
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// Package plugin contains other packages related to the different plugin
|
||||
// types and any helpers related to working with non type specific plugins.
|
||||
package plugin
|
||||
@@ -0,0 +1,4 @@
|
||||
// Package host provides a plugin type used to interface with boundary's host
|
||||
// related resources. Additionally it provides a repository for performing
|
||||
// CRUDL and custom operations on this plugin type.
|
||||
package host
|
||||
@@ -0,0 +1,19 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
)
|
||||
|
||||
// PublicId prefixes for the resources in the plugin package.
|
||||
const (
|
||||
PluginPrefix = "plgh"
|
||||
)
|
||||
|
||||
func newPluginId() (string, error) {
|
||||
id, err := db.NewPublicId(PluginPrefix)
|
||||
if err != nil {
|
||||
return "", errors.WrapDeprecated(err, "plugin.newPluginId")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/plugin/host/store"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func TestHostPlugin_ImmutableFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
w := db.New(conn)
|
||||
|
||||
ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}}
|
||||
plg := TestPlugin(t, conn, "test", "prefix")
|
||||
|
||||
newPlugin := plg
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
update *Plugin
|
||||
fieldMask []string
|
||||
}{
|
||||
{
|
||||
name: "public_id",
|
||||
update: func() *Plugin {
|
||||
c := newPlugin.testClonePlugin()
|
||||
c.PublicId = "hc_thisIsNotAValidId"
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"PublicId"},
|
||||
},
|
||||
{
|
||||
name: "create time",
|
||||
update: func() *Plugin {
|
||||
c := newPlugin.testClonePlugin()
|
||||
c.CreateTime = &ts
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"CreateTime"},
|
||||
},
|
||||
{
|
||||
name: "pluginName",
|
||||
update: func() *Plugin {
|
||||
c := newPlugin.testClonePlugin()
|
||||
c.PluginName = "different pluginName"
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"PluginName"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
orig := newPlugin.testClonePlugin()
|
||||
err := w.LookupById(context.Background(), orig)
|
||||
require.NoError(err)
|
||||
|
||||
rowsUpdated, err := w.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true))
|
||||
require.Error(err)
|
||||
assert.Equal(0, rowsUpdated)
|
||||
|
||||
after := newPlugin.testClonePlugin()
|
||||
err = w.LookupById(context.Background(), after)
|
||||
require.NoError(err)
|
||||
|
||||
assert.True(proto.Equal(orig, after))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Plugin) testClonePlugin() *Plugin {
|
||||
cp := proto.Clone(c.Plugin)
|
||||
return &Plugin{
|
||||
Plugin: cp.(*store.Plugin),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package host
|
||||
|
||||
// getOpts - iterate the inbound Options and return a struct
|
||||
func getOpts(opt ...Option) options {
|
||||
opts := getDefaultOptions()
|
||||
for _, o := range opt {
|
||||
o(&opts)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// Option - how Options are passed as arguments.
|
||||
type Option func(*options)
|
||||
|
||||
// options = how options are represented
|
||||
type options struct {
|
||||
withName string
|
||||
withDescription string
|
||||
withLimit int
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
return options{
|
||||
withDescription: "",
|
||||
withName: "",
|
||||
}
|
||||
}
|
||||
|
||||
// WithDescription provides an optional description.
|
||||
func WithDescription(desc string) Option {
|
||||
return func(o *options) {
|
||||
o.withDescription = desc
|
||||
}
|
||||
}
|
||||
|
||||
// WithName provides an optional name.
|
||||
func WithName(name string) Option {
|
||||
return func(o *options) {
|
||||
o.withName = name
|
||||
}
|
||||
}
|
||||
|
||||
// WithLimit provides an option to provide a limit. Intentionally allowing
|
||||
// negative integers. If WithLimit < 0, then unlimited results are
|
||||
// returned. If WithLimit == 0, then default limits are used for results.
|
||||
func WithLimit(l int) Option {
|
||||
return func(o *options) {
|
||||
o.withLimit = l
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_GetOpts(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("WithName", func(t *testing.T) {
|
||||
opts := getOpts(WithName("test"))
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withName = "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)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/plugin/host/store"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
)
|
||||
|
||||
// A Plugin enables additional logic to be used by boundary.
|
||||
// It is owned by a scope.
|
||||
type Plugin struct {
|
||||
*store.Plugin
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// NewPlugin creates a new in memory Plugin assigned to the global scope.
|
||||
// Name, Description are the only allowed option. All other options are ignored.
|
||||
func NewPlugin(pluginName, idPrefix string, opt ...Option) *Plugin {
|
||||
opts := getOpts(opt...)
|
||||
p := &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
PluginName: pluginName,
|
||||
IdPrefix: idPrefix,
|
||||
ScopeId: scope.Global.String(),
|
||||
Name: opts.withName,
|
||||
Description: opts.withDescription,
|
||||
},
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// TableName returns the table name for the host plugin.
|
||||
func (c *Plugin) TableName() string {
|
||||
if c.tableName != "" {
|
||||
return c.tableName
|
||||
}
|
||||
return "plugin_host"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name. If the caller attempts to
|
||||
// set the name to "" the name will be reset to the default name.
|
||||
func (c *Plugin) SetTableName(n string) {
|
||||
c.tableName = n
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/plugin/host/store"
|
||||
)
|
||||
|
||||
func TestPlugin_Create(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
|
||||
type args struct {
|
||||
pluginName string
|
||||
idPrefix string
|
||||
semVer string
|
||||
opts []Option
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *Plugin
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "blank-pluginName",
|
||||
args: args{
|
||||
idPrefix: "prefix",
|
||||
},
|
||||
want: &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
IdPrefix: "prefix",
|
||||
ScopeId: scope.Global.String(),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "blank-idPrefix",
|
||||
args: args{
|
||||
pluginName: "plugin name",
|
||||
},
|
||||
want: &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
PluginName: "plugin name",
|
||||
ScopeId: scope.Global.String(),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "idprefix-capitalized",
|
||||
args: args{
|
||||
pluginName: "idprefixcapitalized",
|
||||
idPrefix: "IdPrefixCapitalized",
|
||||
},
|
||||
want: &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
PluginName: "idprefixcapitalized",
|
||||
IdPrefix: "IdPrefixCapitalized",
|
||||
ScopeId: scope.Global.String(),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "idprefix-space",
|
||||
args: args{
|
||||
pluginName: "idprefix space",
|
||||
idPrefix: "idprefix space",
|
||||
},
|
||||
want: &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
PluginName: "idprefix space",
|
||||
IdPrefix: "idprefix space",
|
||||
ScopeId: scope.Global.String(),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "pluginName-capitalized",
|
||||
args: args{
|
||||
pluginName: "PluginNameCapitalized",
|
||||
idPrefix: "pluginnamecapitalized",
|
||||
},
|
||||
want: &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
PluginName: "PluginNameCapitalized",
|
||||
IdPrefix: "pluginnamecapitalized",
|
||||
ScopeId: scope.Global.String(),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid-no-options",
|
||||
args: args{
|
||||
pluginName: "validnooptions",
|
||||
idPrefix: "validnooptions",
|
||||
},
|
||||
want: &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
PluginName: "validnooptions",
|
||||
IdPrefix: "validnooptions",
|
||||
ScopeId: scope.Global.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-with-description",
|
||||
args: args{
|
||||
pluginName: "validwithdescription",
|
||||
idPrefix: "validwithdescription",
|
||||
opts: []Option{
|
||||
WithDescription("description"),
|
||||
},
|
||||
},
|
||||
want: &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
PluginName: "validwithdescription",
|
||||
IdPrefix: "validwithdescription",
|
||||
ScopeId: scope.Global.String(),
|
||||
Description: "description",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-pluginName-name-option",
|
||||
args: args{
|
||||
pluginName: "validpluginnamenameoption",
|
||||
idPrefix: "validpluginnamenameoption",
|
||||
opts: []Option{
|
||||
WithName("valid-pluginName-name-option"),
|
||||
WithDescription("description"),
|
||||
},
|
||||
},
|
||||
want: &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
PluginName: "validpluginnamenameoption",
|
||||
IdPrefix: "validpluginnamenameoption",
|
||||
ScopeId: scope.Global.String(),
|
||||
Name: "valid-pluginName-name-option",
|
||||
Description: "description",
|
||||
},
|
||||
},
|
||||
},
|
||||
// This must be run after the "valid-no-options" test
|
||||
{
|
||||
name: "duplicate-pluginName",
|
||||
args: args{
|
||||
pluginName: "validnooptions",
|
||||
idPrefix: "duplicatepluginname",
|
||||
},
|
||||
want: &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
PluginName: "validnooptions",
|
||||
IdPrefix: "duplicatepluginname",
|
||||
ScopeId: scope.Global.String(),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "duplicate-idPrefix",
|
||||
args: args{
|
||||
pluginName: "duplicateidprefix",
|
||||
idPrefix: "validnooptions",
|
||||
},
|
||||
want: &Plugin{
|
||||
Plugin: &store.Plugin{
|
||||
PluginName: "duplicateidprefix",
|
||||
IdPrefix: "validnooptions",
|
||||
ScopeId: scope.Global.String(),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := NewPlugin(tt.args.pluginName, tt.args.idPrefix, tt.args.opts...)
|
||||
require.NotNil(t, got)
|
||||
require.Emptyf(t, got.PublicId, "PublicId set")
|
||||
|
||||
id, err := newPluginId()
|
||||
require.NoError(t, err)
|
||||
got.PublicId = id
|
||||
|
||||
tt.want.PublicId = id
|
||||
assert.Equal(t, tt.want, got)
|
||||
|
||||
w := db.New(conn)
|
||||
err = w.Create(context.Background(), got)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlugin_Update(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
w := db.New(conn)
|
||||
ctx := context.Background()
|
||||
plg := TestPlugin(t, conn, "delete-plugin", "prefix")
|
||||
assert.Equal(t, uint32(1), plg.Version)
|
||||
|
||||
plg.Name = "New"
|
||||
plg.Description = "Description"
|
||||
rowCount, err := w.Update(ctx, plg, []string{"Name", "Description"}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, rowCount)
|
||||
|
||||
assert.Equal(t, uint32(2), plg.Version)
|
||||
assert.Equal(t, "New", plg.Name)
|
||||
assert.Equal(t, "Description", plg.Description)
|
||||
}
|
||||
|
||||
func TestPlugin_Delete(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
w := db.New(conn)
|
||||
ctx := context.Background()
|
||||
plg := TestPlugin(t, conn, "delete-plugin", "prefix")
|
||||
|
||||
deleted, err := w.Delete(ctx, plg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, deleted)
|
||||
}
|
||||
|
||||
func TestPlugin_SetTableName(t *testing.T) {
|
||||
defaultTableName := "plugin_host"
|
||||
tests := []struct {
|
||||
name string
|
||||
initialName string
|
||||
setNameTo string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "new-pluginName",
|
||||
initialName: "",
|
||||
setNameTo: "new-pluginName",
|
||||
want: "new-pluginName",
|
||||
},
|
||||
{
|
||||
name: "reset to default",
|
||||
initialName: "initial",
|
||||
setNameTo: "",
|
||||
want: defaultTableName,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
def := NewPlugin("", "prefix")
|
||||
require.Equal(defaultTableName, def.TableName())
|
||||
s := &Plugin{
|
||||
Plugin: &store.Plugin{},
|
||||
tableName: tt.initialName,
|
||||
}
|
||||
s.SetTableName(tt.setNameTo)
|
||||
assert.Equal(tt.want, s.TableName())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.15.8
|
||||
// source: controller/storage/plugin/host/store/v1/plugin.proto
|
||||
|
||||
// Package store provides protobufs for storing types in the host package.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
timestamp "github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
_ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Plugin struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
|
||||
// The create_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *timestamp.Timestamp `protobuf:"bytes,20,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,30,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 scope_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Description string `protobuf:"bytes,50,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
|
||||
// The scope_id of the owning scope and must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
ScopeId string `protobuf:"bytes,60,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"not_null"`
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Version uint32 `protobuf:"varint,70,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
|
||||
// plugin_name is required. The value must be unique across all host plugins
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
PluginName string `protobuf:"bytes,80,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty" gorm:"default:null"`
|
||||
// id_prefix is required. The value must be unique across all host plugins
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
IdPrefix string `protobuf:"bytes,90,opt,name=id_prefix,json=idPrefix,proto3" json:"id_prefix,omitempty" gorm:"default:null"`
|
||||
}
|
||||
|
||||
func (x *Plugin) Reset() {
|
||||
*x = Plugin{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_plugin_host_store_v1_plugin_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Plugin) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Plugin) ProtoMessage() {}
|
||||
|
||||
func (x *Plugin) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_plugin_host_store_v1_plugin_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Plugin.ProtoReflect.Descriptor instead.
|
||||
func (*Plugin) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Plugin) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Plugin) GetCreateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Plugin) GetUpdateTime() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Plugin) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Plugin) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Plugin) GetScopeId() string {
|
||||
if x != nil {
|
||||
return x.ScopeId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Plugin) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Plugin) GetPluginName() string {
|
||||
if x != nil {
|
||||
return x.PluginName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Plugin) GetIdPrefix() string {
|
||||
if x != nil {
|
||||
return x.IdPrefix
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_controller_storage_plugin_host_store_v1_plugin_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc = []byte{
|
||||
0x0a, 0x34, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x68, 0x6f, 0x73, 0x74,
|
||||
0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
|
||||
0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69,
|
||||
0x6e, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a,
|
||||
0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73,
|
||||
0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f,
|
||||
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x03, 0x0a,
|
||||
0x06, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69,
|
||||
0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c,
|
||||
0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x18, 0x14, 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, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
|
||||
0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x52, 0x0a, 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, 0x4e, 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, 0x32, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a,
|
||||
0x0b, 0x44, 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, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49,
|
||||
0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x46, 0x20, 0x01,
|
||||
0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x70,
|
||||
0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09,
|
||||
0x69, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x08, 0x69, 0x64, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 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, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x2f,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescOnce sync.Once
|
||||
file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescData = file_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescGZIP() []byte {
|
||||
file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescOnce.Do(func() {
|
||||
file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescData)
|
||||
})
|
||||
return file_controller_storage_plugin_host_store_v1_plugin_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_plugin_host_store_v1_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_controller_storage_plugin_host_store_v1_plugin_proto_goTypes = []interface{}{
|
||||
(*Plugin)(nil), // 0: controller.storage.plugin.host.store.v1.Plugin
|
||||
(*timestamp.Timestamp)(nil), // 1: controller.storage.timestamp.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_plugin_host_store_v1_plugin_proto_depIdxs = []int32{
|
||||
1, // 0: controller.storage.plugin.host.store.v1.Plugin.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
1, // 1: controller.storage.plugin.host.store.v1.Plugin.update_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||
2, // [2:2] is the sub-list for method output_type
|
||||
2, // [2:2] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_plugin_host_store_v1_plugin_proto_init() }
|
||||
func file_controller_storage_plugin_host_store_v1_plugin_proto_init() {
|
||||
if File_controller_storage_plugin_host_store_v1_plugin_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_controller_storage_plugin_host_store_v1_plugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Plugin); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_controller_storage_plugin_host_store_v1_plugin_proto_goTypes,
|
||||
DependencyIndexes: file_controller_storage_plugin_host_store_v1_plugin_proto_depIdxs,
|
||||
MessageInfos: file_controller_storage_plugin_host_store_v1_plugin_proto_msgTypes,
|
||||
}.Build()
|
||||
File_controller_storage_plugin_host_store_v1_plugin_proto = out.File
|
||||
file_controller_storage_plugin_host_store_v1_plugin_proto_rawDesc = nil
|
||||
file_controller_storage_plugin_host_store_v1_plugin_proto_goTypes = nil
|
||||
file_controller_storage_plugin_host_store_v1_plugin_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPlugin(t *testing.T, conn *gorm.DB, name string, prefix string) *Plugin {
|
||||
t.Helper()
|
||||
p := NewPlugin(name, prefix)
|
||||
id, err := newPluginId()
|
||||
require.NoError(t, err)
|
||||
p.PublicId = id
|
||||
|
||||
w := db.New(conn)
|
||||
require.NoError(t, w.Create(context.Background(), p))
|
||||
return p
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_TestPlugins(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
|
||||
plg := TestPlugin(t, conn, "test", "prefix")
|
||||
require.NotNil(plg)
|
||||
assert.NotEmpty(plg.GetPublicId())
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/plugin/store"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestPlugin_ImmutableFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
w := db.New(conn)
|
||||
plg := testPlugin(t, conn, "test")
|
||||
|
||||
newPlugin := plg
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
update *plugin
|
||||
fieldMask []string
|
||||
}{
|
||||
{
|
||||
name: "public_id",
|
||||
update: func() *plugin {
|
||||
c := newPlugin.testClonePlugin()
|
||||
c.PublicId = "pi_thisIsNotAValidId"
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"PublicId"},
|
||||
},
|
||||
{
|
||||
name: "scope",
|
||||
update: func() *plugin {
|
||||
c := newPlugin.testClonePlugin()
|
||||
c.ScopeId = "o_1234567890"
|
||||
return c
|
||||
}(),
|
||||
fieldMask: []string{"ScopeId"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
orig := newPlugin.testClonePlugin()
|
||||
err := w.LookupById(context.Background(), orig)
|
||||
require.NoError(err)
|
||||
|
||||
rowsUpdated, err := w.Update(context.Background(), tt.update, tt.fieldMask, nil, db.WithSkipVetForWrite(true))
|
||||
require.Error(err)
|
||||
assert.Equal(0, rowsUpdated)
|
||||
|
||||
after := newPlugin.testClonePlugin()
|
||||
err = w.LookupById(context.Background(), after)
|
||||
require.NoError(err)
|
||||
|
||||
assert.True(proto.Equal(orig, after))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (c *plugin) testClonePlugin() *plugin {
|
||||
cp := proto.Clone(c.Plugin)
|
||||
return &plugin{
|
||||
Plugin: cp.(*store.Plugin),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.15.8
|
||||
// source: controller/storage/plugin/store/v1/plugin.proto
|
||||
|
||||
// Package store provides protobufs for storing types in the host package.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
_ "github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a typeless plugin. In practice this should never be used directly.
|
||||
// This is included for testing purposes only.
|
||||
type Plugin struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
|
||||
// The scope_id of the owning scope and must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
ScopeId string `protobuf:"bytes,20,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"not_null"`
|
||||
// name is optional. If set, it must be unique within scope_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Name string `protobuf:"bytes,30,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
|
||||
}
|
||||
|
||||
func (x *Plugin) Reset() {
|
||||
*x = Plugin{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Plugin) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Plugin) ProtoMessage() {}
|
||||
|
||||
func (x *Plugin) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Plugin.ProtoReflect.Descriptor instead.
|
||||
func (*Plugin) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_plugin_store_v1_plugin_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Plugin) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Plugin) GetScopeId() string {
|
||||
if x != nil {
|
||||
return x.ScopeId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Plugin) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_controller_storage_plugin_store_v1_plugin_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_storage_plugin_store_v1_plugin_proto_rawDesc = []byte{
|
||||
0x0a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72,
|
||||
0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x12, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
|
||||
0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x54, 0x0a, 0x06, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e,
|
||||
0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x19, 0x0a,
|
||||
0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x3b, 0x5a, 0x39,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69,
|
||||
0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 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 (
|
||||
file_controller_storage_plugin_store_v1_plugin_proto_rawDescOnce sync.Once
|
||||
file_controller_storage_plugin_store_v1_plugin_proto_rawDescData = file_controller_storage_plugin_store_v1_plugin_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_controller_storage_plugin_store_v1_plugin_proto_rawDescGZIP() []byte {
|
||||
file_controller_storage_plugin_store_v1_plugin_proto_rawDescOnce.Do(func() {
|
||||
file_controller_storage_plugin_store_v1_plugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_plugin_store_v1_plugin_proto_rawDescData)
|
||||
})
|
||||
return file_controller_storage_plugin_store_v1_plugin_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_plugin_store_v1_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_controller_storage_plugin_store_v1_plugin_proto_goTypes = []interface{}{
|
||||
(*Plugin)(nil), // 0: controller.storage.plugin.store.v1.Plugin
|
||||
}
|
||||
var file_controller_storage_plugin_store_v1_plugin_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_plugin_store_v1_plugin_proto_init() }
|
||||
func file_controller_storage_plugin_store_v1_plugin_proto_init() {
|
||||
if File_controller_storage_plugin_store_v1_plugin_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_controller_storage_plugin_store_v1_plugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Plugin); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_storage_plugin_store_v1_plugin_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_controller_storage_plugin_store_v1_plugin_proto_goTypes,
|
||||
DependencyIndexes: file_controller_storage_plugin_store_v1_plugin_proto_depIdxs,
|
||||
MessageInfos: file_controller_storage_plugin_store_v1_plugin_proto_msgTypes,
|
||||
}.Build()
|
||||
File_controller_storage_plugin_store_v1_plugin_proto = out.File
|
||||
file_controller_storage_plugin_store_v1_plugin_proto_rawDesc = nil
|
||||
file_controller_storage_plugin_store_v1_plugin_proto_goTypes = nil
|
||||
file_controller_storage_plugin_store_v1_plugin_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/plugin/store"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// A typeless plugin used for tests.
|
||||
type plugin struct {
|
||||
*store.Plugin
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// newPlugin is used in tests and creates a typeless plugin in the global scope.
|
||||
func newPlugin(name string) *plugin {
|
||||
p := &plugin{
|
||||
Plugin: &store.Plugin{
|
||||
ScopeId: scope.Global.String(),
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// TableName returns the table name for the host plugin.
|
||||
func (c *plugin) TableName() string {
|
||||
if c.tableName != "" {
|
||||
return c.tableName
|
||||
}
|
||||
return "plugin"
|
||||
}
|
||||
|
||||
// SetTableName sets the table name. If the caller attempts to
|
||||
// set the name to "" the name will be reset to the default name.
|
||||
func (c *plugin) SetTableName(n string) {
|
||||
c.tableName = n
|
||||
}
|
||||
|
||||
func testPlugin(t *testing.T, conn *gorm.DB, name string) *plugin {
|
||||
t.Helper()
|
||||
p := newPlugin(name)
|
||||
id, err := db.NewPublicId("plg")
|
||||
require.NoError(t, err)
|
||||
p.PublicId = id
|
||||
|
||||
w := db.New(conn)
|
||||
require.NoError(t, w.Create(context.Background(), p))
|
||||
return p
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_testPlugin(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
|
||||
plg := testPlugin(t, conn, "test")
|
||||
require.NotNil(plg)
|
||||
assert.NotEmpty(plg.GetPublicId())
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
syntax = "proto3";
|
||||
|
||||
// Package store provides protobufs for storing types in the plugin host
|
||||
// package.
|
||||
package controller.storage.host.plugin.store.v1;
|
||||
option go_package = "github.com/hashicorp/boundary/internal/host/plugin/store;store";
|
||||
|
||||
import "google/protobuf/struct.proto";
|
||||
import "controller/storage/timestamp/v1/timestamp.proto";
|
||||
import "controller/custom_options/v1/options.proto";
|
||||
|
||||
message HostCatalog {
|
||||
// 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 scope_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string name = 4 [(custom_options.v1.mask_mapping) = {this:"name" that: "name"}];
|
||||
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string description = 5 [(custom_options.v1.mask_mapping) = {this:"description" that: "description"}];
|
||||
|
||||
// The scope_id of the owning scope and must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string scope_id = 6;
|
||||
|
||||
// The public id of the plugin this catalog uses.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string plugin_id = 7;
|
||||
|
||||
// version allows optimistic locking of the resource
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
uint32 version = 8;
|
||||
|
||||
// attributes is a jsonb formatted field.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
bytes attributes = 9;
|
||||
}
|
||||
|
||||
message HostSet {
|
||||
// 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 [(custom_options.v1.mask_mapping) = {this:"name" that: "name"}];
|
||||
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string description = 5 [(custom_options.v1.mask_mapping) = {this:"description" that: "description"}];
|
||||
|
||||
// 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;
|
||||
|
||||
// attributes is a jsonb formatted field.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
bytes attributes = 8;
|
||||
}
|
||||
|
||||
message HostCatalogSecret {
|
||||
// catalog_id is the public id of the catalog containing this secret.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
string catalog_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;
|
||||
|
||||
// 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"`
|
||||
bytes secret = 4;
|
||||
|
||||
// ct_attributes is the ciphertext of the attribute data stored in the db.
|
||||
// @inject_tag: `gorm:"column:secret;not_null" wrapping:"ct,secret_data"`
|
||||
bytes ct_secret = 5;
|
||||
|
||||
// The key_id of the kms database key used for encrypting this entry.
|
||||
// It must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string key_id = 6;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
syntax = "proto3";
|
||||
|
||||
// Package store provides protobufs for storing types in the host package.
|
||||
package controller.storage.plugin.host.store.v1;
|
||||
option go_package = "github.com/hashicorp/boundary/internal/plugin/host/store;store";
|
||||
|
||||
import "controller/storage/timestamp/v1/timestamp.proto";
|
||||
import "controller/custom_options/v1/options.proto";
|
||||
|
||||
message Plugin {
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
string public_id = 10;
|
||||
|
||||
// The create_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
timestamp.v1.Timestamp create_time = 20;
|
||||
|
||||
// The update_time is set by the database.
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
timestamp.v1.Timestamp update_time = 30;
|
||||
|
||||
// name is optional. If set, it must be unique within scope_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string name = 4 [(custom_options.v1.mask_mapping) = {this:"Name" that: "name"}];
|
||||
|
||||
// description is optional.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string description = 50 [(custom_options.v1.mask_mapping) = {this:"Description" that: "description"}];
|
||||
|
||||
// The scope_id of the owning scope and must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string scope_id = 60;
|
||||
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
uint32 version = 70;
|
||||
|
||||
// plugin_name is required. The value must be unique across all host plugins
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string plugin_name = 80;
|
||||
|
||||
// id_prefix is required. The value must be unique across all host plugins
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string id_prefix = 90;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
syntax = "proto3";
|
||||
|
||||
// Package store provides protobufs for storing types in the host package.
|
||||
package controller.storage.plugin.store.v1;
|
||||
option go_package = "github.com/hashicorp/boundary/internal/plugin/store;store";
|
||||
|
||||
import "controller/storage/timestamp/v1/timestamp.proto";
|
||||
|
||||
// This is a typeless plugin. In practice this should never be used directly.
|
||||
// This is included for testing purposes only.
|
||||
message Plugin {
|
||||
// public_id is a surrogate key suitable for use in a public API.
|
||||
// @inject_tag: `gorm:"primary_key"`
|
||||
string public_id = 10;
|
||||
|
||||
// The scope_id of the owning scope and must be set.
|
||||
// @inject_tag: `gorm:"not_null"`
|
||||
string scope_id = 20;
|
||||
|
||||
// name is optional. If set, it must be unique within scope_id.
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string name = 30;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ func TestPlugin(t *testing.T) {
|
||||
|
||||
// Create test plugin id
|
||||
testPluginId := "pi_test1234"
|
||||
numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id) values (?)", []interface{}{testPluginId})
|
||||
numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id, scope_id) values (?, 'global')", []interface{}{testPluginId})
|
||||
require.NoError(err)
|
||||
assert.Equal(1, numRows)
|
||||
|
||||
@@ -154,7 +154,7 @@ func TestPlugin(t *testing.T) {
|
||||
|
||||
// Create test plugin id
|
||||
testPluginId := "pi_test1234"
|
||||
numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id) values (?)", []interface{}{testPluginId})
|
||||
numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id, scope_id) values (?, 'global')", []interface{}{testPluginId})
|
||||
assert.NoError(err)
|
||||
assert.Equal(1, numRows)
|
||||
|
||||
@@ -177,7 +177,7 @@ func TestPlugin(t *testing.T) {
|
||||
|
||||
// Create test plugin id
|
||||
testPluginId := "pi_test1234"
|
||||
numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id) values (?)", []interface{}{testPluginId})
|
||||
numRows, err := rw.Exec(context.Background(), "insert into plugin(public_id, scope_id) values (?, 'global')", []interface{}{testPluginId})
|
||||
assert.NoError(err)
|
||||
assert.Equal(1, numRows)
|
||||
|
||||
|
||||
Reference in new issue
Block a user