refactor (kms): adopt the go-kms-wrapping/extras/kms package (#2027)
* feature (db): add Db.UnderlyingDB() This is needed to allow for integration with bits that need a *dbw.DB * feature (kms): add new go-kms-wrapping/extras/kms dependency * refactor (kms): schema changes required to adopt extras/kms pkg * refactor (kms/iam): rename to DeprecateCreateKeysTx(...) * refactor (kms): remove deprecated test * refactor (kms): refactor Kms type This refactor includes renaming the existing type to DeprecatedKms and then refactoring the Kms type to become a wrapper of go-kms-wrapping/extras/kms * refactor (kms): delete all the unneeded bits All this stuff is not needed now that we're using go-kms-wrapping/extras/kms * refactor (db): fixup tests after kms refactor * refactor (kms/tests): use testing.TB for test helpers Our test helper packages were not possible to use with benchmarks. This refactor switches to testing.TB to allow both tests and benchmarks to use the helpers. * tests (session benchmarks): refactor to use new Kms type * refactor (schema): rm test which relies on deprecated Kms code * refactor (kms): post rebase on main changes required * chore (kms refactor): re-add unit test from main * refactor (kms): uses migrations that don't include KMS schema changes
This commit is contained in:
97 files changed
+2580
-15154
No files matched your search
@@ -208,6 +208,11 @@ func New(underlying *DB) *Db {
|
||||
return &Db{underlying: underlying}
|
||||
}
|
||||
|
||||
// UnderlyingDB returns the underlying *dbw.DB
|
||||
func (rw *Db) UnderlyingDB() *dbw.DB {
|
||||
return rw.underlying.wrapped
|
||||
}
|
||||
|
||||
// Exec will execute the sql with the values as parameters. The int returned
|
||||
// is the number of rows affected by the sql. WithDebug is supported.
|
||||
func (rw *Db) Exec(ctx context.Context, sql string, values []interface{}, opt ...Option) (int, error) {
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
begin;
|
||||
|
||||
-- make the required schema changes to adopt
|
||||
-- github.com/hashicorp/go-kms-wrapping/extras/kms/v2
|
||||
-- this migration is from:
|
||||
-- https://github.com/hashicorp/go-kms-wrapping/blob/main/extras/kms/migrations/postgres/01_domain_types.up.sql
|
||||
|
||||
create domain kms_private_id as text
|
||||
not null
|
||||
check(
|
||||
length(trim(value)) > 0
|
||||
);
|
||||
comment on domain kms_private_id is
|
||||
'standard column for private id';
|
||||
|
||||
create domain kms_scope_id as text
|
||||
check(
|
||||
length(trim(value)) > 0
|
||||
);
|
||||
comment on domain kms_scope_id is
|
||||
'standard column for scope id';
|
||||
|
||||
create domain kms_timestamp as
|
||||
timestamp with time zone
|
||||
default current_timestamp;
|
||||
comment on domain kms_timestamp is
|
||||
'Standard timestamp for all create_time and update_time columns';
|
||||
|
||||
create domain kms_version as bigint
|
||||
default 1
|
||||
not null
|
||||
check(
|
||||
value > 0
|
||||
);
|
||||
comment on domain kms_version is
|
||||
'standard column for row version';
|
||||
|
||||
-- kms_immutable_columns() will make the column names immutable which are passed as
|
||||
-- parameters when the trigger is created. It raises error code 23601 which is a
|
||||
-- class 23 integrity constraint violation: immutable column
|
||||
create function kms_immutable_columns()
|
||||
returns trigger
|
||||
as $$
|
||||
declare
|
||||
col_name text;
|
||||
new_value text;
|
||||
old_value text;
|
||||
begin
|
||||
foreach col_name in array tg_argv loop
|
||||
execute format('SELECT $1.%I', col_name) into new_value using new;
|
||||
execute format('SELECT $1.%I', col_name) into old_value using old;
|
||||
if new_value is distinct from old_value then
|
||||
raise exception 'immutable column: %.%', tg_table_name, col_name using
|
||||
errcode = '23601',
|
||||
schema = tg_table_schema,
|
||||
table = tg_table_name,
|
||||
column = col_name;
|
||||
end if;
|
||||
end loop;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
comment on function
|
||||
kms_immutable_columns()
|
||||
is
|
||||
'function used in before update triggers to make columns immutable';
|
||||
|
||||
|
||||
create function kms_default_create_time()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
if new.create_time is distinct from now() then
|
||||
new.create_time = now();
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function
|
||||
kms_default_create_time()
|
||||
is
|
||||
'function used to properly set create_time columns';
|
||||
|
||||
create function
|
||||
kms_update_time_column()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
if row(new.*) is distinct from row(old.*) then
|
||||
new.update_time = now();
|
||||
return new;
|
||||
else
|
||||
return old;
|
||||
end if;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function
|
||||
kms_update_time_column()
|
||||
is
|
||||
'function used in before update triggers to properly set update_time columns';
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,40 @@
|
||||
begin;
|
||||
|
||||
-- make the required schema changes to adopt
|
||||
-- github.com/hashicorp/go-kms-wrapping/extras/kms/v2
|
||||
-- this migration is from:
|
||||
-- https://github.com/hashicorp/go-kms-wrapping/blob/main/extras/kms/migrations/postgres/02_version.up.sql
|
||||
|
||||
create table kms_schema_version(
|
||||
version text not null,
|
||||
create_time kms_timestamp,
|
||||
update_time kms_timestamp
|
||||
);
|
||||
comment on table kms_schema_version is
|
||||
'kms_schema_version contains the kms schema version';
|
||||
|
||||
-- this index ensures that there will only ever be one row in the
|
||||
-- table. see:
|
||||
-- https://www.postgresql.org/docs/current/indexes-expressional.html
|
||||
create unique index kms_schema_version_one_row
|
||||
ON kms_schema_version((version is not null));
|
||||
|
||||
-- define the immutable fields for kms_root_key (all of them)
|
||||
create trigger kms_immutable_columns
|
||||
before
|
||||
update on kms_schema_version
|
||||
for each row execute procedure kms_immutable_columns('create_time');
|
||||
|
||||
create trigger kms_default_create_time_column
|
||||
before
|
||||
insert on kms_schema_version
|
||||
for each row execute procedure kms_default_create_time();
|
||||
|
||||
create trigger kms_update_time_column
|
||||
before
|
||||
update on kms_schema_version
|
||||
for each row execute procedure kms_update_time_column();
|
||||
|
||||
insert into kms_schema_version(version) values('v0.0.1');
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,37 @@
|
||||
begin;
|
||||
|
||||
-- make the required schema changes to adopt
|
||||
-- github.com/hashicorp/go-kms-wrapping/extras/kms/v2
|
||||
-- this migration is from:
|
||||
-- https://github.com/hashicorp/go-kms-wrapping/blob/main/extras/kms/migrations/postgres/03_keys.up.sql
|
||||
|
||||
-- we need to replace the existing function and there are existing tables that
|
||||
-- depend on it.. so let's just create/replace it
|
||||
|
||||
-- kms_version_column() will increment the version column whenever row data
|
||||
-- is inserted and should only be used in an before insert trigger. This
|
||||
-- function will overwrite any explicit values to the version column.
|
||||
create or replace function kms_version_column()
|
||||
returns trigger
|
||||
as $$
|
||||
declare
|
||||
_key_id text;
|
||||
_max bigint;
|
||||
begin
|
||||
execute format('SELECT $1.%I', tg_argv[0]) into _key_id using new;
|
||||
execute format('select max(version) + 1 from %I where %I = $1', tg_relid::regclass, tg_argv[0]) using _key_id into _max;
|
||||
if _max is null then
|
||||
_max = 1;
|
||||
end if;
|
||||
new.version = _max;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
comment on function
|
||||
kms_version_column()
|
||||
is
|
||||
'function used in before insert triggers to properly set version columns for kms_* tables with a version column';
|
||||
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,281 @@
|
||||
begin;
|
||||
|
||||
-- make the required schema changes to adopt
|
||||
-- github.com/hashicorp/go-kms-wrapping/extras/kms/v2
|
||||
-- this migration is from:
|
||||
-- https://github.com/hashicorp/go-kms-wrapping/blob/main/extras/kms/migrations/postgres/04_keys.up.sql
|
||||
|
||||
|
||||
alter table kms_root_key rename to kms_deprecated_root_key;
|
||||
alter table kms_root_key_version rename to kms_deprecated_root_key_version;
|
||||
|
||||
create table kms_root_key (
|
||||
private_id kms_private_id primary key,
|
||||
scope_id kms_scope_id not null unique -- there can only be one root key for a scope.
|
||||
references iam_scope(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
create_time kms_timestamp
|
||||
);
|
||||
comment on table kms_root_key is
|
||||
'kms_root_key defines a root key for a scope';
|
||||
|
||||
-- define the immutable fields for kms_root_key (all of them)
|
||||
create trigger kms_immutable_columns
|
||||
before
|
||||
update on kms_root_key
|
||||
for each row execute procedure kms_immutable_columns('private_id', 'scope_id', 'create_time');
|
||||
|
||||
-- not adding the kms_default_create_time_column trigger until after we've
|
||||
-- converted the data (we don't want the timestamp to change)
|
||||
|
||||
create table kms_root_key_version (
|
||||
private_id kms_private_id primary key,
|
||||
root_key_id kms_private_id not null
|
||||
constraint kms_root_key_fkey
|
||||
references kms_root_key(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
version kms_version,
|
||||
key bytea not null
|
||||
constraint not_empty_key
|
||||
check (
|
||||
length(key) > 0
|
||||
),
|
||||
create_time kms_timestamp,
|
||||
constraint kms_root_key_version_root_key_id_version_uq
|
||||
unique(root_key_id, version)
|
||||
);
|
||||
comment on table kms_root_key_version is
|
||||
'kms_root_key_version contains versions of a kms_root_key';
|
||||
|
||||
-- define the immutable fields for kms_root_key_version (all of them)
|
||||
create trigger kms_immutable_columns
|
||||
before
|
||||
update on kms_root_key_version
|
||||
for each row execute procedure kms_immutable_columns('private_id', 'root_key_id', 'version', 'key', 'create_time');
|
||||
|
||||
-- not adding the kms_default_create_time_column or kms_version_column triggers
|
||||
-- until after we've converted the data (we don't want the timestamp or versions
|
||||
-- to change)
|
||||
|
||||
create table kms_data_key (
|
||||
private_id kms_private_id primary key,
|
||||
root_key_id kms_private_id not null
|
||||
constraint kms_root_key_fkey
|
||||
references kms_root_key(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
purpose text not null
|
||||
constraint not_start_end_whitespace_purpose
|
||||
check (length(trim(purpose)) = length(purpose)),
|
||||
create_time kms_timestamp,
|
||||
constraint kms_data_key_root_key_id_purpose_uq
|
||||
unique (root_key_id, purpose) -- there can only be one dek for a specific purpose per root key
|
||||
);
|
||||
comment on table kms_data_key is
|
||||
'kms_data_key contains deks (data keys) for specific purposes';
|
||||
|
||||
-- define the immutable fields for kms_data_key (all of them)
|
||||
create trigger kms_immutable_columns
|
||||
before
|
||||
update on kms_data_key
|
||||
for each row execute procedure kms_immutable_columns('private_id', 'root_key_id', 'purpose', 'create_time');
|
||||
|
||||
-- not adding the kms_default_create_time_column trigger until after we've
|
||||
-- converted the data (we don't want the timestamp to change)
|
||||
|
||||
create table kms_data_key_version (
|
||||
private_id kms_private_id primary key,
|
||||
data_key_id kms_private_id not null
|
||||
constraint kms_data_key_fkey
|
||||
references kms_data_key(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
root_key_version_id kms_private_id not null
|
||||
constraint kms_root_key_version_fkey
|
||||
references kms_root_key_version(private_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
version kms_version,
|
||||
key bytea not null
|
||||
constraint not_empty_key
|
||||
check (
|
||||
length(key) > 0
|
||||
),
|
||||
create_time kms_timestamp,
|
||||
constraint kms_data_key_version_data_key_id_version_uq
|
||||
unique(data_key_id, version)
|
||||
);
|
||||
comment on table kms_data_key is
|
||||
'kms_data_key_version contains versions of a kms_data_key (dek aka data keys)';
|
||||
|
||||
-- define the immutable fields for kms_data_key_version (all of them)
|
||||
create trigger kms_immutable_columns
|
||||
before
|
||||
update on kms_data_key_version
|
||||
for each row execute procedure immutable_columns('private_id', 'data_key_id', 'root_key_version_id', 'version', 'key', 'create_time');
|
||||
|
||||
-- not adding the kms_default_create_time_column or kms_version_column triggers
|
||||
-- until after we've converted the data (we don't want the timestamp or versions
|
||||
-- to change)
|
||||
|
||||
-- ############################################################################
|
||||
-- Next: we will convert all the existing KEKs/DEKs into the new schema model
|
||||
-- before adding triggers for create_time, and version
|
||||
-- this is important, especially for the version column!
|
||||
|
||||
-- convert KEK tables
|
||||
insert into kms_root_key (private_id, scope_id, create_time)
|
||||
select private_id, scope_id, create_time
|
||||
from kms_deprecated_root_key;
|
||||
|
||||
insert into kms_root_key_version (private_id, root_key_id, version, key, create_time)
|
||||
select private_id, root_key_id, version, key, create_time
|
||||
from kms_deprecated_root_key_version;
|
||||
|
||||
-- convert database DEK tables
|
||||
insert into kms_data_key (private_id, root_key_id, purpose, create_time)
|
||||
select private_id, root_key_id, 'database', create_time
|
||||
from kms_database_key;
|
||||
|
||||
insert into kms_data_key_version (private_id, data_key_id, root_key_version_id, version, key, create_time)
|
||||
select private_id, database_key_id, root_key_version_id, version, key, create_time
|
||||
from kms_database_key_version;
|
||||
|
||||
-- convert oplog DEK tables
|
||||
insert into kms_data_key (private_id, root_key_id, purpose, create_time)
|
||||
select private_id, root_key_id, 'oplog', create_time
|
||||
from kms_oplog_key;
|
||||
|
||||
insert into kms_data_key_version (private_id, data_key_id, root_key_version_id, version, key, create_time)
|
||||
select private_id, oplog_key_id, root_key_version_id, version, key, create_time
|
||||
from kms_oplog_key_version;
|
||||
|
||||
-- convert the audit DEK tables
|
||||
insert into kms_data_key (private_id, root_key_id, purpose, create_time)
|
||||
select private_id, root_key_id, 'audit', create_time
|
||||
from kms_audit_key;
|
||||
|
||||
insert into kms_data_key_version (private_id, data_key_id, root_key_version_id, version, key, create_time)
|
||||
select private_id, audit_key_id, root_key_version_id, version, key, create_time
|
||||
from kms_audit_key_version;
|
||||
|
||||
-- convert the oidc DEK tables
|
||||
insert into kms_data_key (private_id, root_key_id, purpose, create_time)
|
||||
select private_id, root_key_id, 'oidc', create_time
|
||||
from kms_oidc_key;
|
||||
|
||||
insert into kms_data_key_version (private_id, data_key_id, root_key_version_id, version, key, create_time)
|
||||
select private_id, oidc_key_id, root_key_version_id, version, key, create_time
|
||||
from kms_oidc_key_version;
|
||||
|
||||
-- convert the token DEK tables
|
||||
insert into kms_data_key (private_id, root_key_id, purpose, create_time)
|
||||
select private_id, root_key_id, 'tokens', create_time
|
||||
from kms_token_key;
|
||||
|
||||
insert into kms_data_key_version (private_id, data_key_id, root_key_version_id, version, key, create_time)
|
||||
select private_id, token_key_id, root_key_version_id, version, key, create_time
|
||||
from kms_token_key_version;
|
||||
|
||||
-- convert the session DEK tables
|
||||
insert into kms_data_key (private_id, root_key_id, purpose, create_time)
|
||||
select private_id, root_key_id, 'sessions', create_time
|
||||
from kms_session_key;
|
||||
|
||||
insert into kms_data_key_version (private_id, data_key_id, root_key_version_id, version, key, create_time)
|
||||
select private_id, session_key_id, root_key_version_id, version, key, create_time
|
||||
from kms_session_key_version;
|
||||
|
||||
-- ############################################################################
|
||||
-- post conversion, we add the required triggers
|
||||
create trigger kms_default_create_time_column
|
||||
before
|
||||
insert on kms_root_key
|
||||
for each row execute procedure kms_default_create_time();
|
||||
|
||||
create trigger kms_default_create_time_column
|
||||
before
|
||||
insert on kms_root_key_version
|
||||
for each row execute procedure kms_default_create_time();
|
||||
|
||||
create trigger kms_version_column
|
||||
before insert on kms_root_key_version
|
||||
for each row execute procedure kms_version_column('root_key_id');
|
||||
|
||||
create trigger default_create_time_column
|
||||
before
|
||||
insert on kms_data_key
|
||||
for each row execute procedure kms_default_create_time();
|
||||
|
||||
create trigger default_create_time
|
||||
before
|
||||
insert on kms_data_key_version
|
||||
for each row execute procedure kms_default_create_time();
|
||||
|
||||
create trigger kms_version_column
|
||||
before insert on kms_data_key_version
|
||||
for each row execute procedure kms_version_column('data_key_id');
|
||||
|
||||
-- Next: we will convert all the existing DEKs FKs to the new model
|
||||
alter table credential_vault_token drop constraint kms_database_key_version_fkey;
|
||||
alter table credential_vault_token
|
||||
add constraint kms_data_key_version_fkey
|
||||
foreign key(key_id)
|
||||
references kms_data_key_version (private_id)
|
||||
on delete restrict
|
||||
on update cascade;
|
||||
|
||||
alter table credential_vault_client_certificate drop constraint kms_database_key_version_fkey;
|
||||
alter table credential_vault_client_certificate
|
||||
add constraint kms_data_key_version_fkey
|
||||
foreign key(key_id)
|
||||
references kms_data_key_version (private_id)
|
||||
on delete restrict
|
||||
on update cascade;
|
||||
|
||||
alter table auth_oidc_method drop constraint kms_database_key_version_fkey;
|
||||
alter table auth_oidc_method
|
||||
add constraint kms_data_key_version_fkey
|
||||
foreign key(key_id)
|
||||
references kms_data_key_version (private_id)
|
||||
on delete restrict
|
||||
on update cascade;
|
||||
|
||||
alter table host_plugin_catalog_secret drop constraint kms_database_key_version_fkey;
|
||||
alter table host_plugin_catalog_secret
|
||||
add constraint kms_data_key_version_fkey
|
||||
foreign key(key_id)
|
||||
references kms_data_key_version (private_id)
|
||||
on delete restrict
|
||||
on update cascade;
|
||||
|
||||
alter table session_credential drop constraint kms_database_key_version_fkey;
|
||||
alter table session_credential
|
||||
add constraint kms_data_key_version_fkey
|
||||
foreign key(key_id)
|
||||
references kms_data_key_version (private_id)
|
||||
on delete restrict
|
||||
on update cascade;
|
||||
|
||||
-- rename tables so we know that they won't be used by mistake
|
||||
alter table kms_database_key_version rename to kms_deprecated_database_key_version;
|
||||
alter table kms_database_key rename to kms_deprecated_database_key;
|
||||
|
||||
alter table kms_oplog_key_version rename to kms_deprecated_oplog_key_version;
|
||||
alter table kms_oplog_key rename to kms_deprecated_oplog_key;
|
||||
|
||||
alter table kms_audit_key_version rename to kms_deprecated_audit_key_version;
|
||||
alter table kms_audit_key rename to kms_audit_database_key;
|
||||
|
||||
alter table kms_oidc_key_version rename to kms_deprecated_oidc_key_version;
|
||||
alter table kms_oidc_key rename to kms_deprecated_oidc_key;
|
||||
|
||||
alter table kms_session_key_version rename to kms_deprecated_session_key_version;
|
||||
alter table kms_session_key rename to kms_deprecated_session_key;
|
||||
|
||||
alter table kms_token_key_version rename to kms_deprecated_token_key_version;
|
||||
alter table kms_token_key rename to kms_deprecated_token_key;
|
||||
|
||||
commit;
|
||||
@@ -1,162 +0,0 @@
|
||||
package oss_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/common"
|
||||
"github.com/hashicorp/boundary/internal/db/schema"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/servers"
|
||||
"github.com/hashicorp/boundary/testing/dbtest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMigrations_Nonce(t *testing.T) {
|
||||
const (
|
||||
priorMigration = 16005
|
||||
currentMigration = 18001
|
||||
)
|
||||
|
||||
t.Parallel()
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
ctx := context.Background()
|
||||
dialect := dbtest.Postgres
|
||||
|
||||
c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1))
|
||||
require.NoError(err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(c())
|
||||
})
|
||||
d, err := common.SqlOpen(dialect, u)
|
||||
require.NoError(err)
|
||||
|
||||
// migration to the prior migration (before the one we want to test)
|
||||
m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}),
|
||||
))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(m.ApplyMigrations(ctx))
|
||||
state, err := m.CurrentState(ctx)
|
||||
require.NoError(err)
|
||||
want := &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: priorMigration,
|
||||
DatabaseSchemaVersion: priorMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(want, state)
|
||||
|
||||
// get a connection
|
||||
dbType, err := db.StringToDbType(dialect)
|
||||
require.NoError(err)
|
||||
conn, err := db.Open(dbType, u)
|
||||
require.NoError(err)
|
||||
rw := db.New(conn)
|
||||
|
||||
// okay, now we can seed the database with test data and validate existing
|
||||
// functionality. At the end of this we will have two values in the table
|
||||
// with no purpose.
|
||||
{
|
||||
// Create an old version of the struct
|
||||
type RecoveryNonce struct {
|
||||
Nonce string
|
||||
}
|
||||
|
||||
require.NoError(rw.CreateItems(ctx, []interface{}{
|
||||
&RecoveryNonce{Nonce: "abcd"},
|
||||
&RecoveryNonce{Nonce: "dcba"},
|
||||
}))
|
||||
|
||||
var results []*RecoveryNonce
|
||||
require.NoError(rw.SearchWhere(ctx, &results, "", nil))
|
||||
require.Len(results, 2)
|
||||
}
|
||||
|
||||
// now we're ready for the migration we want to test.
|
||||
m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}),
|
||||
))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(m.ApplyMigrations(ctx))
|
||||
state, err = m.CurrentState(ctx)
|
||||
require.NoError(err)
|
||||
want = &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: currentMigration,
|
||||
DatabaseSchemaVersion: currentMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(want, state)
|
||||
|
||||
// now we have migrated and we will validate functionality; we'll do this
|
||||
// via the repo
|
||||
{
|
||||
wrapper := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
repo, err := servers.NewRepository(rw, rw, kmsCache)
|
||||
require.NoError(err)
|
||||
require.NotNil(repo)
|
||||
|
||||
// First list and validate that the upgrade adjusted purpose correctly
|
||||
results, err := repo.ListNonces(ctx, servers.NoncePurposeRecovery)
|
||||
require.NoError(err)
|
||||
assert.Len(results, 2)
|
||||
for _, n := range results {
|
||||
assert.Equal(servers.NoncePurposeRecovery, n.Purpose)
|
||||
}
|
||||
|
||||
// Test the constraint, first via the repo, then directly
|
||||
const badPurpose = "foobar"
|
||||
err = repo.AddNonce(ctx, "xyz", badPurpose)
|
||||
require.Error(err)
|
||||
assert.Contains(err.Error(), "unknown nonce purpose")
|
||||
|
||||
// Check that null errors
|
||||
require.Error(rw.CreateItems(ctx, []interface{}{
|
||||
&servers.Nonce{Nonce: "dcba"},
|
||||
}))
|
||||
// Check that bad purpose errors
|
||||
require.Error(rw.CreateItems(ctx, []interface{}{
|
||||
&servers.Nonce{Nonce: "dcba", Purpose: badPurpose},
|
||||
}))
|
||||
|
||||
// Add some valid values, ensure that we can then list for both purposes
|
||||
// and get different values
|
||||
require.NoError(repo.AddNonce(ctx, "wxyz", servers.NoncePurposeWorkerAuth))
|
||||
require.NoError(repo.AddNonce(ctx, "zyxw", servers.NoncePurposeWorkerAuth))
|
||||
results, err = repo.ListNonces(ctx, servers.NoncePurposeRecovery)
|
||||
require.NoError(err)
|
||||
assert.Len(results, 2)
|
||||
for _, n := range results {
|
||||
assert.Equal(servers.NoncePurposeRecovery, n.Purpose)
|
||||
}
|
||||
results, err = repo.ListNonces(ctx, servers.NoncePurposeWorkerAuth)
|
||||
require.NoError(err)
|
||||
assert.Len(results, 2)
|
||||
for _, n := range results {
|
||||
assert.Equal(servers.NoncePurposeWorkerAuth, n.Purpose)
|
||||
}
|
||||
|
||||
// Last: add the same nonce, ensure we error
|
||||
require.NoError(repo.AddNonce(ctx, "double", servers.NoncePurposeWorkerAuth))
|
||||
err = repo.AddNonce(ctx, "double", servers.NoncePurposeWorkerAuth)
|
||||
require.Error(err)
|
||||
require.Contains(err.Error(), "duplicate key value")
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
package oss_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/common"
|
||||
"github.com/hashicorp/boundary/internal/db/schema"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
"github.com/hashicorp/boundary/testing/dbtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMigrations_SessionState(t *testing.T) {
|
||||
t.Parallel()
|
||||
require := require.New(t)
|
||||
|
||||
const (
|
||||
priorMigration = 21002
|
||||
currentMigration = 24001
|
||||
)
|
||||
dialect := dbtest.Postgres
|
||||
ctx := context.Background()
|
||||
|
||||
c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1))
|
||||
require.NoError(err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(c())
|
||||
})
|
||||
d, err := common.SqlOpen(dialect, u)
|
||||
require.NoError(err)
|
||||
|
||||
// migration to the prior migration (before the one we want to test)
|
||||
m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}),
|
||||
))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(m.ApplyMigrations(ctx))
|
||||
state, err := m.CurrentState(ctx)
|
||||
require.NoError(err)
|
||||
want := &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: priorMigration,
|
||||
DatabaseSchemaVersion: priorMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(want, state)
|
||||
|
||||
// Seed the database with test data
|
||||
dbType, err := db.StringToDbType(dialect)
|
||||
require.NoError(err)
|
||||
|
||||
conn, err := db.Open(dbType, u)
|
||||
require.NoError(err)
|
||||
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NotNil(prj)
|
||||
|
||||
hc := static.TestCatalogs(t, conn, prj.GetPublicId(), 1)[0]
|
||||
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
||||
static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
|
||||
|
||||
tar := tcp.TestTarget(ctx, t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()}))
|
||||
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
{
|
||||
at := authtoken.TestAuthToken(t, conn, kmsCache, org.GetPublicId())
|
||||
uId := at.GetIamUserId()
|
||||
|
||||
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
|
||||
UserId: uId,
|
||||
HostId: h.GetPublicId(),
|
||||
TargetId: tar.GetPublicId(),
|
||||
HostSetId: hs.GetPublicId(),
|
||||
AuthTokenId: at.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Endpoint: "tcp://127.0.0.1:22",
|
||||
ConnectionLimit: -1,
|
||||
})
|
||||
|
||||
session.TestConnection(t, conn, sess.PublicId, "127.0.0.1", 22,
|
||||
"127.0.0.2", 23, "127.0.0.1")
|
||||
}
|
||||
|
||||
// now we're ready for the migration we want to test.
|
||||
m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}),
|
||||
))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(m.ApplyMigrations(ctx))
|
||||
state, err = m.CurrentState(ctx)
|
||||
require.NoError(err)
|
||||
want = &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: currentMigration,
|
||||
DatabaseSchemaVersion: currentMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(want, state)
|
||||
|
||||
sessionRepo, err := session.NewRepository(rw, rw, kmsCache)
|
||||
connectionRepo, err := session.NewConnectionRepository(ctx, rw, rw, kmsCache)
|
||||
require.NoError(err)
|
||||
|
||||
// Ensure session is cancelled
|
||||
repoSessions, err := sessionRepo.ListSessions(ctx)
|
||||
require.NoError(err)
|
||||
var sessionTermReason []string
|
||||
var repoSessionId string
|
||||
for i := 0; i < len(repoSessions); i++ {
|
||||
s := repoSessions[i]
|
||||
repoSessionId = s.PublicId
|
||||
sessionTermReason = append(sessionTermReason, s.TerminationReason)
|
||||
}
|
||||
require.Equal([]string{"canceled"}, sessionTermReason)
|
||||
|
||||
// Ensure connection is also cancelled
|
||||
connections, err := connectionRepo.ListConnectionsBySessionId(ctx, repoSessionId)
|
||||
require.NoError(err)
|
||||
|
||||
var connTermReason []string
|
||||
for i := 0; i < len(connections); i++ {
|
||||
c := connections[i]
|
||||
connTermReason = append(connTermReason, c.ClosedReason)
|
||||
}
|
||||
require.Equal([]string{"canceled"}, connTermReason)
|
||||
|
||||
// Validate new table contents
|
||||
rows, err := d.QueryContext(ctx, "select * from session_valid_state")
|
||||
require.NoError(err)
|
||||
|
||||
var validStates []string
|
||||
for rows.Next() {
|
||||
var a, b string
|
||||
require.NoError(rows.Scan(&a, &b))
|
||||
validStates = append(validStates, []string{a, b}...)
|
||||
}
|
||||
require.Equal([]string{
|
||||
"pending", "pending", "pending", "active", "pending", "terminated",
|
||||
"pending", "canceling", "active", "canceling", "active", "terminated", "canceling", "terminated",
|
||||
}, validStates)
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
package oss_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/common"
|
||||
"github.com/hashicorp/boundary/internal/db/schema"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/testing/dbtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMigrations_SessionStateTrigger(t *testing.T) {
|
||||
t.Parallel()
|
||||
require := require.New(t)
|
||||
|
||||
const (
|
||||
priorMigration = 27002
|
||||
currentMigration = 28002
|
||||
)
|
||||
dialect := dbtest.Postgres
|
||||
ctx := context.Background()
|
||||
|
||||
c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1))
|
||||
require.NoError(err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(c())
|
||||
})
|
||||
d, err := common.SqlOpen(dialect, u)
|
||||
require.NoError(err)
|
||||
|
||||
// migration to the prior migration (before the one we want to test)
|
||||
m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}),
|
||||
))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(m.ApplyMigrations(ctx))
|
||||
state, err := m.CurrentState(ctx)
|
||||
require.NoError(err)
|
||||
want := &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: priorMigration,
|
||||
DatabaseSchemaVersion: priorMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(want, state)
|
||||
|
||||
// Seed the database with test data
|
||||
dbType, err := db.StringToDbType(dialect)
|
||||
require.NoError(err)
|
||||
|
||||
conn, err := db.Open(dbType, u)
|
||||
require.NoError(err)
|
||||
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NotNil(prj)
|
||||
|
||||
hc := static.TestCatalogs(t, conn, prj.GetPublicId(), 1)[0]
|
||||
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
||||
static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
|
||||
tar := tcp.TestTarget(ctx, t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()}))
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
serverId := "worker"
|
||||
tofu := session.TestTofu(t)
|
||||
session.TestWorker(t, conn, wrapper, session.WithServerId(serverId))
|
||||
at := authtoken.TestAuthToken(t, conn, kmsCache, org.GetPublicId())
|
||||
uId := at.GetIamUserId()
|
||||
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
|
||||
UserId: uId,
|
||||
HostId: h.GetPublicId(),
|
||||
TargetId: tar.GetPublicId(),
|
||||
HostSetId: hs.GetPublicId(),
|
||||
AuthTokenId: at.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Endpoint: "tcp://127.0.0.1:22",
|
||||
ConnectionLimit: 1,
|
||||
})
|
||||
|
||||
sessionRepo, err := session.NewRepository(rw, rw, kmsCache)
|
||||
require.NoError(err)
|
||||
|
||||
// Make and transition a valid session through pending, active, canceling, and terminated
|
||||
// Unfortunately, could not recreate an invalid session P-A-T-C using the session repo
|
||||
_, _, err = sessionRepo.ActivateSession(ctx, sess.PublicId, sess.Version, serverId, resource.Worker.String(), tofu)
|
||||
require.NoError(err)
|
||||
connection := session.TestConnection(t, conn, sess.PublicId, "127.0.0.1", 22,
|
||||
"127.0.0.2", 23, "127.0.0.1")
|
||||
session.TestConnectionState(t, conn, connection.PublicId, session.StatusConnected)
|
||||
session.TestConnectionState(t, conn, connection.PublicId, session.StatusClosed)
|
||||
_, err = sessionRepo.CancelSession(ctx, sess.PublicId, sess.Version+1)
|
||||
require.NoError(err)
|
||||
sessionRepo.TerminateCompletedSessions(ctx)
|
||||
|
||||
repoSessions, err := sessionRepo.ListSessions(ctx)
|
||||
require.NoError(err)
|
||||
var numStates int
|
||||
for _, s := range repoSessions {
|
||||
numStates = len(s.States)
|
||||
}
|
||||
require.Equal(4, numStates)
|
||||
|
||||
// now we're ready for the migration we want to test.
|
||||
m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}),
|
||||
))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(m.ApplyMigrations(ctx))
|
||||
state, err = m.CurrentState(ctx)
|
||||
require.NoError(err)
|
||||
want = &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: currentMigration,
|
||||
DatabaseSchemaVersion: currentMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(want, state)
|
||||
|
||||
// Check that we haven't removed a state
|
||||
repoSessions, err = sessionRepo.ListSessions(ctx)
|
||||
require.NoError(err)
|
||||
for _, s := range repoSessions {
|
||||
numStates = len(s.States)
|
||||
}
|
||||
require.Equal(4, numStates)
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package oss_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/common"
|
||||
"github.com/hashicorp/boundary/internal/db/schema"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/testing/dbtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMigrations_SessionFKeyDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
require := require.New(t)
|
||||
|
||||
const (
|
||||
priorMigration = 28002
|
||||
currentMigration = 29001
|
||||
)
|
||||
dialect := dbtest.Postgres
|
||||
ctx := context.Background()
|
||||
|
||||
c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1))
|
||||
require.NoError(err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(c())
|
||||
})
|
||||
d, err := common.SqlOpen(dialect, u)
|
||||
require.NoError(err)
|
||||
|
||||
// migration to the prior migration (before the one we want to test)
|
||||
m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}),
|
||||
))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(m.ApplyMigrations(ctx))
|
||||
state, err := m.CurrentState(ctx)
|
||||
require.NoError(err)
|
||||
want := &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: priorMigration,
|
||||
DatabaseSchemaVersion: priorMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(want, state)
|
||||
|
||||
// Seed the database with test data
|
||||
dbType, err := db.StringToDbType(dialect)
|
||||
require.NoError(err)
|
||||
|
||||
conn, err := db.Open(dbType, u)
|
||||
require.NoError(err)
|
||||
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NotNil(prj)
|
||||
|
||||
hc := static.TestCatalogs(t, conn, prj.GetPublicId(), 1)[0]
|
||||
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
||||
static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
|
||||
tar := tcp.TestTarget(ctx, t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()}))
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
targetRepo, err := target.NewRepository(rw, rw, kmsCache)
|
||||
require.NoError(err)
|
||||
|
||||
serverId := "worker"
|
||||
tofu := session.TestTofu(t)
|
||||
session.TestWorker(t, conn, wrapper, session.WithServerId(serverId))
|
||||
at := authtoken.TestAuthToken(t, conn, kmsCache, org.GetPublicId())
|
||||
uId := at.GetIamUserId()
|
||||
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
|
||||
UserId: uId,
|
||||
HostId: h.GetPublicId(),
|
||||
TargetId: tar.GetPublicId(),
|
||||
HostSetId: hs.GetPublicId(),
|
||||
AuthTokenId: at.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Endpoint: "tcp://127.0.0.1:22",
|
||||
ConnectionLimit: 1,
|
||||
})
|
||||
|
||||
sessionRepo, err := session.NewRepository(rw, rw, kmsCache)
|
||||
require.NoError(err)
|
||||
|
||||
// Create and terminate session without canceling
|
||||
_, _, err = sessionRepo.ActivateSession(ctx, sess.PublicId, sess.Version, serverId, resource.Worker.String(), tofu)
|
||||
require.NoError(err)
|
||||
session.TestState(t, conn, sess.PublicId, session.StatusTerminated)
|
||||
|
||||
// Delete target; expect a session_state violation and failure to delete target
|
||||
rows, err := targetRepo.DeleteTarget(ctx, tar.GetPublicId())
|
||||
require.Errorf(err, "target.(Repository).DeleteTarget: db.DoTx: target.(Repository).DeleteTarget: db.Delete: insert or update on table \"session_state\" violates foreign key constraint \"session_valid_state_enm_fkey\": integrity violation: error #1003")
|
||||
require.Equal(0, rows)
|
||||
|
||||
// now we're ready for the migration we want to test.
|
||||
m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}),
|
||||
))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(m.ApplyMigrations(ctx))
|
||||
state, err = m.CurrentState(ctx)
|
||||
require.NoError(err)
|
||||
want = &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: currentMigration,
|
||||
DatabaseSchemaVersion: currentMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(want, state)
|
||||
|
||||
// Try to delete target again, should succeed without error
|
||||
rows, err = targetRepo.DeleteTarget(ctx, tar.GetPublicId())
|
||||
require.NoError(err)
|
||||
require.Equal(1, rows)
|
||||
}
|
||||
@@ -0,0 +1,587 @@
|
||||
package oss_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/common"
|
||||
"github.com/hashicorp/boundary/internal/db/schema"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/testing/dbtest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMigrations_KMS_Refactor(t *testing.T) {
|
||||
const (
|
||||
priorMigration = 28002
|
||||
currentMigration = 30004
|
||||
)
|
||||
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
dialect := dbtest.Postgres
|
||||
|
||||
c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1))
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, c())
|
||||
})
|
||||
d, err := common.SqlOpen(dialect, u)
|
||||
require.NoError(t, err)
|
||||
|
||||
// migration to the prior migration (before the one we want to test)
|
||||
m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}),
|
||||
))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, m.ApplyMigrations(ctx))
|
||||
state, err := m.CurrentState(ctx)
|
||||
require.NoError(t, err)
|
||||
want := &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: priorMigration,
|
||||
DatabaseSchemaVersion: priorMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(t, want, state)
|
||||
|
||||
// get a connection
|
||||
dbType, err := db.StringToDbType(dialect)
|
||||
require.NoError(t, err)
|
||||
conn, err := db.Open(dbType, u)
|
||||
require.NoError(t, err)
|
||||
rw := db.New(conn)
|
||||
|
||||
generateTestKeys(t, rw)
|
||||
// load up the current KEKs and their versions
|
||||
existingKeks := loadKeks(t, rw)
|
||||
existingKeksVersions := loadKekVersions(t, rw)
|
||||
|
||||
// load up the current DEKs and their versions
|
||||
existingDeks := loadCurrentDeks(t, rw)
|
||||
existingKeyVersions := loadCurrentDekVersions(t, rw)
|
||||
|
||||
// now we're ready for the migration we want to test.
|
||||
m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}),
|
||||
))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, m.ApplyMigrations(ctx))
|
||||
state, err = m.CurrentState(ctx)
|
||||
require.NoError(t, err)
|
||||
want = &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: currentMigration,
|
||||
DatabaseSchemaVersion: currentMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(t, want, state)
|
||||
// Now read all the converted keys and see if we have transformed them correctly
|
||||
{
|
||||
// get a new connection
|
||||
dbType, err := db.StringToDbType(dialect)
|
||||
require.NoError(t, err)
|
||||
conn, err := db.Open(dbType, u)
|
||||
require.NoError(t, err)
|
||||
rw := db.New(conn)
|
||||
|
||||
newKeks := loadKeks(t, rw)
|
||||
assert.ElementsMatch(t, newKeks, existingKeks)
|
||||
|
||||
newKeksVersions := loadKekVersions(t, rw)
|
||||
assert.ElementsMatch(t, newKeksVersions, existingKeksVersions)
|
||||
|
||||
newDeks := loadNewDeks(t, rw)
|
||||
assert.ElementsMatch(t, newDeks, existingDeks)
|
||||
|
||||
newKeyVersions := loadNewDekVersions(t, rw)
|
||||
assert.ElementsMatch(t, newKeyVersions, existingKeyVersions)
|
||||
}
|
||||
}
|
||||
|
||||
// we can't use the new KMS bits since all the tables have changed, so we'll
|
||||
// generate a bit of test data by hand
|
||||
func generateTestKeys(t *testing.T, rw *db.Db) {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
testCtx := context.Background()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
k := kek{
|
||||
PrivateId: testId(t, "rk"),
|
||||
ScopeId: testScope(t, rw).PublicId,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &k))
|
||||
|
||||
var kv kekVersion
|
||||
for i := 0; i < 2; i++ {
|
||||
kv = kekVersion{
|
||||
PrivateId: testId(t, "rkv"),
|
||||
RootKeyId: k.PrivateId,
|
||||
Version: 1 + uint32(i),
|
||||
Key: []byte("test-key"),
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &kv))
|
||||
}
|
||||
|
||||
{
|
||||
dbk := kmsDatabaseKey{
|
||||
PrivateId: testId(t, "dk"),
|
||||
RootKeyId: k.PrivateId,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &dbk))
|
||||
dbkv := kmsDatabaseKeyVersion{
|
||||
PrivateId: testId(t, "dkv"),
|
||||
DatabaseKeyId: dbk.PrivateId,
|
||||
RootKeyVersionId: kv.PrivateId,
|
||||
Key: []byte("test-key"),
|
||||
Version: 1,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &dbkv))
|
||||
}
|
||||
{
|
||||
ak := kmsAuditKey{
|
||||
PrivateId: testId(t, "ak"),
|
||||
RootKeyId: k.PrivateId,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &ak))
|
||||
akv := kmsAuditKeyVersion{
|
||||
PrivateId: testId(t, "dkv"),
|
||||
AuditKeyId: ak.PrivateId,
|
||||
RootKeyVersionId: kv.PrivateId,
|
||||
Key: []byte("test-key"),
|
||||
Version: 1,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &akv))
|
||||
}
|
||||
{
|
||||
oidck := kmsOidcKey{
|
||||
PrivateId: testId(t, "oidck"),
|
||||
RootKeyId: k.PrivateId,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &oidck))
|
||||
oidckv := kmsOidcKeyVersion{
|
||||
PrivateId: testId(t, "dkv"),
|
||||
OidcKeyId: oidck.PrivateId,
|
||||
RootKeyVersionId: kv.PrivateId,
|
||||
Key: []byte("test-key"),
|
||||
Version: 1,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &oidckv))
|
||||
}
|
||||
{
|
||||
opk := kmsOplogKey{
|
||||
PrivateId: testId(t, "opk"),
|
||||
RootKeyId: k.PrivateId,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &opk))
|
||||
opkv := kmsOplogKeyVersion{
|
||||
PrivateId: testId(t, "dkv"),
|
||||
OplogKeyId: opk.PrivateId,
|
||||
RootKeyVersionId: kv.PrivateId,
|
||||
Key: []byte("test-key"),
|
||||
Version: 1,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &opkv))
|
||||
}
|
||||
{
|
||||
sk := kmsSessionKey{
|
||||
PrivateId: testId(t, "sk"),
|
||||
RootKeyId: k.PrivateId,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &sk))
|
||||
skv := kmsSessionKeyVersion{
|
||||
PrivateId: testId(t, "dkv"),
|
||||
SessionKeyId: sk.PrivateId,
|
||||
RootKeyVersionId: kv.PrivateId,
|
||||
Key: []byte("test-key"),
|
||||
Version: 1,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &skv))
|
||||
}
|
||||
{
|
||||
tk := kmsTokenKey{
|
||||
PrivateId: testId(t, "tk"),
|
||||
RootKeyId: k.PrivateId,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &tk))
|
||||
tkv := kmsTokenKeyVersion{
|
||||
PrivateId: testId(t, "dkv"),
|
||||
TokenKeyId: tk.PrivateId,
|
||||
RootKeyVersionId: kv.PrivateId,
|
||||
Key: []byte("test-key"),
|
||||
Version: 1,
|
||||
}
|
||||
require.NoError(rw.Create(testCtx, &tkv))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func loadKeks(t *testing.T, rw *db.Db) []kek {
|
||||
t.Helper()
|
||||
testCtx := context.Background()
|
||||
rows, err := rw.Query(testCtx, `select private_id, scope_id, create_time from kms_root_key order by private_id`, nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
var keks []kek
|
||||
for rows.Next() {
|
||||
var key kek
|
||||
require.NoError(t, rw.ScanRows(context.Background(), rows, &key))
|
||||
keks = append(keks, key)
|
||||
}
|
||||
return keks
|
||||
}
|
||||
|
||||
func loadKekVersions(t *testing.T, rw *db.Db) []kekVersion {
|
||||
t.Helper()
|
||||
testCtx := context.Background()
|
||||
rows, err := rw.Query(testCtx, `select private_id, root_key_id, version, key, create_time from kms_root_key_version order by private_id`, nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
var kekVersions []kekVersion
|
||||
for rows.Next() {
|
||||
var v kekVersion
|
||||
require.NoError(t, rw.ScanRows(context.Background(), rows, &v))
|
||||
kekVersions = append(kekVersions, v)
|
||||
}
|
||||
return kekVersions
|
||||
}
|
||||
|
||||
func loadNewDeks(t *testing.T, rw *db.Db) []dek {
|
||||
t.Helper()
|
||||
testCtx := context.Background()
|
||||
rows, err := rw.Query(testCtx, `select private_id, root_key_id, create_time, purpose from kms_data_key order by private_id`, nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
var deks []dek
|
||||
for rows.Next() {
|
||||
var key dek
|
||||
require.NoError(t, rw.ScanRows(context.Background(), rows, &key))
|
||||
deks = append(deks, key)
|
||||
}
|
||||
return deks
|
||||
}
|
||||
|
||||
func loadCurrentDeks(t *testing.T, rw *db.Db) []dek {
|
||||
t.Helper()
|
||||
testCtx := context.Background()
|
||||
var deks []dek
|
||||
for _, purpose := range []string{"audit", "database", "oidc", "oplog", "sessions", "tokens"} {
|
||||
var table string
|
||||
switch purpose {
|
||||
case "audit":
|
||||
table = "kms_audit_key"
|
||||
case "database":
|
||||
table = "kms_database_key"
|
||||
case "oidc":
|
||||
table = "kms_oidc_key"
|
||||
case "oplog":
|
||||
table = "kms_oplog_key"
|
||||
case "sessions":
|
||||
table = "kms_session_key"
|
||||
case "tokens":
|
||||
table = "kms_token_key"
|
||||
default:
|
||||
t.Fatalf("not a supported dek %q", purpose)
|
||||
}
|
||||
sql := fmt.Sprintf(`select private_id, root_key_id, create_time, '%s' as purpose from %s order by private_id`, purpose, table)
|
||||
rows, err := rw.Query(testCtx, sql, nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
for rows.Next() {
|
||||
var key dek
|
||||
require.NoError(t, rw.ScanRows(context.Background(), rows, &key))
|
||||
deks = append(deks, key)
|
||||
}
|
||||
}
|
||||
return deks
|
||||
}
|
||||
|
||||
func loadNewDekVersions(t *testing.T, rw *db.Db) []dekVersion {
|
||||
t.Helper()
|
||||
testCtx := context.Background()
|
||||
rows, err := rw.Query(testCtx, `select private_id, data_key_id, root_key_version_id, version, key, create_time from kms_data_key_version order by private_id`, nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
var dekVersions []dekVersion
|
||||
for rows.Next() {
|
||||
var v dekVersion
|
||||
require.NoError(t, rw.ScanRows(context.Background(), rows, &v))
|
||||
dekVersions = append(dekVersions, v)
|
||||
}
|
||||
return dekVersions
|
||||
}
|
||||
|
||||
func loadCurrentDekVersions(t *testing.T, rw *db.Db) []dekVersion {
|
||||
t.Helper()
|
||||
testCtx := context.Background()
|
||||
var dekVersions []dekVersion
|
||||
for _, versionType := range []string{"audit", "database", "oidc", "oplog", "session", "token"} {
|
||||
var table string
|
||||
switch versionType {
|
||||
case "audit":
|
||||
table = "kms_audit_key_version"
|
||||
case "database":
|
||||
table = "kms_database_key_version"
|
||||
case "oidc":
|
||||
table = "kms_oidc_key_version"
|
||||
case "oplog":
|
||||
table = "kms_oplog_key_version"
|
||||
case "session":
|
||||
table = "kms_session_key_version"
|
||||
case "token":
|
||||
table = "kms_token_key_version"
|
||||
}
|
||||
sql := fmt.Sprintf(`select private_id, %s_key_id, root_key_version_id, key, version, create_time from %s order by private_id`, versionType, table)
|
||||
rows, err := rw.Query(testCtx, sql, nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
for rows.Next() {
|
||||
result := map[string]interface{}{}
|
||||
require.NoError(t, rw.ScanRows(context.Background(), rows, &result))
|
||||
var v dekVersion
|
||||
switch versionType {
|
||||
case "audit":
|
||||
v = dekVersion{
|
||||
PrivateId: result["private_id"].(string),
|
||||
DataKeyId: result["audit_key_id"].(string),
|
||||
RootKeyVersionId: result["root_key_version_id"].(string),
|
||||
Key: result["key"].([]byte),
|
||||
Version: uint32(result["version"].(int64)),
|
||||
CreateTime: result["create_time"].(time.Time),
|
||||
}
|
||||
case "database":
|
||||
v = dekVersion{
|
||||
PrivateId: result["private_id"].(string),
|
||||
DataKeyId: result["database_key_id"].(string),
|
||||
RootKeyVersionId: result["root_key_version_id"].(string),
|
||||
Key: result["key"].([]byte),
|
||||
Version: uint32(result["version"].(int64)),
|
||||
CreateTime: result["create_time"].(time.Time),
|
||||
}
|
||||
case "oidc":
|
||||
v = dekVersion{
|
||||
PrivateId: result["private_id"].(string),
|
||||
DataKeyId: result["oidc_key_id"].(string),
|
||||
RootKeyVersionId: result["root_key_version_id"].(string),
|
||||
Key: result["key"].([]byte),
|
||||
Version: uint32(result["version"].(int64)),
|
||||
CreateTime: result["create_time"].(time.Time),
|
||||
}
|
||||
case "oplog":
|
||||
v = dekVersion{
|
||||
PrivateId: result["private_id"].(string),
|
||||
DataKeyId: result["oplog_key_id"].(string),
|
||||
RootKeyVersionId: result["root_key_version_id"].(string),
|
||||
Key: result["key"].([]byte),
|
||||
Version: uint32(result["version"].(int64)),
|
||||
CreateTime: result["create_time"].(time.Time),
|
||||
}
|
||||
case "session":
|
||||
v = dekVersion{
|
||||
PrivateId: result["private_id"].(string),
|
||||
DataKeyId: result["session_key_id"].(string),
|
||||
RootKeyVersionId: result["root_key_version_id"].(string),
|
||||
Key: result["key"].([]byte),
|
||||
Version: uint32(result["version"].(int64)),
|
||||
CreateTime: result["create_time"].(time.Time),
|
||||
}
|
||||
case "token":
|
||||
v = dekVersion{
|
||||
PrivateId: result["private_id"].(string),
|
||||
DataKeyId: result["token_key_id"].(string),
|
||||
RootKeyVersionId: result["root_key_version_id"].(string),
|
||||
Key: result["key"].([]byte),
|
||||
Version: uint32(result["version"].(int64)),
|
||||
CreateTime: result["create_time"].(time.Time),
|
||||
}
|
||||
}
|
||||
dekVersions = append(dekVersions, v)
|
||||
}
|
||||
}
|
||||
return dekVersions
|
||||
}
|
||||
|
||||
func testId(t testing.TB, prefix string) string {
|
||||
t.Helper()
|
||||
id, err := db.NewPublicId(prefix)
|
||||
require.NoError(t, err)
|
||||
return id
|
||||
}
|
||||
|
||||
func testScope(t *testing.T, rw *db.Db) *iam.Scope {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
testCtx := context.Background()
|
||||
|
||||
s, err := iam.NewOrg()
|
||||
require.NoError(err)
|
||||
s.PublicId = testId(t, "o")
|
||||
|
||||
require.NoError(rw.Create(testCtx, &s))
|
||||
return s
|
||||
}
|
||||
|
||||
type kek struct {
|
||||
PrivateId string
|
||||
ScopeId string
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kek) TableName() string { return "kms_root_key" }
|
||||
|
||||
type kekVersion struct {
|
||||
PrivateId string
|
||||
RootKeyId string
|
||||
Version uint32
|
||||
Key []byte
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kekVersion) TableName() string { return "kms_root_key_version" }
|
||||
|
||||
type dek struct {
|
||||
PrivateId string
|
||||
RootKeyId string
|
||||
CreateTime time.Time
|
||||
Purpose string
|
||||
}
|
||||
type dekVersion struct {
|
||||
PrivateId string
|
||||
DataKeyId string
|
||||
RootKeyVersionId string
|
||||
Key []byte
|
||||
Version uint32
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
type kmsDatabaseKey struct {
|
||||
PrivateId string
|
||||
RootKeyId string
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsDatabaseKey) TableName() string { return "kms_database_key" }
|
||||
|
||||
type kmsDatabaseKeyVersion struct {
|
||||
PrivateId string
|
||||
DatabaseKeyId string
|
||||
RootKeyVersionId string
|
||||
Key []byte
|
||||
Version uint32
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsDatabaseKeyVersion) TableName() string { return "kms_database_key_version" }
|
||||
|
||||
type kmsAuditKey struct {
|
||||
PrivateId string
|
||||
RootKeyId string
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsAuditKey) TableName() string { return "kms_audit_key" }
|
||||
|
||||
type kmsAuditKeyVersion struct {
|
||||
PrivateId string
|
||||
AuditKeyId string
|
||||
RootKeyVersionId string
|
||||
Key []byte
|
||||
Version uint32
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsAuditKeyVersion) TableName() string { return "kms_audit_key_version" }
|
||||
|
||||
type kmsOidcKey struct {
|
||||
PrivateId string
|
||||
RootKeyId string
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsOidcKey) TableName() string { return "kms_oidc_key" }
|
||||
|
||||
type kmsOidcKeyVersion struct {
|
||||
PrivateId string
|
||||
OidcKeyId string
|
||||
RootKeyVersionId string
|
||||
Key []byte
|
||||
Version uint32
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsOidcKeyVersion) TableName() string { return "kms_oidc_key_version" }
|
||||
|
||||
type kmsOplogKey struct {
|
||||
PrivateId string
|
||||
RootKeyId string
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsOplogKey) TableName() string { return "kms_oplog_key" }
|
||||
|
||||
type kmsOplogKeyVersion struct {
|
||||
PrivateId string
|
||||
OplogKeyId string
|
||||
RootKeyVersionId string
|
||||
Key []byte
|
||||
Version uint32
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsOplogKeyVersion) TableName() string { return "kms_oplog_key_version" }
|
||||
|
||||
type kmsSessionKey struct {
|
||||
PrivateId string
|
||||
RootKeyId string
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsSessionKey) TableName() string { return "kms_session_key" }
|
||||
|
||||
type kmsSessionKeyVersion struct {
|
||||
PrivateId string
|
||||
SessionKeyId string
|
||||
RootKeyVersionId string
|
||||
Key []byte
|
||||
Version uint32
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsSessionKeyVersion) TableName() string { return "kms_session_key_version" }
|
||||
|
||||
type kmsTokenKey struct {
|
||||
PrivateId string
|
||||
RootKeyId string
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsTokenKey) TableName() string { return "kms_token_key" }
|
||||
|
||||
type kmsTokenKeyVersion struct {
|
||||
PrivateId string
|
||||
TokenKeyId string
|
||||
RootKeyVersionId string
|
||||
Key []byte
|
||||
Version uint32
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
func (*kmsTokenKeyVersion) TableName() string { return "kms_token_key_version" }
|
||||
@@ -105,13 +105,13 @@ begin;
|
||||
values
|
||||
('krkv___widget', 'krk___widget', 'krk___widget'::bytea);
|
||||
|
||||
insert into kms_database_key
|
||||
(private_id, root_key_id)
|
||||
insert into kms_data_key
|
||||
(private_id, root_key_id, purpose)
|
||||
values
|
||||
('kdk____widget', 'krk___widget');
|
||||
('kdk____widget', 'krk___widget', 'database');
|
||||
|
||||
insert into kms_database_key_version
|
||||
(private_id, database_key_id, root_key_version_id, key)
|
||||
insert into kms_data_key_version
|
||||
(private_id, data_key_id, root_key_version_id, key)
|
||||
values
|
||||
('kdkv___widget', 'kdk____widget', 'krkv___widget', 'kdk____widget'::bytea);
|
||||
|
||||
|
||||
Reference in new issue
Block a user