-- The store of Terraform modules, their instances and the instances' variable files: the tables tf_modules, -- tf_instances and tf_variable_files, 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 ); -- 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 $$;