Merge lp:~adam-collard/charm-helpers/amulet-get-process-id-list-fixes into lp:charm-helpers

Proposed by Adam Collard
Status: Merged
Merged at revision: 429
Proposed branch: lp:~adam-collard/charm-helpers/amulet-get-process-id-list-fixes
Merge into: lp:charm-helpers
Diff against target: 207 lines (+140/-11)
2 files modified
charmhelpers/contrib/amulet/utils.py (+22/-8)
tests/contrib/amulet/test_utils.py (+118/-3)
To merge this branch: bzr merge lp:~adam-collard/charm-helpers/amulet-get-process-id-list-fixes
Reviewer Review Type Date Requested Status
Chris Glass (community) Approve
Review via email: mp+268204@code.launchpad.net

Description of the change

Extend get_process_id and get_unit_process_ids to allow them to be used for testing that processes are /not/ running.

To post a comment you must log in.
Revision history for this message
Chris Glass (tribaal) wrote :

Thanks for your branch. On principle it looks good, but some tests fail and others clutter stdout/stderr on "make test".

I'm going to have to mark this as need fixing until "make test" produces a clean (and passing) output.

review: Needs Fixing
431. By Adam Collard

Use six.iteritems() to fix Py3 test failure (tribaal's review)

432. By Adam Collard

Capture stdout/stderr around tests that expect to run amulet.raise_status and verify the message

Revision history for this message
Adam Collard (adam-collard) wrote :

> Thanks for your branch. On principle it looks good, but some tests fail and
> others clutter stdout/stderr on "make test".

Thanks for the review. Sorry for not running the full test suite before proposing.

The extra tests I added exposed existing failures in the amulet utils when running under Python 3. I've fixed those, and added code to capture stdout.

> I'm going to have to mark this as need fixing until "make test" produces a
> clean (and passing) output.

Please take another look, believed to have been fixed now!

Revision history for this message
Chris Glass (tribaal) wrote :

Yep, looks good now, thanks.

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charmhelpers/contrib/amulet/utils.py'
--- charmhelpers/contrib/amulet/utils.py 2015-08-14 17:09:52 +0000
+++ charmhelpers/contrib/amulet/utils.py 2015-08-17 11:03:41 +0000
@@ -460,15 +460,20 @@
460 cmd, code, output))460 cmd, code, output))
461 return None461 return None
462462
463 def get_process_id_list(self, sentry_unit, process_name):463 def get_process_id_list(self, sentry_unit, process_name,
464 expect_success=True):
464 """Get a list of process ID(s) from a single sentry juju unit465 """Get a list of process ID(s) from a single sentry juju unit
465 for a single process name.466 for a single process name.
466467
467 :param sentry_unit: Pointer to amulet sentry instance (juju unit)468 :param sentry_unit: Amulet sentry instance (juju unit)
468 :param process_name: Process name469 :param process_name: Process name
470 :param expect_success: If False, expect the PID to be missing,
471 raise if it is present.
469 :returns: List of process IDs472 :returns: List of process IDs
470 """473 """
471 cmd = 'pidof {}'.format(process_name)474 cmd = 'pidof -x {}'.format(process_name)
475 if not expect_success:
476 cmd += " || exit 0 && exit 1"
472 output, code = sentry_unit.run(cmd)477 output, code = sentry_unit.run(cmd)
473 if code != 0:478 if code != 0:
474 msg = ('{} `{}` returned {} '479 msg = ('{} `{}` returned {} '
@@ -477,14 +482,23 @@
477 amulet.raise_status(amulet.FAIL, msg=msg)482 amulet.raise_status(amulet.FAIL, msg=msg)
478 return str(output).split()483 return str(output).split()
479484
480 def get_unit_process_ids(self, unit_processes):485 def get_unit_process_ids(self, unit_processes, expect_success=True):
481 """Construct a dict containing unit sentries, process names, and486 """Construct a dict containing unit sentries, process names, and
482 process IDs."""487 process IDs.
488
489 :param unit_processes: A dictionary of Amulet sentry instance
490 to list of process names.
491 :param expect_success: if False expect the processes to not be
492 running, raise if they are.
493 :returns: Dictionary of Amulet sentry instance to dictionary
494 of process names to PIDs.
495 """
483 pid_dict = {}496 pid_dict = {}
484 for sentry_unit, process_list in unit_processes.iteritems():497 for sentry_unit, process_list in six.iteritems(unit_processes):
485 pid_dict[sentry_unit] = {}498 pid_dict[sentry_unit] = {}
486 for process in process_list:499 for process in process_list:
487 pids = self.get_process_id_list(sentry_unit, process)500 pids = self.get_process_id_list(
501 sentry_unit, process, expect_success=expect_success)
488 pid_dict[sentry_unit].update({process: pids})502 pid_dict[sentry_unit].update({process: pids})
489 return pid_dict503 return pid_dict
490504
@@ -498,7 +512,7 @@
498 return ('Unit count mismatch. expected, actual: {}, '512 return ('Unit count mismatch. expected, actual: {}, '
499 '{} '.format(len(expected), len(actual)))513 '{} '.format(len(expected), len(actual)))
500514
501 for (e_sentry, e_proc_names) in expected.iteritems():515 for (e_sentry, e_proc_names) in six.iteritems(expected):
502 e_sentry_name = e_sentry.info['unit_name']516 e_sentry_name = e_sentry.info['unit_name']
503 if e_sentry in actual.keys():517 if e_sentry in actual.keys():
504 a_proc_names = actual[e_sentry]518 a_proc_names = actual[e_sentry]
505519
=== modified file 'tests/contrib/amulet/test_utils.py'
--- tests/contrib/amulet/test_utils.py 2015-08-14 17:09:52 +0000
+++ tests/contrib/amulet/test_utils.py 2015-08-17 11:03:41 +0000
@@ -3,16 +3,35 @@
3# Authors:3# Authors:
4# Adam Collard <adam.collard@canonical.com>4# Adam Collard <adam.collard@canonical.com>
55
6from contextlib import contextmanager
7import sys
6import unittest8import unittest
79
10import six
11
8from charmhelpers.contrib.amulet.utils import AmuletUtils12from charmhelpers.contrib.amulet.utils import AmuletUtils
913
1014
15@contextmanager
16def captured_output():
17 """Simple context manager to capture stdout/stderr.
18
19 Source: http://stackoverflow.com/a/17981937/56219.
20 """
21 new_out, new_err = six.StringIO(), six.StringIO()
22 old_out, old_err = sys.stdout, sys.stderr
23 try:
24 sys.stdout, sys.stderr = new_out, new_err
25 yield sys.stdout, sys.stderr
26 finally:
27 sys.stdout, sys.stderr = old_out, old_err
28
29
11class FakeSentry(object):30class FakeSentry(object):
1231
13 commands = {}32 def __init__(self, name="foo"):
1433 self.commands = {}
15 info = {"unit_name": "foo"}34 self.info = {"unit_name": name}
1635
17 def run(self, command):36 def run(self, command):
18 return self.commands[command]37 return self.commands[command]
@@ -193,3 +212,99 @@
193212
194 self.assertFalse(self.utils.wait_on_action(213 self.assertFalse(self.utils.wait_on_action(
195 "action-id", _check_output=fake_check_output))214 "action-id", _check_output=fake_check_output))
215
216
217class GetProcessIdListTestCase(unittest.TestCase):
218
219 def setUp(self):
220 self.utils = AmuletUtils()
221 self.sentry_unit = FakeSentry()
222
223 def test_returns_pids(self):
224 """
225 Normal execution returns a list of pids
226 """
227 self.sentry_unit.commands["pidof -x foo"] = ("123 124 125", 0)
228 result = self.utils.get_process_id_list(self.sentry_unit, "foo")
229 self.assertEqual(["123", "124", "125"], result)
230
231 def test_fails_if_no_process_found(self):
232 """
233 By default, the expectation is that a process is running. Failure
234 to find a given process results in an amulet.FAIL being
235 raised.
236 """
237 self.sentry_unit.commands["pidof -x foo"] = ("", 1)
238 with self.assertRaises(SystemExit) as cm, captured_output() as (
239 out, err):
240 self.utils.get_process_id_list(self.sentry_unit, "foo")
241 the_exception = cm.exception
242 self.assertEqual(1, the_exception.code)
243 self.assertEqual(
244 "foo `pidof -x foo` returned 1", out.getvalue().rstrip())
245
246 def test_looks_for_scripts(self):
247 """
248 pidof command uses -x to return a list of pids of scripts
249 """
250 self.sentry_unit.commands["pidof foo"] = ("", 1)
251 self.sentry_unit.commands["pidof -x foo"] = ("123 124 125", 0)
252 result = self.utils.get_process_id_list(self.sentry_unit, "foo")
253 self.assertEqual(["123", "124", "125"], result)
254
255 def test_expect_no_pid(self):
256 """
257 By setting expectation that there are no pids running the logic
258 about when to fail is reversed.
259 """
260 self.sentry_unit.commands["pidof -x foo || exit 0 && exit 1"] = ("", 0)
261 self.sentry_unit.commands["pidof -x bar || exit 0 && exit 1"] = ("", 1)
262 result = self.utils.get_process_id_list(
263 self.sentry_unit, "foo", expect_success=False)
264 self.assertEqual([], result)
265 with self.assertRaises(SystemExit) as cm, captured_output() as (
266 out, err):
267 self.utils.get_process_id_list(
268 self.sentry_unit, "bar", expect_success=False)
269 the_exception = cm.exception
270 self.assertEqual(1, the_exception.code)
271 self.assertEqual(
272 "foo `pidof -x bar || exit 0 && exit 1` returned 1",
273 out.getvalue().rstrip())
274
275
276class GetUnitProcessIdsTestCase(unittest.TestCase):
277
278 def setUp(self):
279 self.utils = AmuletUtils()
280 self.sentry_unit = FakeSentry()
281
282 def test_returns_map(self):
283 """
284 Normal execution returns a dictionary mapping process names to
285 PIDs for each unit.
286 """
287 second_sentry = FakeSentry(name="bar")
288 self.sentry_unit.commands["pidof -x foo"] = ("123 124", 0)
289 second_sentry.commands["pidof -x bar"] = ("456 457", 0)
290
291 result = self.utils.get_unit_process_ids({
292 self.sentry_unit: ["foo"], second_sentry: ["bar"]})
293 self.assertEqual({
294 self.sentry_unit: {"foo": ["123", "124"]},
295 second_sentry: {"bar": ["456", "457"]}}, result)
296
297 def test_expect_failure(self):
298 """
299 Expected failures return empty lists.
300 """
301 second_sentry = FakeSentry(name="bar")
302 self.sentry_unit.commands["pidof -x foo || exit 0 && exit 1"] = ("", 0)
303 second_sentry.commands["pidof -x bar || exit 0 && exit 1"] = ("", 0)
304
305 result = self.utils.get_unit_process_ids(
306 {self.sentry_unit: ["foo"], second_sentry: ["bar"]},
307 expect_success=False)
308 self.assertEqual({
309 self.sentry_unit: {"foo": []},
310 second_sentry: {"bar": []}}, result)

Subscribers

People subscribed via source and target branches