CREATE OR REPLACE FUNCTION trigger_set_timestamp() RETURNS TRIGGER LANGUAGE plpgsql AS $func$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END $func$; -- tbl artifact_versions CREATE TABLE IF NOT EXISTS artifact_versions ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, image TEXT NOT NULL, tag TEXT, sha256_digest TEXT NOT NULL CHECK (length(sha256_digest) = 64), UNIQUE (image, sha256_digest), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE OR REPLACE TRIGGER artifact_versions_timestamp BEFORE UPDATE ON artifact_versions FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- fn_artifact_version_id_for gets the unique id for a new or existing artifact_version. if two callers try to insert the same record -- at the same time, the conflict is resolved by retrying, ie the second caller will find the existing id instead CREATE OR REPLACE FUNCTION fn_artifact_version_id_for(_artifact UUID, _tag TEXT , _sha256_digest TEXT, OUT _artifact_version_id UUID) LANGUAGE plpgsql AS $func$ BEGIN LOOP SELECT id FROM artifact_versions WHERE artifact = _artifact INTO _artifact_version_id FOR SHARE; -- lock row until end of tx EXIT WHEN FOUND; INSERT INTO artifact_versions (artifact, tag, sha256_digest) VALUES (_artifact, _tag, _sha256_digest) ON CONFLICT (artifact, sha256_digest) DO UPDATE SET id = excluded.id WHERE false RETURNING id INTO _artifact_version_id; EXIT WHEN FOUND; END LOOP; END $func$; -- tbl artifacts CREATE TABLE IF NOT EXISTS artifacts ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, project TEXT, repository TEXT, artifact_version UUID NOT NULL REFERENCES artifact_versions(id) ON DELETE RESTRICT, -- prevent delete of artifact_version if it is still present in a registry UNIQUE (project, repository, artifact_version), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE OR REPLACE TRIGGER artifacts_timestamp BEFORE UPDATE ON artifacts FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- fn_artifact_id_for gets the unique id for a new or existing artifact. if two callers try to insert the same record -- at the same time, the conflict is resolved by retrying, ie the second caller will find the existing id instead CREATE OR REPLACE FUNCTION fn_artifact_id_for(_image TEXT, OUT _artifact_id UUID) LANGUAGE plpgsql AS $func$ BEGIN LOOP SELECT id FROM artifacts WHERE image = _image INTO _artifact_id FOR SHARE; -- lock row until end of tx EXIT WHEN FOUND; INSERT INTO artifacts (image) VALUES (_image) ON CONFLICT (image) DO UPDATE SET id = excluded.id WHERE false RETURNING id INTO _artifact_id; EXIT WHEN FOUND; END LOOP; END $func$; -- tbl clusters CREATE TABLE IF NOT EXISTS clusters ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, name TEXT, endpoint TEXT, version_major INT NOT NULL, version_minor INT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE OR REPLACE TRIGGER clusters_timestamp BEFORE UPDATE ON clusters FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl artifact_installations CREATE TABLE IF NOT EXISTS artifact_installations ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, cluster UUID NOT NULL REFERENCES clusters(id), artifact_version UUID NOT NULL REFERENCES artifact_versions(id), UNIQUE (cluster, artifact_version), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE OR REPLACE TRIGGER artifact_installations_timestamp BEFORE UPDATE ON artifact_installations FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl config_sources CREATE TABLE IF NOT EXISTS config_sources ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, name TEXT, url TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE OR REPLACE TRIGGER config_sources_timestamp BEFORE UPDATE ON config_sources FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl synced_clusters CREATE TABLE IF NOT EXISTS synced_clusters ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, cluster UUID NOT NULL REFERENCES clusters(id), config_source UUID NOT NULL REFERENCES config_sources(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE OR REPLACE TRIGGER synced_clusters_timestamp BEFORE UPDATE ON synced_clusters FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl observed_states CREATE TABLE IF NOT EXISTS observed_states ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), cluster UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE, artifact_version UUID NOT NULL REFERENCES artifact_versions(id) ON DELETE CASCADE, UNIQUE (cluster, artifact_version), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE OR REPLACE TRIGGER observed_states_timestamp BEFORE UPDATE ON observed_states FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl desired_states CREATE TABLE IF NOT EXISTS desired_states ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE OR REPLACE TRIGGER desired_states_timestamp BEFORE UPDATE ON desired_states FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl watched_resources CREATE TABLE IF NOT EXISTS watched_resources ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, cluster UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE, api_version TEXT NOT NULL, kind TEXT NOT NULL, resource JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE UNIQUE INDEX IF NOT EXISTS idx_uid ON watched_resources ((resource ['metadata'] ['uid'])); CREATE INDEX IF NOT EXISTS idx_gvknn ON watched_resources ( cluster, (resource ['apiVersion']), (resource ['kind']), (resource ['metadata'] ['namespace']), (resource ['metadata'] ['name']) ); CREATE INDEX IF NOT EXISTS idx_gin ON watched_resources USING GIN (resource); CREATE OR REPLACE TRIGGER watched_resources_timestamp BEFORE UPDATE ON watched_resources FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl remote_commands CREATE TABLE IF NOT EXISTS remote_commands ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, cluster UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE, command_type TEXT NOT NULL, command_args TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_cluster ON remote_commands (cluster); CREATE OR REPLACE TRIGGER remote_commands_timestamp BEFORE UPDATE ON remote_commands FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- TODO(dk185217): experimental. this is essentially just the complete history, append only. it surely wont -- scale, but should provide interesting data early -- tbl watched_resource_observations CREATE TABLE IF NOT EXISTS watched_resource_observations ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, cluster UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE, api_version TEXT NOT NULL, kind TEXT NOT NULL, resource JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_uid ON watched_resource_observations ((resource ['metadata'] ['uid'])); CREATE INDEX IF NOT EXISTS idx_gvknn ON watched_resource_observations ( cluster, (resource ['apiVersion']), (resource ['kind']), (resource ['metadata'] ['namespace']), (resource ['metadata'] ['name']) ); CREATE OR REPLACE TRIGGER watched_resource_observations_timestamp BEFORE UPDATE ON watched_resource_observations FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl cluster_labels CREATE TABLE IF NOT EXISTS cluster_labels ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, cluster UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE, key TEXT NOT NULL, value TEXT NOT NULL, UNIQUE (cluster, key), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_cluster_id_inc_key_value ON cluster_labels (cluster) INCLUDE (key, value); CREATE INDEX IF NOT EXISTS idx_key_value ON cluster_labels (key, value); CREATE OR REPLACE TRIGGER cluster_labels_timestamp BEFORE UPDATE ON cluster_labels FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl artifact_labels CREATE TABLE IF NOT EXISTS artifact_labels ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, artifact UUID NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE, key TEXT NOT NULL, value TEXT NOT NULL, UNIQUE (artifact, key), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_artifact_id_inc_key_value ON artifact_labels (artifact) INCLUDE (key, value); CREATE OR REPLACE TRIGGER artifact_labels_timestamp BEFORE UPDATE ON artifact_labels FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl kinform_pubsub_subscriptions -- TODO(dk18517): this table is currently a temp. cache, drop at startup, but not in future DROP TABLE IF EXISTS kinform_pubsub_subscriptions; CREATE TABLE IF NOT EXISTS kinform_pubsub_subscriptions ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, subscription TEXT NOT NULL, -- TODO(dk18517): ref. int. via fk on projects project TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE UNIQUE INDEX IF NOT EXISTS idx_uid ON kinform_pubsub_subscriptions (subscription, project); CREATE OR REPLACE TRIGGER kinform_pubsub_subscriptions_timestamp BEFORE UPDATE ON kinform_pubsub_subscriptions FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); -- tbl kinform_sessions CREATE TABLE IF NOT EXISTS kinform_sessions ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, cluster UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE, session UUID NOT NULL UNIQUE, last_heartbeat TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_last_heartbeat ON kinform_sessions (last_heartbeat); CREATE OR REPLACE TRIGGER kinform_sessions_timestamp BEFORE UPDATE ON kinform_sessions FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp();