Merge lp:~abentley/juju-ci-tools/remove-machine-2 into lp:juju-ci-tools

Proposed by Aaron Bentley
Status: Merged
Merged at revision: 1921
Proposed branch: lp:~abentley/juju-ci-tools/remove-machine-2
Merge into: lp:juju-ci-tools
Diff against target: 240 lines (+96/-17)
7 files modified
assess_cloud.py (+3/-4)
assess_heterogeneous_control.py (+2/-2)
industrial_test.py (+1/-1)
jujupy/client.py (+28/-1)
jujupy/fake.py (+3/-0)
jujupy/tests/test_client.py (+58/-0)
tests/test_assess_cloud.py (+1/-9)
To merge this branch: bzr merge lp:~abentley/juju-ci-tools/remove-machine-2
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+318802@code.launchpad.net

Commit message

Add ModelClient.remove_machine, get unicode from ModelClient.get_full_path.

Description of the change

This branch adds changes useful to Hammer Time.

It adds remove_machine, so that it can return WaitMachineNotPresent.

It changes get_juju_path to return a unicode string (ModelClient.full_path is unicode when user-supplied).

It changes FakeBackend.add_machines so that it behaves more like Juju itself, puking when both -n and a placement directive are specified.

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you. I have a trivial request inline.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'assess_cloud.py'
2--- assess_cloud.py 2017-02-22 16:08:27 +0000
3+++ assess_cloud.py 2017-03-02 16:55:21 +0000
4@@ -90,11 +90,10 @@
5 client.juju('add-machine', ('--series', current_series))
6 new_status = client.wait_for_started()
7 new_machines = [k for k, v in new_status.iter_new_machines(old_status)]
8+ conditions = []
9 for machine in new_machines:
10- client.juju('remove-machine', (machine,))
11- new_status = client.wait_for(
12- ConditionList([client.make_remove_machine_condition(n)
13- for n in new_machines]))
14+ conditions.append(client.remove_machine(machine))
15+ new_status = client.wait_for(ConditionList(conditions))
16
17
18 def assess_cloud_kill_controller(bs_manager):
19
20=== modified file 'assess_heterogeneous_control.py'
21--- assess_heterogeneous_control.py 2017-01-17 18:04:08 +0000
22+++ assess_heterogeneous_control.py 2017-03-02 16:55:21 +0000
23@@ -174,9 +174,9 @@
24 container_machine, = set(k for k, v in status.agent_items() if
25 k.endswith('/{}/0'.format(container_type)))
26 container_holder = container_machine.split('/')[0]
27- other.juju('remove-machine', (container_machine,))
28+ other.remove_machine(container_machine)
29 wait_until_removed(other, container_machine)
30- other.juju('remove-machine', (container_holder,))
31+ other.remove_machine(container_holder)
32 wait_until_removed(other, container_holder)
33
34 # suppress nosetests
35
36=== modified file 'industrial_test.py'
37--- industrial_test.py 2017-01-20 20:58:04 +0000
38+++ industrial_test.py 2017-03-02 16:55:21 +0000
39@@ -749,7 +749,7 @@
40 for application in applications:
41 for unit in application['units'].values():
42 container_machines.add(unit['machine'])
43- client.juju('remove-machine', ('--force', unit['machine']))
44+ client.remove_machine(unit['machine'], force=True)
45 remove_timeout = {
46 LXC_MACHINE: 30,
47 LXD_MACHINE: 900,
48
49=== modified file 'jujupy/client.py'
50--- jujupy/client.py 2017-03-01 19:02:24 +0000
51+++ jujupy/client.py 2017-03-02 16:55:21 +0000
52@@ -15,6 +15,7 @@
53 import errno
54 from itertools import chain
55 import json
56+from locale import getpreferredencoding
57 import logging
58 import os
59 import re
60@@ -1315,6 +1316,18 @@
61 super(WaitMachineNotPresent, self).__init__(timeout)
62 self.machine = machine
63
64+ def __eq__(self, other):
65+ if not type(self) is type(other):
66+ return False
67+ if self.timeout != other.timeout:
68+ return False
69+ if self.machine != other.machine:
70+ return False
71+ return True
72+
73+ def __ne__(self, other):
74+ return not self.__eq__(other)
75+
76 def iter_blocking_state(self, status):
77 for machine, info in status.iter_machines():
78 if machine == self.machine:
79@@ -1460,7 +1473,8 @@
80 def get_full_path(cls):
81 if sys.platform == 'win32':
82 return WIN_JUJU_CMD
83- return subprocess.check_output(('which', 'juju')).rstrip(b'\n')
84+ return subprocess.check_output(
85+ ('which', 'juju')).decode(getpreferredencoding()).rstrip('\n')
86
87 def clone_path_cls(self, juju_path):
88 """Clone using the supplied path to determine the class."""
89@@ -1621,6 +1635,19 @@
90 timeout = 600
91 return WaitMachineNotPresent(machine, timeout)
92
93+ def remove_machine(self, machine_id, force=False):
94+ """Remove a machine (or container).
95+
96+ :param machine_id: The id of the machine to remove.
97+ :return: A WaitMachineNotPresent instance for client.wait_for.
98+ """
99+ if force:
100+ options = ('--force',)
101+ else:
102+ options = ()
103+ self.juju('remove-machine', options + (machine_id,))
104+ return self.make_remove_machine_condition(machine_id)
105+
106 @staticmethod
107 def get_cloud_region(cloud, region):
108 if region is None:
109
110=== modified file 'jujupy/fake.py'
111--- jujupy/fake.py 2017-02-28 21:38:36 +0000
112+++ jujupy/fake.py 2017-03-02 16:55:21 +0000
113@@ -704,6 +704,9 @@
114 parser.add_argument('-n', type=int, dest='count', default='1')
115 parser.add_argument('--series')
116 parsed = parser.parse_args(args)
117+ if len(parsed.host_placement) > 0 and parsed.count != 1:
118+ raise subprocess.CalledProcessError(
119+ 1, 'cannot use -n when specifying a placement directive')
120 if len(parsed.host_placement) == 1:
121 split = parsed.host_placement[0].split(':')
122 if len(split) == 1:
123
124=== modified file 'jujupy/tests/test_client.py'
125--- jujupy/tests/test_client.py 2017-02-28 21:38:36 +0000
126+++ jujupy/tests/test_client.py 2017-03-02 16:55:21 +0000
127@@ -307,6 +307,14 @@
128 self.assertEqual(('ctrl1', 'user1', 'model1'), result)
129
130
131+def backend_call(client, cmd, args, model=None, check=True, timeout=None,
132+ extra_env=None):
133+ """Return the mock.call for this command."""
134+ return call(cmd, args, client.used_feature_flags,
135+ client.env.juju_home, model, check, timeout, extra_env,
136+ suppress_err=False)
137+
138+
139 class TestBaseCondition(ClientTest):
140
141 def test_timeout(self):
142@@ -361,6 +369,29 @@
143
144 class TestModelClient(ClientTest):
145
146+ def test_get_full_path(self):
147+ with patch('subprocess.check_output',
148+ return_value=b'asdf\n') as co_mock:
149+ with patch('sys.platform', 'linux2'):
150+ path = ModelClient.get_full_path()
151+ co_mock.assert_called_once_with(('which', 'juju'))
152+ expected = u'asdf'
153+ self.assertEqual(expected, path)
154+ self.assertIs(type(expected), type(path))
155+
156+ def test_get_full_path_encoding(self):
157+ # Test with non-ascii-compatible encoding
158+ with patch('subprocess.check_output',
159+ return_value='asdf\n'.encode('EBCDIC-CP-BE')) as co_mock:
160+ with patch('sys.platform', 'linux2'):
161+ with patch('jujupy.client.getpreferredencoding',
162+ return_value='EBCDIC-CP-BE'):
163+ path = ModelClient.get_full_path()
164+ co_mock.assert_called_once_with(('which', 'juju'))
165+ expected = u'asdf'
166+ self.assertEqual(expected, path)
167+ self.assertIs(type(expected), type(path))
168+
169 def test_no_duplicate_env(self):
170 env = JujuData('foo', {})
171 client = ModelClient(env, '1.25', 'full_path')
172@@ -1471,6 +1502,33 @@
173 1)
174 self.assertEqual(cc_mock.call_count, 2)
175
176+ def test_remove_machine(self):
177+ client = fake_juju_client()
178+ with patch.object(client._backend, 'juju') as juju_mock:
179+ condition = client.remove_machine('0')
180+ call = backend_call(
181+ client, 'remove-machine', ('0',), 'name:name')
182+ juju_mock.assert_called_once_with(*call[1], **call[2])
183+ self.assertEqual(condition, WaitMachineNotPresent('0', 600))
184+
185+ def test_remove_machine_force(self):
186+ client = fake_juju_client()
187+ with patch.object(client._backend, 'juju') as juju_mock:
188+ client.remove_machine('0', force=True)
189+ call = backend_call(
190+ client, 'remove-machine', ('--force', '0'), 'name:name')
191+ juju_mock.assert_called_once_with(*call[1], **call[2])
192+
193+ def test_remove_machine_azure(self):
194+ client = fake_juju_client(JujuData('name', {
195+ 'type': 'azure',
196+ 'location': 'usnorth',
197+ }))
198+ client.bootstrap()
199+ client.juju('add-machine', ())
200+ condition = client.remove_machine('0')
201+ self.assertEqual(condition, WaitMachineNotPresent('0', 1200))
202+
203 def test_wait_for_started(self):
204 value = self.make_status_yaml('agent-state', 'started', 'started')
205 client = ModelClient(JujuData('local'), None, None)
206
207=== modified file 'tests/test_assess_cloud.py'
208--- tests/test_assess_cloud.py 2017-02-25 06:09:40 +0000
209+++ tests/test_assess_cloud.py 2017-03-02 16:55:21 +0000
210@@ -3,7 +3,6 @@
211 import logging
212
213 from mock import (
214- call,
215 patch,
216 )
217 from assess_cloud import (
218@@ -22,6 +21,7 @@
219 Juju2Backend,
220 )
221 from jujupy.version_client import ModelClient2_0
222+from jujupy.tests.test_client import backend_call
223 from tests import (
224 FakeHomeTestCase,
225 observable_temp_file,
226@@ -33,14 +33,6 @@
227 )
228
229
230-def backend_call(client, cmd, args, model=None, check=True, timeout=None,
231- extra_env=None):
232- """Return the mock.call for this command."""
233- return call(cmd, args, client.used_feature_flags,
234- client.env.juju_home, model, check, timeout, extra_env,
235- suppress_err=False)
236-
237-
238 @contextmanager
239 def mocked_bs_manager(juju_home):
240 client = fake_juju_client()

Subscribers

People subscribed via source and target branches