Projet

Général

Profil

0001-command-add-a-delete_tenant-command-15636.patch

Jean-Baptiste Jaillet, 12 mai 2017 18:50

Télécharger (10,2 ko)

Voir les différences:

Subject: [PATCH] command: add a delete_tenant command (#15636)

 tests/test_ctl.py        | 105 +++++++++++++++++++++++++++++++++++++++++++
 wcs/ctl/check_hobos.py   |   9 +++-
 wcs/ctl/delete_tenant.py | 114 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 227 insertions(+), 1 deletion(-)
 create mode 100644 wcs/ctl/delete_tenant.py
tests/test_ctl.py
3 3
import collections
4 4
from email.mime.text import MIMEText
5 5
from email.mime.multipart import MIMEMultipart
6
import psycopg2
6 7

  
7 8
from wcs.formdef import FormDef
8 9
from wcs.workflows import Workflow
......
13 14
from wcs.ctl.process_bounce import CmdProcessBounce
14 15
from wcs.ctl.wipe_data import CmdWipeData
15 16
from wcs.ctl.trigger_jumps import select_and_jump_formdata
17
from wcs.ctl.delete_tenant import CmdDeleteTenant
18
from wcs.sql import get_connection_and_cursor, cleanup_connection
16 19

  
17 20
from utilities import create_temporary_pub, clean_temporary_pub
18 21

  
......
186 189
    assert f1.status == f2.status == 'wf-%s' % st1.id
187 190
    assert not f1.workflow_data
188 191
    assert not f2.workflow_data
192

  
193

  
194
def test_delete_tenant():
195
    pub = create_temporary_pub(sql_mode=True)
196
    delete_cmd = CmdDeleteTenant()
197

  
198
    assert os.path.isdir(pub.app_dir)
199

  
200
    sub_options_class = collections.namedtuple('Options', ['force_drop'])
201
    sub_options = sub_options_class(False)
202

  
203
    delete_cmd.delete_tenant(pub, sub_options, [])
204

  
205
    assert not os.path.isdir(pub.app_dir)
206
    parent_dir = os.path.dirname(pub.app_dir)
207
    if not [filename for filename in os.listdir(parent_dir) if 'removed' in filename]:
208
        assert False
209

  
210
    conn, cur = get_connection_and_cursor()
211
    cur.execute("""SELECT schema_name
212
                FROM information_schema.schemata
213
                WHERE schema_name like '%removed%'""")
214

  
215
    assert len(cur.fetchall()) == 1
216

  
217
    clean_temporary_pub()
218
    pub = create_temporary_pub(sql_mode=True)
219

  
220
    sub_options = sub_options_class(True)
221
    delete_cmd.delete_tenant(pub, sub_options, [])
222

  
223
    conn, cur = get_connection_and_cursor(new=True)
224

  
225
    assert not os.path.isdir(pub.app_dir)
226
    cur.execute("""SELECT table_name
227
                FROM information_schema.tables
228
                WHERE table_schema = 'public' AND
229
                table_type = 'BASE TABLE'""")
230

  
231
    assert not cur.fetchall()
232

  
233
    cur.execute("""SELECT datname
234
                FROM pg_database
235
                WHERE datname = '%s'""" % pub.cfg['postgresql']['database'])
236

  
237
    assert cur.fetchall()
238

  
239
    clean_temporary_pub()
240
    pub = create_temporary_pub(sql_mode=True)
241

  
242
    cleanup_connection()
243
    sub_options = sub_options_class(True)
244
    pub.cfg['postgresql']['createdb-connection-params'] = {
245
        'user': pub.cfg['postgresql']['user'],
246
        'database': 'postgres'
247
    }
248
    delete_cmd.delete_tenant(pub, sub_options, [])
249

  
250
    pgconn = psycopg2.connect(**pub.cfg['postgresql']['createdb-connection-params'])
251
    cur = pgconn.cursor()
252

  
253
    cur.execute("""SELECT datname
254
                FROM pg_database
255
                WHERE datname = '%s'""" % pub.cfg['postgresql']['database'])
256
    assert not cur.fetchall()
257
    cur.close()
258
    pgconn.close()
259

  
260
    clean_temporary_pub()
261
    pub = create_temporary_pub(sql_mode=True)
262
    cleanup_connection()
263

  
264
    sub_options = sub_options_class(False)
265
    pub.cfg['postgresql']['createdb-connection-params'] = {
266
        'user': pub.cfg['postgresql']['user'],
267
        'database': 'postgres'
268
    }
269
    delete_cmd.delete_tenant(pub, sub_options, [])
270

  
271
    pgconn = psycopg2.connect(**pub.cfg['postgresql']['createdb-connection-params'])
272
    pgconn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
273
    cur = pgconn.cursor()
274

  
275
    cur.execute("""SELECT datname
276
                FROM pg_database
277
                WHERE datname like '%removed%'""")
278

  
279
    result = cur.fetchall()
280
    assert len(result) == 1
281

  
282
    #clean this db after test
283
    cur.execute("""DROP DATABASE %s""" % result[0][0])
284

  
285
    cur.execute("""SELECT datname
286
                FROM pg_database
287
                WHERE datname = '%s'""" % pub.cfg['postgresql']['database'])
288

  
289
    assert not cur.fetchall()
290
    cur.close()
291
    conn.close()
292

  
293
    clean_temporary_pub()
wcs/ctl/check_hobos.py
422 422
            cur.execute('''CREATE DATABASE %s''' % database_name)
423 423
        except psycopg2.Error as e:
424 424
            if e.pgcode == psycopg2.errorcodes.DUPLICATE_DATABASE:
425
                new_database = False
425
                cur.execute("""SELECT table_name
426
                            FROM information_schema.tables
427
                            WHERE table_schema = 'public' AND
428
                            table_type = 'BASE TABLE' AND
429
                            table_name = 'wcs_meta'""")
430

  
431
                if cur.fetchall():
432
                    new_database = False
426 433
            else:
427 434
                print >> sys.stderr, 'failed to create database (%s)' % \
428 435
                        psycopg2.errorcodes.lookup(e.pgcode)
wcs/ctl/delete_tenant.py
1
#w.c.s. -  web application for online forms 
2
# Copyright (C) 2005-2014  Entr'ouvert 
3
# 
4
# This program is free software; you can redistribute it and/or modify 
5
# it under the terms of the GNU General Public License as published by 
6
# the Free Software Foundation; either version 2 of the License, or 
7
# (at your option) any later version. 
8
# 
9
# This program is distributed in the hope that it will be useful, 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
12
# GNU General Public License for more details. 
13
# 
14
# You should have received a copy of the GNU General Public License 
15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16

  
17
import os
18
import sys
19
import psycopg2
20
import psycopg2.errorcodes
21
from psycopg2 import OperationalError
22
from datetime import datetime
23
from shutil import rmtree
24

  
25
from qommon.ctl import Command, make_option
26

  
27

  
28
class CmdDeleteTenant(Command):
29
    name = 'delete_tenant'
30

  
31
    def __init__(self):
32
        Command.__init__(self, [
33
                make_option('--force-drop', action='store_true', default=False,
34
                           dest='force_drop'),
35
                ])
36

  
37
    def execute(self, base_options, sub_options, args):
38
        import publisher
39

  
40
        publisher.WcsPublisher.configure(self.config)
41
        pub = publisher.WcsPublisher.create_publisher(
42
                register_cron=False, register_tld_names=False)
43

  
44
        hostname = args[0]
45
        pub.app_dir = os.path.join(pub.app_dir, hostname)
46
        pub.set_config()
47
        self.delete_tenant(pub, sub_options, args)
48

  
49
    def delete_tenant(self, pub, options, args):
50
        postgresql_cfg = {}
51
        for k, v in pub.cfg['postgresql'].items():
52
            if v and isinstance(v, basestring):
53
                postgresql_cfg[k] = v
54

  
55
        createdb_cfg = pub.cfg['postgresql'].get('createdb-connection-params', {})
56
        createdb = True
57
        if not createdb_cfg:
58
            createdb_cfg = postgresql_cfg
59
            createdb = False
60
        try:
61
            pgconn = psycopg2.connect(**createdb_cfg)
62
        except psycopg2.Error as e:
63
            print >> sys.stderr, 'failed to connect to postgresql (%s)' % psycopg2.errorcodes.lookup(e.pgcode)
64
            return
65

  
66
        pgconn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
67
        cur = pgconn.cursor()
68
        try:
69
            dbname = pub.cfg['postgresql']['database']
70
            if createdb:
71
                if options.force_drop:
72
                    cur.execute('DROP DATABASE %s' % dbname)
73
                else:
74
                    deletion_date = datetime.now().strftime('%Y%m%d_%H%M%S_%f')
75
                    cur.execute('ALTER DATABASE %s RENAME TO removed_%s_%s' % (dbname, deletion_date, dbname))
76
            else:
77
                cur.execute("""SELECT table_name
78
                            FROM information_schema.tables
79
                            WHERE table_schema = 'public' AND
80
                            table_type = 'BASE TABLE'""")
81

  
82
                tables_names = [x[0] for x in cur.fetchall()]
83

  
84
                if options.force_drop:
85
                    for table_name in tables_names:
86
                        cur.execute('DROP TABLE %s CASCADE' % table_name)
87

  
88
                else:
89
                    deletion_date = datetime.now().strftime('%Y%m%d_%H%M%S_%f')
90
                    schema_name = 'removed_%s_%s' % (deletion_date, dbname)
91
                    cur.execute("CREATE SCHEMA %s" % schema_name[:63])
92
                    for table_name in tables_names:
93
                        cur.execute('ALTER TABLE %s SET SCHEMA %s' %
94
                                    (table_name, schema_name[:63]))
95

  
96
            if options.force_drop:
97
                rmtree(pub.app_dir)
98
            else:
99
                os.rename(pub.app_dir, pub.app_dir + '_removed_%s.invalid' % deletion_date)
100

  
101
        except psycopg2.Error as e:
102
            if e.pgcode == psycopg2.errorcodes.DUPLICATE_TABLE:
103
                # when tables names are too long, postgresql truncates, which
104
                # can create some duplicates
105
                temp_date = datetime.now().strftime('%Y%m%d_%H%M%S_%f')
106
                cur.execute('ALTER TABLE %s RENAME TO removed_%s_%s' %
107
                            (table_name, temp_date, table_name))
108
            else:
109
                print >> sys.stderr, 'failed to alter database %s : (%s)' % (createdb_cfg['database'], psycopg2.errorcodes.lookup(e.pgcode))
110
                return
111

  
112
        cur.close()
113

  
114
CmdDeleteTenant.register()
0
-