def test_run_with_unit(self): env = JujuData("an-env", {"type": "nonlocal"}) client = ModelClient(env, None, None) unit = "a-application/0" remote = remote_from_unit(client, unit, series="trusty") with patch.object(client, "get_juju_output") as mock_cmd: mock_cmd.return_value = "contents of /a/file" output = remote.run("cat /a/file") self.assertEqual(output, "contents of /a/file") mock_cmd.assert_called_once_with("ssh", unit, "cat /a/file", timeout=120)
def test_basic_args(self): args = ['bundles', 'an-env', '/bin/juju', 'logs', 'deployer-env'] env = JujuData('an-env') client = ModelClient(env, '1.234-76', None) with patch('run_deployer.client_from_config', return_value=client) as c_mock: with patch('run_deployer.boot_context'): with patch('run_deployer.assess_deployer') as ad_mock: main(args) c_mock.assert_called_once_with('an-env', '/bin/juju', debug=False, soft_deadline=None) ad_mock.assert_called_once_with(parse_args(args), client, 1200, 1800)
def test_is_healthy_fail(self): SCRIPT = """#!/bin/bash\necho -n 'FAIL'\nexit 1""" client = ModelClient(JujuData('foo', {}), None, '/foo/juju') with NamedTemporaryFile(delete=False) as health_script: health_script.write(SCRIPT) os.fchmod(health_script.fileno(), stat.S_IEXEC | stat.S_IREAD) health_script.close() monkey_runner = MonkeyRunner('foo', client, health_checker=health_script.name) with patch('logging.error') as le_mock: result = monkey_runner.is_healthy() os.unlink(health_script.name) self.assertFalse(result) self.assertEqual(le_mock.call_args[0][0], 'FAIL')
def test_wait_for_chaos_started(self): client = ModelClient(JujuData('foo', {}), None, '/foo') runner = MonkeyRunner('foo', client) units = [('blib', 'blab')] with patch.object(runner, 'iter_chaos_monkey_units', autospec=True, return_value=units) as ic_mock: with patch.object(runner, 'get_unit_status', autospec=True, return_value='running') as us_mock: returned = runner.wait_for_chaos(state='start') self.assertEqual(returned, None) self.assertEqual(ic_mock.call_count, 1) self.assertEqual(us_mock.call_count, 1)
def test_background_chaos_exits(self): client = ModelClient(JujuData('foo', {}), None, '/foo/juju') with patch('chaos.MonkeyRunner.deploy_chaos_monkey', autospec=True): with patch('chaos.MonkeyRunner.unleash_once', autospec=True): with patch('chaos.MonkeyRunner.wait_for_chaos', autospec=True): with patch('chaos.remote_from_unit'): with patch('remote.SSHRemote.copy'): with patch('logging.exception') as le_mock: with patch('sys.exit', autospec=True) as sm: log_dir = mkdtemp() with background_chaos( 'foo', client, log_dir, 1): raise Exception() rmtree(log_dir) self.assertEqual(1, le_mock.call_count) sm.assert_called_once_with(1)
def test_cat_on_windows(self): env = JujuData("an-env", {"type": "nonlocal"}) client = ModelClient(env, None, None) unit = "a-application/0" with patch.object(client, "get_status", autospec=True) as st: st.return_value = Status.from_text(self.win2012hvr2_status_output) response = winrm.Response(("contents of /a/file", "", 0)) remote = remote_from_unit(client, unit) with patch.object(remote.session, "run_cmd", autospec=True, return_value=response) as mock_run: output = remote.cat("/a/file") self.assertEqual(output, "contents of /a/file") st.assert_called_once_with() mock_run.assert_called_once_with("type", ["/a/file"])
def test_deploy_chaos_monkey(self): def output(*args, **kwargs): status = yaml.safe_dump({ 'machines': { '0': { 'agent-state': 'started' } }, 'applications': { 'ser1': { 'units': { 'bar': { 'agent-state': 'started', 'subordinates': { 'chaos-monkey/1': { 'agent-state': 'started' } } } } } } }) output = { ('show-status', '--format', 'yaml'): status, } return output[args] client = ModelClient(JujuData('foo', {}), '1.25.0', '/foo/juju') with patch.object(client, 'get_juju_output', side_effect=output, autospec=True) as gjo_mock: with patch('subprocess.check_call', autospec=True) as cc_mock: monkey_runner = MonkeyRunner('foo', client, service='ser1') with patch('jujupy.client.GroupReporter._write', autospec=True): monkey_runner.deploy_chaos_monkey() assert_juju_call(self, cc_mock, client, ('juju', '--show-log', 'deploy', '-m', 'foo:foo', 'local:chaos-monkey'), 0) assert_juju_call(self, cc_mock, client, ('juju', '--show-log', 'add-relation', '-m', 'foo:foo', 'ser1', 'chaos-monkey'), 1) self.assertEqual(cc_mock.call_count, 2) self.assertEqual(gjo_mock.call_count, 2)
def test_run_finally(self): do_finally = MagicMock() def fake_iter_steps(): try: yield {'bootstrap_host': 'foo'} finally: do_finally() client = ModelClient( JujuData('foo', {'type': 'local'}), '1.234-76', None) bs_manager = make_bootstrap_manager(client) quickstart = QuickstartTest(bs_manager, '/tmp/bundle.yaml', 2) with patch.object(quickstart, 'iter_steps', side_effect=fake_iter_steps): quickstart.run() do_finally.assert_called_once_with()
def test_is_healthy_with_no_execute_perms(self): SCRIPT = """#!/bin/bash\nexit 0""" client = ModelClient(JujuData('foo', {}), None, '/foo/juju') with NamedTemporaryFile(delete=False) as health_script: health_script.write(SCRIPT) os.fchmod(health_script.fileno(), stat.S_IREAD) health_script.close() monkey_runner = MonkeyRunner('foo', client, health_checker=health_script.name) with patch('logging.error') as le_mock: with self.assertRaises(OSError): monkey_runner.is_healthy() os.unlink(health_script.name) self.assertRegexpMatches( le_mock.call_args[0][0], r'The health check failed to execute with: \[Errno 13\].*')
def test_run_cmd(self): env = JujuData("an-env", {"type": "nonlocal"}) client = ModelClient(env, None, None) unit = "a-application/0" with patch.object(client, "get_status", autospec=True) as st: st.return_value = Status.from_text(self.win2012hvr2_status_output) response = winrm.Response(("some out", "some err", 0)) remote = remote_from_unit(client, unit) with patch.object(remote.session, "run_cmd", autospec=True, return_value=response) as mock_run: output = remote.run_cmd( ["C:\\Program Files\\bin.exe", "/IN", "Bob's Stuff"]) self.assertEqual(output, response) st.assert_called_once_with() mock_run.assert_called_once_with('"C:\\Program Files\\bin.exe"', ['/IN "Bob\'s Stuff"'])
def test_run_exception(self): tear_down = MagicMock() def fake_iter_steps(): try: yield {'bootstrap_host': 'foo'} except: tear_down() client = ModelClient( JujuData('foo', {'type': 'local'}), '1.234-76', None) bs_manager = make_bootstrap_manager(client) quickstart = QuickstartTest(bs_manager, '/tmp/bundle.yaml', 2) with patch.object(quickstart, 'iter_steps', side_effect=fake_iter_steps): with self.assertRaises(BaseException): with patch('logging.info', side_effect=Exception): quickstart.run() tear_down.assert_called_once_with()
def test_run_default_command_error_fallback(self): env = JujuData("an-env", {"type": "nonlocal"}) client = ModelClient(env, None, None) unit = "a-application/0" error = subprocess.CalledProcessError(1, "ssh", output="bad command") with patch.object(client, "get_status") as st: st.return_value = Status.from_text(self.precise_status_output) remote = remote_from_unit(client, unit) with patch.object(client, "get_juju_output") as mock_gjo: mock_gjo.side_effect = error with self.assertRaises(subprocess.CalledProcessError) as c: remote.run("cat /a/file") self.assertIs(c.exception, error) mock_gjo.assert_called_once_with("ssh", unit, "cat /a/file", timeout=120) self.assertRegexpMatches( self.log_stream.getvalue(), "(?m)^WARNING juju ssh to 'a-application/0' failed, .*")
def test_iter_steps(self): log_dir = use_context(self, temp_dir()) client = ModelClient( JujuData('foo', {'type': 'local'}), '1.234-76', None) bs_manager = make_bootstrap_manager(client, log_dir=log_dir) quickstart = QuickstartTest(bs_manager, '/tmp/bundle.yaml', 2) steps = quickstart.iter_steps() with patch.object(client, 'quickstart') as qs_mock: # Test first yield with patch('jujupy.client.check_free_disk_space', autospec=True): with patch.object(client, 'kill_controller', autospec=True) as kill_mock: step = steps.next() kill_mock.assert_called_once_with() qs_mock.assert_called_once_with('/tmp/bundle.yaml') expected = {'juju-quickstart': 'Returned from quickstart'} self.assertEqual(expected, step) with patch('deploy_stack.get_machine_dns_name', return_value='mocked_name') as dns_mock: # Test second yield with patch.object(client, 'get_controller_client') as gcc_mock: step = steps.next() dns_mock.assert_called_once_with(gcc_mock.return_value, '0') self.assertEqual('mocked_name', step['bootstrap_host']) with patch.object(client, 'wait_for_deploy_started') as wds_mock: # Test third yield step = steps.next() wds_mock.assert_called_once_with(2) self.assertEqual('Deploy stated', step['deploy_started']) with patch.object(client, 'wait_for_started') as ws_mock: # Test forth yield step = steps.next() ws_mock.assert_called_once_with(ANY) self.assertEqual('All Agents started', step['agents_started']) with patch('deploy_stack.safe_print_status'): with patch('deploy_stack.BootstrapManager.tear_down'): with patch('quickstart_deploy.BootstrapManager.dump_all_logs'): with patch('jujupy.ModelClient.iter_model_clients', return_value=[]): steps.close()
def test_suppresses_deadline(self): client = ModelClient(JujuData('local', juju_home=''), None, None) with client_past_deadline(client): real_check_timeouts = client.check_timeouts def get_models(): with real_check_timeouts(): return {'models': [{'name': 'TestModelName'}]} controller_client = Mock() controller_client.get_models.side_effect = get_models with patch.object(client, 'get_controller_client', autospec=True, return_value=controller_client): with patch.object(client, 'check_timeouts', autospec=True): amm._wait_for_model_check(client, lambda x: 'test', timeout=60)
def test_basic_args_native_deploy_landscape(self): args = [ 'cs:~landscape/bundle/landscape-scalable', 'an-env', '/bin/juju', 'logs', 'deployer-env', '--allow-native-deploy', '--bundle-verification-script', 'verify_landscape_bundle.py' ] env = JujuData('an-env') client = ModelClient(env, '1.234-76', None) with patch('run_deployer.client_from_config', return_value=client) as c_mock: with patch('run_deployer.boot_context'): with patch('run_deployer.assess_deployer') as ad_mock: with patch('run_deployer.run_command') as rc: main(args) c_mock.assert_called_once_with('an-env', '/bin/juju', debug=False, soft_deadline=None) ad_mock.assert_called_once_with(parse_args(args), client, 1200, 1800) client_ser = pickle.dumps(client) rc.assert_called_once_with(['verify_landscape_bundle.py', client_ser])
def test_copy_on_windows(self): env = JujuData("an-env", {"type": "nonlocal"}) client = ModelClient(env, None, None) unit = "a-application/0" dest = "/local/path" with patch.object(client, "get_status", autospec=True) as st: st.return_value = Status.from_text(self.win2012hvr2_status_output) response = winrm.Response(("fake output", "", 0)) remote = remote_from_unit(client, unit) with patch.object(remote.session, "run_ps", autospec=True, return_value=response) as mock_run: with patch.object(remote, "_encoded_copy_to_dir", autospec=True) as mock_cpdir: remote.copy(dest, ["C:\\logs\\*", "%APPDATA%\\*.log"]) mock_cpdir.assert_called_once_with(dest, "fake output") st.assert_called_once_with() self.assertEquals(mock_run.call_count, 1) self.assertRegexpMatches(mock_run.call_args[0][0], r'.*"C:\\logs\\[*]","%APPDATA%\\[*].log".*')
def test_unleash_once_raises_for_unexpected_action_output(self): def output(*args, **kwargs): status = yaml.safe_dump({ 'machines': { '0': { 'agent-state': 'started' } }, 'applications': { 'jenkins': { 'units': { 'foo': { 'subordinates': { 'chaos-monkey/0': { 'baz': 'qux' }, } } } } } }) output = { ('show-status', '--format', 'yaml'): status, ('run-action', 'chaos-monkey/0', 'start', 'mode=single', 'enablement-timeout=120'): 'Action fail', } return output[args] client = ModelClient(JujuData('foo', {}), None, '/foo/juju') monkey_runner = MonkeyRunner('foo', client, service='jenkins') with patch.object(client, 'get_juju_output', side_effect=output, autospec=True): with self.assertRaisesRegexp( Exception, 'Action id not found in output: Action fail'): monkey_runner.unleash_once()
def setUp(self): self.client = ModelClient(JujuData('foo', {'type': 'local'}), '1.234-76', None) def nil_func(): return None self.juju_mock = JujuMock() self.ssh_mock = Mock() patches = [ patch.object(self.client, 'juju', self.juju_mock.juju), patch.object(self.client, 'get_status', self.juju_mock.get_status), patch.object(self.client, 'juju_async', self.juju_mock.juju_async), patch.object(self.client, 'wait_for_started', self.juju_mock.get_status), patch.object(self.client, 'get_juju_output', self.juju_mock.juju), ] for patcher in patches: patcher.start() self.addCleanup(patcher.stop)
def setUp(self): self.client = ModelClient( JujuData('foo', {'type': 'local'}), '1.234-76', None) self.juju_mock = fake_juju_client(cls=JujuMock) self.juju_mock.bootstrap() self.ssh_mock = Mock() patches = [ patch.object(self.client, 'juju', self.juju_mock.juju), patch.object(self.client, 'get_status', self.juju_mock.get_status), patch.object(self.client, 'juju_async', self.juju_mock.juju_async), patch.object(self.client, 'wait_for', lambda *args, **kw: None), patch.object(self.client, 'wait_for_started', self.juju_mock.get_status), patch.object( self.client, 'get_juju_output', self.juju_mock.get_juju_output), ] for patcher in patches: patcher.start() self.addCleanup(patcher.stop)
def test_unleash_once(self): def output(*args, **kwargs): status = yaml.safe_dump({ 'machines': { '0': { 'agent-state': 'started' } }, 'applications': { 'jenkins': { 'units': { 'foo': { 'subordinates': { 'chaos-monkey/0': { 'baz': 'qux' }, 'not-chaos/0': { 'qwe': 'rty' }, } }, 'bar': { 'subordinates': { 'chaos-monkey/1': { 'abc': '123' }, } } } } } }) charm_config = yaml.safe_dump({ 'charm': {'jenkins'}, 'service': {'jenkins'}, 'settings': { 'chaos-dir': { 'default': 'true', 'description': 'bla bla', 'type': 'string', 'value': '/tmp/charm-dir', } } }) output = { ('show-status', '--format', 'yaml'): status, ('get', 'jenkins'): charm_config, ('run-action', 'chaos-monkey/0', 'start', 'mode=single', 'enablement-timeout=120'): ('Action queued with id: ' 'abcdabcdabcdabcdabcdabcdabcdabcdabcd'), ('run-action', 'chaos-monkey/0', 'start', 'mode=single', 'enablement-timeout=120', 'monkey-id=abcdabcdabcdabcdabcdabcdabcdabcdabcd'): ('Action queued with id: ' 'efabefabefabefabefabefabefabefabefab'), ('run-action', 'chaos-monkey/1', 'start', 'mode=single', 'enablement-timeout=120'): ('Action queued with id: ' '123412341234123412341234123412341234'), ('run-action', 'chaos-monkey/1', 'start', 'mode=single', 'enablement-timeout=120', 'monkey-id=123412341234123412341234123412341234'): ('Action queued with id: ' '567856785678567856785678567856785678'), } return output[args] def output2(*args, **kwargs): status = yaml.safe_dump({ 'machines': { '0': { 'agent-state': 'started' } }, 'applications': { 'jenkins': { 'units': { 'bar': { 'subordinates': { 'chaos-monkey/1': { 'abc': '123' }, } } } } } }) output = { ('show-status', '--format', 'yaml'): status, ('run-action', 'chaos-monkey/1', 'start', 'mode=single', 'enablement-timeout=120', 'monkey-id=123412341234123412341234123412341234'): ('Action queued with id: ' 'abcdabcdabcdabcdabcdabcdabcdabcdabcd'), } return output[args] client = ModelClient(JujuData('foo', {}), None, '/foo/juju') monkey_runner = MonkeyRunner('foo', client, service='jenkins') with patch.object(client, 'get_juju_output', side_effect=output, autospec=True) as gjo_mock: returned = monkey_runner.unleash_once() expected = ['abcd' * 9, '1234' * 9] self.assertEqual([ call('show-status', '--format', 'yaml', controller=False), call('run-action', 'chaos-monkey/1', 'start', 'mode=single', 'enablement-timeout=120'), call('run-action', 'chaos-monkey/0', 'start', 'mode=single', 'enablement-timeout=120'), ], gjo_mock.call_args_list) self.assertEqual(['chaos-monkey/1', 'chaos-monkey/0'], monkey_runner.monkey_ids.keys()) self.assertEqual(len(monkey_runner.monkey_ids), 2) self.assertItemsEqual(returned, expected) with patch.object(client, 'get_juju_output', side_effect=output, autospec=True) as gjo_mock: monkey_runner.unleash_once() self.assertEqual([ call('show-status', '--format', 'yaml', controller=False), call('run-action', 'chaos-monkey/1', 'start', 'mode=single', 'enablement-timeout=120', 'monkey-id=123412341234123412341234123412341234'), call('run-action', 'chaos-monkey/0', 'start', 'mode=single', 'enablement-timeout=120', 'monkey-id=abcdabcdabcdabcdabcdabcdabcdabcdabcd'), ], gjo_mock.call_args_list) self.assertTrue('1234', monkey_runner.monkey_ids['chaos-monkey/1']) # Test monkey_ids.get(unit_name) does not change on second call to # unleash_once() with patch.object(client, 'get_juju_output', side_effect=output2, autospec=True): monkey_runner.unleash_once() self.assertEqual('1234' * 9, monkey_runner.monkey_ids['chaos-monkey/1'])
def test_wait_for_chaos_unexpected_state(self): client = ModelClient(JujuData('foo', {}), None, '/foo') runner = MonkeyRunner('foo', client) with self.assertRaisesRegexp(Exception, 'Unexpected state value: foo'): runner.wait_for_chaos(state='foo')
def test_returns_true_when_greater_than_21(self): client = ModelClient(JujuData('local', juju_home=''), None, None) self.assertTrue(amm.client_is_at_least_2_1(client))
def test_wait_for_chaos_complete_timesout(self): client = ModelClient(JujuData('foo', {}), None, '/foo') runner = MonkeyRunner('foo', client) with self.assertRaisesRegexp(Exception, 'Chaos operations did not complete.'): runner.wait_for_chaos(timeout=0)
def fake_ModelClient(env, path=None, debug=None): return ModelClient(env=env, version='1.2.3.4', full_path=path)
def test_get_unit_status(self): def output(*args, **kwargs): status = yaml.safe_dump({ 'machines': { '0': { 'agent-state': 'started' } }, 'applications': { 'jenkins': { 'units': { 'foo': { 'subordinates': { 'chaos-monkey/0': { 'baz': 'qux' }, 'not-chaos/0': { 'qwe': 'rty' }, } }, 'bar': { 'subordinates': { 'chaos-monkey/1': { 'abc': '123' }, } } } } } }) charm_config = yaml.safe_dump({ 'charm': {'jenkins'}, 'service': {'jenkins'}, 'settings': { 'chaos-dir': { 'default': 'true', 'description': 'bla bla', 'type': 'string', 'value': '/tmp/charm-dir', } } }) output = { ('status', ): status, ('config', 'chaos-monkey'): charm_config, } return output[args] client = ModelClient(JujuData('foo', {}), None, '/foo') monkey_runner = MonkeyRunner('foo', client, service='jenkins') monkey_runner.monkey_ids = { 'chaos-monkey/0': 'workspace0', 'chaos-monkey/1': 'workspace1' } with patch.object(client, 'get_juju_output', side_effect=output, autospec=True): with patch('subprocess.call', autospec=True, return_value=0) as call_mock: for unit_name in ['chaos-monkey/1', 'chaos-monkey/0']: self.assertEqual(monkey_runner.get_unit_status(unit_name), 'running') self.assertEqual(call_mock.call_count, 2) with patch.object(client, 'get_juju_output', side_effect=output, autospec=True): with patch('subprocess.call', autospec=True, return_value=1) as call_mock: for unit_name in ['chaos-monkey/1', 'chaos-monkey/0']: self.assertEqual(monkey_runner.get_unit_status(unit_name), 'done') self.assertEqual(call_mock.call_count, 2)