Handling scrubber's exit in non-daemon mode.
Currently, Scrubber exits with a status code of zero upon job fetching errors both in daemon and non daemon mode. It is more appropriate to have scrubber exit with status code 1 if it is not in daemon mode. This patch causes scrubber to exit with status code 1 (RuntimeError)if the scrubber is not running in daemon mode, facilitating accurate exits. Co-Authored-By: Steve Lewis <steve.lewis@rackspace.com> Co-Authored-By: Dharini Chandrasekar <dharini.chandrasekar@intel.com> Closes-Bug: #1548289 Change-Id: Ib75676dc90349f008395fb118978f4e37fa876ea
This commit is contained in:
parent
e842cffc12
commit
91434be160
@ -292,6 +292,11 @@ class ImageSizeLimitExceeded(GlanceException):
|
|||||||
message = _("The provided image is too large.")
|
message = _("The provided image is too large.")
|
||||||
|
|
||||||
|
|
||||||
|
class FailedToGetScrubberJobs(GlanceException):
|
||||||
|
message = _("Scrubber encountered an error while trying to fetch "
|
||||||
|
"scrub jobs.")
|
||||||
|
|
||||||
|
|
||||||
class ImageMemberLimitExceeded(LimitExceeded):
|
class ImageMemberLimitExceeded(LimitExceeded):
|
||||||
message = _("The limit has been exceeded on the number of allowed image "
|
message = _("The limit has been exceeded on the number of allowed image "
|
||||||
"members for this image. Attempted: %(attempted)s, "
|
"members for this image. Attempted: %(attempted)s, "
|
||||||
|
@ -27,7 +27,7 @@ from glance.common import crypt
|
|||||||
from glance.common import exception
|
from glance.common import exception
|
||||||
from glance import context
|
from glance import context
|
||||||
import glance.db as db_api
|
import glance.db as db_api
|
||||||
from glance.i18n import _, _LE, _LI, _LW
|
from glance.i18n import _, _LC, _LE, _LI, _LW
|
||||||
import glance.registry.client.v1.api as registry
|
import glance.registry.client.v1.api as registry
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -409,9 +409,12 @@ class Scrubber(object):
|
|||||||
try:
|
try:
|
||||||
records = self.db_queue.get_all_locations()
|
records = self.db_queue.get_all_locations()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
LOG.error(_LE("Can not get scrub jobs from queue: %s") %
|
# Note(dharinic): spawn_n, in Daemon mode will log the
|
||||||
encodeutils.exception_to_unicode(err))
|
# exception raised. Otherwise, exit 1 will occur.
|
||||||
return {}
|
msg = (_LC("Can not get scrub jobs from queue: %s") %
|
||||||
|
encodeutils.exception_to_unicode(err))
|
||||||
|
LOG.critical(msg)
|
||||||
|
raise exception.FailedToGetScrubberJobs()
|
||||||
|
|
||||||
delete_jobs = {}
|
delete_jobs = {}
|
||||||
for image_id, loc_id, loc_uri in records:
|
for image_id, loc_id, loc_uri in records:
|
||||||
|
@ -603,6 +603,10 @@ class FunctionalTest(test_utils.BaseTestCase):
|
|||||||
self.api_protocol = 'http'
|
self.api_protocol = 'http'
|
||||||
self.api_port, api_sock = test_utils.get_unused_port_and_socket()
|
self.api_port, api_sock = test_utils.get_unused_port_and_socket()
|
||||||
self.registry_port, reg_sock = test_utils.get_unused_port_and_socket()
|
self.registry_port, reg_sock = test_utils.get_unused_port_and_socket()
|
||||||
|
# NOTE: Scrubber is enabled by default for the functional tests.
|
||||||
|
# Please disbale it by explicitly setting 'self.include_scrubber' to
|
||||||
|
# False in the test SetUps that do not require Scrubber to run.
|
||||||
|
self.include_scrubber = True
|
||||||
|
|
||||||
self.tracecmd = tracecmd_osmap.get(platform.system())
|
self.tracecmd = tracecmd_osmap.get(platform.system())
|
||||||
|
|
||||||
@ -799,11 +803,11 @@ class FunctionalTest(test_utils.BaseTestCase):
|
|||||||
|
|
||||||
self.start_with_retry(self.api_server, 'api_port', 3, **kwargs)
|
self.start_with_retry(self.api_server, 'api_port', 3, **kwargs)
|
||||||
|
|
||||||
exitcode, out, err = self.scrubber_daemon.start(**kwargs)
|
if self.include_scrubber:
|
||||||
|
exitcode, out, err = self.scrubber_daemon.start(**kwargs)
|
||||||
self.assertEqual(0, exitcode,
|
self.assertEqual(0, exitcode,
|
||||||
"Failed to spin up the Scrubber daemon. "
|
"Failed to spin up the Scrubber daemon. "
|
||||||
"Got: %s" % err)
|
"Got: %s" % err)
|
||||||
|
|
||||||
def ping_server(self, port):
|
def ping_server(self, port):
|
||||||
"""
|
"""
|
||||||
@ -919,7 +923,8 @@ class FunctionalTest(test_utils.BaseTestCase):
|
|||||||
# Spin down the API and default registry server
|
# Spin down the API and default registry server
|
||||||
self.stop_server(self.api_server, 'API server')
|
self.stop_server(self.api_server, 'API server')
|
||||||
self.stop_server(self.registry_server, 'Registry server')
|
self.stop_server(self.registry_server, 'Registry server')
|
||||||
self.stop_server(self.scrubber_daemon, 'Scrubber daemon')
|
if self.include_scrubber:
|
||||||
|
self.stop_server(self.scrubber_daemon, 'Scrubber daemon')
|
||||||
|
|
||||||
self._reset_database(self.registry_server.sql_connection)
|
self._reset_database(self.registry_server.sql_connection)
|
||||||
|
|
||||||
|
@ -274,6 +274,33 @@ class TestScrubber(functional.FunctionalTest):
|
|||||||
|
|
||||||
self.stop_servers()
|
self.stop_servers()
|
||||||
|
|
||||||
|
def test_scrubber_app_queue_errors_not_daemon(self):
|
||||||
|
"""
|
||||||
|
test that the glance-scrubber exits with an exit code > 0 when it
|
||||||
|
fails to lookup images, indicating a configuration error when not
|
||||||
|
in daemon mode.
|
||||||
|
|
||||||
|
Related-Bug: #1548289
|
||||||
|
"""
|
||||||
|
# Don't start the registry server to cause intended failure
|
||||||
|
# Don't start the api server to save time
|
||||||
|
exitcode, out, err = self.scrubber_daemon.start(
|
||||||
|
delayed_delete=True, daemon=False, registry_port=28890)
|
||||||
|
self.assertEqual(0, exitcode,
|
||||||
|
"Failed to spin up the Scrubber daemon. "
|
||||||
|
"Got: %s" % err)
|
||||||
|
|
||||||
|
# Run the Scrubber
|
||||||
|
exe_cmd = "%s -m glance.cmd.scrubber" % sys.executable
|
||||||
|
cmd = ("%s --config-file %s" %
|
||||||
|
(exe_cmd, self.scrubber_daemon.conf_file_name))
|
||||||
|
exitcode, out, err = execute(cmd, raise_error=False)
|
||||||
|
|
||||||
|
self.assertEqual(1, exitcode)
|
||||||
|
self.assertIn('Can not get scrub jobs from queue', err)
|
||||||
|
|
||||||
|
self.stop_server(self.scrubber_daemon, 'Scrubber daemon')
|
||||||
|
|
||||||
def wait_for_scrub(self, path, headers=None):
|
def wait_for_scrub(self, path, headers=None):
|
||||||
"""
|
"""
|
||||||
NOTE(jkoelker) The build servers sometimes take longer than 15 seconds
|
NOTE(jkoelker) The build servers sometimes take longer than 15 seconds
|
||||||
|
@ -40,6 +40,7 @@ class TestImages(functional.FunctionalTest):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestImages, self).setUp()
|
super(TestImages, self).setUp()
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
|
self.include_scrubber = False
|
||||||
self.api_server.deployment_flavor = 'noauth'
|
self.api_server.deployment_flavor = 'noauth'
|
||||||
self.api_server.data_api = 'glance.db.sqlalchemy.api'
|
self.api_server.data_api = 'glance.db.sqlalchemy.api'
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
@ -3028,6 +3029,7 @@ class TestImagesIPv6(functional.FunctionalTest):
|
|||||||
# Setting up monkey patch (2), after object is ready...
|
# Setting up monkey patch (2), after object is ready...
|
||||||
self.ping_server_ipv4 = self.ping_server
|
self.ping_server_ipv4 = self.ping_server
|
||||||
self.ping_server = self.ping_server_ipv6
|
self.ping_server = self.ping_server_ipv6
|
||||||
|
self.include_scrubber = False
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
# Cleaning up monkey patch (2).
|
# Cleaning up monkey patch (2).
|
||||||
@ -3083,6 +3085,7 @@ class TestImageDirectURLVisibility(functional.FunctionalTest):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestImageDirectURLVisibility, self).setUp()
|
super(TestImageDirectURLVisibility, self).setUp()
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
|
self.include_scrubber = False
|
||||||
self.api_server.deployment_flavor = 'noauth'
|
self.api_server.deployment_flavor = 'noauth'
|
||||||
|
|
||||||
def _url(self, path):
|
def _url(self, path):
|
||||||
@ -3292,6 +3295,7 @@ class TestImageLocationSelectionStrategy(functional.FunctionalTest):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestImageLocationSelectionStrategy, self).setUp()
|
super(TestImageLocationSelectionStrategy, self).setUp()
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
|
self.include_scrubber = False
|
||||||
self.api_server.deployment_flavor = 'noauth'
|
self.api_server.deployment_flavor = 'noauth'
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
ret = test_utils.start_http_server("foo_image_id%d" % i,
|
ret = test_utils.start_http_server("foo_image_id%d" % i,
|
||||||
@ -3395,6 +3399,7 @@ class TestImageMembers(functional.FunctionalTest):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestImageMembers, self).setUp()
|
super(TestImageMembers, self).setUp()
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
|
self.include_scrubber = False
|
||||||
self.api_server.deployment_flavor = 'fakeauth'
|
self.api_server.deployment_flavor = 'fakeauth'
|
||||||
self.registry_server.deployment_flavor = 'fakeauth'
|
self.registry_server.deployment_flavor = 'fakeauth'
|
||||||
self.start_servers(**self.__dict__.copy())
|
self.start_servers(**self.__dict__.copy())
|
||||||
@ -3761,6 +3766,7 @@ class TestQuotas(functional.FunctionalTest):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestQuotas, self).setUp()
|
super(TestQuotas, self).setUp()
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
|
self.include_scrubber = False
|
||||||
self.api_server.deployment_flavor = 'noauth'
|
self.api_server.deployment_flavor = 'noauth'
|
||||||
self.registry_server.deployment_flavor = 'trusted-auth'
|
self.registry_server.deployment_flavor = 'trusted-auth'
|
||||||
self.user_storage_quota = 100
|
self.user_storage_quota = 100
|
||||||
|
@ -143,3 +143,4 @@ class TestTasksWithRegistry(TestTasks):
|
|||||||
self.api_server.data_api = (
|
self.api_server.data_api = (
|
||||||
'glance.tests.functional.v2.registry_data_api')
|
'glance.tests.functional.v2.registry_data_api')
|
||||||
self.registry_server.deployment_flavor = 'trusted-auth'
|
self.registry_server.deployment_flavor = 'trusted-auth'
|
||||||
|
self.include_scrubber = False
|
||||||
|
@ -16,12 +16,14 @@
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import glance_store
|
import glance_store
|
||||||
|
import mock
|
||||||
from mock import patch
|
from mock import patch
|
||||||
from mox3 import mox
|
from mox3 import mox
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
# NOTE(jokke): simplified transition to py3, behaves like py2 xrange
|
# NOTE(jokke): simplified transition to py3, behaves like py2 xrange
|
||||||
from six.moves import range
|
from six.moves import range
|
||||||
|
|
||||||
|
from glance.common import exception
|
||||||
from glance import scrubber
|
from glance import scrubber
|
||||||
from glance.tests import utils as test_utils
|
from glance.tests import utils as test_utils
|
||||||
|
|
||||||
@ -110,6 +112,16 @@ class TestScrubber(test_utils.BaseTestCase):
|
|||||||
scrub._scrub_image(id, [(id, '-', uri)])
|
scrub._scrub_image(id, [(id, '-', uri)])
|
||||||
self.mox.VerifyAll()
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def test_scrubber_exits(self):
|
||||||
|
# Checks for Scrubber exits when it is not able to fetch jobs from
|
||||||
|
# the queue
|
||||||
|
scrub_jobs = scrubber.ScrubDBQueue.get_all_locations
|
||||||
|
scrub_jobs = mock.MagicMock()
|
||||||
|
scrub_jobs.side_effect = exception.NotFound
|
||||||
|
scrub = scrubber.Scrubber(glance_store)
|
||||||
|
self.assertRaises(exception.FailedToGetScrubberJobs,
|
||||||
|
scrub._get_delete_jobs)
|
||||||
|
|
||||||
|
|
||||||
class TestScrubDBQueue(test_utils.BaseTestCase):
|
class TestScrubDBQueue(test_utils.BaseTestCase):
|
||||||
|
|
||||||
|
12
releasenotes/notes/scrubber-exit-e5d77f6f1a38ffb7.yaml
Normal file
12
releasenotes/notes/scrubber-exit-e5d77f6f1a38ffb7.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Please note a change in the Scrubber's behavior in case
|
||||||
|
of job fetching errors:
|
||||||
|
|
||||||
|
* If configured to work in daemon mode, the Scrubber
|
||||||
|
will log an error message at level critical, but
|
||||||
|
will not exit the process.
|
||||||
|
* If configured to work in non-daemon mode, the Scrubber
|
||||||
|
will log an error message at level critical and exit
|
||||||
|
with status one.
|
Loading…
x
Reference in New Issue
Block a user