Merge lp:~veebers/juju-ci-tools/introduce_commandcomplete into lp:juju-ci-tools
- introduce_commandcomplete
- Merge into trunk
Proposed by
Christopher Lee
Status: | Superseded |
---|---|
Proposed branch: | lp:~veebers/juju-ci-tools/introduce_commandcomplete |
Merge into: | lp:juju-ci-tools |
Diff against target: |
1629 lines (+450/-138) 14 files modified
assess_container_networking.py (+3/-2) chaos.py (+3/-2) deploy_stack.py (+0/-1) jujupy/client.py (+130/-26) jujupy/fake.py (+5/-1) jujupy/tests/test_client.py (+185/-57) jujupy/tests/test_version_client.py (+42/-31) jujupy/version_client.py (+19/-9) tests/__init__.py (+21/-1) tests/test_assess_block.py (+2/-1) tests/test_assess_bootstrap.py (+2/-1) tests/test_assess_container_networking.py (+28/-2) tests/test_assess_user_grant_revoke.py (+2/-1) tests/test_deploy_stack.py (+8/-3) |
To merge this branch: | bzr merge lp:~veebers/juju-ci-tools/introduce_commandcomplete |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Release Engineering | Pending | ||
Review via email: mp+320753@code.launchpad.net |
This proposal has been superseded by a proposal from 2017-03-23.
Commit message
Description of the change
Introduce CommandComplete to command wrappers (deploy etc.)
Introduces the CompleteComplete return to command wrappers and updates test to match the new return behaviour.
Sorry for the massive diff, most changes are touching lines for patching of ModelClient.juju() return.
To post a comment you must log in.
Unmerged revisions
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 2017-02-24 18:28:04 +0000 |
3 | +++ assess_container_networking.py 2017-03-23 04:46:32 +0000 |
4 | @@ -248,8 +248,9 @@ |
5 | |
6 | d = re.search(r'^default\s+via\s+([\d\.]+)\s+', routes, re.MULTILINE) |
7 | if d: |
8 | - rc = client.juju('ssh', ('--proxy', target, |
9 | - 'ping -c1 -q ' + d.group(1)), check=False) |
10 | + rc, _ = client.juju( |
11 | + 'ssh', |
12 | + ('--proxy', target, 'ping -c1 -q ' + d.group(1)), check=False) |
13 | if rc != 0: |
14 | raise ValueError('%s unable to ping default route' % target) |
15 | else: |
16 | |
17 | === modified file 'chaos.py' |
18 | --- chaos.py 2016-06-27 11:41:04 +0000 |
19 | +++ chaos.py 2017-03-23 04:46:32 +0000 |
20 | @@ -146,8 +146,9 @@ |
21 | check_cmd += '/chaos_monkey.' + self.monkey_ids[unit_name] |
22 | check_cmd += '/chaos_runner.lock' |
23 | check_cmd += ' ]' |
24 | - if self.client.juju('run', ('--unit', unit_name, check_cmd), |
25 | - check=False): |
26 | + retvar, _ = self.client.juju( |
27 | + 'run', ('--unit', unit_name, check_cmd), check=False) |
28 | + if retvar != 0: |
29 | return 'done' |
30 | return 'running' |
31 | |
32 | |
33 | === modified file 'deploy_stack.py' |
34 | --- deploy_stack.py 2017-03-01 20:34:01 +0000 |
35 | +++ deploy_stack.py 2017-03-23 04:46:32 +0000 |
36 | @@ -8,7 +8,6 @@ |
37 | from contextlib import nested |
38 | except ImportError: |
39 | from contextlib import ExitStack as nested |
40 | - |
41 | import glob |
42 | import logging |
43 | import os |
44 | |
45 | === modified file 'jujupy/client.py' |
46 | --- jujupy/client.py 2017-03-17 20:26:17 +0000 |
47 | +++ jujupy/client.py 2017-03-23 04:46:32 +0000 |
48 | @@ -1079,7 +1079,7 @@ |
49 | self.feature_flags = feature_flags |
50 | self.debug = debug |
51 | self._timeout_path = get_timeout_path() |
52 | - self.juju_timings = {} |
53 | + self.juju_timings = [] |
54 | self.soft_deadline = soft_deadline |
55 | self._ignore_soft_deadline = False |
56 | |
57 | @@ -1181,7 +1181,11 @@ |
58 | def juju(self, command, args, used_feature_flags, |
59 | juju_home, model=None, check=True, timeout=None, extra_env=None, |
60 | suppress_err=False): |
61 | - """Run a command under juju for the current environment.""" |
62 | + """Run a command under juju for the current environment. |
63 | + |
64 | + :return: Tuple rval, CommandTime rval being the commands exit code and |
65 | + a CommandTime object used for storing command timing data. |
66 | + """ |
67 | args = self.full_args(command, args, model, timeout) |
68 | log.info(' '.join(args)) |
69 | env = self.shell_environ(used_feature_flags, juju_home) |
70 | @@ -1191,17 +1195,17 @@ |
71 | call_func = subprocess.check_call |
72 | else: |
73 | call_func = subprocess.call |
74 | - start_time = time.time() |
75 | # Mutate os.environ instead of supplying env parameter so Windows can |
76 | # search env['PATH'] |
77 | stderr = subprocess.PIPE if suppress_err else None |
78 | + # Keep track of commands and how long the take. |
79 | + command_time = CommandTime(command, args, env) |
80 | with scoped_environ(env): |
81 | log.debug('Running juju with env: {}'.format(env)) |
82 | with self._check_timeouts(): |
83 | rval = call_func(args, stderr=stderr) |
84 | - self.juju_timings.setdefault(args, []).append( |
85 | - (time.time() - start_time)) |
86 | - return rval |
87 | + self.juju_timings.append(command_time) |
88 | + return rval, command_time |
89 | |
90 | def expect(self, command, args, used_feature_flags, juju_home, model=None, |
91 | timeout=None, extra_env=None): |
92 | @@ -1403,6 +1407,87 @@ |
93 | raise VersionsNotUpdated(model_name, status) |
94 | |
95 | |
96 | +class CommandTime: |
97 | + """Store timing details for a juju command.""" |
98 | + |
99 | + def __init__(self, cmd, full_args, envvars=None, start=None): |
100 | + """Constructor. |
101 | + |
102 | + :param cmd: Command string for command run (e.g. bootstrap) |
103 | + :param args: List of all args the command was called with. |
104 | + :param envvars: Dict of any extra envvars set before command was |
105 | + called. |
106 | + :param start: datetime.datetime object representing when the command |
107 | + was run. If None defaults to datetime.utcnow() |
108 | + """ |
109 | + self.cmd = cmd |
110 | + self.full_args = full_args |
111 | + self.envvars = envvars |
112 | + self.start = start if start else datetime.utcnow() |
113 | + self.end = None |
114 | + |
115 | + def actual_completion(self, end=None): |
116 | + """Signify that actual completion time of the command. |
117 | + |
118 | + Note. ignores multiple calls after the initial call. |
119 | + |
120 | + :param end: datetime.datetime object. If None defaults to |
121 | + datetime.datetime.utcnow() |
122 | + """ |
123 | + if self.end is None: |
124 | + self.end = end if end else datetime.utcnow() |
125 | + |
126 | + @property |
127 | + def actual_time(self): |
128 | + """The actual time a command took. |
129 | + |
130 | + :return: A datetime.timedelta object or None if the command timing has |
131 | + never been completed. |
132 | + """ |
133 | + if self.end is None: |
134 | + return None |
135 | + return self.end - self.start |
136 | + |
137 | + |
138 | +class CommandComplete(BaseCondition): |
139 | + """Wraps a CommandTime and gives the ability to wait_for completion.""" |
140 | + |
141 | + def __init__(self, real_condition, command_time): |
142 | + """Constructor. |
143 | + |
144 | + :param real_condition: BaseCondition object. |
145 | + :param command_time: CommandTime object representing the command to |
146 | + wait for completion. |
147 | + """ |
148 | + super(CommandComplete, self).__init__( |
149 | + real_condition.timeout, |
150 | + real_condition.already_satisfied) |
151 | + self._real_condition = real_condition |
152 | + self.command_time = command_time |
153 | + if real_condition.already_satisfied: |
154 | + self.command_time.actual_completion() |
155 | + |
156 | + def iter_blocking_state(self, status): |
157 | + """Wraps the iter_blocking_state of the stored BaseCondition. |
158 | + |
159 | + When the operation is complete iter_blocking_state yields nothing. |
160 | + Otherwise iter_blocking_state yields details as to why the action |
161 | + cannot be considered complete yet. |
162 | + """ |
163 | + completed = True |
164 | + for item, state in self._real_condition.iter_blocking_state(status): |
165 | + completed = False |
166 | + yield item, state |
167 | + if completed: |
168 | + self.command_time.actual_completion() |
169 | + |
170 | + def do_raise(self, status): |
171 | + raise RuntimeError( |
172 | + 'Timed out waiting for "{}" command to complete: "{}"'.format( |
173 | + self.command_time.cmd, |
174 | + ' '.join(self.command_time.full_args))) |
175 | + |
176 | + |
177 | class ModelClient: |
178 | """Wraps calls to a juju instance, associated with a single model. |
179 | |
180 | @@ -1845,7 +1930,7 @@ |
181 | '--config', config_file)) |
182 | |
183 | def destroy_model(self): |
184 | - exit_status = self.juju( |
185 | + exit_status, _ = self.juju( |
186 | 'destroy-model', (self.env.environment, '-y',), |
187 | include_e=False, timeout=get_teardown_timeout(self)) |
188 | return exit_status |
189 | @@ -1853,22 +1938,28 @@ |
190 | def kill_controller(self, check=False): |
191 | """Kill a controller and its models. Hard kill option. |
192 | |
193 | - :return: Subprocess's exit code.""" |
194 | - return self.juju( |
195 | + :return: Tuple: Subprocess's exit code, CommandComplete object. |
196 | + """ |
197 | + retvar, ct = self.juju( |
198 | 'kill-controller', (self.env.controller.name, '-y'), |
199 | include_e=False, check=check, timeout=get_teardown_timeout(self)) |
200 | + return retvar, CommandComplete(BaseCondition(), ct) |
201 | |
202 | def destroy_controller(self, all_models=False): |
203 | """Destroy a controller and its models. Soft kill option. |
204 | |
205 | :param all_models: If true will attempt to destroy all the |
206 | controller's models as well. |
207 | - :raises: subprocess.CalledProcessError if the operation fails.""" |
208 | + :raises: subprocess.CalledProcessError if the operation fails. |
209 | + :return: Tuple: Subprocess's exit code, CommandComplete object. |
210 | + """ |
211 | args = (self.env.controller.name, '-y') |
212 | if all_models: |
213 | args += ('--destroy-all-models',) |
214 | - return self.juju('destroy-controller', args, include_e=False, |
215 | - timeout=get_teardown_timeout(self)) |
216 | + retvar, ct = self.juju( |
217 | + 'destroy-controller', args, include_e=False, |
218 | + timeout=get_teardown_timeout(self)) |
219 | + return retvar, CommandComplete(BaseCondition(), ct) |
220 | |
221 | def tear_down(self): |
222 | """Tear down the client as cleanly as possible. |
223 | @@ -1879,7 +1970,7 @@ |
224 | self.destroy_controller(all_models=True) |
225 | except subprocess.CalledProcessError: |
226 | logging.warning('tear_down destroy-controller failed') |
227 | - retval = self.kill_controller() |
228 | + retval, _ = self.kill_controller() |
229 | message = 'tear_down kill-controller result={}'.format(retval) |
230 | if retval == 0: |
231 | logging.info(message) |
232 | @@ -1953,7 +2044,8 @@ |
233 | |
234 | def set_model_constraints(self, constraints): |
235 | constraint_strings = self._dict_as_option_strings(constraints) |
236 | - return self.juju('set-model-constraints', constraint_strings) |
237 | + retvar, ct = self.juju('set-model-constraints', constraint_strings) |
238 | + return retvar, CommandComplete(BaseCondition(), ct) |
239 | |
240 | def get_model_config(self): |
241 | """Return the value of the environment's configured options.""" |
242 | @@ -1968,11 +2060,13 @@ |
243 | def set_env_option(self, option, value): |
244 | """Set the value of the option in the environment.""" |
245 | option_value = "%s=%s" % (option, value) |
246 | - return self.juju('model-config', (option_value,)) |
247 | + retvar, ct = self.juju('model-config', (option_value,)) |
248 | + return CommandComplete(BaseCondition(), ct) |
249 | |
250 | def unset_env_option(self, option): |
251 | """Unset the value of the option in the environment.""" |
252 | - return self.juju('model-config', ('--reset', option,)) |
253 | + retvar, ct = self.juju('model-config', ('--reset', option,)) |
254 | + return CommandComplete(BaseCondition(), ct) |
255 | |
256 | @staticmethod |
257 | def _format_cloud_region(cloud=None, region=None): |
258 | @@ -2056,7 +2150,8 @@ |
259 | |
260 | def controller_juju(self, command, args): |
261 | args = ('-c', self.env.controller.name) + args |
262 | - return self.juju(command, args, include_e=False) |
263 | + retvar, ct = self.juju(command, args, include_e=False) |
264 | + return CommandComplete(BaseCondition(), ct) |
265 | |
266 | def get_juju_timings(self): |
267 | stringified_timings = {} |
268 | @@ -2093,11 +2188,13 @@ |
269 | args.extend(['--bind', bind]) |
270 | if alias is not None: |
271 | args.extend([alias]) |
272 | - return self.juju('deploy', tuple(args)) |
273 | + retvar, ct = self.juju('deploy', tuple(args)) |
274 | + return CommandComplete(BaseCondition(), ct) |
275 | |
276 | def attach(self, service, resource): |
277 | args = (service, resource) |
278 | - return self.juju('attach', args) |
279 | + retvar, ct = self.juju('attach', args) |
280 | + return retvar, CommandComplete(BaseCondition(), ct) |
281 | |
282 | def list_resources(self, service_or_unit, details=True): |
283 | args = ('--format', 'yaml', service_or_unit) |
284 | @@ -2798,11 +2895,14 @@ |
285 | args = ('generate-tools', '-d', source_dir) |
286 | if stream is not None: |
287 | args += ('--stream', stream) |
288 | - return self.juju('metadata', args, include_e=False) |
289 | + retvar, ct = self.juju('metadata', args, include_e=False) |
290 | + return retvar, CommandComplete(BaseCondition(), ct) |
291 | |
292 | def add_cloud(self, cloud_name, cloud_file): |
293 | - return self.juju('add-cloud', ("--replace", cloud_name, cloud_file), |
294 | - include_e=False) |
295 | + retvar, ct = self.juju( |
296 | + 'add-cloud', ("--replace", cloud_name, cloud_file), |
297 | + include_e=False) |
298 | + return retvar, CommandComplete(BaseCondition(), ct) |
299 | |
300 | def add_cloud_interactive(self, cloud_name, cloud): |
301 | child = self.expect('add-cloud', include_e=False) |
302 | @@ -2912,11 +3012,13 @@ |
303 | |
304 | def disable_command(self, command_set, message=''): |
305 | """Disable a command-set.""" |
306 | - return self.juju('disable-command', (command_set, message)) |
307 | + retvar, ct = self.juju('disable-command', (command_set, message)) |
308 | + return retvar, CommandComplete(BaseCondition(), ct) |
309 | |
310 | def enable_command(self, args): |
311 | """Enable a command-set.""" |
312 | - return self.juju('enable-command', args) |
313 | + retvar, ct = self.juju('enable-command', args) |
314 | + return CommandComplete(BaseCondition(), ct) |
315 | |
316 | def sync_tools(self, local_dir=None, stream=None, source=None): |
317 | """Copy tools into a local directory or model.""" |
318 | @@ -2926,10 +3028,12 @@ |
319 | if source is not None: |
320 | args += ('--source', source) |
321 | if local_dir is None: |
322 | - return self.juju('sync-tools', args) |
323 | + retvar, ct = self.juju('sync-tools', args) |
324 | + return retvar, CommandComplete(BaseCondition(), ct) |
325 | else: |
326 | args += ('--local-dir', local_dir) |
327 | - return self.juju('sync-tools', args, include_e=False) |
328 | + retvar, ct = self.juju('sync-tools', args, include_e=False) |
329 | + return retvar, CommandComplete(BaseCondition(), ct) |
330 | |
331 | def switch(self, model=None, controller=None): |
332 | """Switch between models.""" |
333 | |
334 | === modified file 'jujupy/fake.py' |
335 | --- jujupy/fake.py 2017-03-17 20:43:14 +0000 |
336 | +++ jujupy/fake.py 2017-03-23 04:46:32 +0000 |
337 | @@ -19,6 +19,7 @@ |
338 | JujuData, |
339 | SoftDeadlineExceeded, |
340 | ) |
341 | +from jujupy.client import CommandTime |
342 | |
343 | __metaclass__ = type |
344 | |
345 | @@ -877,6 +878,7 @@ |
346 | num = int(parsed.n or 1) |
347 | self.deploy(model_state, parsed.charm_name, num, |
348 | parsed.service_name, parsed.series) |
349 | + return (0, CommandTime(command, args)) |
350 | if command == 'remove-application': |
351 | model_state.destroy_service(*args) |
352 | if command == 'add-relation': |
353 | @@ -925,8 +927,9 @@ |
354 | self.controller_state.destroy() |
355 | if command == 'kill-controller': |
356 | if self.controller_state.state == 'not-bootstrapped': |
357 | - return |
358 | + return (0, CommandTime(command, args)) |
359 | self.controller_state.destroy(kill=True) |
360 | + return (0, CommandTime(command, args)) |
361 | if command == 'destroy-model': |
362 | if not self.is_feature_enabled('jes'): |
363 | raise JESNotSupported() |
364 | @@ -977,6 +980,7 @@ |
365 | self.controller_state.shares.remove(username) |
366 | if command == 'restore-backup': |
367 | model_state.restore_backup() |
368 | + return 0, CommandTime(command, args) |
369 | |
370 | @contextmanager |
371 | def juju_async(self, command, args, used_feature_flags, |
372 | |
373 | === modified file 'jujupy/tests/test_client.py' |
374 | --- jujupy/tests/test_client.py 2017-03-20 18:43:52 +0000 |
375 | +++ jujupy/tests/test_client.py 2017-03-23 04:46:32 +0000 |
376 | @@ -43,6 +43,8 @@ |
377 | AgentUnresolvedError, |
378 | AppError, |
379 | BaseCondition, |
380 | + CommandTime, |
381 | + CommandComplete, |
382 | CannotConnectEnv, |
383 | ConditionList, |
384 | Controller, |
385 | @@ -99,9 +101,11 @@ |
386 | from tests import ( |
387 | assert_juju_call, |
388 | client_past_deadline, |
389 | + make_fake_juju_return, |
390 | FakeHomeTestCase, |
391 | FakePopen, |
392 | observable_temp_file, |
393 | + patch_juju_call, |
394 | TestCase, |
395 | ) |
396 | from jujupy.utility import ( |
397 | @@ -702,7 +706,7 @@ |
398 | env = JujuData('foo', {'type': 'foo', 'region': 'baz'}) |
399 | client = ModelClient(env, '2.0-zeta1', None) |
400 | with observable_temp_file() as config_file: |
401 | - with patch.object(client, 'juju') as mock: |
402 | + with patch_juju_call(client) as mock: |
403 | client.bootstrap(upload_tools=True) |
404 | mock.assert_called_with( |
405 | 'bootstrap', ( |
406 | @@ -715,7 +719,7 @@ |
407 | env = JujuData('foo', {'type': 'foo', 'region': 'baz'}) |
408 | client = ModelClient(env, '2.0-zeta1', None) |
409 | with observable_temp_file() as config_file: |
410 | - with patch.object(client, 'juju') as mock: |
411 | + with patch_juju_call(client) as mock: |
412 | client.bootstrap(credential='credential_name') |
413 | mock.assert_called_with( |
414 | 'bootstrap', ( |
415 | @@ -728,7 +732,7 @@ |
416 | def test_bootstrap_bootstrap_series(self): |
417 | env = JujuData('foo', {'type': 'bar', 'region': 'baz'}) |
418 | client = ModelClient(env, '2.0-zeta1', None) |
419 | - with patch.object(client, 'juju') as mock: |
420 | + with patch_juju_call(client) as mock: |
421 | with observable_temp_file() as config_file: |
422 | client.bootstrap(bootstrap_series='angsty') |
423 | mock.assert_called_with( |
424 | @@ -742,7 +746,7 @@ |
425 | def test_bootstrap_auto_upgrade(self): |
426 | env = JujuData('foo', {'type': 'bar', 'region': 'baz'}) |
427 | client = ModelClient(env, '2.0-zeta1', None) |
428 | - with patch.object(client, 'juju') as mock: |
429 | + with patch_juju_call(client) as mock: |
430 | with observable_temp_file() as config_file: |
431 | client.bootstrap(auto_upgrade=True) |
432 | mock.assert_called_with( |
433 | @@ -755,7 +759,7 @@ |
434 | def test_bootstrap_no_gui(self): |
435 | env = JujuData('foo', {'type': 'bar', 'region': 'baz'}) |
436 | client = ModelClient(env, '2.0-zeta1', None) |
437 | - with patch.object(client, 'juju') as mock: |
438 | + with patch_juju_call(client) as mock: |
439 | with observable_temp_file() as config_file: |
440 | client.bootstrap(no_gui=True) |
441 | mock.assert_called_with( |
442 | @@ -768,7 +772,7 @@ |
443 | def test_bootstrap_metadata(self): |
444 | env = JujuData('foo', {'type': 'bar', 'region': 'baz'}) |
445 | client = ModelClient(env, '2.0-zeta1', None) |
446 | - with patch.object(client, 'juju') as mock: |
447 | + with patch_juju_call(client) as mock: |
448 | with observable_temp_file() as config_file: |
449 | client.bootstrap(metadata_source='/var/test-source') |
450 | mock.assert_called_with( |
451 | @@ -862,7 +866,7 @@ |
452 | client = ModelClient(model_data, None, None) |
453 | with patch.object(client, 'get_jes_command', |
454 | return_value=jes_command): |
455 | - with patch.object(controller_client, 'juju') as ccj_mock: |
456 | + with patch_juju_call(controller_client) as ccj_mock: |
457 | with observable_temp_file() as config_file: |
458 | controller_client.add_model(model_data) |
459 | ccj_mock.assert_called_once_with( |
460 | @@ -874,7 +878,7 @@ |
461 | client.bootstrap() |
462 | client.env.controller.explicit_region = True |
463 | model = client.env.clone('new-model') |
464 | - with patch.object(client._backend, 'juju') as juju_mock: |
465 | + with patch_juju_call(client._backend) as juju_mock: |
466 | with observable_temp_file() as config_file: |
467 | client.add_model(model) |
468 | juju_mock.assert_called_once_with('add-model', ( |
469 | @@ -886,7 +890,7 @@ |
470 | def test_add_model_by_name(self): |
471 | client = fake_juju_client() |
472 | client.bootstrap() |
473 | - with patch.object(client._backend, 'juju') as juju_mock: |
474 | + with patch_juju_call(client._backend) as juju_mock: |
475 | with observable_temp_file() as config_file: |
476 | client.add_model('new-model') |
477 | juju_mock.assert_called_once_with('add-model', ( |
478 | @@ -902,7 +906,7 @@ |
479 | def test_destroy_model(self): |
480 | env = JujuData('foo', {'type': 'ec2'}) |
481 | client = ModelClient(env, None, None) |
482 | - with patch.object(client, 'juju') as mock: |
483 | + with patch_juju_call(client) as mock: |
484 | client.destroy_model() |
485 | mock.assert_called_with( |
486 | 'destroy-model', ('foo', '-y'), |
487 | @@ -911,7 +915,7 @@ |
488 | def test_destroy_model_azure(self): |
489 | env = JujuData('foo', {'type': 'azure'}) |
490 | client = ModelClient(env, None, None) |
491 | - with patch.object(client, 'juju') as mock: |
492 | + with patch_juju_call(client) as mock: |
493 | client.destroy_model() |
494 | mock.assert_called_with( |
495 | 'destroy-model', ('foo', '-y'), |
496 | @@ -920,7 +924,7 @@ |
497 | def test_destroy_model_gce(self): |
498 | env = JujuData('foo', {'type': 'gce'}) |
499 | client = ModelClient(env, None, None) |
500 | - with patch.object(client, 'juju') as mock: |
501 | + with patch_juju_call(client) as mock: |
502 | client.destroy_model() |
503 | mock.assert_called_with( |
504 | 'destroy-model', ('foo', '-y'), |
505 | @@ -928,7 +932,7 @@ |
506 | |
507 | def test_kill_controller(self): |
508 | client = ModelClient(JujuData('foo', {'type': 'ec2'}), None, None) |
509 | - with patch.object(client, 'juju') as juju_mock: |
510 | + with patch_juju_call(client) as juju_mock: |
511 | client.kill_controller() |
512 | juju_mock.assert_called_once_with( |
513 | 'kill-controller', ('foo', '-y'), check=False, include_e=False, |
514 | @@ -936,7 +940,7 @@ |
515 | |
516 | def test_kill_controller_check(self): |
517 | client = ModelClient(JujuData('foo', {'type': 'ec2'}), None, None) |
518 | - with patch.object(client, 'juju') as juju_mock: |
519 | + with patch_juju_call(client) as juju_mock: |
520 | client.kill_controller(check=True) |
521 | juju_mock.assert_called_once_with( |
522 | 'kill-controller', ('foo', '-y'), check=True, include_e=False, |
523 | @@ -946,7 +950,7 @@ |
524 | client = ModelClient(JujuData('foo', {'type': 'azure'}), None, None) |
525 | with patch.object(client, 'get_jes_command', |
526 | return_value=jes_command): |
527 | - with patch.object(client, 'juju') as juju_mock: |
528 | + with patch_juju_call(client) as juju_mock: |
529 | client.kill_controller() |
530 | juju_mock.assert_called_once_with( |
531 | kill_command, ('foo', '-y'), check=False, include_e=False, |
532 | @@ -954,7 +958,7 @@ |
533 | |
534 | def test_kill_controller_gce(self): |
535 | client = ModelClient(JujuData('foo', {'type': 'gce'}), None, None) |
536 | - with patch.object(client, 'juju') as juju_mock: |
537 | + with patch_juju_call(client) as juju_mock: |
538 | client.kill_controller() |
539 | juju_mock.assert_called_once_with( |
540 | 'kill-controller', ('foo', '-y'), check=False, include_e=False, |
541 | @@ -962,7 +966,7 @@ |
542 | |
543 | def test_destroy_controller(self): |
544 | client = ModelClient(JujuData('foo', {'type': 'ec2'}), None, None) |
545 | - with patch.object(client, 'juju') as juju_mock: |
546 | + with patch_juju_call(client) as juju_mock: |
547 | client.destroy_controller() |
548 | juju_mock.assert_called_once_with( |
549 | 'destroy-controller', ('foo', '-y'), include_e=False, |
550 | @@ -970,7 +974,7 @@ |
551 | |
552 | def test_destroy_controller_all_models(self): |
553 | client = ModelClient(JujuData('foo', {'type': 'ec2'}), None, None) |
554 | - with patch.object(client, 'juju') as juju_mock: |
555 | + with patch_juju_call(client) as juju_mock: |
556 | client.destroy_controller(all_models=True) |
557 | juju_mock.assert_called_once_with( |
558 | 'destroy-controller', ('foo', '-y', '--destroy-all-models'), |
559 | @@ -988,7 +992,9 @@ |
560 | side_effect=raise_error) as mock: |
561 | yield mock |
562 | else: |
563 | - with patch.object(target, attribute, autospec=True) as mock: |
564 | + with patch.object( |
565 | + target, attribute, autospec=True, |
566 | + return_value=make_fake_juju_return()) as mock: |
567 | yield mock |
568 | |
569 | with patch_raise(client, 'destroy_controller', destroy_raises |
570 | @@ -1219,21 +1225,21 @@ |
571 | def test_deploy_non_joyent(self): |
572 | env = ModelClient( |
573 | JujuData('foo', {'type': 'local'}), '1.234-76', None) |
574 | - with patch.object(env, 'juju') as mock_juju: |
575 | + with patch_juju_call(env) as mock_juju: |
576 | env.deploy('mondogb') |
577 | mock_juju.assert_called_with('deploy', ('mondogb',)) |
578 | |
579 | def test_deploy_joyent(self): |
580 | env = ModelClient( |
581 | JujuData('foo', {'type': 'local'}), '1.234-76', None) |
582 | - with patch.object(env, 'juju') as mock_juju: |
583 | + with patch_juju_call(env) as mock_juju: |
584 | env.deploy('mondogb') |
585 | mock_juju.assert_called_with('deploy', ('mondogb',)) |
586 | |
587 | def test_deploy_repository(self): |
588 | env = ModelClient( |
589 | JujuData('foo', {'type': 'local'}), '1.234-76', None) |
590 | - with patch.object(env, 'juju') as mock_juju: |
591 | + with patch_juju_call(env) as mock_juju: |
592 | env.deploy('/home/jrandom/repo/mongodb') |
593 | mock_juju.assert_called_with( |
594 | 'deploy', ('/home/jrandom/repo/mongodb',)) |
595 | @@ -1241,7 +1247,7 @@ |
596 | def test_deploy_to(self): |
597 | env = ModelClient( |
598 | JujuData('foo', {'type': 'local'}), '1.234-76', None) |
599 | - with patch.object(env, 'juju') as mock_juju: |
600 | + with patch_juju_call(env) as mock_juju: |
601 | env.deploy('mondogb', to='0') |
602 | mock_juju.assert_called_with( |
603 | 'deploy', ('mondogb', '--to', '0')) |
604 | @@ -1249,7 +1255,7 @@ |
605 | def test_deploy_service(self): |
606 | env = ModelClient( |
607 | JujuData('foo', {'type': 'local'}), '1.234-76', None) |
608 | - with patch.object(env, 'juju') as mock_juju: |
609 | + with patch_juju_call(env) as mock_juju: |
610 | env.deploy('local:mondogb', service='my-mondogb') |
611 | mock_juju.assert_called_with( |
612 | 'deploy', ('local:mondogb', 'my-mondogb',)) |
613 | @@ -1257,14 +1263,14 @@ |
614 | def test_deploy_force(self): |
615 | env = ModelClient( |
616 | JujuData('foo', {'type': 'local'}), '1.234-76', None) |
617 | - with patch.object(env, 'juju') as mock_juju: |
618 | + with patch_juju_call(env) as mock_juju: |
619 | env.deploy('local:mondogb', force=True) |
620 | mock_juju.assert_called_with('deploy', ('local:mondogb', '--force',)) |
621 | |
622 | def test_deploy_series(self): |
623 | env = ModelClient( |
624 | JujuData('foo', {'type': 'local'}), '1.234-76', None) |
625 | - with patch.object(env, 'juju') as mock_juju: |
626 | + with patch_juju_call(env) as mock_juju: |
627 | env.deploy('local:blah', series='xenial') |
628 | mock_juju.assert_called_with( |
629 | 'deploy', ('local:blah', '--series', 'xenial')) |
630 | @@ -1272,14 +1278,14 @@ |
631 | def test_deploy_multiple(self): |
632 | env = ModelClient( |
633 | JujuData('foo', {'type': 'local'}), '1.234-76', None) |
634 | - with patch.object(env, 'juju') as mock_juju: |
635 | + with patch_juju_call(env) as mock_juju: |
636 | env.deploy('local:blah', num=2) |
637 | mock_juju.assert_called_with( |
638 | 'deploy', ('local:blah', '-n', '2')) |
639 | |
640 | def test_deploy_resource(self): |
641 | env = ModelClient(JujuData('foo', {'type': 'local'}), None, None) |
642 | - with patch.object(env, 'juju') as mock_juju: |
643 | + with patch_juju_call(env) as mock_juju: |
644 | env.deploy('local:blah', resource='foo=/path/dir') |
645 | mock_juju.assert_called_with( |
646 | 'deploy', ('local:blah', '--resource', 'foo=/path/dir')) |
647 | @@ -1287,7 +1293,7 @@ |
648 | def test_deploy_storage(self): |
649 | env = EnvJujuClient1X( |
650 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
651 | - with patch.object(env, 'juju') as mock_juju: |
652 | + with patch_juju_call(env) as mock_juju: |
653 | env.deploy('mondogb', storage='rootfs,1G') |
654 | mock_juju.assert_called_with( |
655 | 'deploy', ('mondogb', '--storage', 'rootfs,1G')) |
656 | @@ -1295,27 +1301,27 @@ |
657 | def test_deploy_constraints(self): |
658 | env = EnvJujuClient1X( |
659 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
660 | - with patch.object(env, 'juju') as mock_juju: |
661 | + with patch_juju_call(env) as mock_juju: |
662 | env.deploy('mondogb', constraints='virt-type=kvm') |
663 | mock_juju.assert_called_with( |
664 | 'deploy', ('mondogb', '--constraints', 'virt-type=kvm')) |
665 | |
666 | def test_deploy_bind(self): |
667 | env = ModelClient(JujuData('foo', {'type': 'local'}), None, None) |
668 | - with patch.object(env, 'juju') as mock_juju: |
669 | + with patch_juju_call(env) as mock_juju: |
670 | env.deploy('mydb', bind='backspace') |
671 | mock_juju.assert_called_with('deploy', ('mydb', '--bind', 'backspace')) |
672 | |
673 | def test_deploy_aliased(self): |
674 | env = ModelClient(JujuData('foo', {'type': 'local'}), None, None) |
675 | - with patch.object(env, 'juju') as mock_juju: |
676 | + with patch_juju_call(env) as mock_juju: |
677 | env.deploy('local:blah', alias='blah-blah') |
678 | mock_juju.assert_called_with( |
679 | 'deploy', ('local:blah', 'blah-blah')) |
680 | |
681 | def test_attach(self): |
682 | env = ModelClient(JujuData('foo', {'type': 'local'}), None, None) |
683 | - with patch.object(env, 'juju') as mock_juju: |
684 | + with patch_juju_call(env) as mock_juju: |
685 | env.attach('foo', resource='foo=/path/dir') |
686 | mock_juju.assert_called_with('attach', ('foo', 'foo=/path/dir')) |
687 | |
688 | @@ -1383,7 +1389,7 @@ |
689 | def test_deploy_bundle_2x(self): |
690 | client = ModelClient(JujuData('an_env', None), |
691 | '1.23-series-arch', None) |
692 | - with patch.object(client, 'juju') as mock_juju: |
693 | + with patch_juju_call(client) as mock_juju: |
694 | client.deploy_bundle('bundle:~juju-qa/some-bundle') |
695 | mock_juju.assert_called_with( |
696 | 'deploy', ('bundle:~juju-qa/some-bundle'), timeout=3600) |
697 | @@ -1391,7 +1397,7 @@ |
698 | def test_deploy_bundle_template(self): |
699 | client = ModelClient(JujuData('an_env', None), |
700 | '1.23-series-arch', None) |
701 | - with patch.object(client, 'juju') as mock_juju: |
702 | + with patch_juju_call(client) as mock_juju: |
703 | client.deploy_bundle('bundle:~juju-qa/some-{container}-bundle') |
704 | mock_juju.assert_called_with( |
705 | 'deploy', ('bundle:~juju-qa/some-lxd-bundle'), timeout=3600) |
706 | @@ -1399,7 +1405,7 @@ |
707 | def test_upgrade_charm(self): |
708 | env = ModelClient( |
709 | JujuData('foo', {'type': 'local'}), '2.34-74', None) |
710 | - with patch.object(env, 'juju') as mock_juju: |
711 | + with patch_juju_call(env) as mock_juju: |
712 | env.upgrade_charm('foo-service', |
713 | '/bar/repository/angsty/mongodb') |
714 | mock_juju.assert_called_once_with( |
715 | @@ -1409,7 +1415,7 @@ |
716 | def test_remove_service(self): |
717 | env = ModelClient( |
718 | JujuData('foo', {'type': 'local'}), '1.234-76', None) |
719 | - with patch.object(env, 'juju') as mock_juju: |
720 | + with patch_juju_call(env) as mock_juju: |
721 | env.remove_service('mondogb') |
722 | mock_juju.assert_called_with('remove-application', ('mondogb',)) |
723 | |
724 | @@ -1578,7 +1584,7 @@ |
725 | |
726 | def test_remove_machine(self): |
727 | client = fake_juju_client() |
728 | - with patch.object(client._backend, 'juju') as juju_mock: |
729 | + with patch_juju_call(client._backend) as juju_mock: |
730 | condition = client.remove_machine('0') |
731 | call = backend_call( |
732 | client, 'remove-machine', ('0',), 'name:name') |
733 | @@ -1587,7 +1593,7 @@ |
734 | |
735 | def test_remove_machine_force(self): |
736 | client = fake_juju_client() |
737 | - with patch.object(client._backend, 'juju') as juju_mock: |
738 | + with patch_juju_call(client._backend) as juju_mock: |
739 | client.remove_machine('0', force=True) |
740 | call = backend_call( |
741 | client, 'remove-machine', ('--force', '0'), 'name:name') |
742 | @@ -1982,7 +1988,7 @@ |
743 | |
744 | def test_list_models(self): |
745 | client = ModelClient(JujuData('foo'), None, None) |
746 | - with patch.object(client, 'juju') as j_mock: |
747 | + with patch_juju_call(client) as j_mock: |
748 | client.list_models() |
749 | j_mock.assert_called_once_with( |
750 | 'list-models', ('-c', 'foo'), include_e=False) |
751 | @@ -2194,7 +2200,7 @@ |
752 | |
753 | def test_list_controllers(self): |
754 | client = ModelClient(JujuData('foo'), None, None) |
755 | - with patch.object(client, 'juju') as j_mock: |
756 | + with patch_juju_call(client) as j_mock: |
757 | client.list_controllers() |
758 | j_mock.assert_called_once_with('list-controllers', (), include_e=False) |
759 | |
760 | @@ -2662,7 +2668,7 @@ |
761 | |
762 | def test_set_model_constraints(self): |
763 | client = ModelClient(JujuData('bar', {}), None, '/foo') |
764 | - with patch.object(client, 'juju') as juju_mock: |
765 | + with patch_juju_call(client) as juju_mock: |
766 | client.set_model_constraints({'bar': 'baz'}) |
767 | juju_mock.assert_called_once_with('set-model-constraints', |
768 | ('bar=baz',)) |
769 | @@ -2948,7 +2954,7 @@ |
770 | def test_restore_backup(self): |
771 | env = JujuData('qux') |
772 | client = ModelClient(env, None, '/foobar/baz') |
773 | - with patch.object(client, 'juju') as gjo_mock: |
774 | + with patch_juju_call(client) as gjo_mock: |
775 | client.restore_backup('quxx') |
776 | gjo_mock.assert_called_once_with( |
777 | 'restore-backup', |
778 | @@ -3230,7 +3236,7 @@ |
779 | |
780 | def test_set_config(self): |
781 | client = ModelClient(JujuData('bar', {}), None, '/foo') |
782 | - with patch.object(client, 'juju') as juju_mock: |
783 | + with patch_juju_call(client) as juju_mock: |
784 | client.set_config('foo', {'bar': 'baz'}) |
785 | juju_mock.assert_called_once_with('config', ('foo', 'bar=baz')) |
786 | |
787 | @@ -3285,7 +3291,7 @@ |
788 | |
789 | def test_upgrade_mongo(self): |
790 | client = ModelClient(JujuData('bar', {}), None, '/foo') |
791 | - with patch.object(client, 'juju') as juju_mock: |
792 | + with patch_juju_call(client) as juju_mock: |
793 | client.upgrade_mongo() |
794 | juju_mock.assert_called_once_with('upgrade-mongo', ()) |
795 | |
796 | @@ -3330,7 +3336,7 @@ |
797 | default_model = fake_client.model_name |
798 | default_controller = fake_client.env.controller.name |
799 | |
800 | - with patch.object(fake_client, 'juju', return_value=True): |
801 | + with patch_juju_call(fake_client): |
802 | fake_client.revoke(username) |
803 | fake_client.juju.assert_called_with('revoke', |
804 | ('-c', default_controller, |
805 | @@ -3410,7 +3416,7 @@ |
806 | env = JujuData('foo') |
807 | username = 'fakeuser' |
808 | client = ModelClient(env, None, None) |
809 | - with patch.object(client, 'juju') as mock: |
810 | + with patch_juju_call(client) as mock: |
811 | client.disable_user(username) |
812 | mock.assert_called_with( |
813 | 'disable-user', ('-c', 'foo', 'fakeuser'), include_e=False) |
814 | @@ -3419,7 +3425,7 @@ |
815 | env = JujuData('foo') |
816 | username = 'fakeuser' |
817 | client = ModelClient(env, None, None) |
818 | - with patch.object(client, 'juju') as mock: |
819 | + with patch_juju_call(client) as mock: |
820 | client.enable_user(username) |
821 | mock.assert_called_with( |
822 | 'enable-user', ('-c', 'foo', 'fakeuser'), include_e=False) |
823 | @@ -3427,7 +3433,7 @@ |
824 | def test_logout(self): |
825 | env = JujuData('foo') |
826 | client = ModelClient(env, None, None) |
827 | - with patch.object(client, 'juju') as mock: |
828 | + with patch_juju_call(client) as mock: |
829 | client.logout() |
830 | mock.assert_called_with( |
831 | 'logout', ('-c', 'foo'), include_e=False) |
832 | @@ -3667,32 +3673,32 @@ |
833 | |
834 | def test_disable_command(self): |
835 | client = ModelClient(JujuData('foo'), None, None) |
836 | - with patch.object(client, 'juju', autospec=True) as mock: |
837 | + with patch_juju_call(client) as mock: |
838 | client.disable_command('all', 'message') |
839 | mock.assert_called_once_with('disable-command', ('all', 'message')) |
840 | |
841 | def test_enable_command(self): |
842 | client = ModelClient(JujuData('foo'), None, None) |
843 | - with patch.object(client, 'juju', autospec=True) as mock: |
844 | + with patch_juju_call(client) as mock: |
845 | client.enable_command('all') |
846 | mock.assert_called_once_with('enable-command', 'all') |
847 | |
848 | def test_sync_tools(self): |
849 | client = ModelClient(JujuData('foo'), None, None) |
850 | - with patch.object(client, 'juju', autospec=True) as mock: |
851 | + with patch_juju_call(client) as mock: |
852 | client.sync_tools() |
853 | mock.assert_called_once_with('sync-tools', ()) |
854 | |
855 | def test_sync_tools_local_dir(self): |
856 | client = ModelClient(JujuData('foo'), None, None) |
857 | - with patch.object(client, 'juju', autospec=True) as mock: |
858 | + with patch_juju_call(client) as mock: |
859 | client.sync_tools('/agents') |
860 | mock.assert_called_once_with('sync-tools', ('--local-dir', '/agents'), |
861 | include_e=False) |
862 | |
863 | def test_generate_tool(self): |
864 | client = ModelClient(JujuData('foo'), None, None) |
865 | - with patch.object(client, 'juju', autospec=True) as mock: |
866 | + with patch_juju_call(client) as mock: |
867 | client.generate_tool('/agents') |
868 | mock.assert_called_once_with('metadata', |
869 | ('generate-tools', '-d', '/agents'), |
870 | @@ -3700,7 +3706,7 @@ |
871 | |
872 | def test_generate_tool_with_stream(self): |
873 | client = ModelClient(JujuData('foo'), None, None) |
874 | - with patch.object(client, 'juju', autospec=True) as mock: |
875 | + with patch_juju_call(client) as mock: |
876 | client.generate_tool('/agents', "testing") |
877 | mock.assert_called_once_with( |
878 | 'metadata', ('generate-tools', '-d', '/agents', |
879 | @@ -3708,7 +3714,7 @@ |
880 | |
881 | def test_add_cloud(self): |
882 | client = ModelClient(JujuData('foo'), None, None) |
883 | - with patch.object(client, 'juju', autospec=True) as mock: |
884 | + with patch_juju_call(client) as mock: |
885 | client.add_cloud('localhost', 'cfile') |
886 | mock.assert_called_once_with('add-cloud', |
887 | ('--replace', 'localhost', 'cfile'), |
888 | @@ -3717,7 +3723,7 @@ |
889 | def test_switch(self): |
890 | def run_switch_test(expect, model=None, controller=None): |
891 | client = ModelClient(JujuData('foo'), None, None) |
892 | - with patch.object(client, 'juju', autospec=True) as mock: |
893 | + with patch_juju_call(client) as mock: |
894 | client.switch(model=model, controller=controller) |
895 | mock.assert_called_once_with('switch', (expect,), include_e=False) |
896 | run_switch_test('default', 'default') |
897 | @@ -6010,3 +6016,125 @@ |
898 | self.assertEqual(host, "2001:db8::3") |
899 | fake_client.status_until.assert_called_once_with(timeout=600) |
900 | self.assertEqual(self.log_stream.getvalue(), "") |
901 | + |
902 | + |
903 | +class TestCommandTime(TestCase): |
904 | + |
905 | + def test_default_values(self): |
906 | + full_args = ['juju', '--showlog', 'bootstrap'] |
907 | + utcnow = datetime(2017, 3, 22, 23, 36, 52, 530631) |
908 | + with patch('jujupy.client.datetime', autospec=True) as m_dt: |
909 | + m_dt.utcnow.return_value = utcnow |
910 | + ct = CommandTime('bootstrap', full_args) |
911 | + self.assertEqual(ct.cmd, 'bootstrap') |
912 | + self.assertEqual(ct.full_args, full_args) |
913 | + self.assertEqual(ct.envvars, None) |
914 | + self.assertEqual(ct.start, utcnow) |
915 | + self.assertEqual(ct.end, None) |
916 | + |
917 | + def test_set_start_time(self): |
918 | + ct = CommandTime('cmd', [], start='abc') |
919 | + self.assertEqual(ct.start, 'abc') |
920 | + |
921 | + def test_set_envvar(self): |
922 | + details = {'abc': 123} |
923 | + ct = CommandTime('cmd', [], envvars=details) |
924 | + self.assertEqual(ct.envvars, details) |
925 | + |
926 | + def test_actual_completion_sets_default(self): |
927 | + utcnow = datetime(2017, 3, 22, 23, 36, 52, 530631) |
928 | + ct = CommandTime('cmd', []) |
929 | + with patch('jujupy.client.datetime', autospec=True) as m_dt: |
930 | + m_dt.utcnow.return_value = utcnow |
931 | + ct.actual_completion() |
932 | + self.assertEqual(ct.end, utcnow) |
933 | + |
934 | + def test_actual_completion_idempotent(self): |
935 | + ct = CommandTime('cmd', []) |
936 | + ct.actual_completion(end='a') |
937 | + ct.actual_completion(end='b') |
938 | + self.assertEqual(ct.end, 'a') |
939 | + |
940 | + def test_actual_completion_set_value(self): |
941 | + utcnow = datetime(2017, 3, 22, 23, 36, 52, 530631) |
942 | + ct = CommandTime('cmd', []) |
943 | + ct.actual_completion(end=utcnow) |
944 | + self.assertEqual(ct.end, utcnow) |
945 | + |
946 | + def test_actual_time_returns_None_when_not_complete(self): |
947 | + ct = CommandTime('cmd', []) |
948 | + self.assertEqual(ct.actual_time, None) |
949 | + |
950 | + def test_actual_time_returns_time_difference_when_complete(self): |
951 | + utcstart = datetime(2017, 3, 22, 23, 36, 52, 530631) |
952 | + utcend = utcstart + timedelta(seconds=1) |
953 | + with patch('jujupy.client.datetime', autospec=True) as m_dt: |
954 | + m_dt.utcnow.side_effect = [utcstart, utcend] |
955 | + ct = CommandTime('cmd', []) |
956 | + ct.actual_completion() |
957 | + self.assertEqual(ct.actual_time, utcend - utcstart) |
958 | + |
959 | + |
960 | +class TestCommandComplete(TestCase): |
961 | + |
962 | + def test_default_values(self): |
963 | + ct = CommandTime('cmd', []) |
964 | + base_condition = BaseCondition() |
965 | + cc = CommandComplete(base_condition, ct) |
966 | + |
967 | + self.assertEqual(cc.timeout, 300) |
968 | + self.assertEqual(cc.already_satisfied, False) |
969 | + self.assertEqual(cc._real_condition, base_condition) |
970 | + self.assertEqual(cc.command_time, ct) |
971 | + # actual_completion shouldn't be set as the condition is not already |
972 | + # satisfied. |
973 | + self.assertEqual(cc.command_time.end, None) |
974 | + |
975 | + def test_sets_actual_comletion_when_already_satisfied(self): |
976 | + base_condition = BaseCondition(already_satisfied=True) |
977 | + ct = CommandTime('cmd', []) |
978 | + cc = CommandComplete(base_condition, ct) |
979 | + |
980 | + self.assertIsNotNone(cc.command_time.actual_time) |
981 | + |
982 | + def test_calls_wrapper_condition_iter(self): |
983 | + class TestCondition(BaseCondition): |
984 | + def iter_blocking_state(self, status): |
985 | + yield 'item', status |
986 | + |
987 | + ct = CommandTime('cmd', []) |
988 | + cc = CommandComplete(TestCondition(), ct) |
989 | + |
990 | + k, v = next(cc.iter_blocking_state('status_obj')) |
991 | + self.assertEqual(k, 'item') |
992 | + self.assertEqual(v, 'status_obj') |
993 | + |
994 | + def test_sets_actual_completion_when_complete(self): |
995 | + """When the condition hits success must set actual_completion.""" |
996 | + class TestCondition(BaseCondition): |
997 | + def __init__(self): |
998 | + super(TestCondition, self).__init__() |
999 | + self._already_called = False |
1000 | + |
1001 | + def iter_blocking_state(self, status): |
1002 | + if not self._already_called: |
1003 | + self._already_called = True |
1004 | + yield 'item', status |
1005 | + |
1006 | + ct = CommandTime('cmd', []) |
1007 | + cc = CommandComplete(TestCondition(), ct) |
1008 | + |
1009 | + next(cc.iter_blocking_state('status_obj')) |
1010 | + self.assertIsNone(cc.command_time.end) |
1011 | + next(cc.iter_blocking_state('status_obj'), None) |
1012 | + self.assertIsNotNone(cc.command_time.end) |
1013 | + |
1014 | + def test_raises_exception_with_command_details(self): |
1015 | + ct = CommandTime('cmd', ['cmd', 'arg1', 'arg2']) |
1016 | + cc = CommandComplete(BaseCondition(), ct) |
1017 | + |
1018 | + with self.assertRaises(RuntimeError) as ex: |
1019 | + cc.do_raise('status') |
1020 | + self.assertEqual( |
1021 | + str(ex.exception), |
1022 | + 'Timed out waiting for "cmd" command to complete: "cmd arg1 arg2"') |
1023 | |
1024 | === modified file 'jujupy/tests/test_version_client.py' |
1025 | --- jujupy/tests/test_version_client.py 2017-03-10 21:15:12 +0000 |
1026 | +++ jujupy/tests/test_version_client.py 2017-03-23 04:46:32 +0000 |
1027 | @@ -69,6 +69,7 @@ |
1028 | ) |
1029 | from tests import ( |
1030 | assert_juju_call, |
1031 | + patch_juju_call, |
1032 | FakeHomeTestCase, |
1033 | FakePopen, |
1034 | observable_temp_file, |
1035 | @@ -432,7 +433,7 @@ |
1036 | def test_upgrade_juju_nonlocal(self): |
1037 | client = EnvJujuClient1X( |
1038 | SimpleEnvironment('foo', {'type': 'nonlocal'}), '1.234-76', None) |
1039 | - with patch.object(client, 'juju') as juju_mock: |
1040 | + with patch_juju_call(client) as juju_mock: |
1041 | client.upgrade_juju() |
1042 | juju_mock.assert_called_with( |
1043 | 'upgrade-juju', ('--version', '1.234')) |
1044 | @@ -440,7 +441,7 @@ |
1045 | def test_upgrade_juju_local(self): |
1046 | client = EnvJujuClient1X( |
1047 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
1048 | - with patch.object(client, 'juju') as juju_mock: |
1049 | + with patch_juju_call(client) as juju_mock: |
1050 | client.upgrade_juju() |
1051 | juju_mock.assert_called_with( |
1052 | 'upgrade-juju', ('--version', '1.234', '--upload-tools',)) |
1053 | @@ -448,7 +449,7 @@ |
1054 | def test_upgrade_juju_no_force_version(self): |
1055 | client = EnvJujuClient1X( |
1056 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
1057 | - with patch.object(client, 'juju') as juju_mock: |
1058 | + with patch_juju_call(client) as juju_mock: |
1059 | client.upgrade_juju(force_version=False) |
1060 | juju_mock.assert_called_with( |
1061 | 'upgrade-juju', ('--upload-tools',)) |
1062 | @@ -485,14 +486,14 @@ |
1063 | def test_bootstrap(self): |
1064 | env = SimpleEnvironment('foo') |
1065 | client = EnvJujuClient1X(env, None, None) |
1066 | - with patch.object(client, 'juju') as mock: |
1067 | + with patch_juju_call(client) as mock: |
1068 | client.bootstrap() |
1069 | mock.assert_called_with('bootstrap', ('--constraints', 'mem=2G')) |
1070 | |
1071 | def test_bootstrap_upload_tools(self): |
1072 | env = SimpleEnvironment('foo') |
1073 | client = EnvJujuClient1X(env, None, None) |
1074 | - with patch.object(client, 'juju') as mock: |
1075 | + with patch_juju_call(client) as mock: |
1076 | client.bootstrap(upload_tools=True) |
1077 | mock.assert_called_with( |
1078 | 'bootstrap', ('--upload-tools', '--constraints', 'mem=2G')) |
1079 | @@ -508,7 +509,7 @@ |
1080 | env.update_config({ |
1081 | 'default-series': 'angsty', |
1082 | }) |
1083 | - with patch.object(client, 'juju') as mock: |
1084 | + with patch_juju_call(client) as mock: |
1085 | client.bootstrap(bootstrap_series='angsty') |
1086 | mock.assert_called_with('bootstrap', ('--constraints', 'mem=2G')) |
1087 | |
1088 | @@ -573,7 +574,8 @@ |
1089 | def test_destroy_environment(self): |
1090 | env = SimpleEnvironment('foo', {'type': 'ec2'}) |
1091 | client = EnvJujuClient1X(env, None, None) |
1092 | - with patch.object(client, 'juju') as mock: |
1093 | + with patch.object( |
1094 | + client, 'juju', autospec=True, return_value=(0, None)) as mock: |
1095 | client.destroy_environment() |
1096 | mock.assert_called_with( |
1097 | 'destroy-environment', ('foo', '--force', '-y'), |
1098 | @@ -582,7 +584,9 @@ |
1099 | def test_destroy_environment_no_force(self): |
1100 | env = SimpleEnvironment('foo', {'type': 'ec2'}) |
1101 | client = EnvJujuClient1X(env, None, None) |
1102 | - with patch.object(client, 'juju') as mock: |
1103 | + with patch.object( |
1104 | + client, 'juju', |
1105 | + autospec=True, return_value=(0, None)) as mock: |
1106 | client.destroy_environment(force=False) |
1107 | mock.assert_called_with( |
1108 | 'destroy-environment', ('foo', '-y'), |
1109 | @@ -591,7 +595,8 @@ |
1110 | def test_destroy_environment_azure(self): |
1111 | env = SimpleEnvironment('foo', {'type': 'azure'}) |
1112 | client = EnvJujuClient1X(env, None, None) |
1113 | - with patch.object(client, 'juju') as mock: |
1114 | + with patch.object( |
1115 | + client, 'juju', autospec=True, return_value=(0, None)) as mock: |
1116 | client.destroy_environment(force=False) |
1117 | mock.assert_called_with( |
1118 | 'destroy-environment', ('foo', '-y'), |
1119 | @@ -600,7 +605,9 @@ |
1120 | def test_destroy_environment_gce(self): |
1121 | env = SimpleEnvironment('foo', {'type': 'gce'}) |
1122 | client = EnvJujuClient1X(env, None, None) |
1123 | - with patch.object(client, 'juju') as mock: |
1124 | + with patch.object( |
1125 | + client, 'juju', |
1126 | + autospec=True, return_value=(0, None)) as mock: |
1127 | client.destroy_environment(force=False) |
1128 | mock.assert_called_with( |
1129 | 'destroy-environment', ('foo', '-y'), |
1130 | @@ -609,7 +616,8 @@ |
1131 | def test_destroy_environment_delete_jenv(self): |
1132 | env = SimpleEnvironment('foo', {'type': 'ec2'}) |
1133 | client = EnvJujuClient1X(env, None, None) |
1134 | - with patch.object(client, 'juju'): |
1135 | + with patch.object( |
1136 | + client, 'juju', autospec=True, return_value=(0, None)): |
1137 | with temp_env({}) as juju_home: |
1138 | client.env.juju_home = juju_home |
1139 | jenv_path = get_jenv_path(juju_home, 'foo') |
1140 | @@ -622,7 +630,8 @@ |
1141 | def test_destroy_model(self): |
1142 | env = SimpleEnvironment('foo', {'type': 'ec2'}) |
1143 | client = EnvJujuClient1X(env, None, None) |
1144 | - with patch.object(client, 'juju') as mock: |
1145 | + with patch.object( |
1146 | + client, 'juju', autospec=True, return_value=(0, None)) as mock: |
1147 | client.destroy_model() |
1148 | mock.assert_called_with( |
1149 | 'destroy-environment', ('foo', '-y'), |
1150 | @@ -631,7 +640,9 @@ |
1151 | def test_kill_controller(self): |
1152 | client = EnvJujuClient1X( |
1153 | SimpleEnvironment('foo', {'type': 'ec2'}), None, None) |
1154 | - with patch.object(client, 'juju') as juju_mock: |
1155 | + with patch.object( |
1156 | + client, 'juju', |
1157 | + autospec=True, return_value=(0, None)) as juju_mock: |
1158 | client.kill_controller() |
1159 | juju_mock.assert_called_once_with( |
1160 | 'destroy-environment', ('foo', '--force', '-y'), check=False, |
1161 | @@ -640,7 +651,7 @@ |
1162 | def test_kill_controller_check(self): |
1163 | client = EnvJujuClient1X( |
1164 | SimpleEnvironment('foo', {'type': 'ec2'}), None, None) |
1165 | - with patch.object(client, 'juju') as juju_mock: |
1166 | + with patch_juju_call(client) as juju_mock: |
1167 | client.kill_controller(check=True) |
1168 | juju_mock.assert_called_once_with( |
1169 | 'destroy-environment', ('foo', '--force', '-y'), check=True, |
1170 | @@ -649,7 +660,7 @@ |
1171 | def test_destroy_controller(self): |
1172 | client = EnvJujuClient1X( |
1173 | SimpleEnvironment('foo', {'type': 'ec2'}), None, None) |
1174 | - with patch.object(client, 'juju') as juju_mock: |
1175 | + with patch_juju_call(client) as juju_mock: |
1176 | client.destroy_controller() |
1177 | juju_mock.assert_called_once_with( |
1178 | 'destroy-environment', ('foo', '-y'), |
1179 | @@ -813,21 +824,21 @@ |
1180 | def test_deploy_non_joyent(self): |
1181 | env = EnvJujuClient1X( |
1182 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
1183 | - with patch.object(env, 'juju') as mock_juju: |
1184 | + with patch_juju_call(env) as mock_juju: |
1185 | env.deploy('mondogb') |
1186 | mock_juju.assert_called_with('deploy', ('mondogb',)) |
1187 | |
1188 | def test_deploy_joyent(self): |
1189 | env = EnvJujuClient1X( |
1190 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
1191 | - with patch.object(env, 'juju') as mock_juju: |
1192 | + with patch_juju_call(env) as mock_juju: |
1193 | env.deploy('mondogb') |
1194 | mock_juju.assert_called_with('deploy', ('mondogb',)) |
1195 | |
1196 | def test_deploy_repository(self): |
1197 | env = EnvJujuClient1X( |
1198 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
1199 | - with patch.object(env, 'juju') as mock_juju: |
1200 | + with patch_juju_call(env) as mock_juju: |
1201 | env.deploy('mondogb', '/home/jrandom/repo') |
1202 | mock_juju.assert_called_with( |
1203 | 'deploy', ('mondogb', '--repository', '/home/jrandom/repo')) |
1204 | @@ -835,7 +846,7 @@ |
1205 | def test_deploy_to(self): |
1206 | env = EnvJujuClient1X( |
1207 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
1208 | - with patch.object(env, 'juju') as mock_juju: |
1209 | + with patch_juju_call(env) as mock_juju: |
1210 | env.deploy('mondogb', to='0') |
1211 | mock_juju.assert_called_with( |
1212 | 'deploy', ('mondogb', '--to', '0')) |
1213 | @@ -843,7 +854,7 @@ |
1214 | def test_deploy_service(self): |
1215 | env = EnvJujuClient1X( |
1216 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
1217 | - with patch.object(env, 'juju') as mock_juju: |
1218 | + with patch_juju_call(env) as mock_juju: |
1219 | env.deploy('local:mondogb', service='my-mondogb') |
1220 | mock_juju.assert_called_with( |
1221 | 'deploy', ('local:mondogb', 'my-mondogb',)) |
1222 | @@ -851,7 +862,7 @@ |
1223 | def test_upgrade_charm(self): |
1224 | client = EnvJujuClient1X( |
1225 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
1226 | - with patch.object(client, 'juju') as mock_juju: |
1227 | + with patch_juju_call(client) as mock_juju: |
1228 | client.upgrade_charm('foo-service', |
1229 | '/bar/repository/angsty/mongodb') |
1230 | mock_juju.assert_called_once_with( |
1231 | @@ -861,7 +872,7 @@ |
1232 | def test_remove_service(self): |
1233 | client = EnvJujuClient1X( |
1234 | SimpleEnvironment('foo', {'type': 'local'}), '1.234-76', None) |
1235 | - with patch.object(client, 'juju') as mock_juju: |
1236 | + with patch_juju_call(client) as mock_juju: |
1237 | client.remove_service('mondogb') |
1238 | mock_juju.assert_called_with('destroy-service', ('mondogb',)) |
1239 | |
1240 | @@ -1348,7 +1359,7 @@ |
1241 | |
1242 | def test_set_model_constraints(self): |
1243 | client = EnvJujuClient1X(SimpleEnvironment('bar', {}), None, '/foo') |
1244 | - with patch.object(client, 'juju') as juju_mock: |
1245 | + with patch_juju_call(client) as juju_mock: |
1246 | client.set_model_constraints({'bar': 'baz'}) |
1247 | juju_mock.assert_called_once_with('set-constraints', ('bar=baz',)) |
1248 | |
1249 | @@ -1592,7 +1603,7 @@ |
1250 | def test_enable_ha(self): |
1251 | env = SimpleEnvironment('qux') |
1252 | client = EnvJujuClient1X(env, None, '/foobar/baz') |
1253 | - with patch.object(client, 'juju', autospec=True) as eha_mock: |
1254 | + with patch_juju_call(client) as eha_mock: |
1255 | client.enable_ha() |
1256 | eha_mock.assert_called_once_with('ensure-availability', ('-n', '3')) |
1257 | |
1258 | @@ -1669,7 +1680,7 @@ |
1259 | def test_deploy_bundle_1x(self): |
1260 | client = EnvJujuClient1X(SimpleEnvironment('an_env', None), |
1261 | '1.23-series-arch', None) |
1262 | - with patch.object(client, 'juju') as mock_juju: |
1263 | + with patch_juju_call(client) as mock_juju: |
1264 | client.deploy_bundle('bundle:~juju-qa/some-bundle') |
1265 | mock_juju.assert_called_with( |
1266 | 'deployer', ('--debug', '--deploy-delay', '10', '--timeout', |
1267 | @@ -1678,7 +1689,7 @@ |
1268 | def test_deploy_bundle_template(self): |
1269 | client = EnvJujuClient1X(SimpleEnvironment('an_env', None), |
1270 | '1.23-series-arch', None) |
1271 | - with patch.object(client, 'juju') as mock_juju: |
1272 | + with patch_juju_call(client) as mock_juju: |
1273 | client.deploy_bundle('bundle:~juju-qa/some-{container}-bundle') |
1274 | mock_juju.assert_called_with( |
1275 | 'deployer', ( |
1276 | @@ -1923,14 +1934,14 @@ |
1277 | def test_add_space(self): |
1278 | client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}), |
1279 | '1.23-series-arch', None) |
1280 | - with patch.object(client, 'juju', autospec=True) as juju_mock: |
1281 | + with patch_juju_call(client) as juju_mock: |
1282 | client.add_space('foo-space') |
1283 | juju_mock.assert_called_once_with('space create', ('foo-space')) |
1284 | |
1285 | def test_add_subnet(self): |
1286 | client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}), |
1287 | '1.23-series-arch', None) |
1288 | - with patch.object(client, 'juju', autospec=True) as juju_mock: |
1289 | + with patch_juju_call(client) as juju_mock: |
1290 | client.add_subnet('bar-subnet', 'foo-space') |
1291 | juju_mock.assert_called_once_with('subnet add', |
1292 | ('bar-subnet', 'foo-space')) |
1293 | @@ -1944,7 +1955,7 @@ |
1294 | |
1295 | def test_set_config(self): |
1296 | client = EnvJujuClient1X(SimpleEnvironment('bar', {}), None, '/foo') |
1297 | - with patch.object(client, 'juju') as juju_mock: |
1298 | + with patch_juju_call(client) as juju_mock: |
1299 | client.set_config('foo', {'bar': 'baz'}) |
1300 | juju_mock.assert_called_once_with('set', ('foo', 'bar=baz')) |
1301 | |
1302 | @@ -2065,13 +2076,13 @@ |
1303 | |
1304 | def test_disable_command(self): |
1305 | client = EnvJujuClient1X(SimpleEnvironment('foo'), None, None) |
1306 | - with patch.object(client, 'juju', autospec=True) as mock: |
1307 | + with patch_juju_call(client) as mock: |
1308 | client.disable_command('all', 'message') |
1309 | mock.assert_called_once_with('block all', ('message', )) |
1310 | |
1311 | def test_enable_command(self): |
1312 | client = EnvJujuClient1X(SimpleEnvironment('foo'), None, None) |
1313 | - with patch.object(client, 'juju', autospec=True) as mock: |
1314 | + with patch_juju_call(client) as mock: |
1315 | client.enable_command('all') |
1316 | mock.assert_called_once_with('unblock', 'all') |
1317 | |
1318 | |
1319 | === modified file 'jujupy/version_client.py' |
1320 | --- jujupy/version_client.py 2017-03-10 19:54:30 +0000 |
1321 | +++ jujupy/version_client.py 2017-03-23 04:46:32 +0000 |
1322 | @@ -9,6 +9,8 @@ |
1323 | import yaml |
1324 | |
1325 | from jujupy.client import ( |
1326 | + BaseCondition, |
1327 | + CommandComplete, |
1328 | Controller, |
1329 | _DEFAULT_BUNDLE_TIMEOUT, |
1330 | get_cache_path, |
1331 | @@ -356,7 +358,8 @@ |
1332 | |
1333 | def set_model_constraints(self, constraints): |
1334 | constraint_strings = self._dict_as_option_strings(constraints) |
1335 | - return self.juju('set-constraints', constraint_strings) |
1336 | + retvar, ct = self.juju('set-constraints', constraint_strings) |
1337 | + return retvar, CommandComplete(BaseCondition(), ct) |
1338 | |
1339 | def set_config(self, service, options): |
1340 | option_strings = ['{}={}'.format(*item) for item in options.items()] |
1341 | @@ -377,11 +380,13 @@ |
1342 | def set_env_option(self, option, value): |
1343 | """Set the value of the option in the environment.""" |
1344 | option_value = "%s=%s" % (option, value) |
1345 | - return self.juju('set-env', (option_value,)) |
1346 | + retvar, ct = self.juju('set-env', (option_value,)) |
1347 | + return retvar, CommandComplete(BaseCondition(), ct) |
1348 | |
1349 | def unset_env_option(self, option): |
1350 | """Unset the value of the option in the environment.""" |
1351 | - return self.juju('set-env', ('{}='.format(option),)) |
1352 | + retvar, ct = self.juju('set-env', ('{}='.format(option),)) |
1353 | + return retvar, CommandComplete(BaseCondition(), ct) |
1354 | |
1355 | def get_model_defaults(self, model_key, cloud=None, region=None): |
1356 | log.info('No model-defaults stored for client (attempted get).') |
1357 | @@ -480,25 +485,27 @@ |
1358 | """Destroy the environment, with force. Hard kill option. |
1359 | |
1360 | :return: Subprocess's exit code.""" |
1361 | - return self.juju( |
1362 | + retvar, ct = self.juju( |
1363 | 'destroy-environment', (self.env.environment, '--force', '-y'), |
1364 | check=check, include_e=False, timeout=get_teardown_timeout(self)) |
1365 | + return retvar, CommandComplete(BaseCondition(), ct) |
1366 | |
1367 | def destroy_controller(self, all_models=False): |
1368 | """Destroy the environment, with force. Soft kill option. |
1369 | |
1370 | :param all_models: Ignored. |
1371 | :raises: subprocess.CalledProcessError if the operation fails.""" |
1372 | - return self.juju( |
1373 | + retvar, ct = self.juju( |
1374 | 'destroy-environment', (self.env.environment, '-y'), |
1375 | include_e=False, timeout=get_teardown_timeout(self)) |
1376 | + return retvar, CommandComplete(BaseCondition(), ct) |
1377 | |
1378 | def destroy_environment(self, force=True, delete_jenv=False): |
1379 | if force: |
1380 | force_arg = ('--force',) |
1381 | else: |
1382 | force_arg = () |
1383 | - exit_status = self.juju( |
1384 | + exit_status, _ = self.juju( |
1385 | 'destroy-environment', |
1386 | (self.env.environment,) + force_arg + ('-y',), |
1387 | check=False, include_e=False, |
1388 | @@ -555,7 +562,8 @@ |
1389 | args.extend(['--storage', storage]) |
1390 | if constraints is not None: |
1391 | args.extend(['--constraints', constraints]) |
1392 | - return self.juju('deploy', tuple(args)) |
1393 | + retvar, ct = self.juju('deploy', tuple(args)) |
1394 | + return retvar, CommandComplete(BaseCondition(), ct) |
1395 | |
1396 | def upgrade_charm(self, service, charm_path=None): |
1397 | args = (service,) |
1398 | @@ -644,11 +652,13 @@ |
1399 | |
1400 | def disable_command(self, command_set, message=''): |
1401 | """Disable a command-set.""" |
1402 | - return self.juju('block {}'.format(command_set), (message, )) |
1403 | + retvar, ct = self.juju('block {}'.format(command_set), (message, )) |
1404 | + return retvar, CommandComplete(BaseCondition(), ct) |
1405 | |
1406 | def enable_command(self, args): |
1407 | """Enable a command-set.""" |
1408 | - return self.juju('unblock', args) |
1409 | + retvar, ct = self.juju('unblock', args) |
1410 | + return retvar, CommandComplete(BaseCondition(), ct) |
1411 | |
1412 | |
1413 | class EnvJujuClient22(EnvJujuClient1X): |
1414 | |
1415 | === modified file 'tests/__init__.py' |
1416 | --- tests/__init__.py 2017-03-10 19:46:30 +0000 |
1417 | +++ tests/__init__.py 2017-03-23 04:46:32 +0000 |
1418 | @@ -20,6 +20,7 @@ |
1419 | from unittest.mock import patch |
1420 | import yaml |
1421 | |
1422 | +from jujupy.client import CommandTime |
1423 | import utility |
1424 | |
1425 | |
1426 | @@ -152,12 +153,25 @@ |
1427 | os.environ[key] = org_value |
1428 | |
1429 | |
1430 | +@contextmanager |
1431 | +def patch_juju_call(client, return_value=0): |
1432 | + """Simple patch for client.juju call. |
1433 | + |
1434 | + :param return_value: A tuple to return representing the retvar and |
1435 | + CommandTime object |
1436 | + """ |
1437 | + with patch.object( |
1438 | + client, 'juju', |
1439 | + return_value=make_fake_juju_return(retvar=return_value)) as mock: |
1440 | + yield mock |
1441 | + |
1442 | + |
1443 | def assert_juju_call(test_case, mock_method, client, expected_args, |
1444 | call_index=None): |
1445 | """Check a mock's positional arguments. |
1446 | |
1447 | :param test_case: The test case currently being run. |
1448 | - :param mock_mothod: The mock object to be checked. |
1449 | + :param mock_method: The mock object to be checked. |
1450 | :param client: Ignored. |
1451 | :param expected_args: The expected positional arguments for the call. |
1452 | :param call_index: Index of the call to check, if None checks first call |
1453 | @@ -248,3 +262,9 @@ |
1454 | }, |
1455 | } |
1456 | } |
1457 | + |
1458 | + |
1459 | +def make_fake_juju_return( |
1460 | + retvar=0, cmd='mock_cmd', full_args=[], envvars=None, start=None): |
1461 | + """Shadow fake that defaults construction arguments.""" |
1462 | + return (retvar, CommandTime(cmd, full_args, envvars, start)) |
1463 | |
1464 | === modified file 'tests/test_assess_block.py' |
1465 | --- tests/test_assess_block.py 2017-01-20 20:49:41 +0000 |
1466 | +++ tests/test_assess_block.py 2017-03-23 04:46:32 +0000 |
1467 | @@ -18,6 +18,7 @@ |
1468 | ) |
1469 | from tests import ( |
1470 | parse_error, |
1471 | + patch_juju_call, |
1472 | FakeHomeTestCase, |
1473 | TestCase, |
1474 | ) |
1475 | @@ -149,7 +150,7 @@ |
1476 | autospec=True, side_effect=side_effects): |
1477 | with patch('assess_block.deploy_dummy_stack', autospec=True): |
1478 | with patch.object(client, 'remove_service') as mock_rs: |
1479 | - with patch.object(client, 'juju') as mock_juju: |
1480 | + with patch_juju_call(client) as mock_juju: |
1481 | with patch.object( |
1482 | client, 'wait_for_started') as mock_ws: |
1483 | assess_block(client, 'trusty') |
1484 | |
1485 | === modified file 'tests/test_assess_bootstrap.py' |
1486 | --- tests/test_assess_bootstrap.py 2017-03-01 19:02:24 +0000 |
1487 | +++ tests/test_assess_bootstrap.py 2017-03-23 04:46:32 +0000 |
1488 | @@ -29,6 +29,7 @@ |
1489 | from tests import ( |
1490 | FakeHomeTestCase, |
1491 | TestCase, |
1492 | + make_fake_juju_return, |
1493 | ) |
1494 | from utility import ( |
1495 | JujuAssertionError, |
1496 | @@ -361,7 +362,7 @@ |
1497 | args.temp_env_name = 'qux' |
1498 | with extended_bootstrap_cxt('2.0.0'): |
1499 | with patch('jujupy.ModelClient.juju', autospec=True, |
1500 | - side_effect=['', '']) as j_mock: |
1501 | + return_value=make_fake_juju_return()) as j_mock: |
1502 | with patch('assess_bootstrap.get_controller_hostname', |
1503 | return_value='test-host', autospec=True): |
1504 | bs_manager = BootstrapManager.from_args(args) |
1505 | |
1506 | === modified file 'tests/test_assess_container_networking.py' |
1507 | --- tests/test_assess_container_networking.py 2017-01-20 20:58:41 +0000 |
1508 | +++ tests/test_assess_container_networking.py 2017-03-23 04:46:32 +0000 |
1509 | @@ -17,6 +17,7 @@ |
1510 | LXD_MACHINE, |
1511 | SimpleEnvironment, |
1512 | ) |
1513 | +from jujupy.client import CommandTime |
1514 | |
1515 | import assess_container_networking as jcnet |
1516 | from tests import ( |
1517 | @@ -78,6 +79,29 @@ |
1518 | if cmd != 'bootstrap': |
1519 | self.commands.append((cmd, args)) |
1520 | if cmd == 'ssh': |
1521 | + ct = CommandTime(cmd, args) |
1522 | + if len(self._ssh_output) == 0: |
1523 | + return "", ct |
1524 | + |
1525 | + try: |
1526 | + ct = CommandTime(cmd, args) |
1527 | + return self._ssh_output[self._call_number()], ct |
1528 | + except IndexError: |
1529 | + # If we ran out of values, just return the last one |
1530 | + return self._ssh_output[-1], ct |
1531 | + else: |
1532 | + return super(JujuMock, self).juju(cmd, *rargs, **kwargs) |
1533 | + |
1534 | + def get_juju_output(self, cmd, *rargs, **kwargs): |
1535 | + # Almost exactly like juju() except get_juju_output doesn't return |
1536 | + # a CommandTime |
1537 | + if len(rargs) == 1: |
1538 | + args = rargs[0] |
1539 | + else: |
1540 | + args = rargs |
1541 | + if cmd != 'bootstrap': |
1542 | + self.commands.append((cmd, args)) |
1543 | + if cmd == 'ssh': |
1544 | if len(self._ssh_output) == 0: |
1545 | return "" |
1546 | |
1547 | @@ -87,7 +111,7 @@ |
1548 | # If we ran out of values, just return the last one |
1549 | return self._ssh_output[-1] |
1550 | else: |
1551 | - return super(JujuMock, self).juju(cmd, *rargs, **kwargs) |
1552 | + return super(JujuMock, self).get_juju_output(cmd, *rargs, **kwargs) |
1553 | |
1554 | def _call_number(self): |
1555 | call_number = self._call_n |
1556 | @@ -117,7 +141,9 @@ |
1557 | patch.object(self.client, 'wait_for', lambda *args, **kw: None), |
1558 | patch.object(self.client, 'wait_for_started', |
1559 | self.juju_mock.get_status), |
1560 | - patch.object(self.client, 'get_juju_output', self.juju_mock.juju), |
1561 | + patch.object( |
1562 | + self.client, 'get_juju_output', |
1563 | + self.juju_mock.get_juju_output), |
1564 | ] |
1565 | |
1566 | for patcher in patches: |
1567 | |
1568 | === modified file 'tests/test_assess_user_grant_revoke.py' |
1569 | --- tests/test_assess_user_grant_revoke.py 2017-01-20 21:35:27 +0000 |
1570 | +++ tests/test_assess_user_grant_revoke.py 2017-03-23 04:46:32 +0000 |
1571 | @@ -27,6 +27,7 @@ |
1572 | from tests import ( |
1573 | parse_error, |
1574 | TestCase, |
1575 | + patch_juju_call, |
1576 | ) |
1577 | |
1578 | |
1579 | @@ -150,7 +151,7 @@ |
1580 | def test_assert_write_model(self): |
1581 | fake_client = fake_juju_client() |
1582 | with patch.object(fake_client, 'wait_for_started'): |
1583 | - with patch.object(fake_client, 'juju', return_value=True): |
1584 | + with patch_juju_call(fake_client): |
1585 | assert_write_model(fake_client, 'write', True) |
1586 | with self.assertRaises(JujuAssertionError): |
1587 | assert_write_model(fake_client, 'write', False) |
1588 | |
1589 | === modified file 'tests/test_deploy_stack.py' |
1590 | --- tests/test_deploy_stack.py 2017-03-10 00:50:40 +0000 |
1591 | +++ tests/test_deploy_stack.py 2017-03-23 04:46:32 +0000 |
1592 | @@ -82,6 +82,7 @@ |
1593 | assert_juju_call, |
1594 | FakeHomeTestCase, |
1595 | FakePopen, |
1596 | + make_fake_juju_return, |
1597 | observable_temp_file, |
1598 | temp_os_env, |
1599 | use_context, |
1600 | @@ -1757,7 +1758,7 @@ |
1601 | def do_check(*args, **kwargs): |
1602 | with client.check_timeouts(): |
1603 | with tear_down_client.check_timeouts(): |
1604 | - pass |
1605 | + return make_fake_juju_return() |
1606 | |
1607 | with patch.object(bs_manager.tear_down_client, 'juju', |
1608 | side_effect=do_check, autospec=True): |
1609 | @@ -1996,7 +1997,9 @@ |
1610 | bs_manager.has_controller = False |
1611 | with patch('deploy_stack.safe_print_status', |
1612 | autospec=True) as sp_mock: |
1613 | - with patch.object(client, 'juju', wrap=client.juju) as juju_mock: |
1614 | + with patch.object( |
1615 | + client, 'juju', wrap=client.juju, |
1616 | + return_value=make_fake_juju_return()) as juju_mock: |
1617 | with patch.object(client, 'get_juju_output', |
1618 | wraps=client.get_juju_output) as gjo_mock: |
1619 | with patch.object(bs_manager, '_should_dump', |
1620 | @@ -2085,7 +2088,9 @@ |
1621 | """Preform patches to focus on the call to bootstrap.""" |
1622 | with patch.object(bs_manager, 'dump_all_logs'): |
1623 | with patch.object(bs_manager, 'runtime_context'): |
1624 | - with patch.object(bs_manager.client, 'juju'): |
1625 | + with patch.object( |
1626 | + bs_manager.client, 'juju', |
1627 | + return_value=make_fake_juju_return()): |
1628 | with patch.object(bs_manager.client, 'bootstrap') as mock: |
1629 | yield mock |
1630 |