174 lines
7.9 KiB
PL/PgSQL
174 lines
7.9 KiB
PL/PgSQL
-- The store of Terraform modules, their instances and the instances' variable files: the tables tf_modules,
|
|
-- tf_instances, tf_variable_files and tf_variable_revisions, and the functions and procedures the store calls. The
|
|
-- store applies the file on its first use in the schema its connection's search path names; every statement can run
|
|
-- again.
|
|
|
|
CREATE TABLE IF NOT EXISTS tf_modules (
|
|
name text PRIMARY KEY CHECK (name ~ '^[a-z][a-z0-9-]*$'),
|
|
root text NOT NULL CHECK (root <> ''),
|
|
description text NOT NULL DEFAULT '',
|
|
configurator_url text
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS tf_instances (
|
|
module text NOT NULL REFERENCES tf_modules (name),
|
|
name text NOT NULL CHECK (name ~ '^[a-z][a-z0-9-]*$'),
|
|
state_schema text NOT NULL UNIQUE CHECK (state_schema ~ '^[a-z_][a-z0-9_]*$'),
|
|
created timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (module, name)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS tf_variable_files (
|
|
module text NOT NULL,
|
|
instance text NOT NULL,
|
|
name text NOT NULL CHECK (name ~ '^[A-Za-z0-9_.-]+\.tfvars\.json$'),
|
|
content jsonb NOT NULL CHECK (jsonb_typeof(content) = 'object'),
|
|
previous jsonb CHECK (jsonb_typeof(previous) = 'object'),
|
|
updated timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (module, instance, name),
|
|
FOREIGN KEY (module, instance) REFERENCES tf_instances (module, name) ON DELETE CASCADE
|
|
);
|
|
|
|
-- Every write of an instance's variable files: all its files after the write, by name, the schema version of their
|
|
-- layout, null when not known, and the application that wrote them.
|
|
CREATE TABLE IF NOT EXISTS tf_variable_revisions (
|
|
module text NOT NULL,
|
|
instance text NOT NULL,
|
|
revision integer NOT NULL CHECK (revision > 0),
|
|
files jsonb NOT NULL CHECK (jsonb_typeof(files) = 'object'),
|
|
schema integer CHECK (schema > 0),
|
|
producer text NOT NULL CHECK (producer <> ''),
|
|
saved timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (module, instance, revision),
|
|
FOREIGN KEY (module, instance) REFERENCES tf_instances (module, name) ON DELETE CASCADE
|
|
);
|
|
|
|
-- The files of an instance that has no revision are its revision 1, of an unknown schema and producer.
|
|
INSERT INTO tf_variable_revisions (module, instance, revision, files, schema, producer, saved)
|
|
SELECT f.module, f.instance, 1, jsonb_object_agg(f.name, f.content), NULL, 'unknown', max(f.updated)
|
|
FROM tf_variable_files f
|
|
WHERE NOT EXISTS (SELECT 1 FROM tf_variable_revisions r WHERE r.module = f.module AND r.instance = f.instance)
|
|
GROUP BY f.module, f.instance
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- The modules, by name.
|
|
CREATE OR REPLACE FUNCTION tf_modules_list()
|
|
RETURNS TABLE (name text, root text, description text, configurator_url text)
|
|
LANGUAGE sql STABLE
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
SELECT m.name, m.root, m.description, m.configurator_url FROM tf_modules m ORDER BY m.name
|
|
$$;
|
|
|
|
-- Creates a module, or changes the one of that name.
|
|
CREATE OR REPLACE PROCEDURE tf_module_put(module_name text, module_root text, module_description text, module_configurator_url text)
|
|
LANGUAGE sql
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
INSERT INTO tf_modules (name, root, description, configurator_url)
|
|
VALUES (module_name, module_root, module_description, module_configurator_url)
|
|
ON CONFLICT (name) DO UPDATE SET root = excluded.root, description = excluded.description, configurator_url = excluded.configurator_url
|
|
$$;
|
|
|
|
-- Deletes a module; the foreign key of tf_instances refuses it while the module has instances.
|
|
CREATE OR REPLACE PROCEDURE tf_module_delete(module_name text)
|
|
LANGUAGE sql
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
DELETE FROM tf_modules WHERE name = module_name
|
|
$$;
|
|
|
|
-- The instances of a module, by name.
|
|
CREATE OR REPLACE FUNCTION tf_instances_list(module_name text)
|
|
RETURNS TABLE (module text, name text, state_schema text, created timestamptz)
|
|
LANGUAGE sql STABLE
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
SELECT i.module, i.name, i.state_schema, i.created FROM tf_instances i WHERE i.module = module_name ORDER BY i.name
|
|
$$;
|
|
|
|
-- Creates an instance of a module with the schema of its state.
|
|
CREATE OR REPLACE PROCEDURE tf_instance_create(module_name text, instance_name text, instance_state_schema text)
|
|
LANGUAGE sql
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
INSERT INTO tf_instances (module, name, state_schema) VALUES (module_name, instance_name, instance_state_schema)
|
|
$$;
|
|
|
|
-- Deletes an instance with its variable files.
|
|
CREATE OR REPLACE PROCEDURE tf_instance_delete(module_name text, instance_name text)
|
|
LANGUAGE sql
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
DELETE FROM tf_instances WHERE module = module_name AND name = instance_name
|
|
$$;
|
|
|
|
-- The variable files of an instance, by name: content, whether the content it replaced is kept, and the time of the
|
|
-- last write.
|
|
CREATE OR REPLACE FUNCTION tf_variable_files_read(module_name text, instance_name text)
|
|
RETURNS TABLE (name text, content jsonb, previous boolean, updated timestamptz)
|
|
LANGUAGE sql STABLE
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
SELECT f.name, f.content, f.previous IS NOT NULL, f.updated FROM tf_variable_files f
|
|
WHERE f.module = module_name AND f.instance = instance_name ORDER BY f.name
|
|
$$;
|
|
|
|
-- Keeps other writers out until the end of the transaction, from the comparison of the version to the commit.
|
|
CREATE OR REPLACE PROCEDURE tf_variable_files_lock()
|
|
LANGUAGE sql
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
LOCK TABLE tf_variable_files IN EXCLUSIVE MODE
|
|
$$;
|
|
|
|
-- Writes a variable file of an instance, keeping the content it replaces as previous.
|
|
CREATE OR REPLACE PROCEDURE tf_variable_file_put(module_name text, instance_name text, file_name text, file_content jsonb)
|
|
LANGUAGE sql
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
INSERT INTO tf_variable_files (module, instance, name, content) VALUES (module_name, instance_name, file_name, file_content)
|
|
ON CONFLICT (module, instance, name) DO UPDATE SET previous = tf_variable_files.content, content = excluded.content, updated = now()
|
|
$$;
|
|
|
|
-- Deletes a variable file of an instance.
|
|
CREATE OR REPLACE PROCEDURE tf_variable_file_delete(module_name text, instance_name text, file_name text)
|
|
LANGUAGE sql
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
DELETE FROM tf_variable_files WHERE module = module_name AND instance = instance_name AND name = file_name
|
|
$$;
|
|
|
|
-- Records the variable files of an instance as its next revision, with the schema of their layout and their producer;
|
|
-- called with the lock of tf_variable_files_lock held.
|
|
CREATE OR REPLACE PROCEDURE tf_variable_revision_add(module_name text, instance_name text, revision_schema integer, revision_producer text)
|
|
LANGUAGE sql
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
INSERT INTO tf_variable_revisions (module, instance, revision, files, schema, producer)
|
|
SELECT module_name, instance_name,
|
|
(SELECT coalesce(max(r.revision), 0) + 1 FROM tf_variable_revisions r WHERE r.module = module_name AND r.instance = instance_name),
|
|
(SELECT coalesce(jsonb_object_agg(f.name, f.content), '{}'::jsonb) FROM tf_variable_files f WHERE f.module = module_name AND f.instance = instance_name),
|
|
revision_schema, revision_producer
|
|
$$;
|
|
|
|
-- The revisions of an instance, newest first, with the names of their files.
|
|
CREATE OR REPLACE FUNCTION tf_variable_revisions_list(module_name text, instance_name text)
|
|
RETURNS TABLE (revision integer, schema integer, producer text, saved timestamptz, names text[])
|
|
LANGUAGE sql STABLE
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
SELECT r.revision, r.schema, r.producer, r.saved, ARRAY(SELECT jsonb_object_keys(r.files) ORDER BY 1)
|
|
FROM tf_variable_revisions r WHERE r.module = module_name AND r.instance = instance_name ORDER BY r.revision DESC
|
|
$$;
|
|
|
|
-- A revision of an instance with its files.
|
|
CREATE OR REPLACE FUNCTION tf_variable_revision_get(module_name text, instance_name text, revision_number integer)
|
|
RETURNS TABLE (revision integer, schema integer, producer text, saved timestamptz, files jsonb)
|
|
LANGUAGE sql STABLE
|
|
SET search_path FROM CURRENT
|
|
AS $$
|
|
SELECT r.revision, r.schema, r.producer, r.saved, r.files
|
|
FROM tf_variable_revisions r WHERE r.module = module_name AND r.instance = instance_name AND r.revision = revision_number
|
|
$$;
|