Merge lp:~gz/juju-ci-tools/common_container_networking into lp:juju-ci-tools

Proposed by Martin Packman
Status: Merged
Merged at revision: 1226
Proposed branch: lp:~gz/juju-ci-tools/common_container_networking
Merge into: lp:juju-ci-tools
Diff against target: 335 lines (+148/-93)
2 files modified
assess_container_networking.py (+48/-78)
tests/test_assess_container_networking.py (+100/-15)
To merge this branch: bzr merge lp:~gz/juju-ci-tools/common_container_networking
Reviewer Review Type Date Requested Status
Aaron Bentley (community) Approve
Review via email: mp+282640@code.launchpad.net

Description of the change

Make assess_container_networking function with jes

Switches to use BootstrapManager but retains --clean-environment as a special case of --keep-env. Pulls some of the parts of BootstrapManager out into the local test file for now, still saves a bunch of code.

To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote :

Looks reasonable to me, except I don't understand the update_env call. Shame I didn't ask about why you needed it on the call, but now I can't see why you'd need it.

When you create the client for --clean-environment, you want to be using the actual previous configuration, so that you get the right API server addresses. Since that actual previous configuration used the temp-env-name, presumably you'd need to load the environment using the temp-env-name. And in that case, the environment shouldn't need updating at all (because it was created using update_env).

review: Needs Information
Revision history for this message
Aaron Bentley (abentley) wrote :

Okay, I think I get this better now. Because you're not hitting bootstrap_context, you're not writing client.env to disk-- you're just ignoring it. Its only value is the name, so you're using update_env to change the name.

I think this is a borderline case-- it might be better to load the proper client.env from disk, but this works. update_env is overkill, but it respects DRY.

review: Approve
Revision history for this message
Martin Packman (gz) wrote :

