20260802_473_connector_runtime_hardening.py 4.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. """Harden connector identity bindings and persistent runtime state."""
  2. from alembic import op
  3. revision = "20260802_473"
  4. down_revision = "20260802_472"
  5. branch_labels = None
  6. depends_on = None
  7. def upgrade() -> None:
  8. op.execute("""
  9. DO $$ BEGIN
  10. IF EXISTS (
  11. SELECT 1
  12. FROM public.connector_principals p
  13. JOIN (
  14. SELECT connector_id
  15. FROM public.connector_manifests
  16. WHERE status='active'
  17. GROUP BY connector_id
  18. HAVING COUNT(DISTINCT connector_version)>1
  19. ) ambiguous USING(connector_id)
  20. ) THEN
  21. RAISE EXCEPTION
  22. 'connector 473 upgrade rejected before backfill: multiple active versions require an explicit one-active-version mapping';
  23. END IF;
  24. END $$;
  25. ALTER TABLE public.connector_principals ADD COLUMN connector_version VARCHAR(40);
  26. UPDATE public.connector_principals p SET connector_version=(
  27. SELECT m.connector_version FROM public.connector_manifests m
  28. WHERE m.connector_id=p.connector_id AND m.status='active'
  29. ORDER BY m.created_at DESC LIMIT 1
  30. );
  31. DO $$ BEGIN
  32. IF EXISTS (SELECT 1 FROM public.connector_principals WHERE connector_version IS NULL) THEN
  33. RAISE EXCEPTION 'connector principal version cannot be backfilled safely';
  34. END IF;
  35. END $$;
  36. ALTER TABLE public.connector_principals ALTER COLUMN connector_version SET NOT NULL;
  37. ALTER TABLE public.connector_principals
  38. ADD CONSTRAINT fk_connector_principal_manifest FOREIGN KEY(connector_id,connector_version)
  39. REFERENCES public.connector_manifests(connector_id,connector_version);
  40. ALTER TABLE public.connector_principals DROP CONSTRAINT uq_connector_principal_binding;
  41. ALTER TABLE public.connector_principals ADD CONSTRAINT uq_connector_principal_binding_v2
  42. UNIQUE(connector_id,connector_version,source_uid,business_domain_uid,environment);
  43. ALTER TABLE public.connector_runs
  44. ADD COLUMN principal_uid UUID REFERENCES public.connector_principals(uid),
  45. ADD COLUMN business_domain_uid UUID,
  46. ADD COLUMN environment VARCHAR(20),
  47. ADD COLUMN process_key VARCHAR(300),
  48. ADD COLUMN safe_config JSONB NOT NULL DEFAULT '{}'::jsonb,
  49. ADD COLUMN scope JSONB NOT NULL DEFAULT '{}'::jsonb;
  50. ALTER TABLE public.connector_runs ADD CONSTRAINT ck_connector_run_environment
  51. CHECK(environment IS NULL OR environment IN ('development','staging','production'));
  52. ALTER TABLE public.connector_runs ADD CONSTRAINT ck_connector_machine_run_binding
  53. CHECK(dry_run OR (principal_uid IS NOT NULL AND business_domain_uid IS NOT NULL AND environment IS NOT NULL AND process_key IS NOT NULL)) NOT VALID;
  54. ALTER TABLE public.connector_runs ADD CONSTRAINT ck_connector_run_config_object CHECK(jsonb_typeof(safe_config)='object');
  55. ALTER TABLE public.connector_runs ADD CONSTRAINT ck_connector_run_scope_object CHECK(jsonb_typeof(scope)='object');
  56. CREATE TABLE public.connector_rate_limits(
  57. limit_key VARCHAR(300) NOT NULL,
  58. window_started_at TIMESTAMPTZ NOT NULL,
  59. request_count INTEGER NOT NULL,
  60. updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  61. PRIMARY KEY(limit_key,window_started_at),
  62. CONSTRAINT ck_connector_rate_count CHECK(request_count BETWEEN 1 AND 30)
  63. );
  64. CREATE INDEX ix_connector_rate_limits_updated ON public.connector_rate_limits(updated_at);
  65. """)
  66. def downgrade() -> None:
  67. op.execute("""
  68. DROP TABLE IF EXISTS public.connector_rate_limits;
  69. ALTER TABLE public.connector_runs DROP CONSTRAINT IF EXISTS ck_connector_run_scope_object;
  70. ALTER TABLE public.connector_runs DROP CONSTRAINT IF EXISTS ck_connector_run_config_object;
  71. ALTER TABLE public.connector_runs DROP CONSTRAINT IF EXISTS ck_connector_machine_run_binding;
  72. ALTER TABLE public.connector_runs DROP CONSTRAINT IF EXISTS ck_connector_run_environment;
  73. ALTER TABLE public.connector_runs DROP COLUMN IF EXISTS scope;
  74. ALTER TABLE public.connector_runs DROP COLUMN IF EXISTS safe_config;
  75. ALTER TABLE public.connector_runs DROP COLUMN IF EXISTS process_key;
  76. ALTER TABLE public.connector_runs DROP COLUMN IF EXISTS environment;
  77. ALTER TABLE public.connector_runs DROP COLUMN IF EXISTS business_domain_uid;
  78. ALTER TABLE public.connector_runs DROP COLUMN IF EXISTS principal_uid;
  79. ALTER TABLE public.connector_principals DROP CONSTRAINT IF EXISTS uq_connector_principal_binding_v2;
  80. ALTER TABLE public.connector_principals DROP CONSTRAINT IF EXISTS fk_connector_principal_manifest;
  81. ALTER TABLE public.connector_principals DROP COLUMN IF EXISTS connector_version;
  82. ALTER TABLE public.connector_principals ADD CONSTRAINT uq_connector_principal_binding
  83. UNIQUE(connector_id,source_uid,business_domain_uid,environment);
  84. """)