Hello community,
here is the log from the commit of package python-oslo.service for
openSUSE:Factory checked in at 2015-12-21 12:03:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-oslo.service (Old)
and /work/SRC/openSUSE:Factory/.python-oslo.service.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-oslo.service"
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-oslo.service/python-oslo.service.changes
2015-10-30 13:44:00.000000000 +0100
+++
/work/SRC/openSUSE:Factory/.python-oslo.service.new/python-oslo.service.changes
2015-12-21 12:03:55.000000000 +0100
@@ -1,0 +2,11 @@
+Thu Nov 19 18:25:51 UTC 2015 - [email protected]
+
+- update to 0.12.0:
+ * Document graceful_shutdown_timeout config option
+ * Remove py26 env from test list
+ * Added config option graceful_shutdown_timeout
+ * Add docstring for LoopingCallBase._start()
+ * Add doc8 to py27 tox env and fix raised issues
+ * Document termination of children on SIGHUP
+
+-------------------------------------------------------------------
Old:
----
oslo.service-0.10.0.tar.gz
New:
----
oslo.service-0.12.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-oslo.service.spec ++++++
--- /var/tmp/diff_new_pack.RGMNnS/_old 2015-12-21 12:03:56.000000000 +0100
+++ /var/tmp/diff_new_pack.RGMNnS/_new 2015-12-21 12:03:56.000000000 +0100
@@ -17,7 +17,7 @@
Name: python-oslo.service
-Version: 0.10.0
+Version: 0.12.0
Release: 0
Summary: Oslo service library
License: Apache-2.0
@@ -36,10 +36,10 @@
Requires: python-greenlet >= 0.3.2
Requires: python-monotonic >= 0.3
Requires: python-oslo.concurrency >= 2.3.0
-Requires: python-oslo.config >= 2.3.0
+Requires: python-oslo.config >= 2.6.0
Requires: python-oslo.i18n >= 1.5.0
Requires: python-oslo.log >= 1.8.0
-Requires: python-oslo.utils >= 2.0.0
+Requires: python-oslo.utils >= 2.4.0
Requires: python-six >= 1.9.0
BuildRoot: %{_tmppath}/%{name}-%{version}-build
++++++ oslo.service-0.10.0.tar.gz -> oslo.service-0.12.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/AUTHORS
new/oslo.service-0.12.0/AUTHORS
--- old/oslo.service-0.10.0/AUTHORS 2015-10-12 17:38:47.000000000 +0200
+++ new/oslo.service-0.12.0/AUTHORS 2015-11-10 00:02:48.000000000 +0100
@@ -46,6 +46,7 @@
Jeff Peeler <[email protected]>
Joe Gordon <[email protected]>
Joe Heck <[email protected]>
+John L. Villalovos <[email protected]>
Joshua Harlow <[email protected]>
Joshua Harlow <[email protected]>
Julien Danjou <[email protected]>
@@ -84,6 +85,7 @@
apporc <[email protected]>
fujioka yuuichi <[email protected]>
gongysh <[email protected]>
+lin-hua-cheng <[email protected]>
liu-sheng <[email protected]>
liyingjun <[email protected]>
ravikumar-venkatesan <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/ChangeLog
new/oslo.service-0.12.0/ChangeLog
--- old/oslo.service-0.10.0/ChangeLog 2015-10-12 17:38:47.000000000 +0200
+++ new/oslo.service-0.12.0/ChangeLog 2015-11-10 00:02:48.000000000 +0100
@@ -1,6 +1,25 @@
CHANGES
=======
+0.12.0
+------
+
+* Document graceful_shutdown_timeout config option
+* Remove py26 env from test list
+* Added config option graceful_shutdown_timeout
+* Updated from global requirements
+* Add docstring for LoopingCallBase._start()
+* Updated from global requirements
+
+0.11.0
+------
+
+* Updated from global requirements
+* Add doc8 to py27 tox env and fix raised issues
+* Document termination of children on SIGHUP
+* Updated from global requirements
+* Updated from global requirements
+
0.10.0
------
@@ -16,6 +35,7 @@
* Expand README and clean up intro to sphinx docs
* Add shields.io version/downloads links/badges into README.rst
* add auto-generated docs for config options
+* Move backoff looping call from IPA to oslo.service
* Change ignore-errors to ignore_errors
* Fix the home-page value in setup.cfg
* WSGI module was corrected
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/PKG-INFO
new/oslo.service-0.12.0/PKG-INFO
--- old/oslo.service-0.10.0/PKG-INFO 2015-10-12 17:38:47.000000000 +0200
+++ new/oslo.service-0.12.0/PKG-INFO 2015-11-10 00:02:49.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: oslo.service
-Version: 0.10.0
+Version: 0.12.0
Summary: oslo.service library
Home-page: http://wiki.openstack.org/wiki/Oslo#oslo.service
Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/doc/source/usage.rst
new/oslo.service-0.12.0/doc/source/usage.rst
--- old/oslo.service-0.10.0/doc/source/usage.rst 2015-10-12
17:37:58.000000000 +0200
+++ new/oslo.service-0.12.0/doc/source/usage.rst 2015-11-10
00:02:06.000000000 +0100
@@ -72,23 +72,28 @@
Launching and controlling services
==================================
-oslo_service.service module provides tools for launching OpenStack services
and controlling their lifecycles.
+oslo_service.service module provides tools for launching OpenStack
+services and controlling their lifecycles.
-A service is an instance of any class that subclasses
:py:class:`oslo_service.service.ServiceBase`.
-:py:class:`ServiceBase <oslo_service.service.ServiceBase>` is an abstract
class that defines an interface every
-service should implement. :py:class:`oslo_service.service.Service` can serve
as a base for constructing new services.
+A service is an instance of any class that
+subclasses :py:class:`oslo_service.service.ServiceBase`.
+:py:class:`ServiceBase <oslo_service.service.ServiceBase>` is an
+abstract class that defines an interface every
+service should implement. :py:class:`oslo_service.service.Service` can
+serve as a base for constructing new services.
Launchers
~~~~~~~~~
oslo_service.service module provides two launchers for running services:
- * :py:class:`oslo_service.service.ServiceLauncher` - used for running one
or more service in
- a parent process.
- * :py:class:`oslo_service.service.ProcessLauncher` - forks a given number
of workers in which
- service(s) are then started.
+ * :py:class:`oslo_service.service.ServiceLauncher` - used for
+ running one or more service in a parent process.
+ * :py:class:`oslo_service.service.ProcessLauncher` - forks a given
+ number of workers in which service(s) are then started.
-It is possible to initialize whatever launcher is needed and then launch a
service using it.
+It is possible to initialize whatever launcher is needed and then
+launch a service using it.
::
@@ -104,9 +109,10 @@
process_launcher = service.ProcessLauncher(CONF, wait_interval=1.0)
process_launcher.launch_service(service.Service(), workers=2)
-Or one can simply call :func:`oslo_service.service.launch` which will
automatically pick an appropriate launcher
-based on a number of workers that are passed to it (ServiceLauncher in case
workers=1 or None and ProcessLauncher in
-other case).
+Or one can simply call :func:`oslo_service.service.launch` which will
+automatically pick an appropriate launcher based on a number of workers that
+are passed to it (ServiceLauncher in case workers=1 or None and
+ProcessLauncher in other case).
::
@@ -117,24 +123,36 @@
launcher = service.launch(CONF, service.Service(), workers=3)
-*NOTE:* Please be informed that it is highly recommended to use no more than
one instance of ServiceLauncher and
-ProcessLauncher classes per process.
+*NOTE:* Please be informed that it is highly recommended to use no
+more than one instance of ServiceLauncher and ProcessLauncher classes
+per process.
Signal handling
~~~~~~~~~~~~~~~
-oslo_service.service provides handlers for such signals as SIGTERM, SIGINT and
SIGHUP.
+oslo_service.service provides handlers for such signals as SIGTERM, SIGINT
+and SIGHUP.
-SIGTERM is used for graceful termination of services. This can allow a server
to wait for all clients to close
-connections while rejecting new incoming requests. To force instantaneous
termination SIGINT signal must be sent.
-
-On receiving SIGHUP configuration files are reloaded and a service is being
reset and started again.Thus, SIGHUP
-can be used for changing config options on the go. To achieve this each
service should implement a *reset* method
-which actually enforces changes to config options values.
+SIGTERM is used for graceful termination of services. This can allow a
+server to wait for all clients to close connections while rejecting new
+incoming requests. Config option graceful_shutdown_timeout specifies how
+many seconds after receiving a SIGTERM signal, a server should continue
+to run, handling the existing connections. Setting graceful_shutdown_timeout
+to zero means that the server will wait indefinitely until all remaining
+requests have been fully served.
+
+To force instantaneous termination SIGINT signal must be sent.
+
+On receiving SIGHUP configuration files are reloaded and a service
+is being reset and started again. Then all child workers are gracefully
+stopped using SIGTERM and workers with new configuration are
+spawned. Thus, SIGHUP can be used for changing config options on the go.
*NOTE:* SIGHUP is not supported on Windows.
+*NOTE:* Config option graceful_shutdown_timeout is not supported on Windows.
-Below is the example of a service with a reset method that allows reloading
logging options by sending a SIGHUP.
+Below is the example of a service with a reset method that allows reloading
+logging options by sending a SIGHUP.
::
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/oslo.service.egg-info/PKG-INFO
new/oslo.service-0.12.0/oslo.service.egg-info/PKG-INFO
--- old/oslo.service-0.10.0/oslo.service.egg-info/PKG-INFO 2015-10-12
17:38:47.000000000 +0200
+++ new/oslo.service-0.12.0/oslo.service.egg-info/PKG-INFO 2015-11-10
00:02:48.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: oslo.service
-Version: 0.10.0
+Version: 0.12.0
Summary: oslo.service library
Home-page: http://wiki.openstack.org/wiki/Oslo#oslo.service
Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/oslo.service.egg-info/pbr.json
new/oslo.service-0.12.0/oslo.service.egg-info/pbr.json
--- old/oslo.service-0.10.0/oslo.service.egg-info/pbr.json 2015-10-12
17:38:47.000000000 +0200
+++ new/oslo.service-0.12.0/oslo.service.egg-info/pbr.json 2015-11-10
00:02:48.000000000 +0100
@@ -1 +1 @@
-{"git_version": "7ecf3be", "is_release": true}
\ No newline at end of file
+{"is_release": true, "git_version": "ca82d5f"}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo.service-0.10.0/oslo.service.egg-info/requires.txt
new/oslo.service-0.12.0/oslo.service.egg-info/requires.txt
--- old/oslo.service-0.10.0/oslo.service.egg-info/requires.txt 2015-10-12
17:38:47.000000000 +0200
+++ new/oslo.service-0.12.0/oslo.service.egg-info/requires.txt 2015-11-10
00:02:48.000000000 +0100
@@ -3,9 +3,9 @@
eventlet>=0.17.4
greenlet>=0.3.2
monotonic>=0.3
-oslo.utils>=2.0.0
+oslo.utils!=2.6.0,>=2.4.0
oslo.concurrency>=2.3.0
-oslo.config>=2.3.0
+oslo.config>=2.6.0
oslo.log>=1.8.0
six>=1.9.0
oslo.i18n>=1.5.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/oslo_service/_options.py
new/oslo.service-0.12.0/oslo_service/_options.py
--- old/oslo.service-0.10.0/oslo_service/_options.py 2015-10-12
17:37:58.000000000 +0200
+++ new/oslo.service-0.12.0/oslo_service/_options.py 2015-11-10
00:02:06.000000000 +0100
@@ -39,6 +39,10 @@
default=True,
help='Enables or disables logging values of all registered '
'options when starting a service (at DEBUG level).'),
+ cfg.IntOpt('graceful_shutdown_timeout',
+ default=0,
+ help='Specify a timeout after which a gracefully shutdown '
+ 'server will exit. Zero value means endless wait.'),
]
wsgi_opts = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/oslo_service/loopingcall.py
new/oslo.service-0.12.0/oslo_service/loopingcall.py
--- old/oslo.service-0.10.0/oslo_service/loopingcall.py 2015-10-12
17:37:58.000000000 +0200
+++ new/oslo.service-0.12.0/oslo_service/loopingcall.py 2015-11-10
00:02:06.000000000 +0100
@@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import random
import sys
from eventlet import event
@@ -47,6 +48,15 @@
self.retvalue = retvalue
+class LoopingCallTimeOut(Exception):
+ """Exception for a timed out LoopingCall.
+
+ The LoopingCall will raise this exception when a timeout is provided
+ and it is exceeded.
+ """
+ pass
+
+
def _safe_wrapper(f, kind, func_name):
"""Wrapper that calls into wrapped function and logs errors as needed."""
@@ -89,6 +99,18 @@
self._running = False
def _start(self, idle_for, initial_delay=None, stop_on_exception=True):
+ """Start the looping
+
+ :param idle_for: Callable that takes two positional arguments, returns
+ how long to idle for. The first positional argument is
+ the last result from the function being looped and the
+ second positional argument is the time it took to
+ calculate that result.
+ :param initial_delay: How long to delay before starting the looping.
+ Value is in seconds.
+ :param stop_on_exception: Whether to stop if an exception occurs.
+ :returns: eventlet event instance
+ """
if self._thread is not None:
raise RuntimeError(self._RUN_ONLY_ONE_MESSAGE)
self._running = True
@@ -195,6 +217,92 @@
stop_on_exception=stop_on_exception)
+class BackOffLoopingCall(LoopingCallBase):
+ """Run a method in a loop with backoff on error.
+
+ The passed in function should return True (no error, return to
+ initial_interval),
+ False (error, start backing off), or raise LoopingCallDone(retvalue=None)
+ (quit looping, return retvalue if set).
+
+ When there is an error, the call will backoff on each failure. The
+ backoff will be equal to double the previous base interval times some
+ jitter. If a backoff would put it over the timeout, it halts immediately,
+ so the call will never take more than timeout, but may and likely will
+ take less time.
+
+ When the function return value is True or False, the interval will be
+ multiplied by a random jitter. If min_jitter or max_jitter is None,
+ there will be no jitter (jitter=1). If min_jitter is below 0.5, the code
+ may not backoff and may increase its retry rate.
+
+ If func constantly returns True, this function will not return.
+
+ To run a func and wait for a call to finish (by raising a LoopingCallDone):
+
+ timer = BackOffLoopingCall(func)
+ response = timer.start().wait()
+
+ :param initial_delay: delay before first running of function
+ :param starting_interval: initial interval in seconds between calls to
+ function. When an error occurs and then a
+ success, the interval is returned to
+ starting_interval
+ :param timeout: time in seconds before a LoopingCallTimeout is raised.
+ The call will never take longer than timeout, but may quit
+ before timeout.
+ :param max_interval: The maximum interval between calls during errors
+ :param jitter: Used to vary when calls are actually run to avoid group of
+ calls all coming at the exact same time. Uses
+ random.gauss(jitter, 0.1), with jitter as the mean for the
+ distribution. If set below .5, it can cause the calls to
+ come more rapidly after each failure.
+ :raises: LoopingCallTimeout if time spent doing error retries would exceed
+ timeout.
+ """
+
+ _RNG = random.SystemRandom()
+ _KIND = 'Dynamic backoff interval looping call'
+ _RUN_ONLY_ONE_MESSAGE = ("A dynamic backoff interval looping call can"
+ " only run one function at a time")
+
+ def __init__(self, f=None, *args, **kw):
+ super(BackOffLoopingCall, self).__init__(f=f, *args, **kw)
+ self._error_time = 0
+ self._interval = 1
+
+ def start(self, initial_delay=None, starting_interval=1, timeout=300,
+ max_interval=300, jitter=0.75):
+ if self._thread is not None:
+ raise RuntimeError(self._RUN_ONLY_ONE_MESSAGE)
+
+ # Reset any prior state.
+ self._error_time = 0
+ self._interval = starting_interval
+
+ def _idle_for(success, _elapsed):
+ random_jitter = self._RNG.gauss(jitter, 0.1)
+ if success:
+ # Reset error state now that it didn't error...
+ self._interval = starting_interval
+ self._error_time = 0
+ return self._interval * random_jitter
+ else:
+ # Perform backoff
+ self._interval = idle = min(
+ self._interval * 2 * random_jitter, max_interval)
+ # Don't go over timeout, end early if necessary. If
+ # timeout is 0, keep going.
+ if timeout > 0 and self._error_time + idle > timeout:
+ raise LoopingCallTimeOut(
+ _('Looping call timed out after %.02f seconds')
+ % self._error_time)
+ self._error_time += idle
+ return idle
+
+ return self._start(_idle_for, initial_delay=initial_delay)
+
+
class RetryDecorator(object):
"""Decorator for retrying a function upon suggested exceptions.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/oslo_service/service.py
new/oslo.service-0.12.0/oslo_service/service.py
--- old/oslo.service-0.10.0/oslo_service/service.py 2015-10-12
17:37:58.000000000 +0200
+++ new/oslo.service-0.12.0/oslo_service/service.py 2015-11-10
00:02:06.000000000 +0100
@@ -71,7 +71,8 @@
def _is_sighup_and_daemon(signo):
- if not (SignalHandler().is_sighup_supported and signo == signal.SIGHUP):
+ if not (SignalHandler().is_signal_supported('SIGHUP') and
+ signo == signal.SIGHUP):
# Avoid checking if we are a daemon, because the signal isn't
# SIGHUP.
return False
@@ -135,7 +136,6 @@
self.signals_to_name = dict(
(sigval, name)
for (name, sigval) in self._signals_by_name.items())
- self.is_sighup_supported = 'SIGHUP' in self._signals_by_name
self._signal_handlers = collections.defaultdict(set)
self.clear()
@@ -149,7 +149,7 @@
self.add_handler(sig, handler)
def add_handler(self, sig, handler):
- if sig == "SIGHUP" and not self.is_sighup_supported:
+ if not self.is_signal_supported(sig):
return
signo = self._signals_by_name[sig]
self._signal_handlers[signo].add(handler)
@@ -159,6 +159,9 @@
for handler in self._signal_handlers[signo]:
handler(signo, frame)
+ def is_signal_supported(self, sig_name):
+ return sig_name in self._signals_by_name
+
class Launcher(object):
"""Launch one or more services and wait for them to complete."""
@@ -319,6 +322,7 @@
self.signal_handler.add_handlers(('SIGTERM', 'SIGHUP'),
self._handle_signal)
self.signal_handler.add_handler('SIGINT', self._fast_exit)
+ self.signal_handler.add_handler('SIGALRM', self._on_alarm_exit)
def _handle_signal(self, signo, frame):
"""Set signal handlers.
@@ -336,6 +340,11 @@
LOG.info(_LI('Caught SIGINT signal, instantaneous exiting'))
os._exit(1)
+ def _on_alarm_exit(self, signo, frame):
+ LOG.info(_LI('Graceful shutdown timeout exceeded, '
+ 'instantaneous exiting'))
+ os._exit(1)
+
def _pipe_watcher(self):
# This will block until the write end is closed when the parent
# dies unexpectedly
@@ -531,6 +540,12 @@
except eventlet.greenlet.GreenletExit:
LOG.info(_LI("Wait called after thread killed. Cleaning up."))
+ # if we are here it means that we try to do gracefull shutdown.
+ # add alarm watching that graceful_shutdown_timeout is not exceeded
+ if (self.conf.graceful_shutdown_timeout and
+ self.signal_handler.is_signal_supported('SIGALRM')):
+ signal.alarm(self.conf.graceful_shutdown_timeout)
+
self.stop()
def stop(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo.service-0.10.0/oslo_service/tests/eventlet_service.py
new/oslo.service-0.12.0/oslo_service/tests/eventlet_service.py
--- old/oslo.service-0.10.0/oslo_service/tests/eventlet_service.py
2015-10-12 17:37:58.000000000 +0200
+++ new/oslo.service-0.12.0/oslo_service/tests/eventlet_service.py
2015-11-10 00:02:06.000000000 +0100
@@ -139,12 +139,7 @@
launcher.launch_service(self.server)
-def run():
- # ProcessLauncher uses cfg.CONF.log_opt_values()
- # and cfg.CONF.log_opt_values() uses config_file option.
- # We need to call CONF() to register the --config-file option
- cfg.CONF()
-
+def run(port_queue=None):
eventlet.patcher.monkey_patch()
launcher = service.ProcessLauncher(cfg.CONF)
@@ -156,7 +151,8 @@
server = ServerWrapper(Server(hi_app), workers=3)
server.launch_with(launcher)
- print('%s' % server.server.socket.getsockname()[1])
+ port = server.server.socket.getsockname()[1]
+ port_queue.put(port)
sys.stdout.flush()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo.service-0.10.0/oslo_service/tests/test_loopingcall.py
new/oslo.service-0.12.0/oslo_service/tests/test_loopingcall.py
--- old/oslo.service-0.10.0/oslo_service/tests/test_loopingcall.py
2015-10-12 17:37:58.000000000 +0200
+++ new/oslo.service-0.12.0/oslo_service/tests/test_loopingcall.py
2015-11-10 00:02:06.000000000 +0100
@@ -235,6 +235,86 @@
sleep_mock.assert_has_calls([mock.call(3), mock.call(1)])
+class TestBackOffLoopingCall(test_base.BaseTestCase):
+ @mock.patch('random.SystemRandom.gauss')
+ @mock.patch('eventlet.greenthread.sleep')
+ def test_exponential_backoff(self, sleep_mock, random_mock):
+ def false():
+ return False
+
+ random_mock.return_value = .8
+
+ self.assertRaises(loopingcall.LoopingCallTimeOut,
+ loopingcall.BackOffLoopingCall(false).start()
+ .wait)
+
+ expected_times = [mock.call(1.6000000000000001),
+ mock.call(2.5600000000000005),
+ mock.call(4.096000000000001),
+ mock.call(6.5536000000000021),
+ mock.call(10.485760000000004),
+ mock.call(16.777216000000006),
+ mock.call(26.843545600000013),
+ mock.call(42.949672960000022),
+ mock.call(68.719476736000033),
+ mock.call(109.95116277760006)]
+ self.assertEqual(expected_times, sleep_mock.call_args_list)
+
+ @mock.patch('random.SystemRandom.gauss')
+ @mock.patch('eventlet.greenthread.sleep')
+ def test_no_backoff(self, sleep_mock, random_mock):
+ random_mock.return_value = 1
+ func = mock.Mock()
+ # func.side_effect
+ func.side_effect = [True, True, True, loopingcall.LoopingCallDone(
+ retvalue='return value')]
+
+ retvalue = loopingcall.BackOffLoopingCall(func).start().wait()
+
+ expected_times = [mock.call(1), mock.call(1), mock.call(1)]
+ self.assertEqual(expected_times, sleep_mock.call_args_list)
+ self.assertTrue(retvalue, 'return value')
+
+ @mock.patch('random.SystemRandom.gauss')
+ @mock.patch('eventlet.greenthread.sleep')
+ def test_no_sleep(self, sleep_mock, random_mock):
+ # Any call that executes properly the first time shouldn't sleep
+ random_mock.return_value = 1
+ func = mock.Mock()
+ # func.side_effect
+ func.side_effect = loopingcall.LoopingCallDone(retvalue='return value')
+
+ retvalue = loopingcall.BackOffLoopingCall(func).start().wait()
+ self.assertFalse(sleep_mock.called)
+ self.assertTrue(retvalue, 'return value')
+
+ @mock.patch('random.SystemRandom.gauss')
+ @mock.patch('eventlet.greenthread.sleep')
+ def test_max_interval(self, sleep_mock, random_mock):
+ def false():
+ return False
+
+ random_mock.return_value = .8
+
+ self.assertRaises(loopingcall.LoopingCallTimeOut,
+ loopingcall.BackOffLoopingCall(false).start(
+ max_interval=60)
+ .wait)
+
+ expected_times = [mock.call(1.6000000000000001),
+ mock.call(2.5600000000000005),
+ mock.call(4.096000000000001),
+ mock.call(6.5536000000000021),
+ mock.call(10.485760000000004),
+ mock.call(16.777216000000006),
+ mock.call(26.843545600000013),
+ mock.call(42.949672960000022),
+ mock.call(60),
+ mock.call(60),
+ mock.call(60)]
+ self.assertEqual(expected_times, sleep_mock.call_args_list)
+
+
class AnException(Exception):
pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo.service-0.10.0/oslo_service/tests/test_service.py
new/oslo.service-0.12.0/oslo_service/tests/test_service.py
--- old/oslo.service-0.10.0/oslo_service/tests/test_service.py 2015-10-12
17:37:58.000000000 +0200
+++ new/oslo.service-0.12.0/oslo_service/tests/test_service.py 2015-11-10
00:02:06.000000000 +0100
@@ -20,26 +20,22 @@
from __future__ import print_function
-import threading
-
import logging
import multiprocessing
import os
import signal
import socket
-import sys
import time
import traceback
import eventlet
from eventlet import event
-from eventlet.green import subprocess
import mock
from oslotest import base as test_base
-from six.moves import queue
from oslo_service import service
from oslo_service.tests import base
+from oslo_service.tests import eventlet_service
LOG = logging.getLogger(__name__)
@@ -304,8 +300,9 @@
def test_launch_one_worker(self):
self._test_launch_single(1)
+ @mock.patch('signal.alarm')
@mock.patch('oslo_service.service.ProcessLauncher.launch_service')
- def test_multiple_worker(self, mock_launch):
+ def test_multiple_worker(self, mock_launch, alarm_mock):
svc = service.Service()
service.launch(self.conf, svc, workers=3)
mock_launch.assert_called_with(svc, workers=3)
@@ -316,19 +313,23 @@
svc = mock.Mock()
self.assertRaises(TypeError, service.launch, self.conf, svc)
+ @mock.patch('signal.alarm')
@mock.patch("oslo_service.service.Services.add")
@mock.patch("oslo_service.eventlet_backdoor.initialize_if_enabled")
def test_check_service_base(self, initialize_if_enabled_mock,
- services_mock):
+ services_mock,
+ alarm_mock):
initialize_if_enabled_mock.return_value = None
launcher = service.Launcher(self.conf)
serv = _Service()
launcher.launch_service(serv)
+ @mock.patch('signal.alarm')
@mock.patch("oslo_service.service.Services.add")
@mock.patch("oslo_service.eventlet_backdoor.initialize_if_enabled")
def test_check_service_base_fails(self, initialize_if_enabled_mock,
- services_mock):
+ services_mock,
+ alarm_mock):
initialize_if_enabled_mock.return_value = None
launcher = service.Launcher(self.conf)
@@ -341,8 +342,9 @@
class ProcessLauncherTest(base.ServiceBaseTestCase):
+ @mock.patch('signal.alarm')
@mock.patch("signal.signal")
- def test_stop(self, signal_mock):
+ def test_stop(self, signal_mock, alarm_mock):
signal_mock.SIGTERM = 15
launcher = service.ProcessLauncher(self.conf)
self.assertTrue(launcher.running)
@@ -386,6 +388,7 @@
m.assert_called_once_with(signal.SIGTERM, 'test')
signal_handler.clear()
+ @mock.patch('signal.alarm')
@mock.patch("os.kill")
@mock.patch("oslo_service.service.ProcessLauncher.stop")
@mock.patch("oslo_service.service.ProcessLauncher._respawn_children")
@@ -402,7 +405,8 @@
handle_signal_mock,
respawn_children_mock,
stop_mock,
- kill_mock):
+ kill_mock,
+ alarm_mock):
is_sighup_and_daemon_mock.return_value = True
respawn_children_mock.side_effect = [None,
eventlet.greenlet.GreenletExit()]
@@ -479,39 +483,19 @@
exercise_graceful_test_service(1, 2, False))
-class EventletServerTest(test_base.BaseTestCase):
- def run_server(self):
-
- server_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
- 'eventlet_service.py')
+class EventletServerTest(base.ServiceBaseTestCase):
+ def setUp(self):
+ super(EventletServerTest, self).setUp()
+ self.conf(args=[], default_config_files=[])
+ self.addCleanup(self.conf.reset)
- # Start up an eventlet server.
- server = subprocess.Popen([sys.executable, server_path],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- bufsize=1000,
- close_fds=True)
-
- def enqueue_output(f, q):
- while True:
- line = f.readline()
- if not line:
- break
- q.put(line)
- f.close()
-
- # Start a thread to read stderr so the app doesn't block.
- err_q = queue.Queue()
- err_t = threading.Thread(target=enqueue_output,
- args=(server.stderr, err_q))
- err_t.daemon = True
- err_t.start()
-
- # The server's line of output is the port it picked.
- port_str = server.stdout.readline()
- port = int(port_str)
+ def run_server(self):
+ queue = multiprocessing.Queue()
+ proc = multiprocessing.Process(target=eventlet_service.run,
+ kwargs={'port_queue': queue})
+ proc.start()
- # connect to the server.
+ port = queue.get()
conn = socket.create_connection(('127.0.0.1', port))
# NOTE(blk-u): The sleep shouldn't be necessary. There must be a bug in
@@ -519,38 +503,49 @@
# server or signal handlers.
time.sleep(1)
- return (server, conn)
+ return (proc, conn)
def test_shuts_down_on_sigint_when_client_connected(self):
- server, conn = self.run_server()
+ proc, conn = self.run_server()
# check that server is live
- self.assertIsNone(server.poll())
+ self.assertTrue(proc.is_alive())
# send SIGINT to the server and wait for it to exit while client still
# connected.
- server.send_signal(signal.SIGINT)
- server.wait()
+ os.kill(proc.pid, signal.SIGINT)
+ proc.join()
conn.close()
def test_graceful_shuts_down_on_sigterm_when_client_connected(self):
- server, conn = self.run_server()
+ self.config(graceful_shutdown_timeout=7)
+ proc, conn = self.run_server()
# send SIGTERM to the server and wait for it to exit while client still
# connected.
- server.send_signal(signal.SIGTERM)
+ os.kill(proc.pid, signal.SIGTERM)
- server_wait_thread = threading.Thread(
- target=lambda server: server.wait(), args=(server,))
- server_wait_thread.start()
-
- # server with graceful shutdown must wait forewer
- # for closing connection by client
- # but for test 3 seconds is enough
+ # server with graceful shutdown must wait forewer if
+ # option graceful_shutdown_timeout is not specified.
+ # we can not wait forever ... so 3 seconds are enough
time.sleep(3)
- self.assertEqual(True, server_wait_thread.is_alive())
+ self.assertEqual(True, proc.is_alive())
conn.close()
+ proc.join()
+
+ def test_graceful_stop_with_exceeded_graceful_shutdown_timeout(self):
+ # Server must exit if graceful_shutdown_timeout exceeded
+ graceful_shutdown_timeout = 4
+ self.config(graceful_shutdown_timeout=graceful_shutdown_timeout)
+ proc, conn = self.run_server()
+
+ time_before = time.time()
+ os.kill(proc.pid, signal.SIGTERM)
+ self.assertEqual(True, proc.is_alive())
+ proc.join()
+ self.assertFalse(False, proc.is_alive())
+ time_after = time.time()
- server_wait_thread.join()
+ self.assertTrue(time_after - time_before > graceful_shutdown_timeout)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/requirements.txt
new/oslo.service-0.12.0/requirements.txt
--- old/oslo.service-0.10.0/requirements.txt 2015-10-12 17:37:58.000000000
+0200
+++ new/oslo.service-0.12.0/requirements.txt 2015-11-10 00:02:06.000000000
+0100
@@ -7,9 +7,9 @@
eventlet>=0.17.4
greenlet>=0.3.2
monotonic>=0.3 # Apache-2.0
-oslo.utils>=2.0.0 # Apache-2.0
+oslo.utils!=2.6.0,>=2.4.0 # Apache-2.0
oslo.concurrency>=2.3.0 # Apache-2.0
-oslo.config>=2.3.0 # Apache-2.0
+oslo.config>=2.6.0 # Apache-2.0
oslo.log>=1.8.0 # Apache-2.0
six>=1.9.0
oslo.i18n>=1.5.0 # Apache-2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/setup.cfg
new/oslo.service-0.12.0/setup.cfg
--- old/oslo.service-0.10.0/setup.cfg 2015-10-12 17:38:47.000000000 +0200
+++ new/oslo.service-0.12.0/setup.cfg 2015-11-10 00:02:49.000000000 +0100
@@ -60,7 +60,7 @@
universal = true
[egg_info]
+tag_build =
tag_date = 0
tag_svn_revision = 0
-tag_build =
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/test-requirements.txt
new/oslo.service-0.12.0/test-requirements.txt
--- old/oslo.service-0.10.0/test-requirements.txt 2015-10-12
17:37:58.000000000 +0200
+++ new/oslo.service-0.12.0/test-requirements.txt 2015-11-10
00:02:06.000000000 +0100
@@ -6,8 +6,9 @@
mock>=1.2
oslotest>=1.10.0 # Apache-2.0
-# These are needed for docs generation
+# These are needed for docs generation/testing
oslosphinx>=2.5.0 # Apache-2.0
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
+doc8 # Apache-2.0
coverage>=3.6
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo.service-0.10.0/tox.ini
new/oslo.service-0.12.0/tox.ini
--- old/oslo.service-0.10.0/tox.ini 2015-10-12 17:37:58.000000000 +0200
+++ new/oslo.service-0.12.0/tox.ini 2015-11-10 00:02:06.000000000 +0100
@@ -1,6 +1,6 @@
[tox]
minversion = 1.6
-envlist = py34,py26,py27,pypy,pep8
+envlist = py34,py27,pypy,pep8
# NOTE(dhellmann): We cannot set skipdist=True
# for oslo libraries because of the namespace package.
#skipsdist = True
@@ -19,6 +19,11 @@
[testenv:pep8]
commands = flake8
+[testenv:py27]
+commands =
+ python setup.py testr --slowest --testr-args='{posargs}'
+ doc8 --ignore-path "doc/source/history.rst" doc/source
+
[testenv:py34]
commands =
python -m testtools.run \