summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2023-03-14 19:54:14 +0000
committerGerrit Code Review <review@openstack.org>2023-03-14 19:54:14 +0000
commitb023562e1d31d3e60c195d701e6a9ff52e2fc956 (patch)
tree22d66dfa11a5edc61f85afd678bde6fc2b3a1916
parentcb4ac8d37deeadaa43e74125fa8950cd331d4b3e (diff)
parent6e62b872ef2f3fc879e5c6d6ecb7696c09f28f3c (diff)
downloadglance-b023562e1d31d3e60c195d701e6a9ff52e2fc956.tar.gz
Merge "Add a check on startup for staging directory"
-rw-r--r--glance/common/wsgi.py9
-rw-r--r--glance/common/wsgi_app.py11
-rw-r--r--glance/tests/unit/api/test_cmd.py11
-rw-r--r--glance/tests/unit/common/test_wsgi.py24
-rw-r--r--glance/tests/unit/common/test_wsgi_app.py25
5 files changed, 79 insertions, 1 deletions
diff --git a/glance/common/wsgi.py b/glance/common/wsgi.py
index d9af2280f..452cb3232 100644
--- a/glance/common/wsgi.py
+++ b/glance/common/wsgi.py
@@ -456,6 +456,15 @@ class BaseServer(metaclass=abc.ABCMeta):
self.configure()
self.start_wsgi()
+ # NOTE(danms): This may raise GlanceException if the staging store is
+ # not configured properly, which will be caught and printed by
+ # cmd/api.py as an error message and abort startup.
+ staging = housekeeping.staging_store_path()
+ if not os.path.exists(staging) and CONF.enabled_import_methods:
+ LOG.warning(_LW('Import methods are enabled but staging directory '
+ '%(path)s does not exist; Imports will fail!'),
+ {'path': staging})
+
cleaner = housekeeping.StagingStoreCleaner(glance.db.get_api())
self.pool.spawn_n(cleaner.clean_orphaned_staging_residue)
diff --git a/glance/common/wsgi_app.py b/glance/common/wsgi_app.py
index ab1f12170..c2a0cba2a 100644
--- a/glance/common/wsgi_app.py
+++ b/glance/common/wsgi_app.py
@@ -24,7 +24,7 @@ import glance.async_
from glance.common import config
from glance.common import store_utils
from glance import housekeeping
-from glance.i18n import _
+from glance.i18n import _, _LW
from glance import notifier
CONF = cfg.CONF
@@ -141,6 +141,15 @@ def init_app():
glance_store.create_stores(CONF)
glance_store.verify_default_store()
+ # NOTE(danms): This may raise GlanceException if the staging store is
+ # not configured properly, which will bubble up to the WSGI server,
+ # aborting application load as desired.
+ staging = housekeeping.staging_store_path()
+ if not os.path.exists(staging) and CONF.enabled_import_methods:
+ LOG.warning(_LW('Import methods are enabled but staging directory '
+ '%(path)s does not exist; Imports will fail!'),
+ {'path': staging})
+
run_staging_cleanup()
_setup_os_profiler()
diff --git a/glance/tests/unit/api/test_cmd.py b/glance/tests/unit/api/test_cmd.py
index 49ec38b14..8038ecb61 100644
--- a/glance/tests/unit/api/test_cmd.py
+++ b/glance/tests/unit/api/test_cmd.py
@@ -83,6 +83,17 @@ class TestGlanceApiCmd(test_utils.BaseTestCase):
exit = self.assertRaises(SystemExit, glance.cmd.api.main)
self.assertEqual(2, exit.code)
+ @mock.patch('glance.async_.set_threadpool_model', new=mock.MagicMock())
+ def test_cleaner_store_config_assertion(self):
+ failure = exc.GlanceException('This is what happens with http://')
+ self.config(node_staging_uri='http://good.luck')
+ self.mock_object(glance.common.wsgi.Server, 'start',
+ self._raise(failure))
+ # Make sure that a failure to run the wsgi.Server will call our
+ # clean print-and-abort handler.
+ exit = self.assertRaises(SystemExit, glance.cmd.api.main)
+ self.assertEqual(99, exit.code)
+
@mock.patch.object(glance.common.config, 'parse_cache_args')
@mock.patch.object(logging, 'setup')
@mock.patch.object(glance.image_cache.ImageCache, 'init_driver')
diff --git a/glance/tests/unit/common/test_wsgi.py b/glance/tests/unit/common/test_wsgi.py
index 61b9e74cc..b7c6bedef 100644
--- a/glance/tests/unit/common/test_wsgi.py
+++ b/glance/tests/unit/common/test_wsgi.py
@@ -637,6 +637,30 @@ class ServerTest(test_utils.BaseTestCase):
self.assertEqual(expected_workers,
len(server.children))
+ def test_invalid_staging_uri(self):
+ self.config(node_staging_uri='http://good.luck')
+ server = wsgi.Server()
+ with mock.patch.object(server, 'start_wsgi'):
+ # Make sure a stating URI with an bad scheme will abort startup
+ self.assertRaises(exception.GlanceException,
+ server.start, 'fake-application', 34567)
+
+ @mock.patch('os.path.exists')
+ def test_missing_staging_dir(self, mock_exists):
+ mock_exists.return_value = False
+ server = wsgi.Server()
+ with mock.patch.object(server, 'start_wsgi'):
+ # Since we are mocking out start_wsgi, create a fake pool ourselves
+ server.pool = mock.MagicMock()
+ with mock.patch.object(wsgi, 'LOG') as mock_log:
+ server.start('fake-application', 34567)
+ mock_exists.assert_called_once_with('/tmp/staging/')
+ # Make sure a missing staging directory will log a warning.
+ mock_log.warning.assert_called_once_with(
+ 'Import methods are enabled but staging directory '
+ '%(path)s does not exist; Imports will fail!',
+ {'path': '/tmp/staging/'})
+
class TestHelpers(test_utils.BaseTestCase):
diff --git a/glance/tests/unit/common/test_wsgi_app.py b/glance/tests/unit/common/test_wsgi_app.py
index 87afc40d7..3de42cb77 100644
--- a/glance/tests/unit/common/test_wsgi_app.py
+++ b/glance/tests/unit/common/test_wsgi_app.py
@@ -19,6 +19,7 @@ from unittest import mock
from glance.api import common
from glance.api.v2 import cached_images
import glance.async_
+from glance.common import exception
from glance.common import wsgi_app
from glance.tests import utils as test_utils
@@ -114,3 +115,27 @@ class TestWsgiAppInit(test_utils.BaseTestCase):
mock_conf.return_value = []
wsgi_app.init_app()
mock_Timer.assert_not_called()
+
+ @mock.patch('glance.common.wsgi_app._get_config_files')
+ @mock.patch('glance.async_._THREADPOOL_MODEL', new=None)
+ @mock.patch('glance.common.config.load_paste_app', new=mock.MagicMock())
+ def test_staging_store_uri_assertion(self, mock_conf):
+ self.config(node_staging_uri='http://good.luck')
+ mock_conf.return_value = []
+ # Make sure a staging URI with a bad scheme will abort startup
+ self.assertRaises(exception.GlanceException, wsgi_app.init_app)
+
+ @mock.patch('glance.common.wsgi_app._get_config_files')
+ @mock.patch('glance.async_._THREADPOOL_MODEL', new=None)
+ @mock.patch('glance.common.config.load_paste_app', new=mock.MagicMock())
+ @mock.patch('os.path.exists')
+ def test_staging_store_path_check(self, mock_exists, mock_conf):
+ mock_exists.return_value = False
+ mock_conf.return_value = []
+ with mock.patch.object(wsgi_app, 'LOG') as mock_log:
+ wsgi_app.init_app()
+ # Make sure that a missing staging directory will log a warning.
+ mock_log.warning.assert_called_once_with(
+ 'Import methods are enabled but staging directory '
+ '%(path)s does not exist; Imports will fail!',
+ {'path': '/tmp/staging/'})