Skip to content

Commit 510fbfe

Browse files
Eoghan Glynnpixelb
authored andcommitted
Support DB auto-create suppression.
Adds a new boolean config option, db_auto_create, to allow the DB auto-creation be suppressed on demand. This defaults to True for now to maintain the pre-existing behaviour, but should be changed to False before the Folsom release. The 'glance-manage db_sync' command will now create the image* tables if the DB did not previously exist. The db_auto_create flag is irrelevant in that case. The @glance.tests.function.runs_sql annotation is now obsolete as the glance-api/registry services launched by functional tests must now all run against an on-disk sqlite instance (as opposed to in-memory, as this makes no sense when the DB tables are created in advance). Change-Id: I05fc6b3ca7691dfaf00bc75a0743c921c93b9694 Conflicts: glance/tests/functional/__init__.py glance/tests/functional/test_sqlite.py glance/tests/functional/v1/test_api.py glance/tests/functional/v2/test_images.py
1 parent 7d8791a commit 510fbfe

File tree

9 files changed

+1964
-40
lines changed

9 files changed

+1964
-40
lines changed

bin/glance-manage

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ from glance.common import cfg
4444
from glance.common import config
4545
from glance.common import exception
4646
import glance.registry.db
47+
import glance.registry.db.api
4748
import glance.registry.db.migration
4849

4950

@@ -75,7 +76,14 @@ def do_version_control(conf, args):
7576

7677

7778
def do_db_sync(conf, args):
78-
"""Place a database under migration control and upgrade"""
79+
"""
80+
Place a database under migration control and upgrade,
81+
creating first if necessary.
82+
"""
83+
# override auto-create flag, as complete DB should always
84+
# be created on sync if not already existing
85+
conf.db_auto_create = True
86+
glance.registry.db.api.configure_db(conf)
7987
version = args.pop(0) if args else None
8088
current_version = args.pop(0) if args else None
8189
glance.registry.db.migration.db_sync(conf, version, current_version)

etc/glance-registry.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ backlog = 4096
2323
# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine
2424
sql_connection = sqlite:///glance.sqlite
2525

26+
# Whether the glance service creates the database tables
27+
# automatically at startup, or explicitly with db_sync
28+
db_auto_create = True
29+
2630
# Period in seconds after which SQLAlchemy should reestablish its connection
2731
# to the database.
2832
#

glance/registry/db/api.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
cfg.IntOpt('sql_idle_timeout', default=3600),
6969
cfg.StrOpt('sql_connection', default='sqlite:///glance.sqlite'),
7070
cfg.IntOpt('sql_max_retries', default=10),
71-
cfg.IntOpt('sql_retry_interval', default=1)
71+
cfg.IntOpt('sql_retry_interval', default=1),
72+
cfg.BoolOpt('db_auto_create', default=True),
7273
]
7374

7475

@@ -102,7 +103,12 @@ def configure_db(conf):
102103
"""
103104
global _ENGINE, sa_logger, logger, _MAX_RETRIES, _RETRY_INTERVAL
104105
if not _ENGINE:
105-
conf.register_opts(db_opts)
106+
for opt in db_opts:
107+
# avoid duplicate registration
108+
try:
109+
getattr(conf, opt.name)
110+
except cfg.NoSuchOptError:
111+
conf.register_opt(opt)
106112
sql_connection = conf.sql_connection
107113
_MAX_RETRIES = conf.sql_max_retries
108114
_RETRY_INTERVAL = conf.sql_retry_interval
@@ -131,12 +137,16 @@ def configure_db(conf):
131137
elif conf.verbose:
132138
sa_logger.setLevel(logging.INFO)
133139

134-
models.register_models(_ENGINE)
135-
try:
136-
migration.version_control(conf)
137-
except exception.DatabaseMigrationError:
138-
# only arises when the DB exists and is under version control
139-
pass
140+
if conf.db_auto_create:
141+
logger.info('auto-creating glance registry DB')
142+
models.register_models(_ENGINE)
143+
try:
144+
migration.version_control(conf)
145+
except exception.DatabaseMigrationError:
146+
# only arises when the DB exists and is under version control
147+
pass
148+
else:
149+
logger.info('not auto-creating glance registry DB')
140150

141151

142152
def check_mutate_authorization(context, image_ref):

glance/tests/functional/__init__.py

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,27 +43,6 @@
4343
execute, get_unused_port = test_utils.execute, test_utils.get_unused_port
4444

4545

46-
def runs_sql(func):
47-
"""
48-
Decorator for a test case method that ensures that the
49-
sql_connection setting is overridden to ensure a disk-based
50-
SQLite database so that arbitrary SQL statements can be
51-
executed out-of-process against the datastore...
52-
"""
53-
@functools.wraps(func)
54-
def wrapped(*a, **kwargs):
55-
test_obj = a[0]
56-
orig_sql_connection = test_obj.registry_server.sql_connection
57-
try:
58-
if orig_sql_connection.startswith('sqlite'):
59-
test_obj.registry_server.sql_connection =\
60-
"sqlite:///tests.sqlite"
61-
func(*a, **kwargs)
62-
finally:
63-
test_obj.registry_server.sql_connection = orig_sql_connection
64-
return wrapped
65-
66-
6746
class Server(object):
6847
"""
6948
Class used to easily manage starting and stopping
@@ -89,6 +68,7 @@ def __init__(self, test_dir, port):
8968
self.exec_env = None
9069
self.deployment_flavor = ''
9170
self.server_control_options = ''
71+
self.needs_database = False
9272

