mirror of https://github.com/hashicorp/boundary
feat(db): Introduce migrations for ssh targets (#2960)
Adds the schema for ssh targets. This also changes the target listing func to not error out when it encounters a target with an unknown type. Instead, it now ignores it and continues to the next one.pull/2995/head
parent
0e1b020960
commit
41b1b5b4b7
@ -0,0 +1,364 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
create table if not exists target_ssh (
|
||||
public_id wt_public_id primary key
|
||||
constraint target_fkey
|
||||
references target(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
project_id wt_scope_id not null,
|
||||
name text not null, -- name is not optional for a target subtype
|
||||
description text,
|
||||
default_port int, -- default_port can be null
|
||||
-- max duration of the session in seconds.
|
||||
-- default is 8 hours
|
||||
session_max_seconds int not null default 28800
|
||||
constraint session_max_seconds_must_be_greater_than_0
|
||||
check(session_max_seconds > 0),
|
||||
-- limit on number of session connections allowed. -1 equals no limit
|
||||
session_connection_limit int not null default 1
|
||||
constraint session_connection_limit_must_be_greater_than_0_or_negative_1
|
||||
check(session_connection_limit > 0 or session_connection_limit = -1),
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
worker_filter wt_bexprfilter,
|
||||
egress_worker_filter wt_bexprfilter,
|
||||
ingress_worker_filter wt_bexprfilter,
|
||||
constraint target_ssh_project_id_name_uq
|
||||
unique(project_id, name) -- name must be unique within a project scope.
|
||||
);
|
||||
comment on table target_ssh is
|
||||
'target_ssh is a table where each row is a resource that represents an ssh target. '
|
||||
'It is a target subtype.';
|
||||
|
||||
drop trigger if exists insert_target_subtype on target_ssh;
|
||||
create trigger insert_target_subtype before insert on target_ssh
|
||||
for each row execute procedure insert_target_subtype();
|
||||
|
||||
drop trigger if exists delete_target_subtype on target_ssh;
|
||||
create trigger delete_target_subtype after delete on target_ssh
|
||||
for each row execute procedure delete_target_subtype();
|
||||
|
||||
-- define the immutable fields for target
|
||||
drop trigger if exists immutable_columns on target_ssh;
|
||||
create trigger immutable_columns before update on target_ssh
|
||||
for each row execute procedure immutable_columns('public_id', 'project_id', 'create_time');
|
||||
|
||||
drop trigger if exists update_version_column on target_ssh;
|
||||
create trigger update_version_column after update on target_ssh
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
drop trigger if exists update_time_column on target_ssh;
|
||||
create trigger update_time_column before update on target_ssh
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
drop trigger if exists default_create_time_column on target_ssh;
|
||||
create trigger default_create_time_column before insert on target_ssh
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
drop trigger if exists update_ssh_target_filter_validate on target_ssh;
|
||||
create trigger update_ssh_target_filter_validate before update on target_ssh
|
||||
for each row execute procedure validate_filter_values_on_update();
|
||||
|
||||
drop trigger if exists insert_ssh_target_filter_validate on target_ssh;
|
||||
create trigger insert_ssh_target_filter_validate before insert on target_ssh
|
||||
for each row execute procedure validate_filter_values_on_insert();
|
||||
|
||||
insert into oplog_ticket
|
||||
(name, version)
|
||||
values
|
||||
('target_ssh', 1)
|
||||
on conflict do nothing;
|
||||
|
||||
-- The whx_* views here depend on target_all_subtypes, so we need to drop
|
||||
-- these first.
|
||||
drop view if exists whx_host_dimension_source;
|
||||
drop view if exists whx_credential_dimension_source;
|
||||
drop view if exists target_all_subtypes;
|
||||
|
||||
create view target_all_subtypes as
|
||||
select
|
||||
public_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
default_port,
|
||||
session_max_seconds,
|
||||
session_connection_limit,
|
||||
version,
|
||||
create_time,
|
||||
update_time,
|
||||
worker_filter,
|
||||
egress_worker_filter,
|
||||
ingress_worker_filter,
|
||||
'tcp' as type
|
||||
from target_tcp
|
||||
union
|
||||
select
|
||||
public_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
default_port,
|
||||
session_max_seconds,
|
||||
session_connection_limit,
|
||||
version,
|
||||
create_time,
|
||||
update_time,
|
||||
worker_filter,
|
||||
egress_worker_filter,
|
||||
ingress_worker_filter,
|
||||
'ssh' as type
|
||||
from
|
||||
target_ssh;
|
||||
|
||||
-- replaces view from oss/60/03_wh_sessions.up.sql
|
||||
create view whx_host_dimension_source as
|
||||
with
|
||||
host_sources (
|
||||
host_id, host_type, host_name, host_description,
|
||||
host_set_id, host_set_type, host_set_name, host_set_description,
|
||||
host_catalog_id, host_catalog_type, host_catalog_name, host_catalog_description,
|
||||
target_id, target_type, target_name, target_description,
|
||||
target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id, project_name, project_description,
|
||||
organization_id, organization_name, organization_description
|
||||
) as (
|
||||
select -- id is the first column in the target view
|
||||
h.public_id as host_id,
|
||||
case when sh.public_id is not null then 'static host'
|
||||
when ph.public_id is not null then 'plugin host'
|
||||
else 'Unknown' end as host_type,
|
||||
case when sh.public_id is not null then coalesce(sh.name, 'None')
|
||||
when ph.public_id is not null then coalesce(ph.name, 'None')
|
||||
else 'Unknown' end as host_name,
|
||||
case when sh.public_id is not null then coalesce(sh.description, 'None')
|
||||
when ph.public_id is not null then coalesce(ph.description, 'None')
|
||||
else 'Unknown' end as host_description,
|
||||
hs.public_id as host_set_id,
|
||||
case when shs.public_id is not null then 'static host set'
|
||||
when phs.public_id is not null then 'plugin host set'
|
||||
else 'Unknown' end as host_set_type,
|
||||
case
|
||||
when shs.public_id is not null then coalesce(shs.name, 'None')
|
||||
when phs.public_id is not null then coalesce(phs.name, 'None')
|
||||
else 'None'
|
||||
end as host_set_name,
|
||||
case
|
||||
when shs.public_id is not null then coalesce(shs.description, 'None')
|
||||
when phs.public_id is not null then coalesce(phs.description, 'None')
|
||||
else 'None'
|
||||
end as host_set_description,
|
||||
hc.public_id as host_catalog_id,
|
||||
case when shc.public_id is not null then 'static host catalog'
|
||||
when phc.public_id is not null then 'plugin host catalog'
|
||||
else 'Unknown' end as host_catalog_type,
|
||||
case
|
||||
when shc.public_id is not null then coalesce(shc.name, 'None')
|
||||
when phc.public_id is not null then coalesce(phc.name, 'None')
|
||||
else 'None'
|
||||
end as host_catalog_name,
|
||||
case
|
||||
when shc.public_id is not null then coalesce(shc.description, 'None')
|
||||
when phc.public_id is not null then coalesce(phc.description, 'None')
|
||||
else 'None'
|
||||
end as host_catalog_description,
|
||||
t.public_id as target_id,
|
||||
case
|
||||
when t.type = 'tcp' then 'tcp target'
|
||||
when t.type = 'ssh' then 'ssh target'
|
||||
else 'Unknown'
|
||||
end as target_type,
|
||||
coalesce(t.name, 'None') as target_name,
|
||||
coalesce(t.description, 'None') as target_description,
|
||||
coalesce(t.default_port, 0) as target_default_port_number,
|
||||
t.session_max_seconds as target_session_max_seconds,
|
||||
t.session_connection_limit as target_session_connection_limit,
|
||||
p.public_id as project_id,
|
||||
coalesce(p.name, 'None') as project_name,
|
||||
coalesce(p.description, 'None') as project_description,
|
||||
o.public_id as organization_id,
|
||||
coalesce(o.name, 'None') as organization_name,
|
||||
coalesce(o.description, 'None') as organization_description
|
||||
from host as h
|
||||
join host_catalog as hc on h.catalog_id = hc.public_id
|
||||
join host_set as hs on h.catalog_id = hs.catalog_id
|
||||
join target_host_set as ts on hs.public_id = ts.host_set_id
|
||||
join target_all_subtypes as t on ts.target_id = t.public_id
|
||||
join iam_scope as p on t.project_id = p.public_id and p.type = 'project'
|
||||
join iam_scope as o on p.parent_id = o.public_id and o.type = 'org'
|
||||
|
||||
left join static_host as sh on sh.public_id = h.public_id
|
||||
left join host_plugin_host as ph on ph.public_id = h.public_id
|
||||
left join static_host_catalog as shc on shc.public_id = hc.public_id
|
||||
left join host_plugin_catalog as phc on phc.public_id = hc.public_id
|
||||
left join static_host_set as shs on shs.public_id = hs.public_id
|
||||
left join host_plugin_set as phs on phs.public_id = hs.public_id
|
||||
),
|
||||
host_target_address (
|
||||
host_id, host_type, host_name, host_description,
|
||||
host_set_id, host_set_type, host_set_name, host_set_description,
|
||||
host_catalog_id, host_catalog_type, host_catalog_name, host_catalog_description,
|
||||
target_id, target_type, target_name, target_description,
|
||||
target_default_port_number, target_session_max_seconds, target_session_connection_limit,
|
||||
project_id, project_name, project_description,
|
||||
organization_id, organization_name, organization_description
|
||||
) as (
|
||||
select
|
||||
'Not Applicable' as host_id,
|
||||
'direct address' as host_type,
|
||||
'Not Applicable' as host_name,
|
||||
'Not Applicable' as host_description,
|
||||
'Not Applicable' as host_set_id,
|
||||
'Not Applicable' as host_set_type,
|
||||
'Not Applicable' as host_set_name,
|
||||
'Not Applicable' as host_set_description,
|
||||
'Not Applicable' as host_catalog_id,
|
||||
'Not Applicable' as host_catalog_type,
|
||||
'Not Applicable' as host_catalog_name,
|
||||
'Not Applicable' as host_catalog_description,
|
||||
t.public_id as target_id,
|
||||
case
|
||||
when t.type = 'tcp' then 'tcp target'
|
||||
when t.type = 'ssh' then 'ssh target'
|
||||
else 'Unknown'
|
||||
end as target_type,
|
||||
coalesce(t.name, 'None') as target_name,
|
||||
coalesce(t.description, 'None') as target_description,
|
||||
coalesce(t.default_port, 0) as target_default_port_number,
|
||||
t.session_max_seconds as target_session_max_seconds,
|
||||
t.session_connection_limit as target_session_connection_limit,
|
||||
p.public_id as project_id,
|
||||
coalesce(p.name, 'None') as project_name,
|
||||
coalesce(p.description, 'None') as project_description,
|
||||
o.public_id as organization_id,
|
||||
coalesce(o.name, 'None') as organization_name,
|
||||
coalesce(o.description, 'None') as organization_description
|
||||
from target_all_subtypes as t
|
||||
right join target_address as ta on t.public_id = ta.target_id
|
||||
left join iam_scope as p on p.public_id = t.project_id
|
||||
left join iam_scope as o on o.public_id = p.parent_id
|
||||
)
|
||||
select * from host_sources
|
||||
union
|
||||
select * from host_target_address;
|
||||
|
||||
-- The whx_credential_dimension_source view shows the current values in the
|
||||
-- operational tables of the credential dimension.
|
||||
-- Replaces whx_credential_dimension_source defined in oss/63/03_wh_ssh_cert_library.up.sql
|
||||
create view whx_credential_dimension_source as
|
||||
with vault_generic_library as (
|
||||
select vcl.public_id as public_id,
|
||||
'vault generic credential library' as type,
|
||||
coalesce(vcl.name, 'None') as name,
|
||||
coalesce(vcl.description, 'None') as description,
|
||||
vcl.vault_path as vault_path,
|
||||
vcl.http_method as http_method,
|
||||
case
|
||||
when vcl.http_method = 'GET' then 'Not Applicable'
|
||||
else coalesce(vcl.http_request_body::text, 'None')
|
||||
end as http_request_body,
|
||||
'Not Applicable' as username,
|
||||
'Not Applicable' as key_type_and_bits
|
||||
from credential_vault_library as vcl
|
||||
),
|
||||
vault_ssh_cert_library as (
|
||||
select vsccl.public_id as public_id,
|
||||
'vault ssh certificate credential library' as type,
|
||||
coalesce(vsccl.name, 'None') as name,
|
||||
coalesce(vsccl.description, 'None') as description,
|
||||
vsccl.vault_path as vault_path,
|
||||
'Not Applicable' as http_method,
|
||||
'Not Applicable' as http_request_body,
|
||||
vsccl.username as username,
|
||||
case
|
||||
when vsccl.key_type = 'ed25519' then vsccl.key_type
|
||||
else vsccl.key_type || '-' || vsccl.key_bits::text
|
||||
end as key_type_and_bits
|
||||
from credential_vault_ssh_cert_library as vsccl
|
||||
),
|
||||
final as (
|
||||
select s.public_id as session_id,
|
||||
scd.credential_purpose as credential_purpose,
|
||||
cl.public_id as credential_library_id,
|
||||
coalesce(vcl.type, vsccl.type) as credential_library_type,
|
||||
coalesce(vcl.name, vsccl.name) as credential_library_name,
|
||||
coalesce(vcl.description, vsccl.description) as credential_library_description,
|
||||
coalesce(vcl.vault_path, vsccl.vault_path) as credential_library_vault_path,
|
||||
coalesce(vcl.http_method, vsccl.http_method) as credential_library_vault_http_method,
|
||||
coalesce(vcl.http_request_body, vsccl.http_request_body) as credential_library_vault_http_request_body,
|
||||
coalesce(vcl.username, vsccl.username) as credential_library_username,
|
||||
coalesce(vcl.key_type_and_bits, vsccl.key_type_and_bits) as credential_library_key_type_and_bits,
|
||||
cs.public_id as credential_store_id,
|
||||
case
|
||||
when vcs is null then 'None'
|
||||
else 'vault credential store'
|
||||
end as credential_store_type,
|
||||
coalesce(vcs.name, 'None') as credential_store_name,
|
||||
coalesce(vcs.description, 'None') as credential_store_description,
|
||||
coalesce(vcs.namespace, 'None') as credential_store_vault_namespace,
|
||||
coalesce(vcs.vault_address, 'None') as credential_store_vault_address,
|
||||
t.public_id as target_id,
|
||||
case
|
||||
when tt.type = 'tcp' then 'tcp target'
|
||||
when tt.type = 'ssh' then 'ssh target'
|
||||
else 'Unknown'
|
||||
end as target_type,
|
||||
coalesce(tt.name, 'None') as target_name,
|
||||
coalesce(tt.description, 'None') as target_description,
|
||||
coalesce(tt.default_port, 0) as target_default_port_number,
|
||||
tt.session_max_seconds as target_session_max_seconds,
|
||||
tt.session_connection_limit as target_session_connection_limit,
|
||||
p.public_id as project_id,
|
||||
coalesce(p.name, 'None') as project_name,
|
||||
coalesce(p.description, 'None') as project_description,
|
||||
o.public_id as organization_id,
|
||||
coalesce(o.name, 'None') as organization_name,
|
||||
coalesce(o.description, 'None') as organization_description
|
||||
from session_credential_dynamic as scd
|
||||
join session as s on scd.session_id = s.public_id
|
||||
join credential_library as cl on scd.library_id = cl.public_id
|
||||
join credential_store as cs on cl.store_id = cs.public_id
|
||||
join target as t on s.target_id = t.public_id
|
||||
join iam_scope as p on p.public_id = t.project_id and p.type = 'project'
|
||||
join iam_scope as o on p.parent_id = o.public_id and o.type = 'org'
|
||||
left join vault_generic_library as vcl on cl.public_id = vcl.public_id
|
||||
left join vault_ssh_cert_library as vsccl on cl.public_id = vsccl.public_id
|
||||
left join credential_vault_store as vcs on cs.public_id = vcs.public_id
|
||||
left join target_all_subtypes as tt on t.public_id = tt.public_id
|
||||
)
|
||||
select session_id,
|
||||
credential_purpose,
|
||||
credential_library_id,
|
||||
credential_library_type,
|
||||
credential_library_name,
|
||||
credential_library_description,
|
||||
credential_library_vault_path,
|
||||
credential_library_vault_http_method,
|
||||
credential_library_vault_http_request_body,
|
||||
credential_library_username,
|
||||
credential_library_key_type_and_bits,
|
||||
credential_store_id,
|
||||
credential_store_type,
|
||||
credential_store_name,
|
||||
credential_store_description,
|
||||
credential_store_vault_namespace,
|
||||
credential_store_vault_address,
|
||||
target_id,
|
||||
target_type,
|
||||
target_name,
|
||||
target_description,
|
||||
target_default_port_number,
|
||||
target_session_max_seconds,
|
||||
target_session_connection_limit,
|
||||
project_id,
|
||||
project_name,
|
||||
project_description,
|
||||
organization_id,
|
||||
organization_name,
|
||||
organization_description
|
||||
from final;
|
||||
commit;
|
||||
@ -0,0 +1,158 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
drop index if exists wh_session_accumulating_fact_session_pending_time_idx;
|
||||
create index wh_session_accumulating_fact_session_pending_time_idx on wh_session_accumulating_fact (session_pending_time);
|
||||
analyze wh_session_accumulating_fact;
|
||||
|
||||
/*
|
||||
Implementation Note 1
|
||||
|
||||
SQL is very flexible. I'm 87% sure there are approximately 1,962 different
|
||||
ways to write the queries for each of the views below (plus or minus 2). I
|
||||
chose the ones below based on the explain plans for each view after filling
|
||||
the wh_session_accumulating_fact table with more than 250,000 rows of test
|
||||
data.
|
||||
|
||||
The query I used to populate the test data is at the bottom of this file. If
|
||||
you need to alter these views, please generate test data and use the explain
|
||||
plan to guide your decisions.
|
||||
*/
|
||||
|
||||
drop view if exists hcp_billing_hourly_sessions_last_3_hours;
|
||||
create view hcp_billing_hourly_sessions_last_3_hours as
|
||||
with
|
||||
hourly_counts (hour, sessions_pending_count) as (
|
||||
select date_trunc('hour', session_pending_time), count(*)
|
||||
from wh_session_accumulating_fact
|
||||
where session_pending_time >= date_trunc('hour', now() - '3 hours'::interval)
|
||||
group by date_trunc('hour', session_pending_time)
|
||||
),
|
||||
hourly_range (hour) as (
|
||||
select date_trunc('hour',time)
|
||||
from generate_series(now() - '3 hours'::interval, now(), '1 hour'::interval) as time
|
||||
),
|
||||
final (hour, sessions_pending_count) as (
|
||||
select hourly_range.hour, coalesce(hourly_counts.sessions_pending_count, 0)
|
||||
from hourly_range
|
||||
left join hourly_counts on hourly_range.hour = hourly_counts.hour
|
||||
)
|
||||
select hour, sessions_pending_count
|
||||
from final
|
||||
order by hour desc;
|
||||
comment on view hcp_billing_hourly_sessions_last_3_hours is
|
||||
'hcp_billing_hourly_sessions_last_3_hours is a view where each row contains the timestamp for an hour and the sum of the pending sessions created in that hour. '
|
||||
'4 rows are returned: 1 for the current hour plus 3 for the previous 3 hours. '
|
||||
'Rows are sorted by the hour in descending order.';
|
||||
|
||||
drop view if exists hcp_billing_hourly_sessions_last_7_days;
|
||||
create view hcp_billing_hourly_sessions_last_7_days as
|
||||
with
|
||||
hourly_counts (hour, sessions_pending_count) as (
|
||||
select date_trunc('hour', session_pending_time), count(*)
|
||||
from wh_session_accumulating_fact
|
||||
where session_pending_time >= date_trunc('hour', now() - '7 days'::interval)
|
||||
group by date_trunc('hour', session_pending_time)
|
||||
),
|
||||
hourly_range (hour) as (
|
||||
select date_trunc('hour',time)
|
||||
from generate_series(now() - '7 days'::interval, now(), '1 hour'::interval) as time
|
||||
),
|
||||
final (hour, sessions_pending_count) as (
|
||||
select hourly_range.hour, coalesce(hourly_counts.sessions_pending_count, 0)
|
||||
from hourly_range
|
||||
left join hourly_counts on hourly_range.hour = hourly_counts.hour
|
||||
)
|
||||
select hour, sessions_pending_count
|
||||
from final
|
||||
order by hour desc;
|
||||
comment on view hcp_billing_hourly_sessions_last_7_days is
|
||||
'hcp_billing_hourly_sessions_last_7_days is a view where each row contains the timestamp for an hour and the sum of the pending sessions created in that hour. '
|
||||
'169 rows are returned: 1 for the current hour plus 168 for the previous 7 days. '
|
||||
'Rows are sorted by the hour in descending order.';
|
||||
|
||||
drop view if exists hcp_billing_hourly_sessions_all;
|
||||
create view hcp_billing_hourly_sessions_all as
|
||||
with
|
||||
hourly_counts (hour, sessions_pending_count) as (
|
||||
select date_trunc('hour', session_pending_time), count(*)
|
||||
from wh_session_accumulating_fact
|
||||
group by date_trunc('hour', session_pending_time)
|
||||
),
|
||||
hourly_range (hour) as (
|
||||
select date_trunc('hour',time)
|
||||
from generate_series(
|
||||
(select min(session_pending_time) from wh_session_accumulating_fact),
|
||||
now(),
|
||||
'1 hour'::interval
|
||||
) as time
|
||||
),
|
||||
final (hour, sessions_pending_count) as (
|
||||
select hourly_range.hour, coalesce(hourly_counts.sessions_pending_count, 0)
|
||||
from hourly_range
|
||||
left join hourly_counts on hourly_range.hour = hourly_counts.hour
|
||||
)
|
||||
select hour, sessions_pending_count
|
||||
from final
|
||||
order by hour desc;
|
||||
comment on view hcp_billing_hourly_sessions_all is
|
||||
'hcp_billing_hourly_sessions_all is a view where each row contains the timestamp for an hour and the sum of the pending sessions created in that hour. '
|
||||
'A row is returned for each hour since the first session was created up to and including the current hour. '
|
||||
'Rows are sorted by the hour in descending order.';
|
||||
|
||||
/*
|
||||
Implementation Note 2: Generating test data
|
||||
|
||||
Step 1: Start and connect to the sqltest docker container (see sqltest/README.md).
|
||||
|
||||
The sqltest container initializes with a few sessions already in the data
|
||||
warehouse. These sessions will populate rows in the wh_host_dimension and
|
||||
wh_user_dimension tables which will be used in the query below.
|
||||
|
||||
Step 2: Truncate the warehouse fact tables:
|
||||
|
||||
truncate wh_session_connection_accumulating_fact, wh_session_accumulating_fact;
|
||||
|
||||
Step 3: Fill the wh_session_accumulating_fact table with 261961 rows of data:
|
||||
|
||||
with
|
||||
dim_keys (host_key, user_key, credential_group_key) as (
|
||||
select h.key, u.key, 'no credentials'
|
||||
from (select key from wh_host_dimension limit 1) as h,
|
||||
(select key from wh_user_dimension limit 1) as u
|
||||
),
|
||||
time_series (date_key, time_key, time) as (
|
||||
select wh_date_key(time), wh_time_key(time), time
|
||||
from generate_series(
|
||||
now() - interval '6 months',
|
||||
now() - interval '2 hours',
|
||||
interval '1 minute'
|
||||
) as time
|
||||
),
|
||||
fake_sessions (session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time) as (
|
||||
select substr(md5(random()::text), 0, 15), substr(md5(random()::text), 0, 15),
|
||||
k.host_key, k.user_key, k.credential_group_key,
|
||||
t.date_key, t.time_key,t.time
|
||||
from dim_keys as k,
|
||||
time_series as t
|
||||
)
|
||||
insert into wh_session_accumulating_fact
|
||||
(session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time
|
||||
)
|
||||
select session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time
|
||||
from fake_sessions;
|
||||
|
||||
Step 4: Collect the execution plan for each view to establish a baseline before making changes:
|
||||
|
||||
explain (analyze, buffers) select * from hcp_billing_hourly_sessions_last_3_hours;
|
||||
explain (analyze, buffers) select * from hcp_billing_hourly_sessions_last_7_days;
|
||||
explain (analyze, buffers) select * from hcp_billing_hourly_sessions_all;
|
||||
*/
|
||||
commit;
|
||||
@ -0,0 +1,110 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
drop view if exists hcp_billing_monthly_sessions_current_month;
|
||||
create view hcp_billing_monthly_sessions_current_month as
|
||||
with
|
||||
monthly_counts (month, sessions_pending_count) as (
|
||||
select date_trunc('month', session_pending_time), count(*)
|
||||
from wh_session_accumulating_fact
|
||||
where session_pending_time >= date_trunc('month', now())
|
||||
and session_pending_time < date_trunc('hour', now())
|
||||
group by date_trunc('month', session_pending_time)
|
||||
),
|
||||
monthly_range (month) as (
|
||||
select date_trunc('month',now())
|
||||
),
|
||||
final (start_time, end_time, sessions_pending_count) as (
|
||||
select monthly_range.month, -- start
|
||||
case when monthly_range.month = date_trunc('month', now())
|
||||
then date_trunc('hour', now())
|
||||
else monthly_range.month + interval '1 month'
|
||||
end,
|
||||
coalesce(monthly_counts.sessions_pending_count, 0)
|
||||
from monthly_range
|
||||
left join monthly_counts on monthly_range.month = monthly_counts.month
|
||||
)
|
||||
select start_time, end_time, sessions_pending_count
|
||||
from final
|
||||
order by start_time desc;
|
||||
comment on view hcp_billing_monthly_sessions_current_month is
|
||||
'hcp_billing_monthly_sessions_current_month is a view that contains '
|
||||
'the sum of pending sessions '
|
||||
'from the beginning of the current month '
|
||||
'until the start of the current hour (exclusive).';
|
||||
|
||||
drop view if exists hcp_billing_monthly_sessions_last_2_months;
|
||||
create view hcp_billing_monthly_sessions_last_2_months as
|
||||
with
|
||||
monthly_counts (month, sessions_pending_count) as (
|
||||
select date_trunc('month', session_pending_time), count(*)
|
||||
from wh_session_accumulating_fact
|
||||
where session_pending_time >= date_trunc('month', now() - interval '1 month')
|
||||
and session_pending_time < date_trunc('hour', now())
|
||||
group by date_trunc('month', session_pending_time)
|
||||
),
|
||||
monthly_range (month) as (
|
||||
select date_trunc('month', time)
|
||||
from generate_series(
|
||||
date_trunc('month', now() - interval '1 month'),
|
||||
now(),
|
||||
'1 month'::interval
|
||||
) as time
|
||||
),
|
||||
final (start_time, end_time, sessions_pending_count) as (
|
||||
select monthly_range.month, -- start
|
||||
case when monthly_range.month = date_trunc('month', now())
|
||||
then date_trunc('hour', now())
|
||||
else monthly_range.month + interval '1 month'
|
||||
end,
|
||||
coalesce(monthly_counts.sessions_pending_count, 0)
|
||||
from monthly_range
|
||||
left join monthly_counts on monthly_range.month = monthly_counts.month
|
||||
)
|
||||
select start_time, end_time, sessions_pending_count
|
||||
from final
|
||||
order by start_time desc;
|
||||
comment on view hcp_billing_monthly_sessions_last_2_months is
|
||||
'hcp_billing_monthly_sessions_last_2_months is a view that contains '
|
||||
'the sum of pending sessions for the current month and the previous month. '
|
||||
'The current month is a sum from the beginning of the current month '
|
||||
'until the start of the current hour (exclusive).';
|
||||
|
||||
drop view if exists hcp_billing_monthly_sessions_all;
|
||||
create view hcp_billing_monthly_sessions_all as
|
||||
with
|
||||
monthly_counts (month, sessions_pending_count) as (
|
||||
select date_trunc('month', session_pending_time), count(*)
|
||||
from wh_session_accumulating_fact
|
||||
where session_pending_time < date_trunc('hour', now())
|
||||
group by date_trunc('month', session_pending_time)
|
||||
),
|
||||
monthly_range (month) as (
|
||||
select date_trunc('month',time)
|
||||
from generate_series(
|
||||
(select min(session_pending_time) from wh_session_accumulating_fact),
|
||||
now(),
|
||||
'1 month'::interval
|
||||
) as time
|
||||
),
|
||||
final (start_time, end_time, sessions_pending_count) as (
|
||||
-- select monthly_range.month - interval '1 month', -- start
|
||||
select monthly_range.month, -- start
|
||||
case when monthly_range.month = date_trunc('month', now())
|
||||
then date_trunc('hour', now())
|
||||
else monthly_range.month + interval '1 month'
|
||||
end,
|
||||
coalesce(monthly_counts.sessions_pending_count, 0)
|
||||
from monthly_range
|
||||
left join monthly_counts on monthly_range.month = monthly_counts.month
|
||||
)
|
||||
select start_time, end_time, sessions_pending_count
|
||||
from final
|
||||
order by start_time desc;
|
||||
comment on view hcp_billing_monthly_sessions_all is
|
||||
'hcp_billing_monthly_sessions_all is a view that contains '
|
||||
'the sum of pending sessions for the current month and all previous months. '
|
||||
'The current month is a sum from the beginning of the current month '
|
||||
'until the start of the current hour (exclusive).';
|
||||
commit;
|
||||
@ -0,0 +1,84 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
select plan(12);
|
||||
|
||||
select has_view('hcp_billing_hourly_sessions_all', 'view for hcp billing does not exist');
|
||||
|
||||
select lives_ok('truncate wh_session_connection_accumulating_fact, wh_session_accumulating_fact',
|
||||
'Truncate tables in preparation for testing');
|
||||
|
||||
-- validate the warehouse fact tables are empty
|
||||
select is(count(*), 0::bigint, 'wh_session_connection_accumulating_fact is not empty') from wh_session_connection_accumulating_fact;
|
||||
select is(count(*), 0::bigint, 'wh_session_accumulating_fact is not empty' ) from wh_session_accumulating_fact;
|
||||
|
||||
select is(count(*), 0::bigint, 'hcp_billing_hourly_sessions_all should always return 0 rows when there are no sessions') from hcp_billing_hourly_sessions_all;
|
||||
|
||||
-- insert one session per minute for the past 171 hours
|
||||
-- 171 is 2 more than the hcp_billing_hourly_sessions_last_7_days view should return
|
||||
-- total is 10,261 = 60 minutes * 171 + 1 for the current minute
|
||||
with
|
||||
dim_keys (host_key, user_key, credential_group_key) as (
|
||||
select h.key, u.key, 'no credentials'
|
||||
from (select key from wh_host_dimension limit 1) as h,
|
||||
(select key from wh_user_dimension limit 1) as u
|
||||
),
|
||||
time_series (date_key, time_key, time) as (
|
||||
select wh_date_key(time), wh_time_key(time), time
|
||||
from generate_series(
|
||||
now() - interval '171 hours',
|
||||
now(),
|
||||
interval '1 minute'
|
||||
) as time
|
||||
),
|
||||
fake_sessions (session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time) as (
|
||||
select concat('s__________', t.date_key, t.time_key), concat('a__________', t.date_key, t.time_key),
|
||||
k.host_key, k.user_key, k.credential_group_key,
|
||||
t.date_key, t.time_key,t.time
|
||||
from dim_keys as k,
|
||||
time_series as t
|
||||
)
|
||||
insert into wh_session_accumulating_fact
|
||||
(session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time
|
||||
)
|
||||
select session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time
|
||||
from fake_sessions;
|
||||
|
||||
select is(count(*), 4::bigint, 'hcp_billing_hourly_sessions_last_3_hours should always return 4 rows') from hcp_billing_hourly_sessions_last_3_hours;
|
||||
select is(count(*), 169::bigint, 'hcp_billing_hourly_sessions_last_7_days should always return 169 rows') from hcp_billing_hourly_sessions_last_7_days;
|
||||
select is(count(*), 172::bigint, 'hcp_billing_hourly_sessions_all should return 172 rows') from hcp_billing_hourly_sessions_all;
|
||||
|
||||
select results_eq(
|
||||
'select sessions_pending_count::bigint from hcp_billing_hourly_sessions_all limit 1',
|
||||
'select extract(minute from now())::bigint + 1',
|
||||
'hcp_billing_hourly_sessions_all: session count for the current hour is incorrect'
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select count(*)::bigint from wh_session_accumulating_fact',
|
||||
'select sum(sessions_pending_count)::bigint from hcp_billing_hourly_sessions_all',
|
||||
'hcp_billing_hourly_sessions_all sum of sessions is incorrect'
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select * from hcp_billing_hourly_sessions_all limit 4',
|
||||
'select * from hcp_billing_hourly_sessions_last_3_hours',
|
||||
'hcp_billing_hourly_sessions_all and hcp_billing_hourly_sessions_last_3_hours: latest 3 hours should be equal'
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select * from hcp_billing_hourly_sessions_all limit 169',
|
||||
'select * from hcp_billing_hourly_sessions_last_7_days',
|
||||
'hcp_billing_hourly_sessions_all and hcp_billing_hourly_sessions_last_7_days: last 7 days should be equal'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
@ -0,0 +1,81 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
select plan(10);
|
||||
|
||||
select has_view('hcp_billing_hourly_sessions_last_3_hours', 'view for hcp billing does not exist');
|
||||
|
||||
select lives_ok('truncate wh_session_connection_accumulating_fact, wh_session_accumulating_fact',
|
||||
'Truncate tables in preparation for testing');
|
||||
|
||||
-- validate the warehouse fact tables are empty
|
||||
select is(count(*), 0::bigint, 'wh_session_connection_accumulating_fact is not empty') from wh_session_connection_accumulating_fact;
|
||||
select is(count(*), 0::bigint, 'wh_session_accumulating_fact is not empty' ) from wh_session_accumulating_fact;
|
||||
|
||||
select is(count(*), 4::bigint, 'hcp_billing_hourly_sessions_last_3_hours should always return 4 rows') from hcp_billing_hourly_sessions_last_3_hours;
|
||||
|
||||
-- insert one session per minute for the past 2 hours
|
||||
-- total is 121 = 60 minutes * 2 plus 1 for current minute
|
||||
with
|
||||
dim_keys (host_key, user_key, credential_group_key) as (
|
||||
select h.key, u.key, 'no credentials'
|
||||
from (select key from wh_host_dimension limit 1) as h,
|
||||
(select key from wh_user_dimension limit 1) as u
|
||||
),
|
||||
time_series (date_key, time_key, time) as (
|
||||
select wh_date_key(time), wh_time_key(time), time
|
||||
from generate_series(
|
||||
now() - interval '2 hours',
|
||||
now(),
|
||||
interval '1 minute'
|
||||
) as time
|
||||
),
|
||||
fake_sessions (session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time) as (
|
||||
select concat('s__________', t.date_key, t.time_key), concat('a__________', t.date_key, t.time_key),
|
||||
k.host_key, k.user_key, k.credential_group_key,
|
||||
t.date_key, t.time_key,t.time
|
||||
from dim_keys as k,
|
||||
time_series as t
|
||||
)
|
||||
insert into wh_session_accumulating_fact
|
||||
(session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time
|
||||
)
|
||||
select session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time
|
||||
from fake_sessions;
|
||||
|
||||
select is(count(*), 4::bigint, 'hcp_billing_hourly_sessions_last_3_hours should always return 4 rows') from hcp_billing_hourly_sessions_last_3_hours;
|
||||
|
||||
select results_eq(
|
||||
'select count(*)::bigint from wh_session_accumulating_fact',
|
||||
'select sum(sessions_pending_count)::bigint from hcp_billing_hourly_sessions_last_3_hours',
|
||||
'hcp_billing_hourly_sessions_last_3_hours: the sum of sessions is incorrect'
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select sessions_pending_count::bigint from hcp_billing_hourly_sessions_last_3_hours limit 1',
|
||||
'select extract(minute from now())::bigint + 1',
|
||||
'hcp_billing_hourly_sessions_last_3_hours: session count for the current hour is incorrect'
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select * from hcp_billing_hourly_sessions_last_3_hours',
|
||||
'select * from hcp_billing_hourly_sessions_last_7_days limit 4',
|
||||
'hcp_billing_hourly_sessions_last_3_hours and hcp_billing_hourly_sessions_last_7_days: latest 3 hours should be equal'
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select sessions_pending_count::bigint from hcp_billing_hourly_sessions_last_3_hours order by hour limit 1',
|
||||
'select 0::bigint',
|
||||
'hcp_billing_hourly_sessions_last_3_hours: session count for the last hour is incorrect'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
@ -0,0 +1,82 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
select plan(10);
|
||||
|
||||
select has_view('hcp_billing_hourly_sessions_last_7_days', 'view for hcp billing does not exist');
|
||||
|
||||
select lives_ok('truncate wh_session_connection_accumulating_fact, wh_session_accumulating_fact',
|
||||
'Truncate tables in preparation for testing');
|
||||
|
||||
-- validate the warehouse fact tables are empty
|
||||
select is(count(*), 0::bigint, 'wh_session_connection_accumulating_fact is not empty') from wh_session_connection_accumulating_fact;
|
||||
select is(count(*), 0::bigint, 'wh_session_accumulating_fact is not empty' ) from wh_session_accumulating_fact;
|
||||
|
||||
select is(count(*), 169::bigint, 'hcp_billing_hourly_sessions_last_7_days should always return 169 rows') from hcp_billing_hourly_sessions_last_7_days;
|
||||
|
||||
-- insert one session per minute for the past 167 hours
|
||||
-- 167 hours = 24*7 - 1 hour to test edge cases
|
||||
-- total is 10,021 = 60 minutes * 167 + 1 for the current minute
|
||||
with
|
||||
dim_keys (host_key, user_key, credential_group_key) as (
|
||||
select h.key, u.key, 'no credentials'
|
||||
from (select key from wh_host_dimension limit 1) as h,
|
||||
(select key from wh_user_dimension limit 1) as u
|
||||
),
|
||||
time_series (date_key, time_key, time) as (
|
||||
select wh_date_key(time), wh_time_key(time), time
|
||||
from generate_series(
|
||||
now() - interval '167 hours',
|
||||
now(),
|
||||
interval '1 minute'
|
||||
) as time
|
||||
),
|
||||
fake_sessions (session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time) as (
|
||||
select concat('s__________', t.date_key, t.time_key), concat('a__________', t.date_key, t.time_key),
|
||||
k.host_key, k.user_key, k.credential_group_key,
|
||||
t.date_key, t.time_key,t.time
|
||||
from dim_keys as k,
|
||||
time_series as t
|
||||
)
|
||||
insert into wh_session_accumulating_fact
|
||||
(session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time
|
||||
)
|
||||
select session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time
|
||||
from fake_sessions;
|
||||
|
||||
select is(count(*), 169::bigint, 'hcp_billing_hourly_sessions_last_7_days should always return 169 rows') from hcp_billing_hourly_sessions_last_7_days;
|
||||
|
||||
select results_eq(
|
||||
'select count(*)::bigint from wh_session_accumulating_fact',
|
||||
'select sum(sessions_pending_count)::bigint from hcp_billing_hourly_sessions_last_7_days',
|
||||
'hcp_billing_hourly_sessions_last_7_days: the sum of sessions is incorrect'
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select sessions_pending_count::bigint from hcp_billing_hourly_sessions_last_7_days limit 1',
|
||||
'select extract(minute from now())::bigint + 1',
|
||||
'hcp_billing_hourly_sessions_last_7_days: session count for the current hour is incorrect'
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select * from hcp_billing_hourly_sessions_last_7_days limit 4',
|
||||
'select * from hcp_billing_hourly_sessions_last_3_hours',
|
||||
'hcp_billing_hourly_sessions_last_7_days and hcp_billing_hourly_sessions_last_3_hours: latest 3 hours should be equal'
|
||||
);
|
||||
|
||||
select results_eq(
|
||||
'select sessions_pending_count::bigint from hcp_billing_hourly_sessions_last_7_days order by hour limit 1',
|
||||
'select 0::bigint',
|
||||
'hcp_billing_hourly_sessions_last_7_days: session count for the last hour is incorrect'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
@ -0,0 +1,13 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
select plan(1);
|
||||
|
||||
select has_index('wh_session_accumulating_fact',
|
||||
'wh_session_accumulating_fact_session_pending_time_idx',
|
||||
'session_pending_time',
|
||||
'index for hcp billing views is missing' );
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@ -0,0 +1,294 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
select plan(47);
|
||||
|
||||
create function test_get_hours_between(start_time timestamptz, end_time timestamptz) returns int
|
||||
as $$
|
||||
select count(time)::int
|
||||
from generate_series(start_time, end_time, interval '1 hour') as time;
|
||||
$$ language sql
|
||||
immutable
|
||||
returns null on null input;
|
||||
|
||||
create function test_get_hours_between(start_time timestamptz) returns int
|
||||
as $$
|
||||
select test_get_hours_between(start_time, now());
|
||||
$$ language sql
|
||||
immutable
|
||||
returns null on null input;
|
||||
|
||||
select has_view('hcp_billing_monthly_sessions_all', 'monthly view for hcp billing does not exist');
|
||||
select has_view('hcp_billing_monthly_sessions_current_month', 'monthly view for hcp billing does not exist');
|
||||
select has_view('hcp_billing_monthly_sessions_last_2_months', 'monthly view for hcp billing does not exist');
|
||||
|
||||
select lives_ok('truncate wh_session_connection_accumulating_fact, wh_session_accumulating_fact',
|
||||
'Truncate tables in preparation for testing');
|
||||
|
||||
-- validate the warehouse fact tables are empty
|
||||
select is(count(*), 0::bigint, 'wh_session_connection_accumulating_fact is not empty') from wh_session_connection_accumulating_fact;
|
||||
select is(count(*), 0::bigint, 'wh_session_accumulating_fact is not empty' ) from wh_session_accumulating_fact;
|
||||
|
||||
-- validate the view returns no rows
|
||||
select is(count(*), 0::bigint, 'hcp_billing_monthly_sessions_all should return 0 rows when there are no sessions') from hcp_billing_monthly_sessions_all;
|
||||
select is(count(*), 1::bigint, 'hcp_billing_monthly_sessions_current_month should return 1 rows when there are no sessions') from hcp_billing_monthly_sessions_current_month;
|
||||
select is(count(*), 2::bigint, 'hcp_billing_monthly_sessions_last_2_months should return 1 rows when there are no sessions') from hcp_billing_monthly_sessions_last_2_months;
|
||||
|
||||
create function test_setup_data(start_time timestamptz, end_time timestamptz) returns int
|
||||
as $$
|
||||
declare
|
||||
insert_count int;
|
||||
tmp timestamptz;
|
||||
begin
|
||||
truncate wh_session_connection_accumulating_fact, wh_session_accumulating_fact;
|
||||
|
||||
if start_time = end_time then
|
||||
return 0::int;
|
||||
end if;
|
||||
|
||||
if start_time > end_time then
|
||||
-- swap
|
||||
tmp := start_time; start_time := end_time; end_time := tmp;
|
||||
end if;
|
||||
|
||||
with
|
||||
vars (start_one, start_two) as (
|
||||
select date_trunc('hour', start_time),
|
||||
(date_trunc('hour', start_time) + interval '1 hour') - interval '1 microsecond'
|
||||
),
|
||||
time_series (time) as (
|
||||
select generate_series(vars.start_one, end_time, interval '1 hour') as ts
|
||||
from vars
|
||||
union
|
||||
select generate_series(vars.start_two, end_time, interval '1 hour') as ts
|
||||
from vars
|
||||
order by ts
|
||||
),
|
||||
dim_keys (host_key, user_key, credential_group_key) as (
|
||||
select h.key, u.key, 'no credentials'
|
||||
from (select key from wh_host_dimension limit 1) as h,
|
||||
(select key from wh_user_dimension limit 1) as u
|
||||
),
|
||||
dim_time_series (date_key, time_key, time) as (
|
||||
select wh_date_key(time), wh_time_key(time), time
|
||||
from time_series
|
||||
),
|
||||
fake_sessions (session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time) as (
|
||||
select concat('s__________', t.date_key, t.time_key), concat('a__________', t.date_key, t.time_key),
|
||||
k.host_key, k.user_key, k.credential_group_key,
|
||||
t.date_key, t.time_key,t.time
|
||||
from dim_keys as k,
|
||||
dim_time_series as t
|
||||
)
|
||||
insert into wh_session_accumulating_fact
|
||||
(session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time
|
||||
)
|
||||
select session_id, auth_token_id,
|
||||
host_key, user_key, credential_group_key,
|
||||
session_pending_date_key, session_pending_time_key, session_pending_time
|
||||
from fake_sessions;
|
||||
|
||||
select count(*) into insert_count
|
||||
from wh_session_accumulating_fact;
|
||||
|
||||
return insert_count;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create function test_setup_data(start_time timestamptz) returns int
|
||||
as $$
|
||||
begin
|
||||
return test_setup_data(start_time, now());
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
prepare select_hcp_billing_monthly_sessions_all as
|
||||
select *
|
||||
from hcp_billing_monthly_sessions_all;
|
||||
|
||||
prepare select_hcp_billing_monthly_sessions_current_month as
|
||||
select *
|
||||
from hcp_billing_monthly_sessions_current_month;
|
||||
|
||||
prepare select_hcp_billing_monthly_sessions_last_2_months as
|
||||
select *
|
||||
from hcp_billing_monthly_sessions_last_2_months;
|
||||
|
||||
prepare select_hcp_billing_monthly_sessions_last_2_months_1_row as
|
||||
select *
|
||||
from hcp_billing_monthly_sessions_last_2_months
|
||||
limit 1;
|
||||
|
||||
select is(test_setup_data(now()), 0::int, 'hcp billing: test_setup_data start_time of now() should insert 0 data');
|
||||
select is(count(*), 0::bigint, 'hcp_billing_monthly_sessions_all should return 0 rows when there no rows in the warehouse') from hcp_billing_monthly_sessions_all;
|
||||
select is_empty('select_hcp_billing_monthly_sessions_all', 'hcp_billing_monthly_sessions_all should have no rows when there are no sessions in the warehouse');
|
||||
select is(count(*), 1::bigint, 'hcp_billing_monthly_sessions_current_month should return 1 rows when there no rows in the warehouse') from hcp_billing_monthly_sessions_current_month;
|
||||
select is(count(*), 2::bigint, 'hcp_billing_monthly_sessions_last_2_months should return 2 rows when there no rows in the warehouse') from hcp_billing_monthly_sessions_last_2_months;
|
||||
|
||||
select is(test_setup_data(now(), now()), 0::int, 'hcp billing: test_setup_data start_time of now should insert 0 data');
|
||||
select is(count(*), 0::bigint, 'hcp_billing_monthly_sessions_all should return 0 rows when there no rows in the warehouse') from hcp_billing_monthly_sessions_all;
|
||||
select is_empty('select_hcp_billing_monthly_sessions_all', 'hcp_billing_monthly_sessions_all should have no rows when there are no sessions in the warehouse');
|
||||
select is(count(*), 1::bigint, 'hcp_billing_monthly_sessions_current_month should return 1 rows when there no rows in the warehouse') from hcp_billing_monthly_sessions_current_month;
|
||||
select is(count(*), 2::bigint, 'hcp_billing_monthly_sessions_last_2_months should return 2 rows when there no rows in the warehouse') from hcp_billing_monthly_sessions_last_2_months;
|
||||
|
||||
-- only sessions in this hour
|
||||
select is(test_setup_data(date_trunc('hour', now())), 1::int,
|
||||
'hcp billing: test_setup_data: start_time of this hour should insert 1');
|
||||
select is(count(*), 1::bigint,
|
||||
'hcp_billing_monthly_sessions_all should return 1 row when there are only sessions in this hour') from hcp_billing_monthly_sessions_all;
|
||||
select row_eq('select_hcp_billing_monthly_sessions_all', row(date_trunc('month', now()), date_trunc('hour', now()), 0::bigint),
|
||||
'hcp_billing_monthly_sessions_all should have 1 row with 0 sessions_pending_count when there are only sessions for this hour');
|
||||
select results_eq('select_hcp_billing_monthly_sessions_current_month', 'select_hcp_billing_monthly_sessions_all',
|
||||
'hcp_billing_monthly_sessions_current_month and hcp_billing_monthly_sessions_all should be equal');
|
||||
select results_eq('select_hcp_billing_monthly_sessions_current_month', 'select_hcp_billing_monthly_sessions_last_2_months_1_row',
|
||||
'hcp_billing_monthly_sessions_current_month and the first row of hcp_billing_monthly_sessions_last_2_months should be equal');
|
||||
|
||||
-- only sessions for yesterday
|
||||
select is(test_setup_data( 'yesterday'::timestamptz, 'today'::timestamptz - interval '1 microsecond' ), 48::int,
|
||||
'hcp billing: test_setup_data: should be 48 sessions for yesterday');
|
||||
select is(count(*), 1::bigint,
|
||||
'hcp_billing_monthly_sessions_all should return 1 row when there are only sessions in this month') from hcp_billing_monthly_sessions_all;
|
||||
select row_eq('select_hcp_billing_monthly_sessions_all', row(date_trunc('month', now()), date_trunc('hour', now()), 48::bigint),
|
||||
'hcp_billing_monthly_sessions_all should have 1 row with 48 sessions_pending_count when there are only sessions for yesterday');
|
||||
select results_eq('select_hcp_billing_monthly_sessions_current_month', 'select_hcp_billing_monthly_sessions_all',
|
||||
'hcp_billing_monthly_sessions_current_month and hcp_billing_monthly_sessions_all should be equal');
|
||||
select results_eq('select_hcp_billing_monthly_sessions_current_month', 'select_hcp_billing_monthly_sessions_last_2_months_1_row',
|
||||
'hcp_billing_monthly_sessions_current_month and the first row of hcp_billing_monthly_sessions_last_2_months should be equal');
|
||||
|
||||
-- only sessions for this month
|
||||
-- every hour gets 2 sessions
|
||||
-- 1 at the start of the hour 01:00:00
|
||||
-- 1 at the end of the hour (start of next hour - 1 microsecond)
|
||||
-- the current hour only gets one because the end of the hour has not occurred
|
||||
-- every day gets 48 sesions (2 for each hour)
|
||||
-- every month gets 48 * number of days in the month
|
||||
-- current month gets 48 * number of hours since the start of the month - 1
|
||||
-- the current month from the hcp_billing view returns
|
||||
-- number of hours from the start of the month until the hour before now * 2
|
||||
-- when reporting the current hour is not included in the view
|
||||
select is( test_setup_data( date_trunc('month', now()) ),
|
||||
-- +1 for the current hour
|
||||
(test_get_hours_between( date_trunc('month', now()), now() - interval '1 hour') * 2)::int + 1,
|
||||
'hcp billing: test_setup_data: wrong number of sessions for the current month');
|
||||
|
||||
select is(count(*), 1::bigint,
|
||||
'hcp_billing_monthly_sessions_all should return 1 row when there are only sessions in this month') from hcp_billing_monthly_sessions_all;
|
||||
|
||||
select row_eq('select_hcp_billing_monthly_sessions_all',
|
||||
row( date_trunc('month', now()),
|
||||
date_trunc('hour', now()),
|
||||
-- 2 sessions per hour, the current hour is not included
|
||||
(test_get_hours_between( date_trunc('month', now()), now() - interval '1 hour') * 2)::bigint
|
||||
),
|
||||
'hcp_billing_monthly_sessions_all should have 1 row with 48 sessions_pending_count when there are only sessions for yesterday');
|
||||
|
||||
select results_eq('select_hcp_billing_monthly_sessions_current_month', 'select_hcp_billing_monthly_sessions_all',
|
||||
'hcp_billing_monthly_sessions_current_month and hcp_billing_monthly_sessions_all should be equal');
|
||||
select results_eq('select_hcp_billing_monthly_sessions_current_month', 'select_hcp_billing_monthly_sessions_last_2_months_1_row',
|
||||
'hcp_billing_monthly_sessions_current_month and the first row of hcp_billing_monthly_sessions_last_2_months should be equal');
|
||||
|
||||
-- only sessions for this month and last month
|
||||
-- same rules as above for the current month
|
||||
-- the previous month gets 48 sessions per day * the number of days in the month
|
||||
|
||||
create table test_hcp_billing (
|
||||
start_time timestamptz not null,
|
||||
end_time timestamptz not null,
|
||||
sessions_pending_count bigint not null,
|
||||
primary key(start_time, end_time)
|
||||
);
|
||||
|
||||
prepare insert_2_month_results as
|
||||
insert into test_hcp_billing
|
||||
(start_time, end_time, sessions_pending_count)
|
||||
select date_trunc('month', now()),
|
||||
date_trunc('hour', now()),
|
||||
(test_get_hours_between( date_trunc('month', now()), now() - interval '1 hour' ) * 2)::bigint
|
||||
union
|
||||
select date_trunc('month', now() - interval '1 month' ),
|
||||
date_trunc('month', now()),
|
||||
extract(days from date_trunc('month', now()) - interval '1 day')::int * 48;
|
||||
|
||||
prepare select_test_hcp_billing as
|
||||
select * from test_hcp_billing
|
||||
order by start_time desc;
|
||||
|
||||
prepare select_hcp_billing_monthly_sessions_all_1_row as
|
||||
select *
|
||||
from hcp_billing_monthly_sessions_all
|
||||
limit 1;
|
||||
|
||||
select lives_ok('insert_2_month_results', 'insert rows into test_hcp_billing');
|
||||
|
||||
select is( test_setup_data( date_trunc('month', now() - interval '1 month' ) ),
|
||||
(test_get_hours_between( date_trunc('month', now() - interval '1 month' )) * 2)::int - 1,
|
||||
'hcp billing: test_setup_data: wrong number of sessions for 2 months');
|
||||
|
||||
select is(count(*), 2::bigint,
|
||||
'hcp_billing_monthly_sessions_all should return 2 rows when there are only sessions for the last 2 months') from hcp_billing_monthly_sessions_all;
|
||||
|
||||
select results_eq('select_hcp_billing_monthly_sessions_all', 'select_test_hcp_billing',
|
||||
'hcp_billing_monthly_sessions_all should have 2 rows');
|
||||
|
||||
select results_eq('select_hcp_billing_monthly_sessions_current_month', 'select_hcp_billing_monthly_sessions_all_1_row',
|
||||
'hcp_billing_monthly_sessions_current_month and hcp_billing_monthly_sessions_all should be equal');
|
||||
|
||||
select results_eq('select_hcp_billing_monthly_sessions_all', 'select_hcp_billing_monthly_sessions_last_2_months',
|
||||
'hcp_billing_monthly_sessions_all and hcp_billing_monthly_sessions_last_2_months should be equal');
|
||||
-- sessions for the last 13 months
|
||||
-- same rules as above for the current month
|
||||
-- the all previous months get 48 sessions per day * the number of days in the month
|
||||
|
||||
truncate test_hcp_billing;
|
||||
|
||||
prepare insert_13_months_results as
|
||||
with
|
||||
expected (start_time, end_time, session_count) as (
|
||||
select date_trunc('month', time - interval '1 month'),
|
||||
date_trunc('month', time),
|
||||
extract(days from date_trunc('month', time) - interval '1 day')::bigint * 48
|
||||
from generate_series( now() - interval '12 months', now(), '1 month'::interval) as time
|
||||
union
|
||||
select date_trunc('month', now()),
|
||||
date_trunc('hour', now()),
|
||||
(test_get_hours_between( date_trunc('month', now()), now() - interval '1 hour' ) * 2)::bigint
|
||||
)
|
||||
insert into test_hcp_billing
|
||||
(start_time, end_time, sessions_pending_count)
|
||||
select start_time, end_time, session_count
|
||||
from expected;
|
||||
|
||||
prepare select_hcp_billing_monthly_sessions_all_2_rows as
|
||||
select *
|
||||
from hcp_billing_monthly_sessions_all
|
||||
limit 2;
|
||||
|
||||
select lives_ok('insert_13_months_results', 'insert rows into test_hcp_billing');
|
||||
|
||||
select is(count(*), 14::bigint,
|
||||
'test_hcp_billing should return 14 rows') from test_hcp_billing;
|
||||
|
||||
select is( test_setup_data( date_trunc('month', now() - interval '13 month' ) ),
|
||||
(test_get_hours_between( date_trunc('month', now() - interval '13 month' )) * 2)::int - 1,
|
||||
'hcp billing: test_setup_data: wrong number of sessions for 13 months');
|
||||
|
||||
select is(count(*), 14::bigint,
|
||||
'hcp_billing_monthly_sessions_all should return 14 rows') from hcp_billing_monthly_sessions_all;
|
||||
|
||||
select results_eq('select_hcp_billing_monthly_sessions_all', 'select_test_hcp_billing',
|
||||
'hcp_billing_monthly_sessions_all should have 14 rows');
|
||||
|
||||
select results_eq('select_hcp_billing_monthly_sessions_current_month', 'select_hcp_billing_monthly_sessions_all_1_row',
|
||||
'hcp_billing_monthly_sessions_current_month and hcp_billing_monthly_sessions_all should be equal');
|
||||
|
||||
select results_eq('select_hcp_billing_monthly_sessions_all_2_rows', 'select_hcp_billing_monthly_sessions_last_2_months',
|
||||
'hcp_billing_monthly_sessions_last_2_months should be equal to the first 2 rows of hcp_billing_monthly_sessions_all');
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
@ -0,0 +1,21 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
select plan(4);
|
||||
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets');
|
||||
|
||||
insert into target_ssh
|
||||
(project_id, public_id, name)
|
||||
values
|
||||
('p____bwidget', 'tssh______wb', 'Big Widget SSH Target'),
|
||||
('p____swidget', 'tssh______ws', 'Small Widget SSH Target');
|
||||
|
||||
select is(count(*), 1::bigint) from target_all_subtypes where public_id = 'tssh______wb';
|
||||
select is(type, 'ssh') from target_all_subtypes where public_id = 'tssh______wb';
|
||||
select is(count(*), 1::bigint) from target_all_subtypes where public_id = 't_________wb';
|
||||
select is(type, 'tcp') from target_all_subtypes where public_id = 't_________wb';
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@ -0,0 +1,41 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
select plan(2);
|
||||
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
|
||||
|
||||
insert into target_ssh
|
||||
(project_id, public_id, name)
|
||||
values
|
||||
('p____bwidget', 'tssh______wb', 'Big Widget SSH Target');
|
||||
|
||||
insert into target_host_set
|
||||
(project_id, target_id, host_set_id)
|
||||
values
|
||||
('p____bwidget', 'tssh______wb', 's___1wb-sths');
|
||||
|
||||
insert into target_credential_library
|
||||
(project_id, target_id, credential_library_id, credential_purpose)
|
||||
values
|
||||
('p____bwidget', 'tssh______wb', 'vl______wvl1', 'brokered');
|
||||
|
||||
-- ensure no existing dimensions
|
||||
select is(count(*), 0::bigint) from wh_credential_dimension where organization_id = 'o_____widget';
|
||||
|
||||
-- insert session, should result in a new credentials dimension with an ssh target type
|
||||
insert into session
|
||||
( project_id, target_id, user_id, auth_token_id, certificate, endpoint, public_id)
|
||||
values
|
||||
('p____bwidget', 'tssh______wb', 'u_____walter', 'tok___walter', 'abc'::bytea, 'ep1', 's1____walter');
|
||||
insert into session_host_set_host
|
||||
(session_id, host_set_id, host_id)
|
||||
values
|
||||
('s1____walter', 's___1wb-sths', 'h_____wb__01');
|
||||
insert into session_credential_dynamic
|
||||
( session_id, library_id, credential_id, credential_purpose)
|
||||
values
|
||||
('s1____walter', 'vl______wvl1', null, 'brokered');
|
||||
select is(count(*), 1::bigint) from wh_credential_dimension where organization_id = 'o_____widget' and target_type = 'ssh target';
|
||||
rollback;
|
||||
@ -0,0 +1,45 @@
|
||||
-- Copyright (c) HashiCorp, Inc.
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
begin;
|
||||
select plan(8);
|
||||
|
||||
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets');
|
||||
|
||||
insert into target_ssh
|
||||
(project_id, public_id, name)
|
||||
values
|
||||
('p____bwidget', 'tssh____wbwh', 'Test SSH Target Type W/ HostSet'),
|
||||
('p____bwidget', 'tssh___wbwha', 'Test SSH Target Type W/ Address');
|
||||
|
||||
insert into target_tcp
|
||||
(project_id, public_id, name)
|
||||
values
|
||||
('p____bwidget', 'ttcp____wbwh', 'Test TCP Target Type W/ HostSet'),
|
||||
('p____bwidget', 'ttcp___wbwha', 'Test TCP Target Type W/ Address');
|
||||
|
||||
insert into target_host_set
|
||||
(project_id, target_id, host_set_id)
|
||||
values
|
||||
('p____bwidget', 'tssh____wbwh', 's___1wb-plghs'),
|
||||
('p____bwidget', 'ttcp____wbwh', 's___1wb-plghs');
|
||||
|
||||
insert into target_address
|
||||
(target_id, address)
|
||||
values
|
||||
('tssh___wbwha', '8.6.4.2'),
|
||||
('ttcp___wbwha', '8.6.4.2');
|
||||
|
||||
-- validate ssh target type with host set
|
||||
select is(target_type, 'ssh target') from whx_host_dimension_source where target_id = 'tssh____wbwh';
|
||||
|
||||
-- validate ssh target type with address
|
||||
select is(target_type, 'ssh target') from whx_host_dimension_source where target_id = 'tssh___wbwha';
|
||||
|
||||
-- validate tcp target type with host set
|
||||
select is(target_type, 'tcp target') from whx_host_dimension_source where target_id = 'ttcp____wbwh';
|
||||
|
||||
-- validate tcp target type with address
|
||||
select is(target_type, 'tcp target') from whx_host_dimension_source where target_id = 'ttcp___wbwha';
|
||||
|
||||
rollback;
|
||||
Loading…
Reference in new issue