def client_from_args(args): """Return client from args, as generated by parse_args. If the path given is FAKE, fake_juju_client() is used. Otherwise, the client is determined based on the path and version. """ if args.juju_bin == 'FAKE': client_class = ModelClient controller_state = FakeControllerState() version = '2.0.0' backend = FakeBackend(controller_state, full_path=args.juju_bin, version=version) else: version = ModelClient.get_version(args.juju_bin) client_class = get_client_class(version) backend = None juju_home = get_juju_home() with open(args.clouds_file) as f: clouds = yaml.safe_load(f) if args.config is None: config = {} else: with open(args.config) as f: config = yaml.safe_load(f) juju_data = client_class.config_class.from_cloud_region( args.cloud, args.region, config, clouds, juju_home) return client_class(juju_data, version, args.juju_bin, debug=args.debug, soft_deadline=args.deadline, _backend=backend)
def test_background_chaos(self): client = ModelClient(JujuData('foo', {}), None, '/foo/juju') with patch('chaos.MonkeyRunner.deploy_chaos_monkey', autospec=True) as d_mock: with patch('chaos.MonkeyRunner.unleash_once', return_value=['1'], autospec=True) as u_mock: with patch('chaos.MonkeyRunner.wait_for_chaos', autospec=True) as w_mock: with patch('chaos.remote_from_unit', return_value=SSHRemote(None, None, 'foo')) as ru_mock: with patch('remote.SSHRemote.copy') as rc_mock: log_dir = mkdtemp() with background_chaos('foo', client, log_dir, 1): pass rmtree(log_dir) self.assertEqual(1, d_mock.call_count) self.assertEqual(1, u_mock.call_count) self.assertEqual(1, ru_mock.call_count) self.assertEqual(1, rc_mock.call_count) self.assertEqual({'state': 'start'}, w_mock.mock_calls[0][2]) self.assertEqual({ 'state': 'complete', 'timeout': 1 }, w_mock.mock_calls[1][2])
def test_run_with_unit_fallback(self): env = JujuData("an-env", {"type": "nonlocal"}) client = ModelClient(env, None, None) unit = "a-application/0" 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 = subprocess.CalledProcessError(255, "ssh", output="") with patch.object(remote, "_run_subprocess") as mock_run: mock_run.return_value = "contents of /a/file" output = remote.run("cat /a/file") self.assertEqual(output, "contents of /a/file") mock_gjo.assert_called_once_with("ssh", unit, "cat /a/file", timeout=120) mock_run.assert_called_once_with([ "ssh", "-o", "User ubuntu", "-o", "UserKnownHostsFile /dev/null", "-o", "StrictHostKeyChecking no", "-o", "PasswordAuthentication no", "10.55.60.1", "cat /a/file", ]) self.assertRegexpMatches( self.log_stream.getvalue(), "(?m)^WARNING juju ssh to 'a-application/0' failed, .*")
def test_remote_from_unit_with_series(self): env = JujuData("an-env", {"type": "nonlocal"}) client = ModelClient(env, None, None) unit = "a-application/0" remote = remote_from_unit(client, unit, series="trusty") self.assertEqual(repr(remote), "<SSHRemote env='an-env' unit='a-application/0'>") self.assertIs(False, remote.is_windows())
def test_checks_deadline(self): client = ModelClient(JujuData('local', juju_home=''), None, None) with client_past_deadline(client): with patch.object(client, 'get_controller_client', autospec=True): with self.assertRaises(SoftDeadlineExceeded): amm._wait_for_model_check(client, lambda x: 'test', timeout=60)
def test_remote_from_unit(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.precise_status_output) remote = remote_from_unit(client, unit) self.assertEqual(repr(remote), "<SSHRemote env='an-env' unit='a-application/0'>") self.assertIs(False, remote.is_windows())
def test_remote_from_unit_with_status(self): env = JujuData("an-env", {"type": "nonlocal"}) client = ModelClient(env, None, None) unit = "a-application/0" status = Status.from_text(self.win2012hvr2_status_output) remote = remote_from_unit(client, unit, status=status) self.assertEqual( repr(remote), "<WinRmRemote env='an-env' unit='a-application/0'" " addr='10.55.60.2'>") self.assertIs(True, remote.is_windows())
def test_iter_chaos_monkey_units(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' }, } } } } } }) output = { ('show-status', '--format', 'yaml'): status, } return output[args] client = ModelClient(JujuData('foo', {}), None, '/foo/juju') runner = MonkeyRunner('foo', client, service='jenkins') with patch.object(client, 'get_juju_output', side_effect=output, autospec=True): monkey_units = dict( (k, v) for k, v in runner.iter_chaos_monkey_units()) expected = { 'chaos-monkey/0': { 'baz': 'qux' }, 'chaos-monkey/1': { 'abc': '123' } } self.assertEqual(expected, monkey_units)
def make_industrial_test(self): """Create an IndustrialTest for this MultiIndustrialTest.""" stable_path = ModelClient.get_full_path() paths = [self.really_old_path, stable_path, self.new_juju_path] upgrade_sequence = [p for p in paths if p is not None] stage_attempts = [self.stages.factory(upgrade_sequence, self.log_parent_dir, self.agent_stream)] return IndustrialTest.from_args(self.env, self.new_juju_path, stage_attempts, self.new_agent_url, self.debug)
def test_iter_steps_quickstart_fail(self): client = ModelClient( JujuData('foo', {'type': 'local'}), '1.234-76', None) bs_manager = FakeBootstrapManager(client) quickstart = QuickstartTest(bs_manager, '/tmp/bundle.yaml', 2) step_iter = quickstart.iter_steps() with patch.object(client, 'quickstart', side_effect=Exception): with self.assertRaises(Exception): step_iter.next() self.assertIs(False, bs_manager.entered_runtime) self.assertIs(True, bs_manager.exited_bootstrap) self.assertIs(True, bs_manager.exited_top)
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_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_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_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_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_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_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_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_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_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 main(): args = parse_args() juju_bin = args.juju_bin version = ModelClient.get_version(juju_bin) client_class = get_client_class(version) if client_class.config_class is not JujuData: logging.warn('This test does not support old jujus.') with open(args.example_clouds) as f: clouds = yaml.safe_load(f)['clouds'] cloug_validation = CloudValidation(version) cloud_specs = iter_clouds(clouds, cloug_validation) with temp_dir() as juju_home: env = JujuData('foo', config=None, juju_home=juju_home) client = client_class(env, version, juju_bin) succeeded, xfailed, failed = assess_all_clouds(client, cloud_specs) write_status('Succeeded', succeeded) for bug, failures in sorted(xfailed.items()): write_status('Expected fail (bug #{})'.format(bug), failures) write_status('Failed', failed) if len(failed) > 0: return 1 return 0
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 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 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)