Initial project snapshot
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
DROP TABLE IF EXISTS audit_logs;
|
||||
DROP TABLE IF EXISTS secrets;
|
||||
DROP TABLE IF EXISTS sessions;
|
||||
DROP TABLE IF EXISTS resources;
|
||||
DROP TABLE IF EXISTS devices;
|
||||
DROP TABLE IF EXISTS users;
|
||||
@@ -0,0 +1,65 @@
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
mfa_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS devices (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
device_fingerprint TEXT NOT NULL,
|
||||
trusted_at TIMESTAMPTZ,
|
||||
last_seen_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS resources (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
protocol TEXT NOT NULL,
|
||||
secret_ref TEXT,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE RESTRICT,
|
||||
controller_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
worker_id TEXT,
|
||||
state TEXT NOT NULL,
|
||||
detached_until TIMESTAMPTZ,
|
||||
last_heartbeat_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS secrets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
scope TEXT NOT NULL,
|
||||
encrypted_payload BYTEA NOT NULL,
|
||||
key_version TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
actor_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
action TEXT NOT NULL,
|
||||
target_type TEXT NOT NULL,
|
||||
target_id TEXT NOT NULL,
|
||||
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_devices_user_id ON devices(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_resource_state ON sessions(resource_id, state);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at DESC);
|
||||
@@ -0,0 +1,9 @@
|
||||
DROP TABLE IF EXISTS auth_sessions;
|
||||
DROP INDEX IF EXISTS idx_devices_user_fingerprint;
|
||||
|
||||
ALTER TABLE devices
|
||||
DROP COLUMN IF EXISTS updated_at,
|
||||
DROP COLUMN IF EXISTS revoked_reason,
|
||||
DROP COLUMN IF EXISTS revoked_at,
|
||||
DROP COLUMN IF EXISTS trust_status,
|
||||
DROP COLUMN IF EXISTS device_label;
|
||||
@@ -0,0 +1,32 @@
|
||||
ALTER TABLE devices
|
||||
ADD COLUMN IF NOT EXISTS device_label TEXT,
|
||||
ADD COLUMN IF NOT EXISTS trust_status TEXT NOT NULL DEFAULT 'pending',
|
||||
ADD COLUMN IF NOT EXISTS revoked_at TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS revoked_reason TEXT,
|
||||
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_devices_user_fingerprint
|
||||
ON devices(user_id, device_fingerprint);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS auth_sessions (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
device_id UUID NOT NULL REFERENCES devices(id) ON DELETE RESTRICT,
|
||||
refresh_token_hash TEXT NOT NULL,
|
||||
refresh_expires_at TIMESTAMPTZ NOT NULL,
|
||||
last_seen_at TIMESTAMPTZ,
|
||||
last_rotated_at TIMESTAMPTZ,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
revoked_reason TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_auth_sessions_user_id
|
||||
ON auth_sessions(user_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_auth_sessions_device_id
|
||||
ON auth_sessions(device_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_auth_sessions_revoked_at
|
||||
ON auth_sessions(revoked_at);
|
||||
@@ -0,0 +1,4 @@
|
||||
DROP TABLE IF EXISTS audit_events;
|
||||
DROP TABLE IF EXISTS session_attachments;
|
||||
DROP TABLE IF EXISTS remote_sessions;
|
||||
DROP TABLE IF EXISTS resource_policies;
|
||||
@@ -0,0 +1,79 @@
|
||||
CREATE TABLE IF NOT EXISTS resource_policies (
|
||||
resource_id UUID PRIMARY KEY REFERENCES resources(id) ON DELETE CASCADE,
|
||||
max_concurrent_sessions INTEGER NOT NULL DEFAULT 1,
|
||||
takeover_policy TEXT NOT NULL DEFAULT 'trusted_device',
|
||||
require_trusted_device BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
detach_grace_period_seconds INTEGER NOT NULL DEFAULT 1800,
|
||||
clipboard_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
file_transfer_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS remote_sessions (
|
||||
id UUID PRIMARY KEY,
|
||||
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE RESTRICT,
|
||||
protocol TEXT NOT NULL,
|
||||
state TEXT NOT NULL,
|
||||
worker_id TEXT,
|
||||
controller_user_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
||||
detach_deadline_at TIMESTAMPTZ,
|
||||
last_heartbeat_at TIMESTAMPTZ,
|
||||
takeover_version INTEGER NOT NULL DEFAULT 1,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_remote_sessions_resource_id
|
||||
ON remote_sessions(resource_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_remote_sessions_controller_user_id
|
||||
ON remote_sessions(controller_user_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_remote_sessions_state
|
||||
ON remote_sessions(state);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS session_attachments (
|
||||
id UUID PRIMARY KEY,
|
||||
remote_session_id UUID NOT NULL REFERENCES remote_sessions(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
||||
device_id UUID NOT NULL REFERENCES devices(id) ON DELETE RESTRICT,
|
||||
role TEXT NOT NULL,
|
||||
state TEXT NOT NULL,
|
||||
superseded_by UUID REFERENCES session_attachments(id) ON DELETE SET NULL,
|
||||
takeover_of UUID REFERENCES session_attachments(id) ON DELETE SET NULL,
|
||||
attached_at TIMESTAMPTZ,
|
||||
detached_at TIMESTAMPTZ,
|
||||
last_input_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_session_attachments_remote_session_id
|
||||
ON session_attachments(remote_session_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_session_attachments_user_id
|
||||
ON session_attachments(user_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_session_attachments_state
|
||||
ON session_attachments(state);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_events (
|
||||
id UUID PRIMARY KEY,
|
||||
actor_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
actor_device_id UUID REFERENCES devices(id) ON DELETE SET NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
target_type TEXT NOT NULL,
|
||||
target_id TEXT NOT NULL,
|
||||
remote_session_id UUID REFERENCES remote_sessions(id) ON DELETE SET NULL,
|
||||
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_events_created_at
|
||||
ON audit_events(created_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_events_remote_session_id
|
||||
ON audit_events(remote_session_id);
|
||||
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE resources
|
||||
DROP CONSTRAINT IF EXISTS resources_certificate_verification_mode_check;
|
||||
|
||||
ALTER TABLE resources
|
||||
DROP COLUMN IF EXISTS certificate_verification_mode;
|
||||
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE resources
|
||||
ADD COLUMN certificate_verification_mode TEXT NOT NULL DEFAULT 'strict';
|
||||
|
||||
ALTER TABLE resources
|
||||
ADD CONSTRAINT resources_certificate_verification_mode_check
|
||||
CHECK (certificate_verification_mode IN ('strict', 'ignore'));
|
||||
@@ -0,0 +1,26 @@
|
||||
DROP TABLE IF EXISTS node_agent_update_runs;
|
||||
DROP TABLE IF EXISTS node_partition_states;
|
||||
DROP TABLE IF EXISTS node_update_policies;
|
||||
DROP TABLE IF EXISTS node_services;
|
||||
DROP TABLE IF EXISTS node_capabilities;
|
||||
DROP TABLE IF EXISTS nodes;
|
||||
DROP TABLE IF EXISTS identity_mappings;
|
||||
DROP TABLE IF EXISTS identity_sources;
|
||||
|
||||
DROP INDEX IF EXISTS idx_remote_sessions_organization_id;
|
||||
ALTER TABLE remote_sessions
|
||||
DROP COLUMN IF EXISTS organization_id;
|
||||
|
||||
DROP INDEX IF EXISTS idx_resources_organization_id;
|
||||
ALTER TABLE resources
|
||||
DROP COLUMN IF EXISTS organization_id;
|
||||
|
||||
DROP TABLE IF EXISTS organization_memberships;
|
||||
DROP TABLE IF EXISTS organization_roles;
|
||||
DROP TABLE IF EXISTS organizations;
|
||||
|
||||
ALTER TABLE users
|
||||
DROP CONSTRAINT IF EXISTS users_platform_role_check;
|
||||
|
||||
ALTER TABLE users
|
||||
DROP COLUMN IF EXISTS platform_role;
|
||||
@@ -0,0 +1,219 @@
|
||||
ALTER TABLE users
|
||||
ADD COLUMN IF NOT EXISTS platform_role TEXT NOT NULL DEFAULT 'user';
|
||||
|
||||
ALTER TABLE users
|
||||
ADD CONSTRAINT users_platform_role_check
|
||||
CHECK (platform_role IN ('user', 'platform_admin', 'platform_recovery_admin'));
|
||||
|
||||
CREATE TABLE IF NOT EXISTS organizations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT organizations_status_check
|
||||
CHECK (status IN ('active', 'suspended', 'archived'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS organization_roles (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
scope TEXT NOT NULL DEFAULT 'organization',
|
||||
description TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT organization_roles_scope_check
|
||||
CHECK (scope IN ('organization'))
|
||||
);
|
||||
|
||||
INSERT INTO organization_roles (id, name, scope, description)
|
||||
VALUES
|
||||
('org_owner', 'Organization Owner', 'organization', 'Full organization control including membership administration.'),
|
||||
('org_admin', 'Organization Admin', 'organization', 'Administrative access within one organization.'),
|
||||
('org_operator', 'Organization Operator', 'organization', 'Operational access within one organization.'),
|
||||
('org_member', 'Organization Member', 'organization', 'Standard organization member access.'),
|
||||
('org_viewer', 'Organization Viewer', 'organization', 'Read-only organization visibility.')
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
description = EXCLUDED.description;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS organization_memberships (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
role_id TEXT NOT NULL REFERENCES organization_roles(id) ON DELETE RESTRICT,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
invited_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (organization_id, user_id),
|
||||
CONSTRAINT organization_memberships_status_check
|
||||
CHECK (status IN ('active', 'suspended', 'revoked'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_organization_memberships_user_id
|
||||
ON organization_memberships(user_id);
|
||||
|
||||
INSERT INTO organizations (slug, name, status, metadata)
|
||||
VALUES ('default', 'Default Organization', 'active', '{"bootstrap":true}'::jsonb)
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
ALTER TABLE resources
|
||||
ADD COLUMN IF NOT EXISTS organization_id UUID REFERENCES organizations(id) ON DELETE RESTRICT;
|
||||
|
||||
UPDATE resources
|
||||
SET organization_id = organizations.id
|
||||
FROM organizations
|
||||
WHERE organizations.slug = 'default'
|
||||
AND resources.organization_id IS NULL;
|
||||
|
||||
ALTER TABLE resources
|
||||
ALTER COLUMN organization_id SET NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_resources_organization_id
|
||||
ON resources(organization_id);
|
||||
|
||||
ALTER TABLE remote_sessions
|
||||
ADD COLUMN IF NOT EXISTS organization_id UUID REFERENCES organizations(id) ON DELETE RESTRICT;
|
||||
|
||||
UPDATE remote_sessions
|
||||
SET organization_id = resources.organization_id
|
||||
FROM resources
|
||||
WHERE resources.id = remote_sessions.resource_id
|
||||
AND remote_sessions.organization_id IS NULL;
|
||||
|
||||
ALTER TABLE remote_sessions
|
||||
ALTER COLUMN organization_id SET NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_remote_sessions_organization_id
|
||||
ON remote_sessions(organization_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS identity_sources (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
kind TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
config JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT identity_sources_kind_check
|
||||
CHECK (kind IN ('local', 'ldap_ad', 'oidc')),
|
||||
CONSTRAINT identity_sources_status_check
|
||||
CHECK (status IN ('active', 'disabled'))
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_identity_sources_org_name
|
||||
ON identity_sources(organization_id, name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS identity_mappings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
identity_source_id UUID NOT NULL REFERENCES identity_sources(id) ON DELETE CASCADE,
|
||||
mapping_type TEXT NOT NULL,
|
||||
external_selector JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
internal_target JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT identity_mappings_type_check
|
||||
CHECK (mapping_type IN ('group_binding', 'claim_binding', 'user_binding'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_identity_mappings_source_id
|
||||
ON identity_mappings(identity_source_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS nodes (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
owner_organization_id UUID REFERENCES organizations(id) ON DELETE SET NULL,
|
||||
node_key TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
ownership_type TEXT NOT NULL,
|
||||
registration_status TEXT NOT NULL DEFAULT 'pending',
|
||||
health_status TEXT NOT NULL DEFAULT 'unknown',
|
||||
version_state TEXT NOT NULL DEFAULT 'unknown',
|
||||
partition_state TEXT NOT NULL DEFAULT 'healthy',
|
||||
desired_version TEXT,
|
||||
reported_version TEXT,
|
||||
last_seen_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT nodes_ownership_type_check
|
||||
CHECK (ownership_type IN ('platform_managed', 'customer_managed')),
|
||||
CONSTRAINT nodes_registration_status_check
|
||||
CHECK (registration_status IN ('pending', 'active', 'disabled', 'revoked')),
|
||||
CONSTRAINT nodes_health_status_check
|
||||
CHECK (health_status IN ('unknown', 'healthy', 'warning', 'critical')),
|
||||
CONSTRAINT nodes_version_state_check
|
||||
CHECK (version_state IN ('unknown', 'current', 'outdated', 'updating', 'rollback', 'failed')),
|
||||
CONSTRAINT nodes_partition_state_check
|
||||
CHECK (partition_state IN ('healthy', 'degraded', 'recovery', 'isolated'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_capabilities (
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
capability TEXT NOT NULL,
|
||||
value JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (node_id, capability)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_services (
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
service_type TEXT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
desired_state TEXT NOT NULL DEFAULT 'disabled',
|
||||
reported_state TEXT NOT NULL DEFAULT 'unknown',
|
||||
last_reported_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (node_id, service_type),
|
||||
CONSTRAINT node_services_desired_state_check
|
||||
CHECK (desired_state IN ('enabled', 'disabled', 'drain')),
|
||||
CONSTRAINT node_services_reported_state_check
|
||||
CHECK (reported_state IN ('unknown', 'starting', 'running', 'degraded', 'stopped', 'failed'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_update_policies (
|
||||
node_id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
mode TEXT NOT NULL DEFAULT 'manual',
|
||||
channel TEXT NOT NULL DEFAULT 'stable',
|
||||
maintenance_window JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
canary BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
automatic_rollout BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT node_update_policies_mode_check
|
||||
CHECK (mode IN ('manual', 'automatic', 'staged', 'canary'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_partition_states (
|
||||
node_id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
cluster_state TEXT NOT NULL DEFAULT 'healthy',
|
||||
recovery_mode TEXT NOT NULL DEFAULT 'normal',
|
||||
notes TEXT,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT node_partition_states_cluster_state_check
|
||||
CHECK (cluster_state IN ('healthy', 'degraded', 'recovery', 'isolated')),
|
||||
CONSTRAINT node_partition_states_recovery_mode_check
|
||||
CHECK (recovery_mode IN ('normal', 'manual_recovery', 'emergency'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_agent_update_runs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
action TEXT NOT NULL,
|
||||
target_version TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
acknowledged_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
CONSTRAINT node_agent_update_runs_action_check
|
||||
CHECK (action IN ('update', 'rollback')),
|
||||
CONSTRAINT node_agent_update_runs_status_check
|
||||
CHECK (status IN ('pending', 'acknowledged', 'succeeded', 'failed'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_node_agent_update_runs_node_id
|
||||
ON node_agent_update_runs(node_id, requested_at DESC);
|
||||
@@ -0,0 +1,7 @@
|
||||
DELETE FROM organization_memberships
|
||||
WHERE organization_id IN (
|
||||
SELECT id
|
||||
FROM organizations
|
||||
WHERE slug = 'default'
|
||||
)
|
||||
AND invited_by_user_id IS NULL;
|
||||
@@ -0,0 +1,29 @@
|
||||
INSERT INTO organization_memberships (
|
||||
id,
|
||||
organization_id,
|
||||
user_id,
|
||||
role_id,
|
||||
status,
|
||||
invited_by_user_id,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
gen_random_uuid(),
|
||||
org.id,
|
||||
u.id,
|
||||
CASE
|
||||
WHEN u.platform_role IN ('platform_admin', 'platform_recovery_admin') THEN 'org_owner'
|
||||
ELSE 'org_member'
|
||||
END,
|
||||
'active',
|
||||
NULL,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM users u
|
||||
CROSS JOIN organizations org
|
||||
LEFT JOIN organization_memberships om
|
||||
ON om.organization_id = org.id
|
||||
AND om.user_id = u.id
|
||||
WHERE org.slug = 'default'
|
||||
AND om.id IS NULL;
|
||||
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE resource_policies
|
||||
DROP CONSTRAINT IF EXISTS resource_policies_clipboard_mode_check;
|
||||
|
||||
ALTER TABLE resource_policies
|
||||
DROP COLUMN IF EXISTS clipboard_mode;
|
||||
@@ -0,0 +1,16 @@
|
||||
ALTER TABLE resource_policies
|
||||
ADD COLUMN IF NOT EXISTS clipboard_mode TEXT NOT NULL DEFAULT 'disabled';
|
||||
|
||||
UPDATE resource_policies
|
||||
SET clipboard_mode = CASE
|
||||
WHEN clipboard_enabled THEN 'bidirectional'
|
||||
ELSE 'disabled'
|
||||
END
|
||||
WHERE clipboard_mode = 'disabled';
|
||||
|
||||
ALTER TABLE resource_policies
|
||||
DROP CONSTRAINT IF EXISTS resource_policies_clipboard_mode_check;
|
||||
|
||||
ALTER TABLE resource_policies
|
||||
ADD CONSTRAINT resource_policies_clipboard_mode_check
|
||||
CHECK (clipboard_mode IN ('disabled', 'client_to_server', 'server_to_client', 'bidirectional'));
|
||||
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE resource_policies
|
||||
DROP CONSTRAINT IF EXISTS resource_policies_file_transfer_mode_check;
|
||||
|
||||
ALTER TABLE resource_policies
|
||||
DROP COLUMN IF EXISTS file_transfer_mode;
|
||||
@@ -0,0 +1,16 @@
|
||||
ALTER TABLE resource_policies
|
||||
ADD COLUMN IF NOT EXISTS file_transfer_mode TEXT NOT NULL DEFAULT 'disabled';
|
||||
|
||||
UPDATE resource_policies
|
||||
SET file_transfer_mode = 'disabled',
|
||||
file_transfer_enabled = FALSE
|
||||
WHERE file_transfer_mode IS NULL
|
||||
OR file_transfer_mode = ''
|
||||
OR file_transfer_mode = 'disabled';
|
||||
|
||||
ALTER TABLE resource_policies
|
||||
DROP CONSTRAINT IF EXISTS resource_policies_file_transfer_mode_check;
|
||||
|
||||
ALTER TABLE resource_policies
|
||||
ADD CONSTRAINT resource_policies_file_transfer_mode_check
|
||||
CHECK (file_transfer_mode IN ('disabled', 'client_to_server', 'server_to_client', 'bidirectional'));
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS resource_secrets;
|
||||
@@ -0,0 +1,27 @@
|
||||
CREATE TABLE IF NOT EXISTS resource_secrets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE CASCADE,
|
||||
secret_ref TEXT NOT NULL UNIQUE,
|
||||
protocol TEXT NOT NULL,
|
||||
version INTEGER NOT NULL DEFAULT 1,
|
||||
key_id TEXT NOT NULL,
|
||||
algorithm TEXT NOT NULL DEFAULT 'AES-256-GCM',
|
||||
nonce BYTEA NOT NULL,
|
||||
ciphertext BYTEA NOT NULL,
|
||||
payload_sha256 TEXT NOT NULL,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
rotated_at TIMESTAMPTZ,
|
||||
UNIQUE (resource_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_resource_secrets_organization_id
|
||||
ON resource_secrets(organization_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_resource_secrets_resource_id
|
||||
ON resource_secrets(resource_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_resource_secrets_secret_ref
|
||||
ON resource_secrets(secret_ref);
|
||||
@@ -0,0 +1,9 @@
|
||||
DROP TABLE IF EXISTS cluster_audit_events;
|
||||
DROP TABLE IF EXISTS node_latest_heartbeats;
|
||||
DROP TABLE IF EXISTS node_heartbeats;
|
||||
DROP TABLE IF EXISTS node_role_assignments;
|
||||
DROP TABLE IF EXISTS node_join_requests;
|
||||
DROP TABLE IF EXISTS node_join_tokens;
|
||||
DROP TABLE IF EXISTS node_identities;
|
||||
DROP TABLE IF EXISTS cluster_memberships;
|
||||
DROP TABLE IF EXISTS clusters;
|
||||
@@ -0,0 +1,186 @@
|
||||
CREATE TABLE IF NOT EXISTS clusters (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
region TEXT,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT clusters_status_check
|
||||
CHECK (status IN ('active', 'disabled', 'archived', 'degraded'))
|
||||
);
|
||||
|
||||
INSERT INTO clusters (slug, name, status, region, metadata)
|
||||
VALUES ('default', 'Default Cluster', 'active', NULL, '{"bootstrap":true}'::jsonb)
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cluster_memberships (
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
membership_status TEXT NOT NULL DEFAULT 'active',
|
||||
joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_seen_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
PRIMARY KEY (cluster_id, node_id),
|
||||
CONSTRAINT cluster_memberships_status_check
|
||||
CHECK (membership_status IN ('active', 'draining', 'disabled', 'revoked'))
|
||||
);
|
||||
|
||||
INSERT INTO cluster_memberships (cluster_id, node_id, membership_status, joined_at, last_seen_at, metadata)
|
||||
SELECT c.id, n.id, 'active', COALESCE(n.created_at, NOW()), n.last_seen_at, '{"backfilled":true}'::jsonb
|
||||
FROM clusters c
|
||||
CROSS JOIN nodes n
|
||||
WHERE c.slug = 'default'
|
||||
ON CONFLICT (cluster_id, node_id) DO NOTHING;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_identities (
|
||||
node_id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
public_key TEXT NOT NULL,
|
||||
certificate_serial TEXT,
|
||||
certificate_not_before TIMESTAMPTZ,
|
||||
certificate_not_after TIMESTAMPTZ,
|
||||
identity_status TEXT NOT NULL DEFAULT 'pending',
|
||||
rotated_at TIMESTAMPTZ,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT node_identities_status_check
|
||||
CHECK (identity_status IN ('pending', 'active', 'rotating', 'revoked'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_join_tokens (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
scope JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
max_uses INTEGER NOT NULL DEFAULT 1,
|
||||
used_count INTEGER NOT NULL DEFAULT 0,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
revoked_at TIMESTAMPTZ,
|
||||
CONSTRAINT node_join_tokens_status_check
|
||||
CHECK (status IN ('active', 'revoked', 'expired')),
|
||||
CONSTRAINT node_join_tokens_max_uses_check
|
||||
CHECK (max_uses > 0),
|
||||
CONSTRAINT node_join_tokens_used_count_check
|
||||
CHECK (used_count >= 0)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_node_join_tokens_cluster_status
|
||||
ON node_join_tokens(cluster_id, status, expires_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_join_requests (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
join_token_id UUID REFERENCES node_join_tokens(id) ON DELETE SET NULL,
|
||||
node_name TEXT NOT NULL,
|
||||
node_fingerprint TEXT NOT NULL,
|
||||
public_key TEXT NOT NULL,
|
||||
reported_capabilities JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
reported_facts JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
requested_roles JSONB NOT NULL DEFAULT '[]'::JSONB,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
reviewed_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
approved_node_id UUID REFERENCES nodes(id) ON DELETE SET NULL,
|
||||
rejection_reason TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT node_join_requests_status_check
|
||||
CHECK (status IN ('pending', 'approved', 'rejected', 'cancelled'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_node_join_requests_cluster_status
|
||||
ON node_join_requests(cluster_id, status, created_at DESC);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_node_join_requests_pending_fingerprint
|
||||
ON node_join_requests(cluster_id, node_fingerprint)
|
||||
WHERE status = 'pending';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_role_assignments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
role TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
assigned_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
revoked_at TIMESTAMPTZ,
|
||||
CONSTRAINT node_role_assignments_status_check
|
||||
CHECK (status IN ('active', 'disabled', 'revoked')),
|
||||
CONSTRAINT node_role_assignments_role_check
|
||||
CHECK (role IN (
|
||||
'entry-node',
|
||||
'relay-node',
|
||||
'core-mesh',
|
||||
'rdp-worker',
|
||||
'vnc-worker',
|
||||
'vpn-exit',
|
||||
'vpn-connector',
|
||||
'file-storage-cache',
|
||||
'update-cache',
|
||||
'video-relay'
|
||||
))
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_node_role_assignments_unique_active
|
||||
ON node_role_assignments(cluster_id, node_id, role, COALESCE(organization_id, '00000000-0000-0000-0000-000000000000'::uuid))
|
||||
WHERE status = 'active';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_node_role_assignments_cluster
|
||||
ON node_role_assignments(cluster_id, role, status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_heartbeats (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
health_status TEXT NOT NULL DEFAULT 'unknown',
|
||||
reported_version TEXT,
|
||||
capabilities JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
service_states JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT node_heartbeats_health_status_check
|
||||
CHECK (health_status IN ('unknown', 'healthy', 'warning', 'critical'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_node_heartbeats_cluster_node_observed
|
||||
ON node_heartbeats(cluster_id, node_id, observed_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_latest_heartbeats (
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
heartbeat_id UUID REFERENCES node_heartbeats(id) ON DELETE SET NULL,
|
||||
health_status TEXT NOT NULL DEFAULT 'unknown',
|
||||
reported_version TEXT,
|
||||
capabilities JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
service_states JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (cluster_id, node_id),
|
||||
CONSTRAINT node_latest_heartbeats_health_status_check
|
||||
CHECK (health_status IN ('unknown', 'healthy', 'warning', 'critical'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cluster_audit_events (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID REFERENCES clusters(id) ON DELETE SET NULL,
|
||||
actor_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
target_type TEXT NOT NULL,
|
||||
target_id TEXT,
|
||||
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cluster_audit_events_cluster_created
|
||||
ON cluster_audit_events(cluster_id, created_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cluster_audit_events_type_created
|
||||
ON cluster_audit_events(event_type, created_at DESC);
|
||||
@@ -0,0 +1,3 @@
|
||||
DROP TABLE IF EXISTS node_workload_latest_statuses;
|
||||
DROP TABLE IF EXISTS node_workload_status_reports;
|
||||
DROP TABLE IF EXISTS node_workload_desired_states;
|
||||
@@ -0,0 +1,57 @@
|
||||
CREATE TABLE IF NOT EXISTS node_workload_desired_states (
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
service_type TEXT NOT NULL,
|
||||
desired_state TEXT NOT NULL DEFAULT 'disabled',
|
||||
version TEXT,
|
||||
runtime_mode TEXT NOT NULL DEFAULT 'container',
|
||||
artifact_ref TEXT,
|
||||
config JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
environment JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
updated_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (cluster_id, node_id, service_type),
|
||||
CONSTRAINT node_workload_desired_states_desired_state_check
|
||||
CHECK (desired_state IN ('enabled', 'disabled', 'drain')),
|
||||
CONSTRAINT node_workload_desired_states_runtime_mode_check
|
||||
CHECK (runtime_mode IN ('native', 'container'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_node_workload_desired_states_cluster
|
||||
ON node_workload_desired_states(cluster_id, service_type, desired_state);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_workload_status_reports (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
service_type TEXT NOT NULL,
|
||||
reported_state TEXT NOT NULL DEFAULT 'unknown',
|
||||
runtime_mode TEXT NOT NULL DEFAULT 'container',
|
||||
version TEXT,
|
||||
status_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT node_workload_status_reports_reported_state_check
|
||||
CHECK (reported_state IN ('unknown', 'starting', 'running', 'degraded', 'stopped', 'failed', 'not_implemented')),
|
||||
CONSTRAINT node_workload_status_reports_runtime_mode_check
|
||||
CHECK (runtime_mode IN ('native', 'container'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_node_workload_status_reports_node_observed
|
||||
ON node_workload_status_reports(cluster_id, node_id, observed_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_workload_latest_statuses (
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
service_type TEXT NOT NULL,
|
||||
status_report_id UUID REFERENCES node_workload_status_reports(id) ON DELETE SET NULL,
|
||||
reported_state TEXT NOT NULL DEFAULT 'unknown',
|
||||
runtime_mode TEXT NOT NULL DEFAULT 'container',
|
||||
version TEXT,
|
||||
status_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (cluster_id, node_id, service_type),
|
||||
CONSTRAINT node_workload_latest_statuses_reported_state_check
|
||||
CHECK (reported_state IN ('unknown', 'starting', 'running', 'degraded', 'stopped', 'failed', 'not_implemented')),
|
||||
CONSTRAINT node_workload_latest_statuses_runtime_mode_check
|
||||
CHECK (runtime_mode IN ('native', 'container'))
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
DROP TABLE IF EXISTS mesh_qos_policies;
|
||||
DROP TABLE IF EXISTS mesh_route_intents;
|
||||
DROP TABLE IF EXISTS mesh_latest_links;
|
||||
DROP TABLE IF EXISTS mesh_link_observations;
|
||||
@@ -0,0 +1,94 @@
|
||||
CREATE TABLE IF NOT EXISTS mesh_link_observations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
source_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
target_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
link_status TEXT NOT NULL DEFAULT 'unknown',
|
||||
latency_ms INTEGER,
|
||||
quality_score INTEGER,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT mesh_link_observations_status_check
|
||||
CHECK (link_status IN ('unknown', 'reachable', 'degraded', 'unreachable')),
|
||||
CONSTRAINT mesh_link_observations_quality_check
|
||||
CHECK (quality_score IS NULL OR (quality_score >= 0 AND quality_score <= 100))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_mesh_link_observations_cluster_observed
|
||||
ON mesh_link_observations(cluster_id, observed_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mesh_latest_links (
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
source_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
target_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
observation_id UUID REFERENCES mesh_link_observations(id) ON DELETE SET NULL,
|
||||
link_status TEXT NOT NULL DEFAULT 'unknown',
|
||||
latency_ms INTEGER,
|
||||
quality_score INTEGER,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (cluster_id, source_node_id, target_node_id),
|
||||
CONSTRAINT mesh_latest_links_status_check
|
||||
CHECK (link_status IN ('unknown', 'reachable', 'degraded', 'unreachable')),
|
||||
CONSTRAINT mesh_latest_links_quality_check
|
||||
CHECK (quality_score IS NULL OR (quality_score >= 0 AND quality_score <= 100))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mesh_route_intents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
source_selector JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
destination_selector JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
service_class TEXT NOT NULL,
|
||||
priority INTEGER NOT NULL DEFAULT 100,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT mesh_route_intents_service_class_check
|
||||
CHECK (service_class IN ('input', 'control', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry')),
|
||||
CONSTRAINT mesh_route_intents_status_check
|
||||
CHECK (status IN ('active', 'disabled'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_mesh_route_intents_cluster_class
|
||||
ON mesh_route_intents(cluster_id, service_class, status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mesh_qos_policies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
service_class TEXT NOT NULL,
|
||||
priority INTEGER NOT NULL,
|
||||
reliability_mode TEXT NOT NULL,
|
||||
drop_policy TEXT NOT NULL,
|
||||
bandwidth_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (cluster_id, service_class),
|
||||
CONSTRAINT mesh_qos_policies_service_class_check
|
||||
CHECK (service_class IN ('input', 'control', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry')),
|
||||
CONSTRAINT mesh_qos_policies_reliability_check
|
||||
CHECK (reliability_mode IN ('reliable', 'droppable', 'adaptive')),
|
||||
CONSTRAINT mesh_qos_policies_drop_policy_check
|
||||
CHECK (drop_policy IN ('never', 'latest_only', 'adaptive'))
|
||||
);
|
||||
|
||||
INSERT INTO mesh_qos_policies (
|
||||
cluster_id, service_class, priority, reliability_mode, drop_policy, bandwidth_policy, metadata
|
||||
)
|
||||
SELECT c.id, defaults.service_class, defaults.priority, defaults.reliability_mode,
|
||||
defaults.drop_policy, '{}'::jsonb, '{"default":true}'::jsonb
|
||||
FROM clusters c
|
||||
CROSS JOIN (
|
||||
VALUES
|
||||
('input', 10, 'reliable', 'never'),
|
||||
('control', 20, 'reliable', 'never'),
|
||||
('clipboard', 40, 'reliable', 'never'),
|
||||
('render', 60, 'droppable', 'latest_only'),
|
||||
('file_transfer', 80, 'reliable', 'never'),
|
||||
('telemetry', 120, 'adaptive', 'adaptive'),
|
||||
('vpn_packets', 160, 'adaptive', 'adaptive')
|
||||
) AS defaults(service_class, priority, reliability_mode, drop_policy)
|
||||
ON CONFLICT (cluster_id, service_class) DO NOTHING;
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP VIEW IF EXISTS cluster_admin_summaries;
|
||||
DROP TABLE IF EXISTS cluster_authority_states;
|
||||
@@ -0,0 +1,40 @@
|
||||
CREATE TABLE IF NOT EXISTS cluster_authority_states (
|
||||
cluster_id UUID PRIMARY KEY REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
authority_state TEXT NOT NULL DEFAULT 'authoritative',
|
||||
mutation_mode TEXT NOT NULL DEFAULT 'normal',
|
||||
term BIGINT NOT NULL DEFAULT 1,
|
||||
notes TEXT,
|
||||
updated_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT cluster_authority_states_authority_check
|
||||
CHECK (authority_state IN ('authoritative', 'minority', 'isolated', 'recovery')),
|
||||
CONSTRAINT cluster_authority_states_mutation_check
|
||||
CHECK (mutation_mode IN ('normal', 'read_only', 'recovery_override'))
|
||||
);
|
||||
|
||||
INSERT INTO cluster_authority_states (cluster_id, authority_state, mutation_mode, term, notes)
|
||||
SELECT id, 'authoritative', 'normal', 1, 'default authority state'
|
||||
FROM clusters
|
||||
ON CONFLICT (cluster_id) DO NOTHING;
|
||||
|
||||
CREATE OR REPLACE VIEW cluster_admin_summaries AS
|
||||
SELECT
|
||||
c.id AS cluster_id,
|
||||
c.slug,
|
||||
c.name,
|
||||
c.status,
|
||||
c.region,
|
||||
COALESCE(cas.authority_state, 'authoritative') AS authority_state,
|
||||
COALESCE(cas.mutation_mode, 'normal') AS mutation_mode,
|
||||
COUNT(DISTINCT cm.node_id) AS node_count,
|
||||
COUNT(DISTINCT CASE WHEN n.health_status = 'healthy' THEN n.id END) AS healthy_node_count,
|
||||
COUNT(DISTINCT CASE WHEN njr.status = 'pending' THEN njr.id END) AS pending_join_count,
|
||||
COUNT(DISTINCT nra.id) AS active_role_assignment_count,
|
||||
MAX(n.last_seen_at) AS last_node_seen_at
|
||||
FROM clusters c
|
||||
LEFT JOIN cluster_authority_states cas ON cas.cluster_id = c.id
|
||||
LEFT JOIN cluster_memberships cm ON cm.cluster_id = c.id
|
||||
LEFT JOIN nodes n ON n.id = cm.node_id
|
||||
LEFT JOIN node_join_requests njr ON njr.cluster_id = c.id
|
||||
LEFT JOIN node_role_assignments nra ON nra.cluster_id = c.id AND nra.status = 'active'
|
||||
GROUP BY c.id, c.slug, c.name, c.status, c.region, cas.authority_state, cas.mutation_mode;
|
||||
@@ -0,0 +1,4 @@
|
||||
DROP TABLE IF EXISTS vpn_connection_leases;
|
||||
DROP TABLE IF EXISTS vpn_connection_route_policies;
|
||||
DROP TABLE IF EXISTS vpn_connection_allowed_nodes;
|
||||
DROP TABLE IF EXISTS vpn_connections;
|
||||
@@ -0,0 +1,125 @@
|
||||
CREATE TABLE IF NOT EXISTS vpn_connections (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
target_endpoint JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
protocol_family TEXT NOT NULL DEFAULT 'generic',
|
||||
credential_ref TEXT,
|
||||
mode TEXT NOT NULL DEFAULT 'single_active',
|
||||
desired_state TEXT NOT NULL DEFAULT 'disabled',
|
||||
allowed_node_policy JSONB NOT NULL DEFAULT '{"mode":"explicit","node_ids":[]}'::JSONB,
|
||||
routing_usage JSONB NOT NULL DEFAULT '[]'::JSONB,
|
||||
route_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
qos_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
placement_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
status TEXT NOT NULL DEFAULT 'disabled',
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
updated_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT vpn_connections_mode_check
|
||||
CHECK (mode IN ('single_active')),
|
||||
CONSTRAINT vpn_connections_desired_state_check
|
||||
CHECK (desired_state IN ('enabled', 'disabled')),
|
||||
CONSTRAINT vpn_connections_status_check
|
||||
CHECK (status IN ('disabled', 'enabled', 'connecting', 'active', 'degraded', 'failed')),
|
||||
CONSTRAINT vpn_connections_protocol_family_check
|
||||
CHECK (protocol_family IN ('generic', 'wireguard', 'ipsec', 'openvpn'))
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_vpn_connections_cluster_org_name
|
||||
ON vpn_connections(cluster_id, organization_id, lower(name));
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vpn_connections_cluster_org_state
|
||||
ON vpn_connections(cluster_id, organization_id, desired_state, status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vpn_connection_allowed_nodes (
|
||||
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
role_preference TEXT NOT NULL DEFAULT 'candidate',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (vpn_connection_id, node_id),
|
||||
CONSTRAINT vpn_connection_allowed_nodes_membership_fk
|
||||
FOREIGN KEY (cluster_id, node_id)
|
||||
REFERENCES cluster_memberships(cluster_id, node_id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vpn_connection_allowed_nodes_preference_check
|
||||
CHECK (role_preference IN ('candidate', 'standby', 'preferred')),
|
||||
CONSTRAINT vpn_connection_allowed_nodes_status_check
|
||||
CHECK (status IN ('active', 'disabled'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vpn_connection_allowed_nodes_cluster_node
|
||||
ON vpn_connection_allowed_nodes(cluster_id, node_id, status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vpn_connection_route_policies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
route_type TEXT NOT NULL,
|
||||
destination TEXT NOT NULL,
|
||||
action TEXT NOT NULL DEFAULT 'allow',
|
||||
service_type TEXT,
|
||||
priority INTEGER NOT NULL DEFAULT 100,
|
||||
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT vpn_connection_route_policies_route_type_check
|
||||
CHECK (route_type IN ('cidr', 'dns_suffix', 'service', 'resource')),
|
||||
CONSTRAINT vpn_connection_route_policies_action_check
|
||||
CHECK (action IN ('allow', 'deny')),
|
||||
CONSTRAINT vpn_connection_route_policies_status_check
|
||||
CHECK (status IN ('active', 'disabled'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vpn_connection_route_policies_connection
|
||||
ON vpn_connection_route_policies(vpn_connection_id, status, priority);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vpn_connection_route_policies_cluster_org
|
||||
ON vpn_connection_route_policies(cluster_id, organization_id, route_type, status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vpn_connection_leases (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
owner_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
lease_generation BIGINT NOT NULL,
|
||||
fencing_token TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
acquired_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
renewed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
released_at TIMESTAMPTZ,
|
||||
fenced_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
CONSTRAINT vpn_connection_leases_membership_fk
|
||||
FOREIGN KEY (cluster_id, owner_node_id)
|
||||
REFERENCES cluster_memberships(cluster_id, node_id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vpn_connection_leases_status_check
|
||||
CHECK (status IN ('active', 'released', 'expired', 'fenced')),
|
||||
CONSTRAINT vpn_connection_leases_expiry_check
|
||||
CHECK (expires_at > acquired_at)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_vpn_connection_leases_generation
|
||||
ON vpn_connection_leases(vpn_connection_id, lease_generation);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_vpn_connection_leases_single_active
|
||||
ON vpn_connection_leases(vpn_connection_id)
|
||||
WHERE status = 'active';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vpn_connection_leases_owner
|
||||
ON vpn_connection_leases(cluster_id, owner_node_id, status, expires_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vpn_connection_leases_connection_time
|
||||
ON vpn_connection_leases(vpn_connection_id, acquired_at DESC);
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP TABLE IF EXISTS vpn_connection_assignment_latest_statuses;
|
||||
DROP TABLE IF EXISTS vpn_connection_assignment_status_reports;
|
||||
@@ -0,0 +1,42 @@
|
||||
CREATE TABLE IF NOT EXISTS vpn_connection_assignment_status_reports (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
observed_status TEXT NOT NULL DEFAULT 'unknown',
|
||||
status_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT vpn_connection_assignment_status_membership_fk
|
||||
FOREIGN KEY (cluster_id, node_id)
|
||||
REFERENCES cluster_memberships(cluster_id, node_id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vpn_connection_assignment_status_check
|
||||
CHECK (observed_status IN ('not_started', 'assigned', 'lease_required', 'blocked', 'unknown'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vpn_assignment_status_reports_node
|
||||
ON vpn_connection_assignment_status_reports(cluster_id, node_id, observed_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vpn_assignment_status_reports_connection
|
||||
ON vpn_connection_assignment_status_reports(vpn_connection_id, observed_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vpn_connection_assignment_latest_statuses (
|
||||
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
report_id UUID NOT NULL REFERENCES vpn_connection_assignment_status_reports(id) ON DELETE CASCADE,
|
||||
observed_status TEXT NOT NULL,
|
||||
status_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
observed_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (vpn_connection_id, node_id),
|
||||
CONSTRAINT vpn_connection_assignment_latest_membership_fk
|
||||
FOREIGN KEY (cluster_id, node_id)
|
||||
REFERENCES cluster_memberships(cluster_id, node_id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vpn_connection_assignment_latest_status_check
|
||||
CHECK (observed_status IN ('not_started', 'assigned', 'lease_required', 'blocked', 'unknown'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vpn_assignment_latest_node
|
||||
ON vpn_connection_assignment_latest_statuses(cluster_id, node_id, updated_at DESC);
|
||||
@@ -0,0 +1,5 @@
|
||||
DROP INDEX IF EXISTS idx_node_telemetry_cluster_node_observed;
|
||||
DROP TABLE IF EXISTS node_telemetry_observations;
|
||||
|
||||
DROP INDEX IF EXISTS idx_fabric_testing_flags_unique_scope;
|
||||
DROP TABLE IF EXISTS fabric_testing_flags;
|
||||
@@ -0,0 +1,43 @@
|
||||
CREATE TABLE IF NOT EXISTS fabric_testing_flags (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
scope_type TEXT NOT NULL,
|
||||
scope_id UUID,
|
||||
cluster_id UUID REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
telemetry_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
synthetic_links_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
history_retention_hours INTEGER NOT NULL DEFAULT 24,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
updated_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT fabric_testing_flags_scope_check
|
||||
CHECK (scope_type IN ('platform', 'organization', 'node')),
|
||||
CONSTRAINT fabric_testing_flags_retention_check
|
||||
CHECK (history_retention_hours BETWEEN 1 AND 720)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_fabric_testing_flags_unique_scope
|
||||
ON fabric_testing_flags (
|
||||
scope_type,
|
||||
COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
||||
COALESCE(cluster_id, '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_telemetry_observations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
cpu_percent DOUBLE PRECISION,
|
||||
memory_used_bytes BIGINT,
|
||||
memory_total_bytes BIGINT,
|
||||
disk_used_bytes BIGINT,
|
||||
disk_total_bytes BIGINT,
|
||||
network_rx_bytes BIGINT,
|
||||
network_tx_bytes BIGINT,
|
||||
process_count INTEGER,
|
||||
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_node_telemetry_cluster_node_observed
|
||||
ON node_telemetry_observations(cluster_id, node_id, observed_at DESC);
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP TABLE IF EXISTS cluster_node_group_memberships;
|
||||
DROP TABLE IF EXISTS cluster_node_groups;
|
||||
@@ -0,0 +1,40 @@
|
||||
CREATE TABLE IF NOT EXISTS cluster_node_groups (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
parent_group_id UUID REFERENCES cluster_node_groups(id) ON DELETE SET NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT cluster_node_groups_name_check CHECK (length(trim(name)) > 0),
|
||||
CONSTRAINT cluster_node_groups_not_self_parent CHECK (parent_group_id IS NULL OR parent_group_id <> id),
|
||||
UNIQUE (cluster_id, id)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_cluster_node_groups_unique_sibling_name
|
||||
ON cluster_node_groups(cluster_id, COALESCE(parent_group_id, '00000000-0000-0000-0000-000000000000'::uuid), lower(name));
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cluster_node_groups_parent
|
||||
ON cluster_node_groups(cluster_id, parent_group_id, sort_order, name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cluster_node_group_memberships (
|
||||
cluster_id UUID NOT NULL,
|
||||
node_id UUID NOT NULL,
|
||||
group_id UUID NOT NULL,
|
||||
assigned_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
PRIMARY KEY (cluster_id, node_id),
|
||||
FOREIGN KEY (cluster_id, node_id)
|
||||
REFERENCES cluster_memberships(cluster_id, node_id)
|
||||
ON DELETE CASCADE,
|
||||
FOREIGN KEY (cluster_id, group_id)
|
||||
REFERENCES cluster_node_groups(cluster_id, id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cluster_node_group_memberships_group
|
||||
ON cluster_node_group_memberships(cluster_id, group_id);
|
||||
@@ -0,0 +1,4 @@
|
||||
DROP TABLE IF EXISTS fabric_egress_pool_nodes;
|
||||
DROP TABLE IF EXISTS fabric_egress_pools;
|
||||
DROP TABLE IF EXISTS fabric_entry_point_nodes;
|
||||
DROP TABLE IF EXISTS fabric_entry_points;
|
||||
@@ -0,0 +1,83 @@
|
||||
CREATE TABLE IF NOT EXISTS fabric_entry_points (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
endpoint_type TEXT NOT NULL DEFAULT 'client_access',
|
||||
public_endpoint TEXT,
|
||||
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT fabric_entry_points_status_check
|
||||
CHECK (status IN ('active', 'disabled', 'maintenance')),
|
||||
CONSTRAINT fabric_entry_points_type_check
|
||||
CHECK (endpoint_type IN ('client_access', 'admin', 'api', 'other'))
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_fabric_entry_points_cluster_name
|
||||
ON fabric_entry_points(cluster_id, LOWER(name));
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_fabric_entry_points_cluster_status
|
||||
ON fabric_entry_points(cluster_id, status, endpoint_type);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fabric_entry_point_nodes (
|
||||
entry_point_id UUID NOT NULL REFERENCES fabric_entry_points(id) ON DELETE CASCADE,
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
priority INTEGER NOT NULL DEFAULT 100,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
added_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (entry_point_id, node_id),
|
||||
CONSTRAINT fabric_entry_point_nodes_status_check
|
||||
CHECK (status IN ('active', 'disabled', 'maintenance')),
|
||||
CONSTRAINT fabric_entry_point_nodes_priority_check
|
||||
CHECK (priority >= 0)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_fabric_entry_point_nodes_cluster_node
|
||||
ON fabric_entry_point_nodes(cluster_id, node_id, status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fabric_egress_pools (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
description TEXT,
|
||||
route_scope JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT fabric_egress_pools_status_check
|
||||
CHECK (status IN ('active', 'disabled', 'maintenance'))
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_fabric_egress_pools_cluster_name
|
||||
ON fabric_egress_pools(cluster_id, LOWER(name));
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_fabric_egress_pools_cluster_status
|
||||
ON fabric_egress_pools(cluster_id, status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fabric_egress_pool_nodes (
|
||||
egress_pool_id UUID NOT NULL REFERENCES fabric_egress_pools(id) ON DELETE CASCADE,
|
||||
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
priority INTEGER NOT NULL DEFAULT 100,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
added_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (egress_pool_id, node_id),
|
||||
CONSTRAINT fabric_egress_pool_nodes_status_check
|
||||
CHECK (status IN ('active', 'disabled', 'maintenance')),
|
||||
CONSTRAINT fabric_egress_pool_nodes_priority_check
|
||||
CHECK (priority >= 0)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_fabric_egress_pool_nodes_cluster_node
|
||||
ON fabric_egress_pool_nodes(cluster_id, node_id, status);
|
||||
@@ -0,0 +1,23 @@
|
||||
WITH ranked AS (
|
||||
SELECT ctid,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY cluster_id, source_node_id, target_node_id
|
||||
ORDER BY observed_at DESC
|
||||
) AS rn
|
||||
FROM mesh_latest_links
|
||||
)
|
||||
DELETE FROM mesh_latest_links
|
||||
USING ranked
|
||||
WHERE mesh_latest_links.ctid = ranked.ctid
|
||||
AND ranked.rn > 1;
|
||||
|
||||
DROP INDEX IF EXISTS idx_mesh_latest_links_cluster_observed;
|
||||
|
||||
ALTER TABLE mesh_latest_links
|
||||
DROP CONSTRAINT IF EXISTS mesh_latest_links_pkey;
|
||||
|
||||
ALTER TABLE mesh_latest_links
|
||||
ADD PRIMARY KEY (cluster_id, source_node_id, target_node_id);
|
||||
|
||||
ALTER TABLE mesh_latest_links
|
||||
DROP COLUMN IF EXISTS observation_key;
|
||||
@@ -0,0 +1,11 @@
|
||||
ALTER TABLE mesh_latest_links
|
||||
ADD COLUMN IF NOT EXISTS observation_key TEXT NOT NULL DEFAULT 'default';
|
||||
|
||||
ALTER TABLE mesh_latest_links
|
||||
DROP CONSTRAINT IF EXISTS mesh_latest_links_pkey;
|
||||
|
||||
ALTER TABLE mesh_latest_links
|
||||
ADD PRIMARY KEY (cluster_id, source_node_id, target_node_id, observation_key);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_mesh_latest_links_cluster_observed
|
||||
ON mesh_latest_links(cluster_id, observed_at DESC);
|
||||
@@ -0,0 +1,4 @@
|
||||
DROP INDEX IF EXISTS idx_platform_role_grants_unique_install_role;
|
||||
DROP INDEX IF EXISTS idx_platform_role_grants_user_active;
|
||||
DROP TABLE IF EXISTS platform_role_grants;
|
||||
DROP TABLE IF EXISTS installation_authority;
|
||||
@@ -0,0 +1,39 @@
|
||||
CREATE TABLE IF NOT EXISTS installation_authority (
|
||||
id SMALLINT PRIMARY KEY DEFAULT 1,
|
||||
install_id TEXT NOT NULL,
|
||||
authority_state TEXT NOT NULL DEFAULT 'active',
|
||||
product_root_key_fingerprint TEXT NOT NULL DEFAULT '',
|
||||
activation_payload JSONB NOT NULL,
|
||||
activation_signature TEXT NOT NULL,
|
||||
bootstrapped_owner_email TEXT NOT NULL,
|
||||
bootstrapped_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT installation_authority_singleton_check CHECK (id = 1),
|
||||
CONSTRAINT installation_authority_state_check CHECK (authority_state IN ('active', 'recovery_required', 'locked'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_role_grants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
role TEXT NOT NULL,
|
||||
install_id TEXT NOT NULL,
|
||||
grant_payload JSONB NOT NULL,
|
||||
grant_signature TEXT NOT NULL,
|
||||
grant_source TEXT NOT NULL DEFAULT 'installation_activation',
|
||||
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
CONSTRAINT platform_role_grants_role_check
|
||||
CHECK (role IN ('platform_admin', 'platform_recovery_admin')),
|
||||
CONSTRAINT platform_role_grants_source_check
|
||||
CHECK (grant_source IN ('installation_activation', 'recovery_manifest', 'dev_insecure'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_platform_role_grants_user_active
|
||||
ON platform_role_grants(user_id, role, revoked_at, expires_at);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_platform_role_grants_unique_install_role
|
||||
ON platform_role_grants(user_id, role, install_id)
|
||||
WHERE revoked_at IS NULL;
|
||||
@@ -0,0 +1,33 @@
|
||||
DROP VIEW IF EXISTS cluster_admin_summaries;
|
||||
|
||||
CREATE VIEW cluster_admin_summaries AS
|
||||
SELECT
|
||||
c.id AS cluster_id,
|
||||
c.slug,
|
||||
c.name,
|
||||
c.status,
|
||||
c.region,
|
||||
COALESCE(cas.authority_state, 'authoritative') AS authority_state,
|
||||
COALESCE(cas.mutation_mode, 'normal') AS mutation_mode,
|
||||
COUNT(DISTINCT cm.node_id) AS node_count,
|
||||
COUNT(DISTINCT CASE WHEN n.health_status = 'healthy' THEN n.id END) AS healthy_node_count,
|
||||
COUNT(DISTINCT CASE WHEN njr.status = 'pending' THEN njr.id END) AS pending_join_count,
|
||||
COUNT(DISTINCT nra.id) AS active_role_assignment_count,
|
||||
MAX(n.last_seen_at) AS last_node_seen_at
|
||||
FROM clusters c
|
||||
LEFT JOIN cluster_authority_states cas ON cas.cluster_id = c.id
|
||||
LEFT JOIN cluster_memberships cm ON cm.cluster_id = c.id
|
||||
LEFT JOIN nodes n ON n.id = cm.node_id
|
||||
LEFT JOIN node_join_requests njr ON njr.cluster_id = c.id
|
||||
LEFT JOIN node_role_assignments nra ON nra.cluster_id = c.id AND nra.status = 'active'
|
||||
GROUP BY c.id, c.slug, c.name, c.status, c.region, cas.authority_state, cas.mutation_mode;
|
||||
|
||||
ALTER TABLE node_join_requests
|
||||
DROP COLUMN IF EXISTS approval_signature,
|
||||
DROP COLUMN IF EXISTS approval_payload;
|
||||
|
||||
ALTER TABLE node_join_tokens
|
||||
DROP COLUMN IF EXISTS authority_signature,
|
||||
DROP COLUMN IF EXISTS authority_payload;
|
||||
|
||||
DROP TABLE IF EXISTS cluster_authorities;
|
||||
@@ -0,0 +1,54 @@
|
||||
CREATE TABLE IF NOT EXISTS cluster_authorities (
|
||||
cluster_id UUID PRIMARY KEY REFERENCES clusters(id) ON DELETE CASCADE,
|
||||
authority_state TEXT NOT NULL DEFAULT 'active',
|
||||
key_algorithm TEXT NOT NULL DEFAULT 'ed25519',
|
||||
public_key TEXT NOT NULL,
|
||||
public_key_fingerprint TEXT NOT NULL,
|
||||
private_key TEXT NOT NULL,
|
||||
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
CONSTRAINT cluster_authorities_state_check
|
||||
CHECK (authority_state IN ('active', 'rotating', 'revoked', 'recovery_required')),
|
||||
CONSTRAINT cluster_authorities_algorithm_check
|
||||
CHECK (key_algorithm = 'ed25519')
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_cluster_authorities_fingerprint
|
||||
ON cluster_authorities(public_key_fingerprint);
|
||||
|
||||
ALTER TABLE node_join_tokens
|
||||
ADD COLUMN IF NOT EXISTS authority_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
ADD COLUMN IF NOT EXISTS authority_signature JSONB NOT NULL DEFAULT '{}'::JSONB;
|
||||
|
||||
ALTER TABLE node_join_requests
|
||||
ADD COLUMN IF NOT EXISTS approval_payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
ADD COLUMN IF NOT EXISTS approval_signature JSONB NOT NULL DEFAULT '{}'::JSONB;
|
||||
|
||||
DROP VIEW IF EXISTS cluster_admin_summaries;
|
||||
|
||||
CREATE VIEW cluster_admin_summaries AS
|
||||
SELECT
|
||||
c.id AS cluster_id,
|
||||
c.slug,
|
||||
c.name,
|
||||
c.status,
|
||||
c.region,
|
||||
COALESCE(cas.authority_state, 'authoritative') AS authority_state,
|
||||
COALESCE(cas.mutation_mode, 'normal') AS mutation_mode,
|
||||
ca.key_algorithm AS cluster_key_algorithm,
|
||||
ca.public_key_fingerprint AS cluster_key_fingerprint,
|
||||
COUNT(DISTINCT cm.node_id) AS node_count,
|
||||
COUNT(DISTINCT CASE WHEN n.health_status = 'healthy' THEN n.id END) AS healthy_node_count,
|
||||
COUNT(DISTINCT CASE WHEN njr.status = 'pending' THEN njr.id END) AS pending_join_count,
|
||||
COUNT(DISTINCT nra.id) AS active_role_assignment_count,
|
||||
MAX(n.last_seen_at) AS last_node_seen_at
|
||||
FROM clusters c
|
||||
LEFT JOIN cluster_authority_states cas ON cas.cluster_id = c.id
|
||||
LEFT JOIN cluster_authorities ca ON ca.cluster_id = c.id
|
||||
LEFT JOIN cluster_memberships cm ON cm.cluster_id = c.id
|
||||
LEFT JOIN nodes n ON n.id = cm.node_id
|
||||
LEFT JOIN node_join_requests njr ON njr.cluster_id = c.id
|
||||
LEFT JOIN node_role_assignments nra ON nra.cluster_id = c.id AND nra.status = 'active'
|
||||
GROUP BY c.id, c.slug, c.name, c.status, c.region, cas.authority_state, cas.mutation_mode, ca.key_algorithm, ca.public_key_fingerprint;
|
||||
@@ -0,0 +1,21 @@
|
||||
UPDATE mesh_route_intents
|
||||
SET service_class = 'control',
|
||||
updated_at = NOW()
|
||||
WHERE service_class = 'synthetic';
|
||||
|
||||
DELETE FROM mesh_qos_policies
|
||||
WHERE service_class = 'synthetic';
|
||||
|
||||
ALTER TABLE mesh_route_intents
|
||||
DROP CONSTRAINT IF EXISTS mesh_route_intents_service_class_check;
|
||||
|
||||
ALTER TABLE mesh_route_intents
|
||||
ADD CONSTRAINT mesh_route_intents_service_class_check
|
||||
CHECK (service_class IN ('input', 'control', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry'));
|
||||
|
||||
ALTER TABLE mesh_qos_policies
|
||||
DROP CONSTRAINT IF EXISTS mesh_qos_policies_service_class_check;
|
||||
|
||||
ALTER TABLE mesh_qos_policies
|
||||
ADD CONSTRAINT mesh_qos_policies_service_class_check
|
||||
CHECK (service_class IN ('input', 'control', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry'));
|
||||
@@ -0,0 +1,21 @@
|
||||
ALTER TABLE mesh_route_intents
|
||||
DROP CONSTRAINT IF EXISTS mesh_route_intents_service_class_check;
|
||||
|
||||
ALTER TABLE mesh_route_intents
|
||||
ADD CONSTRAINT mesh_route_intents_service_class_check
|
||||
CHECK (service_class IN ('input', 'control', 'synthetic', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry'));
|
||||
|
||||
ALTER TABLE mesh_qos_policies
|
||||
DROP CONSTRAINT IF EXISTS mesh_qos_policies_service_class_check;
|
||||
|
||||
ALTER TABLE mesh_qos_policies
|
||||
ADD CONSTRAINT mesh_qos_policies_service_class_check
|
||||
CHECK (service_class IN ('input', 'control', 'synthetic', 'render', 'clipboard', 'file_transfer', 'vpn_packets', 'telemetry'));
|
||||
|
||||
INSERT INTO mesh_qos_policies (
|
||||
cluster_id, service_class, priority, reliability_mode, drop_policy, bandwidth_policy, metadata
|
||||
)
|
||||
SELECT c.id, 'synthetic', 30, 'reliable', 'never', '{}'::jsonb,
|
||||
'{"default":true,"control_plane_only":true}'::jsonb
|
||||
FROM clusters c
|
||||
ON CONFLICT (cluster_id, service_class) DO NOTHING;
|
||||
Reference in New Issue
Block a user