Files
nekonest-cloud/drizzle/9000_provisioning_invariants.sql

112 lines
4.5 KiB
SQL

CREATE TRIGGER `provisioning_queue_guard`
BEFORE INSERT ON `provisioning_operations`
WHEN NEW.`status` = 'requested'
BEGIN
SELECT CASE WHEN NOT EXISTS (
SELECT 1
FROM `tenant_instances`
WHERE `id` = NEW.`tenant_id`
AND `desired_generation` + 1 = NEW.`target_generation`
AND `credential_revision` = NEW.`credential_revision`
AND `tombstoned_at` IS NULL
AND (
(NEW.`operation` = 'ensure' AND `lifecycle` IN ('requested', 'failed', 'degraded'))
OR (NEW.`operation` = 'suspend' AND `lifecycle` IN ('ready', 'degraded'))
OR (NEW.`operation` = 'resume' AND `lifecycle` IN ('suspended', 'failed'))
OR (NEW.`operation` = 'deprovision' AND `lifecycle` IN ('requested', 'ready', 'suspended', 'failed', 'degraded'))
)
) THEN RAISE(ABORT, 'invalid_provisioning_transition') END;
END;--> statement-breakpoint
CREATE TRIGGER `provisioning_queue_tenant_update`
AFTER INSERT ON `provisioning_operations`
WHEN NEW.`status` = 'requested'
BEGIN
UPDATE `tenant_instances`
SET `lifecycle` = CASE NEW.`operation`
WHEN 'suspend' THEN 'suspending'
WHEN 'deprovision' THEN 'deprovisioning'
ELSE 'provisioning'
END,
`desired_state` = CASE NEW.`operation`
WHEN 'suspend' THEN 'suspended'
WHEN 'deprovision' THEN 'deleted'
ELSE 'running'
END,
`desired_generation` = NEW.`target_generation`,
`relay_ready` = 0,
`updated_at` = NEW.`created_at`
WHERE `id` = NEW.`tenant_id`;
END;--> statement-breakpoint
CREATE TRIGGER `provisioning_completion_guard`
BEFORE UPDATE OF `status` ON `provisioning_operations`
WHEN OLD.`status` IN ('requested', 'leased') AND NEW.`status` IN ('succeeded', 'failed')
BEGIN
SELECT CASE WHEN NOT EXISTS (
SELECT 1
FROM `tenant_instances`
WHERE `id` = OLD.`tenant_id`
AND `desired_generation` = OLD.`target_generation`
AND (
NEW.`status` = 'failed'
OR `credential_revision` = OLD.`credential_revision`
)
) THEN RAISE(ABORT, 'stale_provisioning_generation') END;
END;--> statement-breakpoint
CREATE TRIGGER `provisioning_completion_tenant_update`
AFTER UPDATE OF `status` ON `provisioning_operations`
WHEN OLD.`status` IN ('requested', 'leased') AND NEW.`status` IN ('succeeded', 'failed')
BEGIN
UPDATE `tenant_instances`
SET `lifecycle` = CASE
WHEN NEW.`status` = 'failed' AND NEW.`operation` IN ('ensure', 'resume') THEN 'failed'
WHEN NEW.`status` = 'failed' THEN 'degraded'
WHEN NEW.`operation` IN ('ensure', 'resume') THEN 'ready'
WHEN NEW.`operation` = 'suspend' THEN 'suspended'
ELSE 'deleted'
END,
`observed_state` = CASE
WHEN NEW.`status` = 'failed' THEN `observed_state`
WHEN NEW.`operation` IN ('ensure', 'resume') THEN 'running'
WHEN NEW.`operation` = 'suspend' THEN 'suspended'
ELSE 'absent'
END,
`active_generation` = CASE
WHEN NEW.`status` = 'succeeded' THEN NEW.`target_generation`
ELSE `active_generation`
END,
`runtime_version` = CASE
WHEN NEW.`status` = 'succeeded' AND NEW.`operation` IN ('ensure', 'resume') THEN NEW.`result_runtime_version`
WHEN NEW.`status` = 'succeeded' AND NEW.`operation` = 'deprovision' THEN NULL
ELSE `runtime_version`
END,
`runtime_ref` = CASE
WHEN NEW.`status` = 'succeeded' AND NEW.`operation` IN ('ensure', 'resume') THEN NEW.`result_runtime_ref`
WHEN NEW.`status` = 'succeeded' AND NEW.`operation` = 'deprovision' THEN NULL
ELSE `runtime_ref`
END,
`relay_origin` = CASE
WHEN NEW.`status` = 'succeeded' AND NEW.`operation` IN ('ensure', 'resume') THEN NEW.`result_relay_origin`
WHEN NEW.`status` = 'succeeded' AND NEW.`operation` = 'deprovision' THEN NULL
ELSE `relay_origin`
END,
`secret_bundle_ref` = CASE
WHEN NEW.`status` = 'succeeded' AND NEW.`operation` IN ('ensure', 'resume') THEN NEW.`result_secret_bundle_ref`
WHEN NEW.`status` = 'succeeded' AND NEW.`operation` = 'deprovision' THEN NULL
ELSE `secret_bundle_ref`
END,
`last_health_at` = CASE
WHEN NEW.`status` = 'succeeded' THEN NEW.`health_checked_at`
ELSE `last_health_at`
END,
`tombstoned_at` = CASE
WHEN NEW.`status` = 'succeeded' AND NEW.`operation` = 'deprovision' THEN NEW.`completed_at`
ELSE `tombstoned_at`
END,
`relay_ready` = 0,
`updated_at` = NEW.`completed_at`
WHERE `id` = NEW.`tenant_id`;
END;