2
from shutil import rmtree
24
from remote import SSHRemote
25
from tests import FakeHomeTestCase
26
from test_jujupy import (
31
class TestBackgroundChaos(FakeHomeTestCase):
33
def test_background_chaos(self):
34
client = EnvJujuClient(JujuData('foo', {}), None, '/foo/juju')
35
with patch('chaos.MonkeyRunner.deploy_chaos_monkey',
36
autospec=True) as d_mock:
37
with patch('chaos.MonkeyRunner.unleash_once',
38
return_value=['1'], autospec=True) as u_mock:
39
with patch('chaos.MonkeyRunner.wait_for_chaos',
40
autospec=True) as w_mock:
41
with patch('chaos.remote_from_unit',
42
return_value=SSHRemote(None, None, 'foo')
44
with patch('remote.SSHRemote.copy') as rc_mock:
46
with background_chaos('foo', client, log_dir, 1):
49
self.assertEqual(1, d_mock.call_count)
50
self.assertEqual(1, u_mock.call_count)
51
self.assertEqual(1, ru_mock.call_count)
52
self.assertEqual(1, rc_mock.call_count)
53
self.assertEqual({'state': 'start'}, w_mock.mock_calls[0][2])
54
self.assertEqual({'state': 'complete', 'timeout': 1},
55
w_mock.mock_calls[1][2])
57
def test_background_chaos_exits(self):
58
client = EnvJujuClient(JujuData('foo', {}), None, '/foo/juju')
59
with patch('chaos.MonkeyRunner.deploy_chaos_monkey',
61
with patch('chaos.MonkeyRunner.unleash_once',
63
with patch('chaos.MonkeyRunner.wait_for_chaos',
65
with patch('chaos.remote_from_unit'):
66
with patch('remote.SSHRemote.copy'):
67
with patch('logging.exception') as le_mock:
68
with patch('sys.exit', autospec=True) as sm:
70
with background_chaos('foo', client,
74
self.assertEqual(1, le_mock.call_count)
75
sm.assert_called_once_with(1)
78
class TestRunChaosMonkey(FakeHomeTestCase):
80
def test_deploy_chaos_monkey(self):
81
def output(*args, **kwargs):
82
status = yaml.safe_dump({
84
'0': {'agent-state': 'started'}
90
'agent-state': 'started',
93
'agent-state': 'started'
102
('show-status', '--format', 'yaml'): status,
105
client = EnvJujuClient(JujuData('foo', {}), '1.25.0', '/foo/juju')
106
with patch.object(client, 'get_juju_output', side_effect=output,
107
autospec=True) as gjo_mock:
108
with patch('subprocess.check_call', autospec=True) as cc_mock:
109
monkey_runner = MonkeyRunner('foo', client, service='ser1')
110
with patch('jujupy.GroupReporter._write', autospec=True):
111
monkey_runner.deploy_chaos_monkey()
116
('juju', '--show-log', 'deploy',
117
'-m', 'foo:foo', 'local:chaos-monkey'),
119
assert_juju_call(self, cc_mock, client, (
120
'juju', '--show-log', 'add-relation', '-m', 'foo:foo', 'ser1',
122
self.assertEqual(cc_mock.call_count, 2)
123
self.assertEqual(gjo_mock.call_count, 2)
125
def test_iter_chaos_monkey_units(self):
126
def output(*args, **kwargs):
127
status = yaml.safe_dump({
129
'0': {'agent-state': 'started'}
136
'chaos-monkey/0': {'baz': 'qux'},
137
'not-chaos/0': {'qwe': 'rty'},
142
'chaos-monkey/1': {'abc': '123'},
150
('show-status', '--format', 'yaml'): status,
153
client = EnvJujuClient(JujuData('foo', {}), None, '/foo/juju')
154
runner = MonkeyRunner('foo', client, service='jenkins')
155
with patch.object(client, 'get_juju_output', side_effect=output,
157
monkey_units = dict((k, v) for k, v in
158
runner.iter_chaos_monkey_units())
160
'chaos-monkey/0': {'baz': 'qux'},
161
'chaos-monkey/1': {'abc': '123'}
163
self.assertEqual(expected, monkey_units)
165
def test_get_unit_status(self):
166
def output(*args, **kwargs):
167
status = yaml.safe_dump({
169
'0': {'agent-state': 'started'}
176
'chaos-monkey/0': {'baz': 'qux'},
177
'not-chaos/0': {'qwe': 'rty'},
182
'chaos-monkey/1': {'abc': '123'},
189
charm_config = yaml.safe_dump({
190
'charm': {'jenkins'},
191
'service': {'jenkins'},
195
'description': 'bla bla',
197
'value': '/tmp/charm-dir',
203
('config', 'chaos-monkey'): charm_config,
206
client = EnvJujuClient(JujuData('foo', {}), None, '/foo')
207
monkey_runner = MonkeyRunner('foo', client, service='jenkins')
208
monkey_runner.monkey_ids = {
209
'chaos-monkey/0': 'workspace0',
210
'chaos-monkey/1': 'workspace1'
212
with patch.object(client, 'get_juju_output', side_effect=output,
214
with patch('subprocess.call', autospec=True,
215
return_value=0) as call_mock:
216
for unit_name in ['chaos-monkey/1', 'chaos-monkey/0']:
217
self.assertEqual(monkey_runner.get_unit_status(unit_name),
219
self.assertEqual(call_mock.call_count, 2)
220
with patch.object(client, 'get_juju_output', side_effect=output,
222
with patch('subprocess.call', autospec=True,
223
return_value=1) as call_mock:
224
for unit_name in ['chaos-monkey/1', 'chaos-monkey/0']:
225
self.assertEqual(monkey_runner.get_unit_status(unit_name),
227
self.assertEqual(call_mock.call_count, 2)
230
class TestUnleashOnce(FakeHomeTestCase):
232
def test_unleash_once(self):
233
def output(*args, **kwargs):
234
status = yaml.safe_dump({
236
'0': {'agent-state': 'started'}
243
'chaos-monkey/0': {'baz': 'qux'},
244
'not-chaos/0': {'qwe': 'rty'},
249
'chaos-monkey/1': {'abc': '123'},
256
charm_config = yaml.safe_dump({
257
'charm': {'jenkins'},
258
'service': {'jenkins'},
262
'description': 'bla bla',
264
'value': '/tmp/charm-dir',
269
('show-status', '--format', 'yaml'): status,
270
('get', 'jenkins'): charm_config,
271
('run-action', 'chaos-monkey/0', 'start', 'mode=single',
272
'enablement-timeout=120'
273
): ('Action queued with id: '
274
'abcdabcdabcdabcdabcdabcdabcdabcdabcd'),
275
('run-action', 'chaos-monkey/0', 'start', 'mode=single',
276
'enablement-timeout=120',
277
'monkey-id=abcdabcdabcdabcdabcdabcdabcdabcdabcd'
278
): ('Action queued with id: '
279
'efabefabefabefabefabefabefabefabefab'),
280
('run-action', 'chaos-monkey/1', 'start', 'mode=single',
281
'enablement-timeout=120'
282
): ('Action queued with id: '
283
'123412341234123412341234123412341234'),
284
('run-action', 'chaos-monkey/1', 'start', 'mode=single',
285
'enablement-timeout=120',
286
'monkey-id=123412341234123412341234123412341234'
287
): ('Action queued with id: '
288
'567856785678567856785678567856785678'),
292
def output2(*args, **kwargs):
293
status = yaml.safe_dump({
295
'0': {'agent-state': 'started'}
302
'chaos-monkey/1': {'abc': '123'},
310
('show-status', '--format', 'yaml'): status,
311
('run-action', 'chaos-monkey/1', 'start', 'mode=single',
312
'enablement-timeout=120',
313
'monkey-id=123412341234123412341234123412341234'
314
): ('Action queued with id: '
315
'abcdabcdabcdabcdabcdabcdabcdabcdabcd'),
318
client = EnvJujuClient(JujuData('foo', {}), None, '/foo/juju')
319
monkey_runner = MonkeyRunner('foo', client, service='jenkins')
320
with patch.object(client, 'get_juju_output', side_effect=output,
321
autospec=True) as gjo_mock:
322
returned = monkey_runner.unleash_once()
323
expected = ['abcd' * 9, '1234' * 9]
326
call('show-status', '--format', 'yaml', controller=False),
327
call('run-action', 'chaos-monkey/1', 'start', 'mode=single',
328
'enablement-timeout=120'),
329
call('run-action', 'chaos-monkey/0', 'start', 'mode=single',
330
'enablement-timeout=120'),
332
gjo_mock.call_args_list)
333
self.assertEqual(['chaos-monkey/1', 'chaos-monkey/0'],
334
monkey_runner.monkey_ids.keys())
335
self.assertEqual(len(monkey_runner.monkey_ids), 2)
336
self.assertItemsEqual(returned, expected)
337
with patch.object(client, 'get_juju_output', side_effect=output,
338
autospec=True) as gjo_mock:
339
monkey_runner.unleash_once()
342
call('show-status', '--format', 'yaml', controller=False),
344
'chaos-monkey/1', 'start', 'mode=single',
345
'enablement-timeout=120',
346
'monkey-id=123412341234123412341234123412341234'),
348
'chaos-monkey/0', 'start', 'mode=single',
349
'enablement-timeout=120',
350
'monkey-id=abcdabcdabcdabcdabcdabcdabcdabcdabcd'),
352
gjo_mock.call_args_list)
353
self.assertTrue('1234', monkey_runner.monkey_ids['chaos-monkey/1'])
354
# Test monkey_ids.get(unit_name) does not change on second call to
356
with patch.object(client, 'get_juju_output', side_effect=output2,
358
monkey_runner.unleash_once()
359
self.assertEqual('1234' * 9,
360
monkey_runner.monkey_ids['chaos-monkey/1'])
362
def test_unleash_once_raises_for_unexpected_action_output(self):
363
def output(*args, **kwargs):
364
status = yaml.safe_dump({
366
'0': {'agent-state': 'started'}
373
'chaos-monkey/0': {'baz': 'qux'},
381
('show-status', '--format', 'yaml'): status,
382
('run-action', 'chaos-monkey/0', 'start', 'mode=single',
383
'enablement-timeout=120'
387
client = EnvJujuClient(JujuData('foo', {}), None, '/foo/juju')
388
monkey_runner = MonkeyRunner('foo', client, service='jenkins')
389
with patch.object(client, 'get_juju_output', side_effect=output,
391
with self.assertRaisesRegexp(
392
Exception, 'Action id not found in output: Action fail'):
393
monkey_runner.unleash_once()
396
class TestIsHealthy(unittest.TestCase):
398
def test_is_healthy(self):
399
SCRIPT = """#!/bin/bash\necho -n 'PASS'\nexit 0"""
400
client = EnvJujuClient(JujuData('foo', {}), None, '/foo/juju')
401
with NamedTemporaryFile(delete=False) as health_script:
402
health_script.write(SCRIPT)
403
os.fchmod(health_script.fileno(), stat.S_IEXEC | stat.S_IREAD)
404
health_script.close()
405
monkey_runner = MonkeyRunner('foo', client,
406
health_checker=health_script.name)
407
with patch('logging.info') as lo_mock:
408
result = monkey_runner.is_healthy()
409
os.unlink(health_script.name)
410
self.assertTrue(result)
411
self.assertEqual(lo_mock.call_args[0][0],
412
'Health check output: PASS')
414
def test_is_healthy_fail(self):
415
SCRIPT = """#!/bin/bash\necho -n 'FAIL'\nexit 1"""
416
client = EnvJujuClient(JujuData('foo', {}), None, '/foo/juju')
417
with NamedTemporaryFile(delete=False) as health_script:
418
health_script.write(SCRIPT)
419
os.fchmod(health_script.fileno(), stat.S_IEXEC | stat.S_IREAD)
420
health_script.close()
421
monkey_runner = MonkeyRunner('foo', client,
422
health_checker=health_script.name)
423
with patch('logging.error') as le_mock:
424
result = monkey_runner.is_healthy()
425
os.unlink(health_script.name)
426
self.assertFalse(result)
427
self.assertEqual(le_mock.call_args[0][0], 'FAIL')
429
def test_is_healthy_with_no_execute_perms(self):
430
SCRIPT = """#!/bin/bash\nexit 0"""
431
client = EnvJujuClient(JujuData('foo', {}), None, '/foo/juju')
432
with NamedTemporaryFile(delete=False) as health_script:
433
health_script.write(SCRIPT)
434
os.fchmod(health_script.fileno(), stat.S_IREAD)
435
health_script.close()
436
monkey_runner = MonkeyRunner('foo', client,
437
health_checker=health_script.name)
438
with patch('logging.error') as le_mock:
439
with self.assertRaises(OSError):
440
monkey_runner.is_healthy()
441
os.unlink(health_script.name)
442
self.assertRegexpMatches(
443
le_mock.call_args[0][0],
444
r'The health check failed to execute with: \[Errno 13\].*')
447
class TestWaitForChaos(FakeHomeTestCase):
449
def test_wait_for_chaos_complete(self):
450
client = EnvJujuClient(JujuData('foo', {}), None, '/foo')
451
runner = MonkeyRunner('foo', client)
452
units = [('blib', 'blab')]
453
with patch.object(runner, 'iter_chaos_monkey_units', autospec=True,
454
return_value=units) as ic_mock:
455
with patch.object(runner, 'get_unit_status',
456
autospec=True, return_value='done') as us_mock:
457
returned = runner.wait_for_chaos()
458
self.assertEqual(returned, None)
459
self.assertEqual(ic_mock.call_count, 1)
460
self.assertEqual(us_mock.call_count, 1)
462
def test_wait_for_chaos_complete_timesout(self):
463
client = EnvJujuClient(JujuData('foo', {}), None, '/foo')
464
runner = MonkeyRunner('foo', client)
465
with self.assertRaisesRegexp(
466
Exception, 'Chaos operations did not complete.'):
467
runner.wait_for_chaos(timeout=0)
469
def test_wait_for_chaos_started(self):
470
client = EnvJujuClient(JujuData('foo', {}), None, '/foo')
471
runner = MonkeyRunner('foo', client)
472
units = [('blib', 'blab')]
473
with patch.object(runner, 'iter_chaos_monkey_units', autospec=True,
474
return_value=units) as ic_mock:
475
with patch.object(runner, 'get_unit_status',
477
return_value='running') as us_mock:
478
returned = runner.wait_for_chaos(state='start')
479
self.assertEqual(returned, None)
480
self.assertEqual(ic_mock.call_count, 1)
481
self.assertEqual(us_mock.call_count, 1)
483
def test_wait_for_chaos_unexpected_state(self):
484
client = EnvJujuClient(JujuData('foo', {}), None, '/foo')
485
runner = MonkeyRunner('foo', client)
486
with self.assertRaisesRegexp(
487
Exception, 'Unexpected state value: foo'):
488
runner.wait_for_chaos(state='foo')