Right, I think what I really want is an alternative first step method on BootstrapManager that loads the tempenv rather than does the bootstrap.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'assess_container_networking.py'
--- assess_container_networking.py 2015-12-12 00:40:50 +0000
+++ assess_container_networking.py 2016-01-14 17:48:44 +0000
@@ -9,28 +9,24 @@
9import re9import re
10import os10import os
11import subprocess11import subprocess
12import sys
12import tempfile13import tempfile
13from textwrap import dedent14from textwrap import dedent
14import time15import time
1516
16from deploy_stack import (17from deploy_stack import (
18 BootstrapManager,
19 get_random_string,
17 update_env,20 update_env,
18 dump_env_logs,
19 get_random_string,
20)
21from jujuconfig import (
22 get_juju_home,
23)21)
24from jujupy import (22from jujupy import (
25 parse_new_state_server_from_error,
26 temp_bootstrap_env,
27 SimpleEnvironment,23 SimpleEnvironment,
28 EnvJujuClient,24 EnvJujuClient,
29)25)
30from utility import (26from utility import (
27 add_basic_testing_arguments,
28 configure_logging,
31 wait_for_port,29 wait_for_port,
32 print_now,
33 add_basic_testing_arguments,
34)30)
3531
3632
@@ -40,6 +36,9 @@
40__metaclass__ = type36__metaclass__ = type
4137
4238
39log = logging.getLogger("assess_container_networking")
40
41
43def parse_args(argv=None):42def parse_args(argv=None):
44 """Parse all arguments."""43 """Parse all arguments."""
4544
@@ -69,7 +68,11 @@
6968
70 At termination, clean out services and machines from the environment69 At termination, clean out services and machines from the environment
71 rather than destroying it."""))70 rather than destroying it."""))
72 return parser.parse_args(argv)71 args = parser.parse_args(argv)
72 # Passing --clean-environment implies --keep-env
73 if args.clean_environment:
74 args.keep_env = True
75 return args
7376
7477
75def ssh(client, machine, cmd):78def ssh(client, machine, cmd):
@@ -107,9 +110,14 @@
107 # A short timeout is used for get_status here because if we don't get a110 # A short timeout is used for get_status here because if we don't get a
108 # response from get_status quickly then the environment almost111 # response from get_status quickly then the environment almost
109 # certainly doesn't exist or needs recreating.112 # certainly doesn't exist or needs recreating.
110 status = client.get_status(5)113 try:
114 status = client.get_status(5)
115 except Exception as e:
116 # TODO(gz): get_status should return a more specific error type.
117 log.info("Could not clean existing env: %s", e)
118 return False
111119
112 for service in status.status['services']:120 for service in status.status.get('services', {}):
113 client.juju('remove-service', service)121 client.juju('remove-service', service)
114122
115 if not services_only:123 if not services_only:
@@ -214,6 +222,7 @@
214 :param targets: machine IDs of machines to test222 :param targets: machine IDs of machines to test
215 :return: None;223 :return: None;
216 """224 """
225 log.info('Waiting for the bootstrap machine agent to start.')
217 status = client.wait_for_started().status226 status = client.wait_for_started().status
218 source = targets[0]227 source = targets[0]
219 dests = targets[1:]228 dests = targets[1:]
@@ -336,15 +345,15 @@
336 _assessment_iteration(client, test_containers)345 _assessment_iteration(client, test_containers)
337346
338347
339def assess_container_networking(client, args):348def assess_container_networking(client, machine_type):
340 """Runs _assess_address_allocation, reboots hosts, repeat.349 """Runs _assess_address_allocation, reboots hosts, repeat.
341 :param client: Juju client350 :param client: Juju client
342 :param args: Parsed command line arguments351 :param machine_type: Container types to test
343 :return: None352 :return: None
344 """353 """
345 # Only test the containers we were asked to test354 # Only test the containers we were asked to test
346 if args.machine_type:355 if machine_type:
347 types = [args.machine_type]356 types = [machine_type]
348 else:357 else:
349 types = [KVM_MACHINE, LXC_MACHINE]358 types = [KVM_MACHINE, LXC_MACHINE]
350359
@@ -377,69 +386,30 @@
377 _assess_container_networking(client, types, hosts, containers)386 _assess_container_networking(client, types, hosts, containers)
378387
379388
380def get_client(args):389def main(argv=None):
381 client = EnvJujuClient.by_version(390 args = parse_args(argv)
382 SimpleEnvironment.from_config(args.env),391 configure_logging(args.verbose)
383 args.juju_bin, args.debug392 bs_manager = BootstrapManager.from_args(args)
384 )393 client = bs_manager.client
385 client.enable_container_address_allocation()394 client.enable_container_address_allocation()
386 update_env(client.env, args.temp_env_name,395 # TODO(gz): Having to manipulate client env state here to get the temp env
387 series=args.series, bootstrap_host=args.bootstrap_host,396 # is ugly, would ideally be captured in an explicit scope.
388 agent_url=args.agent_url, agent_stream=args.agent_stream,397 update_env(client.env, bs_manager.temp_env_name, series=bs_manager.series,
389 region=args.region)398 agent_url=bs_manager.agent_url,
390 return client399 agent_stream=bs_manager.agent_stream, region=bs_manager.region)
391400 with bs_manager.top_context() as machines:
392401 bootstrap_required = True
393def main():402 if args.clean_environment and clean_environment(client):
394 args = parse_args()403 bootstrap_required = False
395 client = get_client(args)404 if bootstrap_required:
396 juju_home = get_juju_home()405 with bs_manager.bootstrap_context(machines):
397 bootstrap_host = None
398 success = True
399 try:
400 if args.clean_environment:
401 try:
402 if not clean_environment(client):
403 with temp_bootstrap_env(juju_home, client):
404 client.bootstrap(args.upload_tools)
405 except Exception as e:
406 logging.exception(e)
407 client.destroy_environment()
408 client = get_client(args)
409 with temp_bootstrap_env(juju_home, client):
410 client.bootstrap(args.upload_tools)
411 else:
412 client.destroy_environment()
413 client = get_client(args)
414 with temp_bootstrap_env(juju_home, client):
415 client.bootstrap(args.upload_tools)406 client.bootstrap(args.upload_tools)
416407 with bs_manager.runtime_context(machines):
417 logging.info('Waiting for the bootstrap machine agent to start.')408 assess_container_networking(client, args.machine_type)
418 status = client.wait_for_started()409 if args.clean_environment and not clean_environment(client):
419 mid, data = list(status.iter_machines())[0]410 return 1
420 bootstrap_host = data['dns-name']411 return 0
421412
422 assess_container_networking(client, args)
423
424 except Exception as e:
425 success = False
426 logging.exception(e)
427 try:
428 if bootstrap_host is None:
429 bootstrap_host = parse_new_state_server_from_error(e)
430 except Exception as e:
431 print_now('exception while dumping logs:\n')
432 logging.exception(e)
433 finally:
434 if bootstrap_host is not None:
435 dump_env_logs(client, bootstrap_host, args.logs)
436
437 if args.clean_environment:
438 clean_environment(client)
439 else:
440 client.destroy_environment()
441 if not success:
442 exit(1)
443413
444if __name__ == '__main__':414if __name__ == '__main__':
445 main()415 sys.exit(main())
446416
=== modified file 'tests/test_assess_container_networking.py'
--- tests/test_assess_container_networking.py 2015-12-14 16:23:40 +0000
+++ tests/test_assess_container_networking.py 2016-01-14 17:48:44 +0000
@@ -1,6 +1,7 @@
1from argparse import Namespace1from argparse import Namespace
2from copy import deepcopy2from copy import deepcopy
3from contextlib import contextmanager3from contextlib import contextmanager
4import logging
45
5from mock import (6from mock import (
6 patch,7 patch,
@@ -16,6 +17,7 @@
1617
17import assess_container_networking as jcnet18import assess_container_networking as jcnet
18from tests import (19from tests import (
20 FakeHomeTestCase,
19 parse_error,21 parse_error,
20 TestCase,22 TestCase,
21)23)
@@ -389,18 +391,101 @@
389 ValueError, "Default route not found",391 ValueError, "Default route not found",
390 jcnet.assess_internet_connection, self.client, targets)392 jcnet.assess_internet_connection, self.client, targets)
391393
392 def test_get_client(self):394
393 args = Namespace(395class TestMain(FakeHomeTestCase):
394 env="e", juju_bin="jb", debug=False,396
395 agent_stream="http://tools.testing/agents", temp_env_name="te",397 @contextmanager
396 series="s", bootstrap_host="bh", agent_url="au", region="r")398 def patch_main(self, argv, client, log_level, debug=False):
397399 env = SimpleEnvironment(argv[0], {"type": "ec2"})
398 upenv = MagicMock()400 client.env = env
399 with patch.object(EnvJujuClient, "by_version"), \401 with patch("assess_container_networking.configure_logging",
400 patch.object(SimpleEnvironment, "from_config"), \402 autospec=True) as mock_cl:
401 patch("assess_container_networking.update_env", upenv):403 with patch("jujupy.SimpleEnvironment.from_config",
402404 return_value=env) as mock_e:
403 jcnet.get_client(args)405 with patch("jujupy.EnvJujuClient.by_version",
404 self.assertEqual(upenv.call_args[0][1], args.temp_env_name)406 return_value=client) as mock_c:
405 for key, value in upenv.call_args[1].iteritems():407 yield
406 self.assertEqual(vars(args)[key], value)408 mock_cl.assert_called_once_with(log_level)
409 mock_e.assert_called_once_with(argv[0])
410 mock_c.assert_called_once_with(env, argv[1], debug=debug)
411
412 @contextmanager
413 def patch_bootstrap_manager(self, runs=True):
414 with patch("deploy_stack.BootstrapManager.top_context",
415 autospec=True) as mock_tc:
416 with patch("deploy_stack.BootstrapManager.bootstrap_context",
417 autospec=True) as mock_bc:
418 with patch("deploy_stack.BootstrapManager.runtime_context",
419 autospec=True) as mock_rc:
420 yield mock_bc
421 self.assertEqual(mock_tc.call_count, 1)
422 if runs:
423 self.assertEqual(mock_rc.call_count, 1)
424
425 def test_bootstrap_required(self):
426 argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod", "--verbose"]
427 client = Mock(spec=["bootstrap", "enable_container_address_allocation",
428 "is_jes_enabled"])
429 with patch("assess_container_networking.assess_container_networking",
430 autospec=True) as mock_assess:
431 with self.patch_bootstrap_manager() as mock_bc:
432 with self.patch_main(argv, client, logging.DEBUG):
433 ret = jcnet.main(argv)
434 client.enable_container_address_allocation.assert_called_once_with()
435 client.bootstrap.assert_called_once_with(False)
436 self.assertEqual("", self.log_stream.getvalue())
437 self.assertEqual(mock_bc.call_count, 1)
438 mock_assess.assert_called_once_with(client, None)
439 self.assertEqual(ret, 0)
440
441 def test_clean_existing_env(self):
442 argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod",
443 "--clean-environment"]
444 client = Mock(spec=["enable_container_address_allocation", "env",
445 "get_status", "is_jes_enabled", "wait_for",
446 "wait_for_started"])
447 client.get_status.return_value = Status.from_text("""
448 machines:
449 "0":
450 agent-state: started
451 """)
452 with patch("assess_container_networking.assess_container_networking",
453 autospec=True) as mock_assess:
454 with self.patch_bootstrap_manager() as mock_bc:
455 with self.patch_main(argv, client, logging.INFO):
456 ret = jcnet.main(argv)
457 client.enable_container_address_allocation.assert_called_once_with()
458 self.assertEqual(client.env.environment, "an-env-mod")
459 self.assertEqual("", self.log_stream.getvalue())
460 self.assertEqual(mock_bc.call_count, 0)
461 mock_assess.assert_called_once_with(client, None)
462 self.assertEqual(ret, 0)
463
464 def test_clean_missing_env(self):
465 argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod",
466 "--clean-environment"]
467 client = Mock(spec=["bootstrap", "enable_container_address_allocation",
468 "env", "get_status", "is_jes_enabled", "wait_for",
469 "wait_for_started"])
470 client.get_status.side_effect = [
471 Exception("Timeout"),
472 Status.from_text("""
473 machines:
474 "0":
475 agent-state: started
476 """),
477 ]
478 with patch("assess_container_networking.assess_container_networking",
479 autospec=True) as mock_assess:
480 with self.patch_bootstrap_manager() as mock_bc:
481 with self.patch_main(argv, client, logging.INFO):
482 ret = jcnet.main(argv)
483 client.enable_container_address_allocation.assert_called_once_with()
484 self.assertEqual(client.env.environment, "an-env-mod")
485 client.bootstrap.assert_called_once_with(False)
486 self.assertEqual(
487 "INFO Could not clean existing env: Timeout\n",
488 self.log_stream.getvalue())
489 self.assertEqual(mock_bc.call_count, 1)
490 mock_assess.assert_called_once_with(client, None)
491 self.assertEqual(ret, 0)

Subscribers

People subscribed via source and target branches