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
1=== modified file 'assess_container_networking.py'
2--- assess_container_networking.py 2015-12-12 00:40:50 +0000
3+++ assess_container_networking.py 2016-01-14 17:48:44 +0000
4@@ -9,28 +9,24 @@
5 import re
6 import os
7 import subprocess
8+import sys
9 import tempfile
10 from textwrap import dedent
11 import time
12
13 from deploy_stack import (
14+ BootstrapManager,
15+ get_random_string,
16 update_env,
17- dump_env_logs,
18- get_random_string,
19-)
20-from jujuconfig import (
21- get_juju_home,
22 )
23 from jujupy import (
24- parse_new_state_server_from_error,
25- temp_bootstrap_env,
26 SimpleEnvironment,
27 EnvJujuClient,
28 )
29 from utility import (
30+ add_basic_testing_arguments,
31+ configure_logging,
32 wait_for_port,
33- print_now,
34- add_basic_testing_arguments,
35 )
36
37
38@@ -40,6 +36,9 @@
39 __metaclass__ = type
40
41
42+log = logging.getLogger("assess_container_networking")
43+
44+
45 def parse_args(argv=None):
46 """Parse all arguments."""
47
48@@ -69,7 +68,11 @@
49
50 At termination, clean out services and machines from the environment
51 rather than destroying it."""))
52- return parser.parse_args(argv)
53+ args = parser.parse_args(argv)
54+ # Passing --clean-environment implies --keep-env
55+ if args.clean_environment:
56+ args.keep_env = True
57+ return args
58
59
60 def ssh(client, machine, cmd):
61@@ -107,9 +110,14 @@
62 # A short timeout is used for get_status here because if we don't get a
63 # response from get_status quickly then the environment almost
64 # certainly doesn't exist or needs recreating.
65- status = client.get_status(5)
66+ try:
67+ status = client.get_status(5)
68+ except Exception as e:
69+ # TODO(gz): get_status should return a more specific error type.
70+ log.info("Could not clean existing env: %s", e)
71+ return False
72
73- for service in status.status['services']:
74+ for service in status.status.get('services', {}):
75 client.juju('remove-service', service)
76
77 if not services_only:
78@@ -214,6 +222,7 @@
79 :param targets: machine IDs of machines to test
80 :return: None;
81 """
82+ log.info('Waiting for the bootstrap machine agent to start.')
83 status = client.wait_for_started().status
84 source = targets[0]
85 dests = targets[1:]
86@@ -336,15 +345,15 @@
87 _assessment_iteration(client, test_containers)
88
89
90-def assess_container_networking(client, args):
91+def assess_container_networking(client, machine_type):
92 """Runs _assess_address_allocation, reboots hosts, repeat.
93 :param client: Juju client
94- :param args: Parsed command line arguments
95+ :param machine_type: Container types to test
96 :return: None
97 """
98 # Only test the containers we were asked to test
99- if args.machine_type:
100- types = [args.machine_type]
101+ if machine_type:
102+ types = [machine_type]
103 else:
104 types = [KVM_MACHINE, LXC_MACHINE]
105
106@@ -377,69 +386,30 @@
107 _assess_container_networking(client, types, hosts, containers)
108
109
110-def get_client(args):
111- client = EnvJujuClient.by_version(
112- SimpleEnvironment.from_config(args.env),
113- args.juju_bin, args.debug
114- )
115+def main(argv=None):
116+ args = parse_args(argv)
117+ configure_logging(args.verbose)
118+ bs_manager = BootstrapManager.from_args(args)
119+ client = bs_manager.client
120 client.enable_container_address_allocation()
121- update_env(client.env, args.temp_env_name,
122- series=args.series, bootstrap_host=args.bootstrap_host,
123- agent_url=args.agent_url, agent_stream=args.agent_stream,
124- region=args.region)
125- return client
126-
127-
128-def main():
129- args = parse_args()
130- client = get_client(args)
131- juju_home = get_juju_home()
132- bootstrap_host = None
133- success = True
134- try:
135- if args.clean_environment:
136- try:
137- if not clean_environment(client):
138- with temp_bootstrap_env(juju_home, client):
139- client.bootstrap(args.upload_tools)
140- except Exception as e:
141- logging.exception(e)
142- client.destroy_environment()
143- client = get_client(args)
144- with temp_bootstrap_env(juju_home, client):
145- client.bootstrap(args.upload_tools)
146- else:
147- client.destroy_environment()
148- client = get_client(args)
149- with temp_bootstrap_env(juju_home, client):
150+ # TODO(gz): Having to manipulate client env state here to get the temp env
151+ # is ugly, would ideally be captured in an explicit scope.
152+ update_env(client.env, bs_manager.temp_env_name, series=bs_manager.series,
153+ agent_url=bs_manager.agent_url,
154+ agent_stream=bs_manager.agent_stream, region=bs_manager.region)
155+ with bs_manager.top_context() as machines:
156+ bootstrap_required = True
157+ if args.clean_environment and clean_environment(client):
158+ bootstrap_required = False
159+ if bootstrap_required:
160+ with bs_manager.bootstrap_context(machines):
161 client.bootstrap(args.upload_tools)
162-
163- logging.info('Waiting for the bootstrap machine agent to start.')
164- status = client.wait_for_started()
165- mid, data = list(status.iter_machines())[0]
166- bootstrap_host = data['dns-name']
167-
168- assess_container_networking(client, args)
169-
170- except Exception as e:
171- success = False
172- logging.exception(e)
173- try:
174- if bootstrap_host is None:
175- bootstrap_host = parse_new_state_server_from_error(e)
176- except Exception as e:
177- print_now('exception while dumping logs:\n')
178- logging.exception(e)
179- finally:
180- if bootstrap_host is not None:
181- dump_env_logs(client, bootstrap_host, args.logs)
182-
183- if args.clean_environment:
184- clean_environment(client)
185- else:
186- client.destroy_environment()
187- if not success:
188- exit(1)
189+ with bs_manager.runtime_context(machines):
190+ assess_container_networking(client, args.machine_type)
191+ if args.clean_environment and not clean_environment(client):
192+ return 1
193+ return 0
194+
195
196 if __name__ == '__main__':
197- main()
198+ sys.exit(main())
199
200=== modified file 'tests/test_assess_container_networking.py'
201--- tests/test_assess_container_networking.py 2015-12-14 16:23:40 +0000
202+++ tests/test_assess_container_networking.py 2016-01-14 17:48:44 +0000
203@@ -1,6 +1,7 @@
204 from argparse import Namespace
205 from copy import deepcopy
206 from contextlib import contextmanager
207+import logging
208
209 from mock import (
210 patch,
211@@ -16,6 +17,7 @@
212
213 import assess_container_networking as jcnet
214 from tests import (
215+ FakeHomeTestCase,
216 parse_error,
217 TestCase,
218 )
219@@ -389,18 +391,101 @@
220 ValueError, "Default route not found",
221 jcnet.assess_internet_connection, self.client, targets)
222
223- def test_get_client(self):
224- args = Namespace(
225- env="e", juju_bin="jb", debug=False,
226- agent_stream="http://tools.testing/agents", temp_env_name="te",
227- series="s", bootstrap_host="bh", agent_url="au", region="r")
228-
229- upenv = MagicMock()
230- with patch.object(EnvJujuClient, "by_version"), \
231- patch.object(SimpleEnvironment, "from_config"), \
232- patch("assess_container_networking.update_env", upenv):
233-
234- jcnet.get_client(args)
235- self.assertEqual(upenv.call_args[0][1], args.temp_env_name)
236- for key, value in upenv.call_args[1].iteritems():
237- self.assertEqual(vars(args)[key], value)
238+
239+class TestMain(FakeHomeTestCase):
240+
241+ @contextmanager
242+ def patch_main(self, argv, client, log_level, debug=False):
243+ env = SimpleEnvironment(argv[0], {"type": "ec2"})
244+ client.env = env
245+ with patch("assess_container_networking.configure_logging",
246+ autospec=True) as mock_cl:
247+ with patch("jujupy.SimpleEnvironment.from_config",
248+ return_value=env) as mock_e:
249+ with patch("jujupy.EnvJujuClient.by_version",
250+ return_value=client) as mock_c:
251+ yield
252+ mock_cl.assert_called_once_with(log_level)
253+ mock_e.assert_called_once_with(argv[0])
254+ mock_c.assert_called_once_with(env, argv[1], debug=debug)
255+
256+ @contextmanager
257+ def patch_bootstrap_manager(self, runs=True):
258+ with patch("deploy_stack.BootstrapManager.top_context",
259+ autospec=True) as mock_tc:
260+ with patch("deploy_stack.BootstrapManager.bootstrap_context",
261+ autospec=True) as mock_bc:
262+ with patch("deploy_stack.BootstrapManager.runtime_context",
263+ autospec=True) as mock_rc:
264+ yield mock_bc
265+ self.assertEqual(mock_tc.call_count, 1)
266+ if runs:
267+ self.assertEqual(mock_rc.call_count, 1)
268+
269+ def test_bootstrap_required(self):
270+ argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod", "--verbose"]
271+ client = Mock(spec=["bootstrap", "enable_container_address_allocation",
272+ "is_jes_enabled"])
273+ with patch("assess_container_networking.assess_container_networking",
274+ autospec=True) as mock_assess:
275+ with self.patch_bootstrap_manager() as mock_bc:
276+ with self.patch_main(argv, client, logging.DEBUG):
277+ ret = jcnet.main(argv)
278+ client.enable_container_address_allocation.assert_called_once_with()
279+ client.bootstrap.assert_called_once_with(False)
280+ self.assertEqual("", self.log_stream.getvalue())
281+ self.assertEqual(mock_bc.call_count, 1)
282+ mock_assess.assert_called_once_with(client, None)
283+ self.assertEqual(ret, 0)
284+
285+ def test_clean_existing_env(self):
286+ argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod",
287+ "--clean-environment"]
288+ client = Mock(spec=["enable_container_address_allocation", "env",
289+ "get_status", "is_jes_enabled", "wait_for",
290+ "wait_for_started"])
291+ client.get_status.return_value = Status.from_text("""
292+ machines:
293+ "0":
294+ agent-state: started
295+ """)
296+ with patch("assess_container_networking.assess_container_networking",
297+ autospec=True) as mock_assess:
298+ with self.patch_bootstrap_manager() as mock_bc:
299+ with self.patch_main(argv, client, logging.INFO):
300+ ret = jcnet.main(argv)
301+ client.enable_container_address_allocation.assert_called_once_with()
302+ self.assertEqual(client.env.environment, "an-env-mod")
303+ self.assertEqual("", self.log_stream.getvalue())
304+ self.assertEqual(mock_bc.call_count, 0)
305+ mock_assess.assert_called_once_with(client, None)
306+ self.assertEqual(ret, 0)
307+
308+ def test_clean_missing_env(self):
309+ argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod",
310+ "--clean-environment"]
311+ client = Mock(spec=["bootstrap", "enable_container_address_allocation",
312+ "env", "get_status", "is_jes_enabled", "wait_for",
313+ "wait_for_started"])
314+ client.get_status.side_effect = [
315+ Exception("Timeout"),
316+ Status.from_text("""
317+ machines:
318+ "0":
319+ agent-state: started
320+ """),
321+ ]
322+ with patch("assess_container_networking.assess_container_networking",
323+ autospec=True) as mock_assess:
324+ with self.patch_bootstrap_manager() as mock_bc:
325+ with self.patch_main(argv, client, logging.INFO):
326+ ret = jcnet.main(argv)
327+ client.enable_container_address_allocation.assert_called_once_with()
328+ self.assertEqual(client.env.environment, "an-env-mod")
329+ client.bootstrap.assert_called_once_with(False)
330+ self.assertEqual(
331+ "INFO Could not clean existing env: Timeout\n",
332+ self.log_stream.getvalue())
333+ self.assertEqual(mock_bc.call_count, 1)
334+ mock_assess.assert_called_once_with(client, None)
335+ self.assertEqual(ret, 0)

Subscribers

People subscribed via source and target branches