From c4d11a8543751e618f52899fa535e072c5723769 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 31 Jul 2026 12:53:51 +0300 Subject: [PATCH] :ambulance: fix(deploy): normalize schema ownership before migrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Production deploy failed at migrate with «must be owner of table identity_organization»: the table (and others created/imported outside the migration role) were not owned by custocrm_schema, which migration_user must belong to to run AddField/AlterField. This is a pre-existing condition that surfaced on the administration migration and recurred across pipelines (#922, #937, #938) — not caused by the calls feature. Fix makes deploy self-healing: before running migrate, reassign ownership of all public-schema objects (tables, sequences, functions) to custocrm_schema under the postgres superuser. Idempotent and safe on every deploy. - deploy/postgres/reassign-schema-ownership.sql: reassign public-schema ownership to custocrm_schema (verified against PG16) - compose.yaml: mount the SQL into the postgres container - deploy/cli/lib/deploy.sh: run the normalization step once postgres is healthy, before the one-shot migrate --- compose.yaml | 1 + deploy/cli/lib/deploy.sh | 19 ++++++ deploy/postgres/reassign-schema-ownership.sql | 62 +++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 deploy/postgres/reassign-schema-ownership.sql diff --git a/compose.yaml b/compose.yaml index bff4cbd..8e1a715 100644 --- a/compose.yaml +++ b/compose.yaml @@ -29,6 +29,7 @@ services: volumes: - ${CUSTOCRM_INSTANCE_DIR:-.}/data/postgres:/var/lib/postgresql/data - ./deploy/postgres/init-runtime-roles.sh:/docker-entrypoint-initdb.d/20-custocrm-runtime-roles.sh:ro + - ./deploy/postgres/reassign-schema-ownership.sql:/custocrm-reassign-ownership.sql:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 10s diff --git a/deploy/cli/lib/deploy.sh b/deploy/cli/lib/deploy.sh index e1b7a11..bf65570 100644 --- a/deploy/cli/lib/deploy.sh +++ b/deploy/cli/lib/deploy.sh @@ -17,6 +17,9 @@ cmd_deploy() { _wait_healthy postgres 60 || die "deploy: postgres did not become healthy" 1 _wait_healthy redis 30 || die "deploy: redis did not become healthy" 1 + log "deploy: normalizing schema ownership for migrations" + _normalize_schema_ownership || die "deploy: schema ownership normalization failed" 1 + log "deploy: running one-shot init (migrate)" run_compose run --rm init || die "deploy: init (migrate) failed" 1 @@ -96,6 +99,22 @@ _wait_running() { return 1 } +_normalize_schema_ownership() { + # Приводит владение объектов public-схемы к роли custocrm_schema, в которую + # входит migration-user. Идемпотентно: безопасно на каждом деплое. Без этого + # миграции от migration-user падают на таблицах, созданных не им + # («must be owner of table …»). Выполняется под суперпользователем POSTGRES_USER. + local env_file pg_user pg_db + env_file="$(instance_env_file)" + pg_user="$(env_get "$env_file" POSTGRES_USER)" + pg_db="$(env_get "$env_file" POSTGRES_DB)" + [[ -n "$pg_user" ]] || { log_err "POSTGRES_USER not set"; return 1; } + [[ -n "$pg_db" ]] || { log_err "POSTGRES_DB not set"; return 1; } + run_compose exec -T postgres \ + psql -v ON_ERROR_STOP=1 -U "$pg_user" -d "$pg_db" \ + -f /custocrm-reassign-ownership.sql >/dev/null +} + _first_json_service_state() { local line IFS= read -r line || return 0 diff --git a/deploy/postgres/reassign-schema-ownership.sql b/deploy/postgres/reassign-schema-ownership.sql new file mode 100644 index 0000000..4a5410b --- /dev/null +++ b/deploy/postgres/reassign-schema-ownership.sql @@ -0,0 +1,62 @@ +-- Нормализация владельца объектов public-схемы под роль миграций. +-- +-- Контекст: при раздельных ролях (custocrm_migration / custocrm_app / +-- custocrm_platform) Django-миграции от migration-user требуют, чтобы +-- выполняющий был ВЛАДЕЛЬЦЕМ изменяемой таблицы («must be owner of table …»). +-- Таблицы, созданные/импортированные иначе (суперпользователем postgres, +-- app-role или до ввода разделения ролей), оказываются «осиротевшими», и +-- AddField/AlterField на них падает в deploy. +-- +-- Скрипт переписывает владение всей public-схемы на custocrm_schema — роль, +-- в которую входит custocrm_migration (см. init-runtime-roles.sh). После этого +-- любой migration-user может мигрировать любые таблицы. +-- +-- Выполнять под суперпользователем postgres (не под app/migration role): +-- docker compose exec -T postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" \ +-- < deploy/postgres/reassign-schema-ownership.sql +-- +-- Идемпотентен: повторный запуск безопасен. Затрагивает только схему public. + +\set ON_ERROR_STOP on + +-- 1. Таблицы, последовательности, функции, типы → custocrm_schema. +ALTER SCHEMA public OWNER TO custocrm_schema; + +DO $$ +DECLARE + r RECORD; +BEGIN + -- Таблицы. + FOR r IN + SELECT tablename FROM pg_tables WHERE schemaname = 'public' + LOOP + EXECUTE format('ALTER TABLE public.%I OWNER TO custocrm_schema', r.tablename); + END LOOP; + + -- Последовательности (включая owned-последовательности таблиц). + FOR r IN + SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema = 'public' + LOOP + EXECUTE format('ALTER SEQUENCE public.%I OWNER TO custocrm_schema', r.sequence_name); + END LOOP; + + -- Функции/процедуры в public. + FOR r IN + SELECT p.oid, p.proname, pg_get_function_identity_arguments(p.oid) AS args + FROM pg_proc p + JOIN pg_namespace n ON n.oid = p.pronamespace + WHERE n.nspname = 'public' + LOOP + EXECUTE format('ALTER FUNCTION %s(%s) OWNER TO custocrm_schema', r.proname, r.args); + END LOOP; +END $$; + +-- 2. Права по умолчанию для будущих объектов, создаваемых migration-user'ом, +-- остаются корректными (владелец = создатель, что для миграций = migration). +-- Явная выдача DDL-прав runtime-ролям не нужна и не делается (RLS-изоляция). + +-- 3. Диагностика: кто чем владеет после нормализации. +SELECT tablename, tableowner +FROM pg_tables +WHERE schemaname = 'public' +ORDER BY tablename;