diff --git a/DOCKER.md b/DOCKER.md index b1c64d53..8dfd06e5 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -76,6 +76,8 @@ One image, `dynamicweb:latest`, serves every role; the `ROLE` variable picks it The entrypoint only picks the role, creates a secret key if `DJANGO_SECRET_KEY` is empty and waits for Postgres. The settings read `POSTGRES_*` directly, so you can also override `command:` (for example `python manage.py migrate` in an init container); then set `DJANGO_SECRET_KEY` yourself. +`migrate` is the project's own: it also copes with a restored old database and creates the sites +from `UNGLEICH_SITE_CONFIGS`. See [K8S.md](K8S.md) for the Kubernetes deployment. ## Configuration diff --git a/dynamicweb/management/__init__.py b/dynamicweb/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dynamicweb/management/commands/__init__.py b/dynamicweb/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dynamicweb/management/commands/migrate.py b/dynamicweb/management/commands/migrate.py new file mode 100644 index 00000000..17164d95 --- /dev/null +++ b/dynamicweb/management/commands/migrate.py @@ -0,0 +1,55 @@ +"""migrate, made safe for databases restored from the old (Django 1.9) installation. + +It replaces Django's own "migrate", so "python manage.py migrate" is all that has to run: +- brings the old django-reversion 1.x tables to the schema of the squashed 3.x migration +- skips the migration-history consistency check on old databases, which record some + migrations as applied before the (newer) migrations they now depend on +- makes sure every site in UNGLEICH_SITE_CONFIGS exists +""" +import json +import os + +from django.contrib.sites.models import Site +from django.core.management.commands import migrate +from django.db import connection +from django.db.migrations.loader import MigrationLoader +from django.db.migrations.recorder import MigrationRecorder + +REVERSION_SQUASH = '0001_squashed_0004_auto_20160611_1202' +REVERSION_FIXES = ( + 'ALTER TABLE reversion_revision DROP COLUMN manager_slug', + 'ALTER TABLE reversion_version DROP COLUMN object_id_int', + 'ALTER TABLE reversion_version ALTER COLUMN object_id TYPE varchar(191)', + "ALTER TABLE reversion_version ADD COLUMN db varchar(191) NOT NULL DEFAULT 'default'", + 'ALTER TABLE reversion_version ALTER COLUMN db DROP DEFAULT', + 'ALTER TABLE reversion_version ADD CONSTRAINT reversion_version_db_unique ' + 'UNIQUE (db, content_type_id, object_id, revision_id)', +) + + +class Command(migrate.Command): + requires_system_checks = [] # as before: the start-up never ran them + + def handle(self, *args, **options): + dry_run = options['plan'] or options['check_unapplied'] + if not dry_run: + self.prepare_old_database() + super().handle(*args, **options) + if not dry_run: + for domain in json.loads(os.environ.get('UNGLEICH_SITE_CONFIGS') or '{}'): + Site.objects.get_or_create(domain=domain, defaults={'name': domain}) + + def prepare_old_database(self): + recorder = MigrationRecorder(connection) + recorder.ensure_schema() + applied = recorder.migration_qs + # django-reversion 3.x only ships one squashed migration that would + # re-create the tables, so fix the old ones and record it as applied. + if (applied.filter(app='reversion', name='0002_auto_20141216_1509').exists() + and not applied.filter(app='reversion', name=REVERSION_SQUASH).exists()): + with connection.cursor() as cursor: + for sql in REVERSION_FIXES: + cursor.execute(sql) + recorder.record_applied('reversion', REVERSION_SQUASH) + if applied.filter(app='cmsplugin_filer_image').exists(): + MigrationLoader.check_consistent_history = lambda *args, **kwargs: None diff --git a/start-web.sh b/start-web.sh index da036fa9..ae233c32 100644 --- a/start-web.sh +++ b/start-web.sh @@ -8,46 +8,7 @@ cd /usr/src/app export DJANGO_SETTINGS_MODULE=dynamicweb.settings if [ "${RUN_MIGRATIONS:-True}" = "True" ]; then -python - <<'PYEOF' -import json, os -import django -django.setup() -from django.core.management import call_command -from django.db.migrations.loader import MigrationLoader - -from django.db import connection -from django.db.migrations.recorder import MigrationRecorder - -recorder = MigrationRecorder(connection) -recorder.ensure_schema() -legacy = recorder.migration_qs.filter(app='cmsplugin_filer_image').exists() -# django-reversion 1.x tables: 3.x only ships one squashed migration that would -# re-create them, so bring the old tables to its schema and record it as applied. -squash = '0001_squashed_0004_auto_20160611_1202' -if (recorder.migration_qs.filter(app='reversion', name='0002_auto_20141216_1509').exists() - and not recorder.migration_qs.filter(app='reversion', name=squash).exists()): - with connection.cursor() as cursor: - for sql in ( - 'ALTER TABLE reversion_revision DROP COLUMN manager_slug', - 'ALTER TABLE reversion_version DROP COLUMN object_id_int', - 'ALTER TABLE reversion_version ALTER COLUMN object_id TYPE varchar(191)', - "ALTER TABLE reversion_version ADD COLUMN db varchar(191) NOT NULL DEFAULT 'default'", - 'ALTER TABLE reversion_version ALTER COLUMN db DROP DEFAULT', - 'ALTER TABLE reversion_version ADD CONSTRAINT reversion_version_db_unique ' - 'UNIQUE (db, content_type_id, object_id, revision_id)', - ): - cursor.execute(sql) - recorder.record_applied('reversion', squash) -# Old databases can have a migration applied before the (newer) migrations it now -# depends on, which Django refuses; for them migrate with that check switched off. -if legacy: - MigrationLoader.check_consistent_history = lambda *args, **kwargs: None -call_command('migrate', interactive=False, skip_checks=True) - -from django.contrib.sites.models import Site -for domain in json.loads(os.environ.get('UNGLEICH_SITE_CONFIGS') or '{}'): - Site.objects.get_or_create(domain=domain, defaults={'name': domain}) -PYEOF + python manage.py migrate --noinput fi if [ "${MIGRATE_ONLY:-False}" = "True" ]; then