9373
def write_conf(self, **kwargs):
9474
"""
@@ -145,6 +125,8 @@ def start(self, expect_exit=True, expected_exitcode=0, **kwargs):
145125
# Ensure the configuration file is written
146126
overridden = self.write_conf(**kwargs)[1]
147127

128+
self.create_database()
129+
148130
cmd = ("%(server_control)s %(server_name)s start "
149131
"%(conf_file_name)s --pid-file=%(pid_file)s "
150132
"%(server_control_options)s"
@@ -156,6 +138,23 @@ def start(self, expect_exit=True, expected_exitcode=0, **kwargs):
156138
expected_exitcode=expected_exitcode,
157139
context=overridden)
158140

141+
def create_database(self):
142+
"""Create database if required for this server"""
143+
if self.needs_database:
144+
conf_dir = os.path.join(self.test_dir, 'etc')
145+
utils.safe_mkdirs(conf_dir)
146+
conf_filepath = os.path.join(conf_dir, 'glance-manage.conf')
147+
148+
with open(conf_filepath, 'wb') as conf_file:
149+
conf_file.write('[DEFAULT]\n')
150+
conf_file.write('sql_connection = %s' % self.sql_connection)
151+
conf_file.flush()
152+
153+
cmd = ('bin/glance-manage db_sync --config-file %s'
154+
% conf_filepath)
155+
execute(cmd, no_venv=self.no_venv, exec_env=self.exec_env,
156+
expect_exit=True)
157+
159158
def stop(self):
160159
"""
161160
Spin down the server.
@@ -212,6 +211,12 @@ def __init__(self, test_dir, port, registry_port, policy_file,
212211
self.policy_file = policy_file
213212
self.policy_default_rule = 'default'
214213
self.server_control_options = '--capture-output'
214+
215+
self.needs_database = True
216+
default_sql_connection = 'sqlite:///tests.sqlite'
217+
self.sql_connection = os.environ.get('GLANCE_TEST_SQL_CONNECTION',
218+
default_sql_connection)
219+
215220
self.conf_base = """[DEFAULT]
216221
verbose = %(verbose)s
217222
debug = %(debug)s
@@ -248,6 +253,8 @@ def __init__(self, test_dir, port, registry_port, policy_file,
248253
image_cache_driver = %(image_cache_driver)s
249254
policy_file = %(policy_file)s
250255
policy_default_rule = %(policy_default_rule)s
256+
db_auto_create = False
257+
sql_connection = %(sql_connection)s
251258
[paste_deploy]
252259
flavor = %(deployment_flavor)s
253260
"""
@@ -300,7 +307,8 @@ def __init__(self, test_dir, port):
300307
super(RegistryServer, self).__init__(test_dir, port)
301308
self.server_name = 'registry'
302309

303-
default_sql_connection = 'sqlite:///'
310+
self.needs_database = True
311+
default_sql_connection = 'sqlite:///tests.sqlite'
304312
self.sql_connection = os.environ.get('GLANCE_TEST_SQL_CONNECTION',
305313
default_sql_connection)
306314

@@ -315,6 +323,7 @@ def __init__(self, test_dir, port):
315323
bind_host = 0.0.0.0
316324
bind_port = %(bind_port)s
317325
log_file = %(log_file)s
326+
db_auto_create = False
318327
sql_connection = %(sql_connection)s
319328
sql_idle_timeout = 3600
320329
api_limit_max = 1000
@@ -625,11 +634,6 @@ def stop_servers(self):
625634
if os.path.exists(self.test_dir):
626635
shutil.rmtree(self.test_dir)
627636

628-
# We do this here because the @runs_sql decorator above
629-
# actually resets the registry server's sql_connection
630-
# to the original (usually memory-based SQLite connection)
631-
# and this block of code is run *before* the finally:
632-
# block in that decorator...
633637
self._reset_database(self.registry_server.sql_connection)
634638

635639
def run_sql_cmd(self, sql):

glance/tests/functional/test_bin_glance.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,6 @@ def test_killed_image_not_in_index(self):
647647

648648
self.stop_servers()
649649

650-
@functional.runs_sql
651650
def test_add_location_with_checksum(self):
652651
"""
653652
We test the following:
@@ -679,7 +678,6 @@ def test_add_location_with_checksum(self):
679678

680679
self.stop_servers()
681680

682-
@functional.runs_sql
683681
def test_add_location_without_checksum(self):
684682
"""
685683
We test the following:
@@ -711,7 +709,6 @@ def test_add_location_without_checksum(self):
711709

712710
self.stop_servers()
713711

714-
@functional.runs_sql
715712
def test_add_clear(self):
716713
"""
717714
We test the following:
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2+
3+
# Copyright 2012 Red Hat, Inc
4+
# All Rights Reserved.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
7+
# not use this file except in compliance with the License. You may obtain
8+
# a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15+
# License for the specific language governing permissions and limitations
16+
# under the License.
17+
18+
"""Functional test cases for glance-manage"""
19+
20+
import os
21+
22+
from glance.common import utils
23+
from glance.tests import functional
24+
from glance.tests.utils import execute, depends_on_exe, skip_if_disabled
25+
26+
27+
class TestGlanceManage(functional.FunctionalTest):
28+
"""Functional tests for glance-manage"""
29+
30+
def setUp(self):
31+
super(TestGlanceManage, self).setUp()
32+
conf_dir = os.path.join(self.test_dir, 'etc')
33+
utils.safe_mkdirs(conf_dir)
34+
self.conf_filepath = os.path.join(conf_dir, 'glance-manage.conf')
35+
self.db_filepath = os.path.join(conf_dir, 'test.sqlite')
36+
self.connection = ('sql_connection = sqlite:///%s' %
37+
self.db_filepath)
38+
39+
def _sync_db(self, auto_create):
40+
with open(self.conf_filepath, 'wb') as conf_file:
41+
conf_file.write('[DEFAULT]\n')
42+
conf_file.write('db_auto_create = %r\n' % auto_create)
43+
conf_file.write(self.connection)
44+
conf_file.flush()
45+
46+
cmd = ('bin/glance-manage db_sync --config-file %s' %
47+
self.conf_filepath)
48+
execute(cmd, raise_error=True)
49+
50+
def _assert_tables(self):
51+
cmd = "sqlite3 %s '.schema'" % self.db_filepath
52+
exitcode, out, err = execute(cmd, raise_error=True)
53+
54+
self.assertTrue('CREATE TABLE images' in out)
55+
self.assertTrue('CREATE TABLE image_tags' in out)
56+
self.assertTrue('CREATE TABLE image_members' in out)
57+
self.assertTrue('CREATE TABLE image_properties' in out)
58+
59+
@depends_on_exe('sqlite3')
60+
@skip_if_disabled
61+
def test_db_creation(self):
62+
"""Test DB creation by db_sync on a fresh DB"""
63+
self._sync_db(True)
64+
65+
self._assert_tables()
66+
67+
self.stop_servers()
68+
69+
@depends_on_exe('sqlite3')
70+
@skip_if_disabled
71+
def test_db_creation_auto_create_overridden(self):
72+
"""Test DB creation with db_auto_create False"""
73+
self._sync_db(False)
74+
75+
self._assert_tables()
76+
77+
self.stop_servers()

glance/tests/functional/test_sqlite.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
class TestSqlite(functional.FunctionalTest):
2626
"""Functional tests for sqlite-specific logic"""
2727

28-
@functional.runs_sql
2928
def test_big_int_mapping(self):
3029
"""Ensure BigInteger not mapped to BIGINT"""
3130
self.cleanup()

0 commit comments

Comments
 (0)