mirror of https://github.com/hashicorp/boundary
Add alias schema changes (#4230)
* Adding alias resource * Update website/content/docs/install-boundary/system-requirements.mdx --------- Co-authored-by: Irena Rindos <irenarindos@users.noreply.github.com>pull/4470/head
parent
df35f85971
commit
7e2038bfcc
@ -0,0 +1,11 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
|
||||
-- https://www.postgresql.org/docs/14/citext.html allows us to make
|
||||
-- case-insensitive uniqueness constraints which is useful to us for
|
||||
-- aliases.
|
||||
create extension "citext";
|
||||
|
||||
commit;
|
||||
@ -0,0 +1,46 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
|
||||
-- wt_alias defines a type for alias values
|
||||
create domain wt_alias as citext
|
||||
constraint wt_alias_too_short
|
||||
check (length(trim(value)) > 0)
|
||||
constraint wt_alias_no_suround_spaces
|
||||
check (trim(value) = value);
|
||||
comment on domain wt_alias is
|
||||
'standard value column for an alias';
|
||||
|
||||
-- wt_target_alias defines a type for target alias values
|
||||
create domain wt_target_alias as wt_alias
|
||||
constraint wt_target_alias_too_long
|
||||
check (length(trim(value)) < 254)
|
||||
-- dns names consists of at least one label joined together by a "."
|
||||
-- each label can consist of a-z 0-9 and "-" case insensitive
|
||||
-- a label cannot start or end with a "-"
|
||||
-- a label can be between 1 and 63 characters long
|
||||
-- the final label in the dns name cannot be all numeric
|
||||
-- see https://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax,_internationalization
|
||||
--
|
||||
-- Notes on the regex:
|
||||
-- "^(?!-)[a-z0-9-]{0,62}[a-z0-9]" ensures that there is at least one label
|
||||
-- * [a-z0-9-]{0,62} allows for the first 0-62 characters to be a-z 0-9 or "-"
|
||||
-- * (?!-) is a look ahead to ensure the string does not start with a "-"
|
||||
-- * [a-z0-9] at the end ensures that the string ends with a-z 0-9 which
|
||||
-- enforces that the label is at least 1 character long which, when
|
||||
-- combined with the previous regex, ensures that the label is between
|
||||
-- 1 and 63 characters long
|
||||
-- "(\.((?!-)[a-z0-9-]{0,62}[a-z0-9]))*$" is almost identical to the
|
||||
-- previous section and allows for 0 or more additional labels, all of
|
||||
-- which must start with a "."
|
||||
-- The constraint that the final label is not all numeric is enforced by
|
||||
-- the separate constraint wt_target_alias_tld_not_only_numeric
|
||||
constraint wt_target_alias_value_shape
|
||||
check (value ~* '^(?!-)[a-z0-9-]{0,62}[a-z0-9](\.((?!-)[a-z0-9-]{0,62}[a-z0-9]))*$')
|
||||
constraint wt_target_alias_tld_not_only_numeric
|
||||
check (substring(value from '[^.]*$') !~ '^[0-9]+$');
|
||||
comment on domain wt_target_alias is
|
||||
'standard value column for a target alias';
|
||||
|
||||
commit;
|
||||
@ -0,0 +1,70 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
|
||||
create table alias (
|
||||
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
|
||||
constraint alias_must_be_in_global_scope
|
||||
check(
|
||||
scope_id = 'global'
|
||||
),
|
||||
value wt_alias not null
|
||||
constraint alias_value_uq
|
||||
unique,
|
||||
constraint alias_scope_id_value_public_id_uq
|
||||
unique(scope_id, value, public_id)
|
||||
);
|
||||
comment on table alias is
|
||||
'alias is a base table for the alias type. '
|
||||
'Each row is owned by a single scope and maps 1-to-1 to a row in one of the alias subtype tables.';
|
||||
|
||||
create trigger immutable_columns before update on alias
|
||||
for each row execute procedure immutable_columns('public_id', 'scope_id');
|
||||
|
||||
-- insert_alias_subtype() is a before insert trigger
|
||||
-- function for subtypes of alias
|
||||
create function insert_alias_subtype() returns trigger
|
||||
as $$
|
||||
begin
|
||||
insert into alias
|
||||
(public_id, value, scope_id)
|
||||
values
|
||||
(new.public_id, new.value, new.scope_id);
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function insert_alias_subtype() is
|
||||
'insert_alias_subtype() inserts a record into the base alias table when a corresponding record is inserted into the subtype table';
|
||||
|
||||
|
||||
-- delete_alias_subtype() is an after delete trigger
|
||||
-- function for subtypes of alias
|
||||
create function delete_alias_subtype() returns trigger
|
||||
as $$
|
||||
begin
|
||||
delete from alias
|
||||
where
|
||||
public_id = old.public_id;
|
||||
return null;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function delete_alias_subtype() is
|
||||
'delete_alias_subtype() deletes the base alias record when the corresponding record is deleted from the subtype table';
|
||||
|
||||
create function update_alias_subtype() returns trigger
|
||||
as $$
|
||||
begin
|
||||
update alias set value = new.value where public_id = new.public_id and new.value != value;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
comment on function update_alias_subtype() is
|
||||
'update_alias_subtype() updates the base table value column with the new value from the subtype table';
|
||||
|
||||
commit;
|
||||
@ -0,0 +1,113 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
|
||||
create table alias_target (
|
||||
public_id wt_public_id primary key,
|
||||
name wt_name,
|
||||
description wt_description,
|
||||
scope_id wt_scope_id not null
|
||||
constraint iam_scope_fkey
|
||||
references iam_scope (public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
value wt_target_alias not null,
|
||||
-- destination_id is used here instead of target_id because many subtyped
|
||||
-- aliases may be coming in the future. Since Boundary's method for
|
||||
-- updating these fields use update masks derived from the API resource and
|
||||
-- there is a single API resource for each subtype with a field that is
|
||||
-- generic enough to be used by all subtypes, this column name also needs to
|
||||
-- be generic enough across subtypes.
|
||||
destination_id wt_public_id
|
||||
constraint target_fkey
|
||||
references target (public_id)
|
||||
on delete set null
|
||||
on update cascade,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
host_id wt_public_id
|
||||
constraint destination_id_set_when_host_id_is_set
|
||||
check(
|
||||
destination_id is not null
|
||||
or
|
||||
(
|
||||
destination_id is null
|
||||
and
|
||||
host_id is null
|
||||
)
|
||||
),
|
||||
constraint alias_target_scope_id_name_uq
|
||||
unique(scope_id, name),
|
||||
constraint alias_fkey
|
||||
foreign key (scope_id, value, public_id)
|
||||
references alias (scope_id, value, public_id)
|
||||
on delete cascade
|
||||
on update cascade
|
||||
deferrable initially deferred
|
||||
);
|
||||
comment on table alias_target is
|
||||
'alias_target is a subtype of alias. '
|
||||
'Each row is owned by a single scope and maps 1-to-1 to a row in the alias table.';
|
||||
|
||||
create index alias_target_create_time_public_id_idx
|
||||
on alias_target (create_time desc, public_id desc);
|
||||
|
||||
create index alias_target_update_time_public_id_idx
|
||||
on alias_target (update_time desc, public_id desc);
|
||||
|
||||
create function delete_host_id_if_destination_id_is_null() returns trigger
|
||||
as $$
|
||||
begin
|
||||
if new.destination_id is null then
|
||||
new.host_id = null;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create trigger delete_host_id_if_destination_id_is_null before update on alias_target
|
||||
for each row execute procedure delete_host_id_if_destination_id_is_null();
|
||||
|
||||
create trigger insert_alias_subtype before insert on alias_target
|
||||
for each row execute procedure insert_alias_subtype();
|
||||
|
||||
create trigger update_alias_subtype after update on alias_target
|
||||
for each row execute procedure update_alias_subtype();
|
||||
|
||||
create trigger delete_alias_subtype after delete on alias_target
|
||||
for each row execute procedure delete_alias_subtype();
|
||||
|
||||
create trigger update_version_column after update on alias_target
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger update_time_column before update on alias_target
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger default_create_time_column before insert on alias_target
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger immutable_columns before update on alias_target
|
||||
for each row execute procedure immutable_columns('public_id', 'scope_id', 'create_time');
|
||||
|
||||
|
||||
-- Alias delete tracking tables
|
||||
create table alias_target_deleted (
|
||||
public_id wt_public_id primary key,
|
||||
delete_time wt_timestamp not null
|
||||
);
|
||||
comment on table alias_target_deleted is
|
||||
'alias_target_deleted holds the ID and delete_time of every deleted target alias. '
|
||||
'It is automatically trimmed of records older than 30 days by a job.';
|
||||
|
||||
create trigger insert_deleted_id after delete on alias_target
|
||||
for each row execute procedure insert_deleted_id('alias_target_deleted');
|
||||
|
||||
create index alias_target_deleted_delete_time_idx on alias_target_deleted (delete_time);
|
||||
|
||||
insert into oplog_ticket (name, version)
|
||||
values
|
||||
('alias_target', 1);
|
||||
|
||||
commit;
|
||||
@ -0,0 +1,76 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
-- alias tests triggers:
|
||||
-- insert_alias_subtype
|
||||
-- update_alias_subtype
|
||||
-- delete_alias_subtype
|
||||
|
||||
begin;
|
||||
select plan(17);
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth');
|
||||
|
||||
-- validate the setup data
|
||||
select is(count(*), 1::bigint) from alias_target where public_id = 'alt__t____cb';
|
||||
select is(count(*), 1::bigint) from alias_target where public_id = 'alt__t____cr';
|
||||
select is(count(*), 1::bigint) from alias_target where public_id = 'alt__t____cg';
|
||||
|
||||
-- validate the insert triggers
|
||||
prepare insert_target_alias as
|
||||
insert into alias_target
|
||||
(scope_id, public_id, value, destination_id)
|
||||
values
|
||||
('global', 'alt__t___2cb', 'second.blue.tcp.target', 't_________cb');
|
||||
select lives_ok('insert_target_alias');
|
||||
|
||||
select is(count(*), 1::bigint) from alias_target where public_id = 'alt__t___2cb';
|
||||
select is(count(*), 1::bigint) from alias where public_id = 'alt__t___2cb';
|
||||
|
||||
-- validate the update triggers
|
||||
prepare update_target_alias as
|
||||
update alias_target
|
||||
set value = 'updated.red.tcp.target.updated'
|
||||
where public_id = 'alt__t____cr';
|
||||
select lives_ok('update_target_alias');
|
||||
|
||||
select is(count(*), 1::bigint) from alias_target where public_id = 'alt__t____cr' and value = 'updated.red.tcp.target.updated';
|
||||
select is(count(*), 1::bigint) from alias where public_id = 'alt__t____cr' and value = 'updated.red.tcp.target.updated';
|
||||
|
||||
-- validate delete_host_id_if_destination_id_is_null
|
||||
update alias_target
|
||||
set host_id = 'hst_1234567890'
|
||||
where
|
||||
public_id = 'alt__t____cr'
|
||||
or public_id = 'alt__tssh_cr';
|
||||
|
||||
select is(count(*), 2::bigint) from alias_target where host_id = 'hst_1234567890';
|
||||
|
||||
prepare delete_destination_target as
|
||||
delete from target_ssh
|
||||
where public_id = 'tssh______cr';
|
||||
select lives_ok('delete_destination_target');
|
||||
|
||||
select is(count(*), 1::bigint) from alias_target where host_id = 'hst_1234567890';
|
||||
|
||||
prepare update_remove_destination_id as
|
||||
update alias_target
|
||||
set destination_id = null
|
||||
where public_id = 'alt__t____cr';
|
||||
|
||||
select lives_ok('update_remove_destination_id');
|
||||
|
||||
select is(count(*), 0::bigint) from alias_target where host_id = 'hst_1234567890';
|
||||
|
||||
|
||||
-- validate the delete triggers
|
||||
prepare delete_target_alias as
|
||||
delete
|
||||
from alias_target
|
||||
where public_id = 'alt__t____cg';
|
||||
select lives_ok('delete_target_alias');
|
||||
|
||||
select is(count(*), 0::bigint) from alias_target where public_id = 'alt__t____cg';
|
||||
select is(count(*), 0::bigint) from alias where public_id = 'alt__t____cg';
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@ -0,0 +1,50 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
select plan(11);
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth');
|
||||
|
||||
select is(count(*), 1::bigint) from alias_target where public_id = 'alt__t____cb' and destination_id = 't_________cb';
|
||||
|
||||
-- validate destination id and host id can be updated
|
||||
prepare update_target_alias_destination as
|
||||
update alias_target
|
||||
set (destination_id, host_id) = ('t_________cg', 'h_________cg')
|
||||
where public_id = 'alt__t____cb';
|
||||
select lives_ok('update_target_alias_destination');
|
||||
|
||||
select is(count(*), 1::bigint) from alias_target where value = 'blue.tcp.target';
|
||||
|
||||
prepare insert_case_insensitive_value_duplicate AS
|
||||
insert into alias_target (public_id, scope_id, value)
|
||||
values ('new_alias_for_tests', 'global', 'BLUE.TCP.TARGET');
|
||||
select throws_like(
|
||||
'insert_case_insensitive_value_duplicate',
|
||||
'duplicate key value violates unique constraint "alias_value_uq"'
|
||||
);
|
||||
|
||||
select is(count(*), 0::bigint) from alias_target where public_id = 'alt__t____cb' and destination_id = 't_________cb';
|
||||
select is(count(*), 1::bigint) from alias_target where public_id = 'alt__t____cb' and destination_id = 't_________cg' and host_id = 'h_________cg';
|
||||
|
||||
-- validate deleting a target nulls out the destination id and host id
|
||||
prepare update_target_alias as
|
||||
delete from target_tcp
|
||||
where public_id = 't_________cg';
|
||||
select lives_ok('update_target_alias');
|
||||
|
||||
select is(count(*), 0::bigint) from alias_target where public_id = 'alt__t____cb' and destination_id = 't_________cb';
|
||||
select is(count(*), 0::bigint) from alias_target where public_id = 'alt__t____cb' and destination_id = 't_________cg';
|
||||
select is(count(*), 1::bigint) from alias_target where public_id = 'alt__t____cb' and destination_id is null and host_id is null;
|
||||
|
||||
-- validate a host id cant be set if the destination id is not set
|
||||
prepare insert_destination_id_not_set_when_host_id_is_set as
|
||||
insert into alias_target (public_id, scope_id, value, host_id)
|
||||
values ('unset_destination_id', 'global', 'unset.destination.id', 'h_________cb');
|
||||
select throws_like(
|
||||
'insert_destination_id_not_set_when_host_id_is_set',
|
||||
'new row for relation "alias_target" violates check constraint "destination_id_set_when_host_id_is_set"'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@ -0,0 +1,135 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
-- controller_id tests:
|
||||
-- validates the wt_controller_id domain
|
||||
|
||||
begin;
|
||||
select plan(16);
|
||||
|
||||
select has_domain('wt_target_alias');
|
||||
|
||||
create table target_alias_testing (
|
||||
v wt_target_alias
|
||||
);
|
||||
|
||||
prepare empty_insert as insert into target_alias_testing (v) values ('');
|
||||
select throws_like(
|
||||
'empty_insert',
|
||||
'%"wt_alias_too_short"',
|
||||
'We should error for empty values'
|
||||
);
|
||||
|
||||
prepare valid_inserts as insert into target_alias_testing (v) values
|
||||
('a'),
|
||||
('A'),
|
||||
('192.168.1.9-9'),
|
||||
('foo'),
|
||||
('a.b.c'),
|
||||
('A.B.C'),
|
||||
('a-b-c'),
|
||||
('a.9-9'),
|
||||
('9things'),
|
||||
('9-things'),
|
||||
('hp--something.test.com'),
|
||||
('test-for-long-name-which-is-almost-over-the-limit-of-characters'),
|
||||
('TEST-FOR-LONG-NAME-WHICH-IS-ALMOST-OVER-THE-LIMIT-OF-CHARACTERS'),
|
||||
('test-for-long-name-which-is-almost-over-the-limit-of-characters.another-label'),
|
||||
('test.test-for-long-name-which-is-almost-over-the-limit-of-characters'),
|
||||
('test-for-long-name-which-is-almost-over-the-limit-of-characters.test-for-long-name-which-is-almost-over-the-limit-of-characters'),
|
||||
('9-things.9-things');
|
||||
select lives_ok('valid_inserts');
|
||||
|
||||
prepare label_too_long as insert into target_alias_testing (v) values
|
||||
('test-for-long-name-which-is-almost-over-the-limit-of-charactersX');
|
||||
select throws_like(
|
||||
'label_too_long',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare label_too_long_2 as insert into target_alias_testing (v) values
|
||||
('a.test-for-long-name-which-is-almost-over-the-limit-of-charactersX');
|
||||
select throws_like(
|
||||
'label_too_long_2',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare label_too_long_3 as insert into target_alias_testing (v) values
|
||||
('test-for-long-name-which-is-almost-over-the-limit-of-charactersX.a');
|
||||
select throws_like(
|
||||
'label_too_long_3',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare starting_with_hyphen as insert into target_alias_testing (v) values
|
||||
('-test.com');
|
||||
select throws_like(
|
||||
'starting_with_hyphen',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare starting_with_hyphen2 as insert into target_alias_testing (v) values
|
||||
('a.-test');
|
||||
select throws_like(
|
||||
'starting_with_hyphen2',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare ending_with_hyphen as insert into target_alias_testing (v) values
|
||||
('test-.com');
|
||||
select throws_like(
|
||||
'ending_with_hyphen',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare ending_with_hyphen2 as insert into target_alias_testing (v) values
|
||||
('a.test-');
|
||||
select throws_like(
|
||||
'ending_with_hyphen2',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare ending_with_hyphen3 as insert into target_alias_testing (v) values
|
||||
('a.9-');
|
||||
select throws_like(
|
||||
'ending_with_hyphen3',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare empty_label as insert into target_alias_testing (v) values
|
||||
('a..com');
|
||||
select throws_like(
|
||||
'empty_label',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare empty_label2 as insert into target_alias_testing (v) values
|
||||
('.a.com');
|
||||
select throws_like(
|
||||
'empty_label2',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare empty_label3 as insert into target_alias_testing (v) values
|
||||
('a.com.');
|
||||
select throws_like(
|
||||
'empty_label3',
|
||||
'%"wt_target_alias_value_shape"'
|
||||
);
|
||||
|
||||
prepare numeric_only_tld as insert into target_alias_testing (v) values
|
||||
('a.123');
|
||||
select throws_like(
|
||||
'numeric_only_tld',
|
||||
'%"wt_target_alias_tld_not_only_numeric"'
|
||||
);
|
||||
|
||||
prepare numeric_only_tld2 as insert into target_alias_testing (v) values
|
||||
('a.9');
|
||||
select throws_like(
|
||||
'numeric_only_tld2',
|
||||
'%"wt_target_alias_tld_not_only_numeric"'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@ -0,0 +1,12 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
begin;
|
||||
select plan(2);
|
||||
|
||||
select has_index('alias_target', 'alias_target_create_time_public_id_idx', array['create_time', 'public_id']);
|
||||
select has_index('alias_target', 'alias_target_update_time_public_id_idx', array['update_time', 'public_id']);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
Loading…
Reference in new issue