mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 12:46:49 +00:00
fix(migrations): a fresh PostgreSQL database migrates, and CI tests it
The first appsettings migrations insert the default rows with fixed ids, which a PostgreSQL id sequence does not see; the login lockout migration, the first to insert without an id, then collided with id 1. It now moves the sequence past the existing rows first (a no-op on sqlite), which also covers a database upgraded from any earlier migration. A test compute password exceeded its 14-character column, which only PostgreSQL enforces.
This commit is contained in:
parent
85706c5df6
commit
f889861e18
4 changed files with 63 additions and 3 deletions
27
.github/workflows/test.yml
vendored
27
.github/workflows/test.yml
vendored
|
|
@ -12,12 +12,37 @@ permissions:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Python ${{ matrix.python-version }}
|
name: Python ${{ matrix.python-version }} / ${{ matrix.database }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ['3.10', '3.11', '3.12', '3.13']
|
python-version: ['3.10', '3.11', '3.12', '3.13']
|
||||||
|
database: [sqlite]
|
||||||
|
include:
|
||||||
|
- python-version: '3.12'
|
||||||
|
database: postgresql
|
||||||
|
|
||||||
|
# Used by the postgresql leg only; settings fall back to sqlite when
|
||||||
|
# WEBVIRTCLOUD_DB_HOST is empty.
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:17-alpine
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: webvirtcloud
|
||||||
|
POSTGRES_PASSWORD: webvirtcloud
|
||||||
|
POSTGRES_DB: webvirtcloud
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 5s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 10
|
||||||
|
|
||||||
|
env:
|
||||||
|
WEBVIRTCLOUD_DB_HOST: ${{ matrix.database == 'postgresql' && 'localhost' || '' }}
|
||||||
|
WEBVIRTCLOUD_DB_PASSWORD: webvirtcloud
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from django.core.management.color import no_style
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12,6 +13,12 @@ SETTINGS = [
|
||||||
def add_settings(apps, schema_editor):
|
def add_settings(apps, schema_editor):
|
||||||
setting = apps.get_model("appsettings", "AppSettings")
|
setting = apps.get_model("appsettings", "AppSettings")
|
||||||
db_alias = schema_editor.connection.alias
|
db_alias = schema_editor.connection.alias
|
||||||
|
# Earlier migrations inserted the default rows with fixed ids, which a
|
||||||
|
# PostgreSQL id sequence does not see: move it past them before inserting
|
||||||
|
# without an id (a no-op on sqlite).
|
||||||
|
with schema_editor.connection.cursor() as cursor:
|
||||||
|
for sql in schema_editor.connection.ops.sequence_reset_sql(no_style(), [setting]):
|
||||||
|
cursor.execute(sql)
|
||||||
for name, key, value, description in SETTINGS:
|
for name, key, value, description in SETTINGS:
|
||||||
setting.objects.using(db_alias).get_or_create(
|
setting.objects.using(db_alias).get_or_create(
|
||||||
key=key, defaults={"name": name, "value": value, "choices": "", "description": description}
|
key=key, defaults={"name": name, "value": value, "choices": "", "description": description}
|
||||||
|
|
|
||||||
28
appsettings/test_migrations.py
Normal file
28
appsettings/test_migrations.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
"""The default settings rows of the first migrations carry fixed ids, which a
|
||||||
|
PostgreSQL id sequence does not see. A later migration that inserts without
|
||||||
|
an id must not collide with them."""
|
||||||
|
|
||||||
|
import importlib
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
from django.db import connection
|
||||||
|
from django.test import TransactionTestCase
|
||||||
|
|
||||||
|
from appsettings.models import AppSettings
|
||||||
|
|
||||||
|
login_lockout = importlib.import_module("appsettings.migrations.0014_login_lockout_settings")
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(connection.vendor == "postgresql", "id sequences are a PostgreSQL concern")
|
||||||
|
class SequenceAfterFixedIdsTestCase(TransactionTestCase):
|
||||||
|
serialized_rollback = True
|
||||||
|
|
||||||
|
def test_login_lockout_settings_insert_after_fixed_ids(self):
|
||||||
|
with connection.schema_editor() as schema_editor:
|
||||||
|
login_lockout.del_settings(apps, schema_editor)
|
||||||
|
# the state after the fixed-id inserts: rows exist, the sequence starts at 1
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute("SELECT setval(pg_get_serial_sequence('appsettings_appsettings', 'id'), 1, false)")
|
||||||
|
login_lockout.add_settings(apps, schema_editor)
|
||||||
|
self.assertEqual(AppSettings.objects.filter(key__startswith="LOGIN_").count(), 2)
|
||||||
|
|
@ -34,14 +34,14 @@ class APISecurityTestCase(TestCase):
|
||||||
name="api-test-compute",
|
name="api-test-compute",
|
||||||
hostname="127.0.0.1",
|
hostname="127.0.0.1",
|
||||||
login="libvirt_user",
|
login="libvirt_user",
|
||||||
password="secret_hypervisor_password",
|
password="hv-secret-pw",
|
||||||
type=1,
|
type=1,
|
||||||
)
|
)
|
||||||
self.empty_compute = Compute.objects.create(
|
self.empty_compute = Compute.objects.create(
|
||||||
name="empty-test-compute",
|
name="empty-test-compute",
|
||||||
hostname="127.0.0.1",
|
hostname="127.0.0.1",
|
||||||
login="libvirt_user",
|
login="libvirt_user",
|
||||||
password="secret_hypervisor_password",
|
password="hv-secret-pw",
|
||||||
type=1,
|
type=1,
|
||||||
)
|
)
|
||||||
self.flavor = Flavor.objects.create(
|
self.flavor = Flavor.objects.create(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue