Merge lp:~louis/charms/precise/nova-cloud-controller/lp1313602-multiline-known-hosts into lp:~openstack-charmers-archive/charms/precise/nova-cloud-controller/trunk

Proposed by Louis Bouchard
Status: Superseded
Proposed branch: lp:~louis/charms/precise/nova-cloud-controller/lp1313602-multiline-known-hosts
Merge into: lp:~openstack-charmers-archive/charms/precise/nova-cloud-controller/trunk
Diff against target: 420 lines (+171/-79)
4 files modified
hooks/nova_cc_hooks.py (+46/-16)
hooks/nova_cc_utils.py (+59/-45)
unit_tests/test_nova_cc_hooks.py (+56/-8)
unit_tests/test_nova_cc_utils.py (+10/-10)
To merge this branch: bzr merge lp:~louis/charms/precise/nova-cloud-controller/lp1313602-multiline-known-hosts
Reviewer Review Type Date Requested Status
Louis Bouchard (community) Needs Resubmitting
James Page Needs Fixing
Review via email: mp+218442@code.launchpad.net

This proposal has been superseded by a proposal from 2014-06-19.

Description of the change

Send known_hosts and authorized_keys line by line

Only empty lines are not carried over

To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote :
Download full text (27.2 KiB)

Hi Louis

The general approach to fixing this looks fine, however the unit tests all need updating:

make test
Starting tests...
test_compute_changed_ssh_migration (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_compute_joined_neutron (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_config_changed_no_upgrade (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_config_changed_with_upgrade (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_db_changed (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_db_changed_missing_relation_data (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_db_joined (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_db_joined_with_postgresql (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_install_hook (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_nova_vmware_joined (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_postgresql_db_changed (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_postgresql_neutron_db_joined (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_postgresql_neutron_joined_with_db (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_postgresql_nova_db_changed_missing_relation_data (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_postgresql_nova_db_joined (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_postgresql_nova_joined_with_db (unit_tests.test_nova_cc_hooks.NovaCCHooksTests) ... ERROR
test_add_known_host_exists (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_add_known_host_exists_added (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_add_known_host_exists_outdated (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_authorized_keys (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_endpoints_base (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_endpoints_nova_volume (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_endpoints_quantum_neutron (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_packages_base (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_packages_base_grizzly_beyond (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_packages_neutron (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_packages_nova_volume (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_packages_quantum (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_ports (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_volume_service_essex (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_volume_service_folsom_cinder (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_volume_service_folsom_nova_vol (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_determine_volume_service_grizzly_and_beyond (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
test_known_hosts (unit_tests.test_nova_cc_utils.NovaCCUtilsTests) ... ok
Migrate database w...

review: Needs Fixing
77. By Louis Bouchard

Adapt unit tests to changes made by previous commit

Tests now cover multi-line known_hosts & authorized_keys

78. By Louis Bouchard

Add new test_compute_changed_nova_public_key unit test

Test the compute_changed hook when 'enable-resize' is
used by nova-compute

79. By Louis Bouchard

Modify hook funcions & util to work in non-relation hooks

Some hook functions did not work when invoked from non-relation
hooks (compute_changed in particular). Modified all utils
hook functions to honour relation-id & unit-id when required

80. By Louis Bouchard

Modified relation_get mechanism in compute_changed

Get all relations in one call in use dictionary

81. By Louis Bouchard

Add missing rel_settings test

82. By Louis Bouchard

Fix unit_test to honour calls with unit & relation ids

83. By Louis Bouchard

More PEP8 cleanup

Revision history for this message
Louis Bouchard (louis) wrote :

Added call in upgrade-charm hook to action the new code that use multi-line mechanism. Tested that it can be used to recover from a previous failure caused by authorised_keys/known_hosts that were too long.

Fixed unit test for previous modifications and adapted them to changes made to support non-relation hook calls (required for upgrade-charm).

Added one unit-test to cover part of the modified code not previously tested.

More test would be needed to cover the non-relation context but out of scope for this bug

review: Needs Resubmitting
84. By Louis Bouchard

Implement changes following Merge Proposal

 - Replaced uid by unit
 - Rolled back change to compute_departed
 - Use rel_settings.get()

85. By Louis Bouchard

Fix unit test following uid->unit change in hook

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hooks/nova_cc_hooks.py'
2--- hooks/nova_cc_hooks.py 2014-04-11 16:41:42 +0000
3+++ hooks/nova_cc_hooks.py 2014-05-27 14:01:55 +0000
4@@ -19,6 +19,7 @@
5 relation_get,
6 relation_ids,
7 relation_set,
8+ related_units,
9 open_port,
10 unit_get,
11 )
12@@ -54,8 +55,8 @@
13 save_script_rc,
14 ssh_compute_add,
15 ssh_compute_remove,
16- ssh_known_hosts_b64,
17- ssh_authorized_keys_b64,
18+ ssh_known_hosts_lines,
19+ ssh_authorized_keys_lines,
20 register_configs,
21 restart_map,
22 volume_service,
23@@ -339,26 +340,52 @@
24
25
26 @hooks.hook('cloud-compute-relation-changed')
27-def compute_changed():
28- migration_auth = relation_get('migration_auth_type')
29- if migration_auth == 'ssh':
30- key = relation_get('ssh_public_key')
31+def compute_changed(rid=None, uid=None):
32+ rel_settings = relation_get(rid=rid, unit=uid)
33+ if 'migration_auth_type' not in rel_settings:
34+ return
35+ if rel_settings['migration_auth_type'] == 'ssh':
36+ key = rel_settings['ssh_public_key']
37 if not key:
38 log('SSH migration set but peer did not publish key.')
39 return
40- ssh_compute_add(key)
41- relation_set(known_hosts=ssh_known_hosts_b64(),
42- authorized_keys=ssh_authorized_keys_b64())
43- if relation_get('nova_ssh_public_key'):
44- key = relation_get('nova_ssh_public_key')
45- ssh_compute_add(key, user='nova')
46- relation_set(nova_known_hosts=ssh_known_hosts_b64(user='nova'),
47- nova_authorized_keys=ssh_authorized_keys_b64(user='nova'))
48+ ssh_compute_add(key, rid=rid, uid=uid)
49+ index = 0
50+ for line in ssh_known_hosts_lines(uid=uid):
51+ relation_set(relation_id=rid, relation_settings=
52+ {'known_hosts_{}'.format(index): line})
53+ index += 1
54+ relation_set(relation_id=rid, known_hosts_max_index=index)
55+ index = 0
56+ for line in ssh_authorized_keys_lines(uid=uid):
57+ relation_set(relation_id=rid, relation_settings=
58+ {'authorized_keys_{}'.format(index): line})
59+ index += 1
60+ relation_set(relation_id=rid, authorized_keys_max_index=index)
61+ if 'nova_ssh_public_key' not in rel_settings:
62+ return
63+ if rel_settings['nova_ssh_public_key']:
64+ ssh_compute_add(rel_settings['nova_ssh_public_key'],
65+ rid=rid, uid=uid, user='nova')
66+ index = 0
67+ for line in ssh_known_hosts_lines(uid=uid, user='nova'):
68+ relation_set(relation_id=rid, relation_settings=
69+ {'{}_known_hosts_{}'.format('nova', index): line})
70+ index += 1
71+ relation_set(relation_id=rid, relation_settings=
72+ {'{}_known_hosts_max_index'.format('nova'): index})
73+ index = 0
74+ for line in ssh_authorized_keys_lines(uid=uid, user='nova'):
75+ relation_set(relation_id=rid, relation_settings=
76+ {'{}_authorized_keys_{}'.format('nova', index): line})
77+ index += 1
78+ relation_set(relation_id=rid, relation_settings=
79+ {'{}_authorized_keys_max_index'.format('nova'): index})
80
81
82 @hooks.hook('cloud-compute-relation-departed')
83-def compute_departed():
84- ssh_compute_remove(public_key=relation_get('ssh_public_key'))
85+def compute_departed(uid=None):
86+ ssh_compute_remove(uid=uid, public_key=relation_get('ssh_public_key'))
87
88
89 @hooks.hook('neutron-network-service-relation-joined',
90@@ -498,6 +525,9 @@
91 amqp_joined(relation_id=r_id)
92 for r_id in relation_ids('identity-service'):
93 identity_joined(rid=r_id)
94+ for r_id in relation_ids('cloud-compute'):
95+ for unit in related_units(r_id):
96+ compute_changed(r_id, unit)
97
98
99 def main():
100
101=== modified file 'hooks/nova_cc_utils.py'
102--- hooks/nova_cc_utils.py 2014-05-21 10:03:01 +0000
103+++ hooks/nova_cc_utils.py 2014-05-27 14:01:55 +0000
104@@ -509,8 +509,11 @@
105 return b64encode(_in.read())
106
107
108-def ssh_directory_for_unit(user=None):
109- remote_service = remote_unit().split('/')[0]
110+def ssh_directory_for_unit(remunit=None, user=None):
111+ if remunit:
112+ remote_service = remunit.split('/')[0]
113+ else:
114+ remote_service = remote_unit().split('/')[0]
115 if user:
116 remote_service = "{}_{}".format(remote_service, user)
117 _dir = os.path.join(NOVA_SSH_DIR, remote_service)
118@@ -524,29 +527,29 @@
119 return _dir
120
121
122-def known_hosts(user=None):
123- return os.path.join(ssh_directory_for_unit(user), 'known_hosts')
124-
125-
126-def authorized_keys(user=None):
127- return os.path.join(ssh_directory_for_unit(user), 'authorized_keys')
128-
129-
130-def ssh_known_host_key(host, user=None):
131- cmd = ['ssh-keygen', '-f', known_hosts(user), '-H', '-F', host]
132+def known_hosts(unit=None, user=None):
133+ return os.path.join(ssh_directory_for_unit(unit, user), 'known_hosts')
134+
135+
136+def authorized_keys(unit=None, user=None):
137+ return os.path.join(ssh_directory_for_unit(unit, user), 'authorized_keys')
138+
139+
140+def ssh_known_host_key(host, uid=None, user=None):
141+ cmd = ['ssh-keygen', '-f', known_hosts(uid, user), '-H', '-F', host]
142 try:
143 return subprocess.check_output(cmd).strip()
144 except subprocess.CalledProcessError:
145 return None
146
147
148-def remove_known_host(host, user=None):
149+def remove_known_host(host, uid=None, user=None):
150 log('Removing SSH known host entry for compute host at %s' % host)
151- cmd = ['ssh-keygen', '-f', known_hosts(user), '-R', host]
152+ cmd = ['ssh-keygen', '-f', known_hosts(uid, user), '-R', host]
153 subprocess.check_call(cmd)
154
155
156-def add_known_host(host, user=None):
157+def add_known_host(host, uid=None, user=None):
158 '''Add variations of host to a known hosts file.'''
159 cmd = ['ssh-keyscan', '-H', '-t', 'rsa', host]
160 try:
161@@ -555,33 +558,34 @@
162 log('Could not obtain SSH host key from %s' % host, level=ERROR)
163 raise e
164
165- current_key = ssh_known_host_key(host, user)
166+ current_key = ssh_known_host_key(host, uid, user)
167 if current_key:
168 if remote_key == current_key:
169 log('Known host key for compute host %s up to date.' % host)
170 return
171 else:
172- remove_known_host(host, user)
173+ remove_known_host(host, uid, user)
174
175 log('Adding SSH host key to known hosts for compute node at %s.' % host)
176- with open(known_hosts(user), 'a') as out:
177+ with open(known_hosts(uid, user), 'a') as out:
178 out.write(remote_key + '\n')
179
180
181-def ssh_authorized_key_exists(public_key, user=None):
182- with open(authorized_keys(user)) as keys:
183+def ssh_authorized_key_exists(public_key, uid=None, user=None):
184+ with open(authorized_keys(uid, user)) as keys:
185 return (' %s ' % public_key) in keys.read()
186
187
188-def add_authorized_key(public_key, user=None):
189- with open(authorized_keys(user), 'a') as keys:
190+def add_authorized_key(public_key, uid=None, user=None):
191+ with open(authorized_keys(uid, user), 'a') as keys:
192 keys.write(public_key + '\n')
193
194
195-def ssh_compute_add(public_key, user=None):
196+def ssh_compute_add(public_key, rid=None, uid=None, user=None):
197 # If remote compute node hands us a hostname, ensure we have a
198 # known hosts entry for its IP, hostname and FQDN.
199- private_address = relation_get('private-address')
200+ private_address = relation_get(rid=rid, unit=uid,
201+ attribute='private-address')
202 hosts = [private_address]
203
204 if not is_ip(private_address):
205@@ -593,31 +597,41 @@
206 hosts.append(hn.split('.')[0])
207
208 for host in list(set(hosts)):
209- if not ssh_known_host_key(host, user):
210- add_known_host(host, user)
211+ if not ssh_known_host_key(host, uid, user):
212+ add_known_host(host, uid, user)
213
214- if not ssh_authorized_key_exists(public_key, user):
215+ if not ssh_authorized_key_exists(public_key, uid, user):
216 log('Saving SSH authorized key for compute host at %s.' %
217 private_address)
218- add_authorized_key(public_key, user)
219-
220-
221-def ssh_known_hosts_b64(user=None):
222- with open(known_hosts(user)) as hosts:
223- return b64encode(hosts.read())
224-
225-
226-def ssh_authorized_keys_b64(user=None):
227- with open(authorized_keys(user)) as keys:
228- return b64encode(keys.read())
229-
230-
231-def ssh_compute_remove(public_key, user=None):
232- if not (os.path.isfile(authorized_keys(user)) or
233- os.path.isfile(known_hosts(user))):
234+ add_authorized_key(public_key, uid, user)
235+
236+
237+def ssh_known_hosts_lines(uid=None, user=None):
238+ known_hosts_list = []
239+
240+ with open(known_hosts(uid, user)) as hosts:
241+ for hosts_line in hosts:
242+ if hosts_line.rstrip():
243+ known_hosts_list.append(hosts_line.rstrip())
244+ return(known_hosts_list)
245+
246+
247+def ssh_authorized_keys_lines(uid=None, user=None):
248+ authorized_keys_list = []
249+
250+ with open(authorized_keys(uid, user)) as keys:
251+ for authkey_line in keys:
252+ if authkey_line.rstrip():
253+ authorized_keys_list.append(authkey_line.rstrip())
254+ return(authorized_keys_list)
255+
256+
257+def ssh_compute_remove(public_key, uid=None, user=None):
258+ if not (os.path.isfile(authorized_keys(uid, user)) or
259+ os.path.isfile(known_hosts(uid, user))):
260 return
261
262- with open(authorized_keys(user)) as _keys:
263+ with open(authorized_keys(uid, user)) as _keys:
264 keys = [k.strip() for k in _keys.readlines()]
265
266 if public_key not in keys:
267@@ -625,7 +639,7 @@
268
269 [keys.remove(key) for key in keys if key == public_key]
270
271- with open(authorized_keys(user), 'w') as _keys:
272+ with open(authorized_keys(uid, user), 'w') as _keys:
273 keys = '\n'.join(keys)
274 if not keys.endswith('\n'):
275 keys += '\n'
276
277=== modified file 'unit_tests/test_nova_cc_hooks.py'
278--- unit_tests/test_nova_cc_hooks.py 2014-05-21 10:03:01 +0000
279+++ unit_tests/test_nova_cc_hooks.py 2014-05-27 14:01:55 +0000
280@@ -35,8 +35,8 @@
281 'relation_set',
282 'relation_ids',
283 'ssh_compute_add',
284- 'ssh_known_hosts_b64',
285- 'ssh_authorized_keys_b64',
286+ 'ssh_known_hosts_lines',
287+ 'ssh_authorized_keys_lines',
288 'save_script_rc',
289 'execd_preinstall',
290 'network_manager',
291@@ -98,12 +98,60 @@
292 self.test_relation.set({
293 'migration_auth_type': 'ssh', 'ssh_public_key': 'fookey',
294 'private-address': '10.0.0.1'})
295- self.ssh_known_hosts_b64.return_value = 'hosts'
296- self.ssh_authorized_keys_b64.return_value = 'keys'
297- hooks.compute_changed()
298- self.ssh_compute_add.assert_called_with('fookey')
299- self.relation_set.assert_called_with(known_hosts='hosts',
300- authorized_keys='keys')
301+ self.ssh_known_hosts_lines.return_value = [
302+ 'k_h_0', 'k_h_1', 'k_h_2']
303+ self.ssh_authorized_keys_lines.return_value = [
304+ 'auth_0', 'auth_1', 'auth_2']
305+ hooks.compute_changed()
306+ self.ssh_compute_add.assert_called_with('fookey', rid=None, uid=None)
307+ expected_relations = [
308+ call(relation_settings={'authorized_keys_0': 'auth_0'},
309+ relation_id=None),
310+ call(relation_settings={'authorized_keys_1': 'auth_1'},
311+ relation_id=None),
312+ call(relation_settings={'authorized_keys_2': 'auth_2'},
313+ relation_id=None),
314+ call(relation_settings={'known_hosts_0': 'k_h_0'},
315+ relation_id=None),
316+ call(relation_settings={'known_hosts_1': 'k_h_1'},
317+ relation_id=None),
318+ call(relation_settings={'known_hosts_2': 'k_h_2'},
319+ relation_id=None),
320+ call(authorized_keys_max_index=3, relation_id=None),
321+ call(known_hosts_max_index=3, relation_id=None)]
322+ self.assertEquals(sorted(self.relation_set.call_args_list),
323+ sorted(expected_relations))
324+
325+ def test_compute_changed_nova_public_key(self):
326+ self.test_relation.set({
327+ 'migration_auth_type': 'sasl', 'nova_ssh_public_key': 'fookey',
328+ 'private-address': '10.0.0.1'})
329+ self.ssh_known_hosts_lines.return_value = [
330+ 'k_h_0', 'k_h_1', 'k_h_2']
331+ self.ssh_authorized_keys_lines.return_value = [
332+ 'auth_0', 'auth_1', 'auth_2']
333+ hooks.compute_changed()
334+ self.ssh_compute_add.assert_called_with('fookey', user='nova',
335+ rid=None, uid=None)
336+ expected_relations = [
337+ call(relation_settings={'nova_authorized_keys_0': 'auth_0'},
338+ relation_id=None),
339+ call(relation_settings={'nova_authorized_keys_1': 'auth_1'},
340+ relation_id=None),
341+ call(relation_settings={'nova_authorized_keys_2': 'auth_2'},
342+ relation_id=None),
343+ call(relation_settings={'nova_known_hosts_0': 'k_h_0'},
344+ relation_id=None),
345+ call(relation_settings={'nova_known_hosts_1': 'k_h_1'},
346+ relation_id=None),
347+ call(relation_settings={'nova_known_hosts_2': 'k_h_2'},
348+ relation_id=None),
349+ call(relation_settings={'nova_known_hosts_max_index': 3},
350+ relation_id=None),
351+ call(relation_settings={'nova_authorized_keys_max_index': 3},
352+ relation_id=None)]
353+ self.assertEquals(sorted(self.relation_set.call_args_list),
354+ sorted(expected_relations))
355
356 @patch.object(hooks, '_auth_config')
357 def test_compute_joined_neutron(self, auth_config):
358
359=== modified file 'unit_tests/test_nova_cc_utils.py'
360--- unit_tests/test_nova_cc_utils.py 2014-05-02 10:06:23 +0000
361+++ unit_tests/test_nova_cc_utils.py 2014-05-27 14:01:55 +0000
362@@ -321,8 +321,8 @@
363 check_output.return_value = 'fookey'
364 host_key.return_value = 'fookey_old'
365 with patch_open() as (_open, _file):
366- utils.add_known_host('foohost')
367- rm.assert_called_with('foohost', None)
368+ utils.add_known_host('foohost', None, None)
369+ rm.assert_called_with('foohost', None, None)
370
371 @patch.object(utils, 'known_hosts')
372 @patch.object(utils, 'remove_known_host')
373@@ -355,19 +355,19 @@
374 def test_known_hosts(self, ssh_dir):
375 ssh_dir.return_value = '/tmp/foo'
376 self.assertEquals(utils.known_hosts(), '/tmp/foo/known_hosts')
377- ssh_dir.assert_called_with(None)
378+ ssh_dir.assert_called_with(None, None)
379 self.assertEquals(utils.known_hosts('bar'), '/tmp/foo/known_hosts')
380- ssh_dir.assert_called_with('bar')
381+ ssh_dir.assert_called_with('bar', None)
382
383 @patch.object(utils, 'ssh_directory_for_unit')
384 def test_authorized_keys(self, ssh_dir):
385 ssh_dir.return_value = '/tmp/foo'
386 self.assertEquals(utils.authorized_keys(), '/tmp/foo/authorized_keys')
387- ssh_dir.assert_called_with(None)
388+ ssh_dir.assert_called_with(None, None)
389 self.assertEquals(
390 utils.authorized_keys('bar'),
391 '/tmp/foo/authorized_keys')
392- ssh_dir.assert_called_with('bar')
393+ ssh_dir.assert_called_with('bar', None)
394
395 @patch.object(utils, 'known_hosts')
396 @patch('subprocess.check_call')
397@@ -461,9 +461,9 @@
398 _check_output.assert_called_with(
399 ['ssh-keygen', '-f', '/foo/known_hosts',
400 '-H', '-F', 'test'])
401- _known_hosts.assert_called_with(None)
402+ _known_hosts.assert_called_with(None, None)
403 utils.ssh_known_host_key('test', 'bar')
404- _known_hosts.assert_called_with('bar')
405+ _known_hosts.assert_called_with('bar', None)
406
407 @patch.object(utils, 'known_hosts')
408 @patch('subprocess.check_call')
409@@ -473,9 +473,9 @@
410 _check_call.assert_called_with(
411 ['ssh-keygen', '-f', '/foo/known_hosts',
412 '-R', 'test'])
413- _known_hosts.assert_called_with(None)
414+ _known_hosts.assert_called_with(None, None)
415 utils.remove_known_host('test', 'bar')
416- _known_hosts.assert_called_with('bar')
417+ _known_hosts.assert_called_with('bar', None)
418
419 @patch('subprocess.check_output')
420 def test_migrate_database(self, check_output):

Subscribers

People subscribed via source and target branches