Projet

Général

Profil

0001-multitenant-add-legacy-hostname-option-to-create_ten.patch

Emmanuel Cazenave, 29 novembre 2021 18:18

Télécharger (9,17 ko)

Voir les différences:

Subject: [PATCH] multitenant: add --legacy-hostname option to create_tenant
 (#59094)

 .../management/commands/create_tenant.py      | 54 ++++++++++++++-----
 hobo/multitenant/models.py                    | 16 ++++++
 tests_multitenant/test_create_tenant.py       | 47 +++++++++++++++-
 3 files changed, 103 insertions(+), 14 deletions(-)
hobo/multitenant/management/commands/create_tenant.py
12 12

  
13 13
    def add_arguments(self, parser):
14 14
        parser.add_argument('hostnames', metavar='HOSTNAME', nargs='+')
15
        parser.add_argument('--legacy-hostname', dest='legacy_hostname')
15 16

  
16
    def handle(self, hostnames, **options):
17
    def handle(self, hostnames, legacy_hostname, **options):
17 18
        verbosity = int(options.get('verbosity'))
18 19
        if not hostnames:
19 20
            raise CommandError("you must give at least one tenant hostname")
......
23 24
            hostnames.remove('-')
24 25
            hostnames.extend([x.strip() for x in sys.stdin.readlines()])
25 26

  
27
        if legacy_hostname and len(hostnames) > 1:
28
            raise CommandError("You must specify only hostname when using --legacy-hostname")
29

  
26 30
        for hostname in hostnames:
27 31
            try:
28 32
                tenant_base = TenantMiddleware.base()
......
33 37
            tenant_dir = os.path.join(tenant_base, hostname)
34 38
            if os.path.exists(tenant_dir):
35 39
                raise CommandError('tenant already exists')
40

  
36 41
            tenant_dir_tmp = os.path.join(tenant_base, hostname + '__CREATION_IN_PROGRESS.invalid')
37
            try:
38
                os.mkdir(tenant_dir_tmp, 0o755)
39
            except OSError as e:
40
                raise CommandError('cannot start tenant creation (%s)' % str(e))
42
            if legacy_hostname:
43
                legacy_tenant_dir = os.path.join(tenant_base, legacy_hostname)
44
                if not os.path.exists(legacy_tenant_dir):
45
                    raise CommandError('legacy tenant does not exists')
46
                try:
47
                    os.rename(legacy_tenant_dir, tenant_dir_tmp)
48
                except OSError as e:
49
                    raise CommandError('cannot change legacy tenant (%s)' % str(e))
50
            else:
51
                try:
52
                    os.mkdir(tenant_dir_tmp, 0o755)
53
                except OSError as e:
54
                    raise CommandError('cannot start tenant creation (%s)' % str(e))
55

  
41 56
            # tenant creation in database
42 57
            try:
43 58
                connection.set_schema_to_public()
44 59
                schema = TenantMiddleware.hostname2schema(hostname)
60
                legacy_schema = None
61
                if legacy_hostname:
62
                    legacy_schema = TenantMiddleware.hostname2schema(legacy_hostname)
45 63
                tenant = get_tenant_model()(schema_name=schema, domain_url=hostname)
46 64
                if verbosity >= 1:
65
                    msg = self.style.NOTICE("=== Creating schema ") + self.style.SQL_TABLE(tenant.schema_name)
66
                    if legacy_schema:
67
                        msg = (
68
                            self.style.NOTICE("=== Renamin schema ")
69
                            + self.style.SQL_TABLE(legacy_schema)
70
                            + " to "
71
                            + self.style.SQL_TABLE(tenant.schema_name)
72
                        )
47 73
                    print()
48
                    print(
49
                        self.style.NOTICE("=== Creating schema ") + self.style.SQL_TABLE(tenant.schema_name)
50
                    )
74
                    print(msg)
51 75
                with transaction.atomic():
52
                    tenant.create_schema(check_if_exists=True)
76
                    tenant.create_schema(check_if_exists=True, legacy_schema_name=legacy_schema)
53 77
            except Exception as e:
54
                os.rmdir(tenant_dir_tmp)
78
                if legacy_hostname:
79
                    os.rename(tenant_dir_tmp, legacy_tenant_dir)
80
                else:
81
                    os.rmdir(tenant_dir_tmp)
55 82
                raise CommandError('tenant creation failed (%s)' % str(e))
56
            for folder in ('media', 'static', 'templates'):
57
                path = os.path.join(tenant_dir_tmp, folder)
58
                os.mkdir(path, 0o755)
83
            if not legacy_hostname:
84
                for folder in ('media', 'static', 'templates'):
85
                    path = os.path.join(tenant_dir_tmp, folder)
86
                    os.mkdir(path, 0o755)
59 87
            # activate the tenant by creating its directory
60 88
            os.rename(tenant_dir_tmp, tenant_dir)
hobo/multitenant/models.py
7 7
from django.utils import timezone
8 8
from django.utils.six.moves.urllib.parse import urljoin
9 9
from tenant_schemas.models import TenantMixin
10
from tenant_schemas.postgresql_backend.base import _check_schema_name
10 11
from tenant_schemas.utils import django_is_in_test_mode, get_public_schema_name, schema_exists
11 12

  
12 13

  
......
57 58
    def build_absolute_uri(self, location):
58 59
        return urljoin(self.get_base_url(), location)
59 60

  
61
    def create_schema(self, check_if_exists=False, sync_schema=True, verbosity=1, legacy_schema_name=None):
62
        if not legacy_schema_name:
63
            return super().create_schema(check_if_exists, sync_schema, verbosity)
64
        # safety check
65
        _check_schema_name(self.schema_name)
66
        _check_schema_name(legacy_schema_name)
67
        if check_if_exists and schema_exists(self.schema_name):
68
            return False
69
        if not schema_exists(legacy_schema_name):
70
            return False
71
        # rename schema
72
        cursor = connection.cursor()
73
        cursor.execute('ALTER SCHEMA %s RENAME TO %s' % (legacy_schema_name, self.schema_name))
74
        connection.set_schema_to_public()
75

  
60 76
    def delete(self, force_drop=False, *args, **kwargs):
61 77
        """
62 78
        Deletes this row. Drops the tenant's schema if the attribute
tests_multitenant/test_create_tenant.py
33 33
def schema_exists(schema_name):
34 34
    with connection.cursor() as cursor:
35 35
        cursor.execute('select schema_name from information_schema.schemata')
36
        return 'www_example_com' in [row[0] for row in cursor.fetchall()]
36
        return schema_name in [row[0] for row in cursor.fetchall()]
37 37

  
38 38

  
39 39
def test_create_tenant(db):
......
84 84
    call_command('migrate_schemas', verbosity=1)
85 85
    captured = capsys.readouterr()
86 86
    assert 'Skipping migrations of tenant www.example.com' in captured.out
87

  
88

  
89
def test_create_tenant_from_existing_tenant(db):
90
    assert not schema_exists('www_example_com')
91
    call_command('create_tenant', 'www.example.com')
92
    assert schema_exists('www_example_com')
93
    tenants = list(TenantMiddleware.get_tenants())
94
    assert len(tenants) == 1
95
    tenant = tenants[0]
96
    assert tenant.domain_url == 'www.example.com'
97
    assert tenant.schema_name == 'www_example_com'
98
    with tenant_context(tenant):
99
        User.objects.create(username='admin')
100

  
101
    assert not schema_exists('www_newexample_com')
102
    call_command('create_tenant', '--legacy-hostname', 'www.example.com', 'www.newexample.com')
103
    assert not schema_exists('www_example_com')
104
    assert schema_exists('www_newexample_com')
105
    tenants = list(TenantMiddleware.get_tenants())
106
    assert len(tenants) == 1
107
    tenant = tenants[0]
108
    assert tenant.domain_url == 'www.newexample.com'
109
    assert tenant.schema_name == 'www_newexample_com'
110

  
111

  
112
def test_create_tenant_from_existing_tenant_error_too_many_hosts(db):
113
    assert not schema_exists('host1_com')
114
    assert not schema_exists('host2_com')
115
    assert not schema_exists('host3_com')
116
    with pytest.raises(CommandError) as exc_info:
117
        call_command('create_tenant', '--legacy-hostname', 'host1.com', 'host2.com', 'host3.com')
118
    assert str(exc_info.value) == 'You must specify only hostname when using --legacy-hostname'
119
    assert not schema_exists('host1_com')
120
    assert not schema_exists('host2_com')
121
    assert not schema_exists('host3_com')
122

  
123

  
124
def test_create_tenant_from_existing_tenant_error_does_not_exists(db):
125
    assert not schema_exists('host1_com')
126
    assert not schema_exists('host2_com')
127
    with pytest.raises(CommandError) as exc_info:
128
        call_command('create_tenant', '--legacy-hostname', 'host1.com', 'host2.com')
129
    assert str(exc_info.value) == 'legacy tenant does not exists'
130
    assert not schema_exists('host1_com')
131
    assert not schema_exists('host2_com')
87
-