Skip to content

Commit

Permalink
Add --await-child option to glance-control.
Browse files Browse the repository at this point in the history
Fixes bug 817032

Previously an immediate non-zero exit status from service
launch was not reflected in the exit status returned from
glance-control.

Now the parent glance-control process configurably waits for
the child to exit ungracefully and if this occurs, it inherits
the non-zero status code from the child.

Change-Id: Ibbe92a5bf40d095951a572d78ae07026d8a9313d
  • Loading branch information
Eoghan Glynn committed Feb 2, 2012
1 parent 16b682d commit 593e8c2
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 8 deletions.
23 changes: 23 additions & 0 deletions bin/glance-control
Expand Up @@ -111,6 +111,18 @@ def do_start(server, conf, args):
fp.write('%d\n' % pid)
fp.close()

def await_child(pid):
if conf.await_child:
bail_time = time.time() + conf.await_child
while time.time() < bail_time:
reported_pid, status = os.waitpid(pid, os.WNOHANG)
if reported_pid == pid:
global exitcode
# the exit code is encoded in 2nd least significant byte
exitcode = status >> 8
break
time.sleep(0.05)

def launch(pid_file, conf_file=None):
args = [server]
print 'Starting %s' % server,
Expand All @@ -136,6 +148,7 @@ def do_start(server, conf, args):
sys.exit(0)
else:
write_pid_file(pid_file, pid)
await_child(pid)

if not conf.pid_file:
pid_file = '/var/run/glance/%s.pid' % server
Expand Down Expand Up @@ -181,11 +194,19 @@ def do_stop(server, conf, args, graceful=False):


if __name__ == '__main__':
exitcode = 0

conf = config.GlanceConfigOpts(usage=USAGE)
conf.register_cli_opt(cfg.StrOpt('pid-file',
metavar='PATH',
help='File to use as pid file. Default: '
'/var/run/glance/$server.pid'))
conf.register_cli_opt(cfg.IntOpt('await-child',
metavar='DELAY',
default=0,
help='Period to wait for service death '
'in order to report exit code '
'(default is to not wait at all)'))
args = conf()

if len(args) < 2:
Expand Down Expand Up @@ -235,3 +256,5 @@ if __name__ == '__main__':
for server in servers:
do_stop(server, conf, args, graceful=True)
do_start(server, conf, args)

sys.exit(exitcode)
23 changes: 17 additions & 6 deletions glance/tests/functional/__init__.py
Expand Up @@ -87,6 +87,7 @@ def __init__(self, test_dir, port):
self.server_control = './bin/glance-control'
self.exec_env = None
self.deployment_flavor = ''
self.server_control_options = ''

def write_conf(self, **kwargs):
"""
Expand Down Expand Up @@ -128,7 +129,7 @@ def override_conf(filepath, base, override):

return self.conf_file_name

def start(self, **kwargs):
def start(self, expected_exitcode=0, **kwargs):
"""
Starts the server.
Expand All @@ -140,9 +141,13 @@ def start(self, **kwargs):
self.write_conf(**kwargs)

cmd = ("%(server_control)s %(server_name)s start "
"%(conf_file_name)s --pid-file=%(pid_file)s"
"%(conf_file_name)s --pid-file=%(pid_file)s "
"%(server_control_options)s"
% self.__dict__)
return execute(cmd, no_venv=self.no_venv, exec_env=self.exec_env)
return execute(cmd,
no_venv=self.no_venv,
exec_env=self.exec_env,
expected_exitcode=expected_exitcode)

def stop(self):
"""
Expand Down Expand Up @@ -439,7 +444,11 @@ def cleanup(self):
if os.path.exists(f):
os.unlink(f)

def start_server(self, server, expect_launch, **kwargs):
def start_server(self,
server,
expect_launch,
expected_exitcode=0,
**kwargs):
"""
Starts a server on an unused port.
Expand All @@ -449,13 +458,15 @@ def start_server(self, server, expect_launch, **kwargs):
:param server: the server to launch
:param expect_launch: true iff the server is expected to
successfully start
:param expected_exitcode: expected exitcode from the launcher
"""
self.cleanup()

# Start up the requested server
exitcode, out, err = server.start(**kwargs)
exitcode, out, err = server.start(expected_exitcode=expected_exitcode,
**kwargs)

self.assertEqual(0, exitcode,
self.assertEqual(expected_exitcode, exitcode,
"Failed to spin up the requested server. "
"Got: %s" % err)
self.assertTrue(re.search("Starting glance-[a-z]+ with", out))
Expand Down
2 changes: 2 additions & 0 deletions glance/tests/functional/test_api.py
Expand Up @@ -1248,8 +1248,10 @@ def test_unsupported_default_store(self):
"""
self.cleanup()
self.api_server.default_store = 'shouldnotexist'
self.api_server.server_control_options += ' --await-child=1'

# ensure that the API server fails to launch
self.start_server(self.api_server,
expect_launch=False,
expected_exitcode=255,
**self.__dict__.copy())
9 changes: 7 additions & 2 deletions glance/tests/utils.py
Expand Up @@ -168,7 +168,11 @@ def wrapped(*a, **kwargs):
return wrapped


def execute(cmd, raise_error=True, no_venv=False, exec_env=None):
def execute(cmd,
raise_error=True,
no_venv=False,
exec_env=None,
expected_exitcode=0):
"""
Executes a command in a subprocess. Returns a tuple
of (exitcode, out, err), where out is the string output
Expand All @@ -183,6 +187,7 @@ def execute(cmd, raise_error=True, no_venv=False, exec_env=None):
variables; values may be callables, which will
be passed the current value of the named
environment variable
:param expected_exitcode: expected exitcode from the launcher
"""

env = os.environ.copy()
Expand Down Expand Up @@ -219,7 +224,7 @@ def execute(cmd, raise_error=True, no_venv=False, exec_env=None):
result = process.communicate()
(out, err) = result
exitcode = process.returncode
if process.returncode != 0 and raise_error:
if process.returncode != expected_exitcode and raise_error:
msg = "Command %(cmd)s did not succeed. Returned an exit "\
"code of %(exitcode)d."\
"\n\nSTDOUT: %(out)s"\
Expand Down

0 comments on commit 593e8c2

Please sign in to comment.