Merge lp:~gocept/landscape-client/py3-broker-registration into lp:~landscape/landscape-client/trunk

Proposed by Steffen Allner
Status: Superseded
Proposed branch: lp:~gocept/landscape-client/py3-broker-registration
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 514 lines (+105/-88)
9 files modified
landscape/broker/exchange.py (+1/-1)
landscape/broker/registration.py (+1/-1)
landscape/broker/tests/test_exchange.py (+44/-44)
landscape/broker/tests/test_registration.py (+19/-8)
landscape/lib/tests/test_vm_info.py (+14/-14)
landscape/lib/vm_info.py (+18/-17)
landscape/message_schemas.py (+1/-1)
landscape/tests/helpers.py (+5/-0)
py3_ready_tests (+2/-2)
To merge this branch: bzr merge lp:~gocept/landscape-client/py3-broker-registration
Reviewer Review Type Date Requested Status
🤖 Landscape Builder test results Approve
Review via email: mp+320798@code.launchpad.net

Description of the change

As the diff for the landscape.broker module would be to large, I MP the sub-modules separately.

Here we have landscape.broker.registration, where the tests were failing because bytes were not uses as input when required by a schema, in particular "api" and "vm-info". The restriction on "vm-info" required larger modifications on landscape.lib.vm_info which returns now bytes for all cases of "vm-info".

The case with the conditional encoding to compare bytes of log messages in unfortunate. I tried a few different approaches but a drop-in replacement of cStringIO with io.StringIO would result in a lot of test failures so I would rather suggest to put that on a trello card and look at this problem separately.

To post a comment you must log in.
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 976
Branch: lp:~gocept/landscape-client/py3-broker-registration
Jenkins: https://ci.lscape.net/job/latch-test-xenial/3714/

review: Approve (test results)

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'landscape/broker/exchange.py'
2--- landscape/broker/exchange.py 2017-03-21 13:00:17 +0000
3+++ landscape/broker/exchange.py 2017-03-23 12:58:06 +0000
4@@ -715,7 +715,7 @@
5 return payload
6
7 def _hash_types(self, types):
8- accepted_types_str = ";".join(types)
9+ accepted_types_str = ";".join(types).encode("ascii")
10 return md5(accepted_types_str).digest()
11
12 def _handle_result(self, payload, result):
13
14=== modified file 'landscape/broker/registration.py'
15--- landscape/broker/registration.py 2017-02-24 09:38:26 +0000
16+++ landscape/broker/registration.py 2017-03-23 12:58:06 +0000
17@@ -196,7 +196,7 @@
18 # version 14.01 has a different format for the juju-info field,
19 # so this makes sure that the correct schema is used by the server
20 # when validating our message.
21- if self._juju_data and is_version_higher(server_api, "3.3"):
22+ if self._juju_data and is_version_higher(server_api, b"3.3"):
23 message["juju-info"] = {
24 "environment-uuid": self._juju_data["environment-uuid"],
25 "api-addresses": self._juju_data["api-addresses"],
26
27=== modified file 'landscape/broker/tests/test_exchange.py'
28--- landscape/broker/tests/test_exchange.py 2016-06-15 17:33:13 +0000
29+++ landscape/broker/tests/test_exchange.py 2017-03-23 12:58:06 +0000
30@@ -104,7 +104,7 @@
31 messages = self.transport.payloads[0]["messages"]
32 self.assertEqual(messages, [{"type": "empty",
33 "timestamp": 0,
34- "api": "3.2"}])
35+ "api": b"3.2"}])
36
37 def test_send_urgent(self):
38 """
39@@ -149,7 +149,7 @@
40 """
41 payload = self.exchanger._make_payload()
42 self.assertIn("accepted-types", payload)
43- self.assertEqual(payload["accepted-types"], md5("").digest())
44+ self.assertEqual(payload["accepted-types"], md5(b"").digest())
45
46 def test_handle_message_sets_accepted_types(self):
47 """
48@@ -170,8 +170,8 @@
49 {"type": "accepted-types", "types": ["a", "b"]})
50 self.exchanger.handle_message(
51 {"type": "accepted-types", "types": ["b", "c"]})
52- self.assertEqual(stash, [("a", True), ("b", True),
53- ("a", False), ("c", True)])
54+ self.assertCountEqual(stash, [("a", True), ("b", True),
55+ ("a", False), ("c", True)])
56
57 def test_wb_accepted_types_roundtrip(self):
58 """
59@@ -183,7 +183,7 @@
60 payload = self.exchanger._make_payload()
61 self.assertIn("accepted-types", payload)
62 self.assertEqual(payload["accepted-types"],
63- md5("ack;bar").digest())
64+ md5(b"ack;bar").digest())
65
66 def test_accepted_types_causes_urgent_if_held_messages_exist(self):
67 """
68@@ -523,33 +523,33 @@
69 payload = self.transport.payloads[-1]
70 self.assertMessages(payload["messages"], [])
71 self.assertEqual(payload.get("client-api"), CLIENT_API)
72- self.assertEqual(payload.get("server-api"), "3.2")
73- self.assertEqual(self.transport.message_api, "3.2")
74-
75- self.mstore.add({"type": "a", "api": "1.0"})
76- self.mstore.add({"type": "b", "api": "1.0"})
77- self.mstore.add({"type": "c", "api": "1.1"})
78- self.mstore.add({"type": "d", "api": "1.1"})
79-
80- self.exchanger.exchange()
81-
82- payload = self.transport.payloads[-1]
83- self.assertMessages(payload["messages"],
84- [{"type": "a", "api": "1.0"},
85- {"type": "b", "api": "1.0"}])
86- self.assertEqual(payload.get("client-api"), CLIENT_API)
87- self.assertEqual(payload.get("server-api"), "1.0")
88- self.assertEqual(self.transport.message_api, "1.0")
89-
90- self.exchanger.exchange()
91-
92- payload = self.transport.payloads[-1]
93- self.assertMessages(payload["messages"],
94- [{"type": "c", "api": "1.1"},
95- {"type": "d", "api": "1.1"}])
96- self.assertEqual(payload.get("client-api"), CLIENT_API)
97- self.assertEqual(payload.get("server-api"), "1.1")
98- self.assertEqual(self.transport.message_api, "1.1")
99+ self.assertEqual(payload.get("server-api"), b"3.2")
100+ self.assertEqual(self.transport.message_api, b"3.2")
101+
102+ self.mstore.add({"type": "a", "api": b"1.0"})
103+ self.mstore.add({"type": "b", "api": b"1.0"})
104+ self.mstore.add({"type": "c", "api": b"1.1"})
105+ self.mstore.add({"type": "d", "api": b"1.1"})
106+
107+ self.exchanger.exchange()
108+
109+ payload = self.transport.payloads[-1]
110+ self.assertMessages(payload["messages"],
111+ [{"type": "a", "api": b"1.0"},
112+ {"type": "b", "api": b"1.0"}])
113+ self.assertEqual(payload.get("client-api"), CLIENT_API)
114+ self.assertEqual(payload.get("server-api"), b"1.0")
115+ self.assertEqual(self.transport.message_api, b"1.0")
116+
117+ self.exchanger.exchange()
118+
119+ payload = self.transport.payloads[-1]
120+ self.assertMessages(payload["messages"],
121+ [{"type": "c", "api": b"1.1"},
122+ {"type": "d", "api": b"1.1"}])
123+ self.assertEqual(payload.get("client-api"), CLIENT_API)
124+ self.assertEqual(payload.get("server-api"), b"1.1")
125+ self.assertEqual(self.transport.message_api, b"1.1")
126
127 def test_exchange_token(self):
128 """
129@@ -827,27 +827,27 @@
130 """
131 self.transport.extra.pop("server-api", None)
132 self.exchanger.exchange()
133- self.assertEqual("3.2", self.mstore.get_server_api())
134+ self.assertEqual(b"3.2", self.mstore.get_server_api())
135
136 def test_wb_client_with_older_api_and_server_with_newer(self):
137 """
138 If a server notifies us that it case use a very new API, but we
139 don't know how to speak it, we keep using ours.
140 """
141- self.exchanger._api = "3.3"
142- self.transport.extra["server-api"] = "3.4"
143+ self.exchanger._api = b"3.3"
144+ self.transport.extra["server-api"] = b"3.4"
145 self.exchanger.exchange()
146- self.assertEqual("3.3", self.mstore.get_server_api())
147+ self.assertEqual(b"3.3", self.mstore.get_server_api())
148
149 def test_wb_client_with_newer_api_and_server_with_older(self):
150 """
151 If a server notifies us that it can use an API which is older
152 than the one we support, we'll just use the server API.
153 """
154- self.exchanger._api = "3.4"
155- self.transport.extra["server-api"] = "3.3"
156+ self.exchanger._api = b"3.4"
157+ self.transport.extra["server-api"] = b"3.3"
158 self.exchanger.exchange()
159- self.assertEqual("3.3", self.mstore.get_server_api())
160+ self.assertEqual(b"3.3", self.mstore.get_server_api())
161
162 def test_server_uuid_is_stored_on_message_store(self):
163 self.transport.extra["server-uuid"] = "first-uuid"
164@@ -1078,10 +1078,10 @@
165 """
166 If we get a 404, we try to donwgrade our server API version.
167 """
168- self.mstore.set_server_api("3.3")
169+ self.mstore.set_server_api(b"3.3")
170 self.transport.responses.append(HTTPCodeError(404, ""))
171 self.exchanger.exchange()
172- self.assertEqual("3.2", self.mstore.get_server_api())
173+ self.assertEqual(b"3.2", self.mstore.get_server_api())
174
175
176 class AcceptedTypesMessageExchangeTest(LandscapeTest):
177@@ -1120,7 +1120,7 @@
178 self.exchanger.register_client_accepted_message_type("type-A")
179 self.exchanger.register_client_accepted_message_type("type-B")
180 types = sorted(["type-A", "type-B"] + DEFAULT_ACCEPTED_TYPES)
181- accepted_types_digest = md5(";".join(types)).digest()
182+ accepted_types_digest = md5(";".join(types).encode("ascii")).digest()
183 self.transport.extra["client-accepted-types-hash"] = \
184 accepted_types_digest
185 self.exchanger.exchange()
186@@ -1146,7 +1146,7 @@
187 client will send a new list to the server.
188 """
189 self.exchanger.register_client_accepted_message_type("type-A")
190- types_hash = md5("type-A").digest()
191+ types_hash = md5(b"type-A").digest()
192 self.transport.extra["client-accepted-types-hash"] = types_hash
193 self.exchanger.exchange()
194 self.exchanger.register_client_accepted_message_type("type-B")
195@@ -1162,7 +1162,7 @@
196 send a new list of types.
197 """
198 self.exchanger.register_client_accepted_message_type("type-A")
199- types_hash = md5("type-A").digest()
200+ types_hash = md5(b"type-A").digest()
201 self.transport.extra["client-accepted-types-hash"] = types_hash
202 self.exchanger.exchange()
203 self.transport.extra["client-accepted-types-hash"] = "lol"
204
205=== modified file 'landscape/broker/tests/test_registration.py'
206--- landscape/broker/tests/test_registration.py 2017-02-24 09:38:26 +0000
207+++ landscape/broker/tests/test_registration.py 2017-03-23 12:58:06 +0000
208@@ -3,6 +3,8 @@
209 import socket
210 import mock
211
212+from twisted.python.compat import _PY3
213+
214 from landscape.broker.registration import RegistrationError, Identity
215 from landscape.tests.helpers import LandscapeTest
216 from landscape.broker.tests.helpers import (
217@@ -167,13 +169,13 @@
218 secure_id is set, and an exchange is about to happen,
219 queue a registration message with VM information.
220 """
221- get_vm_info_mock.return_value = "vmware"
222+ get_vm_info_mock.return_value = b"vmware"
223 self.mstore.set_accepted_types(["register"])
224 self.config.computer_title = "Computer Title"
225 self.config.account_name = "account_name"
226 self.reactor.fire("pre-exchange")
227 messages = self.mstore.get_pending_messages()
228- self.assertEqual("vmware", messages[0]["vm-info"])
229+ self.assertEqual(b"vmware", messages[0]["vm-info"])
230 self.assertEqual(self.logfile.getvalue().strip(),
231 "INFO: Queueing message to register with account "
232 "'account_name' without a password.")
233@@ -261,10 +263,19 @@
234 expected = u"prova\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}o"
235 self.assertEqual(expected, messages[0]["tags"])
236
237- self.assertEqual(self.logfile.getvalue().strip(),
238- "INFO: Queueing message to register with account "
239- "'account_name' and tags prova\xc4\xb5o "
240- "with a password.")
241+ logs = self.logfile.getvalue().strip()
242+ # XXX This is not nice, as it has the origin in a non-consistent way of
243+ # using logging. self.logfile is a cStringIO in Python 2 and
244+ # io.StringIO in Python 3. This results in reading bytes in Python 2
245+ # and unicode in Python 3, but a drop-in replacement of cStringIO with
246+ # io.StringIO in Python 2 is not working. However, we compare bytes
247+ # here, to circumvent that problem.
248+ if _PY3:
249+ logs = logs.encode("utf-8")
250+ self.assertEqual(logs,
251+ b"INFO: Queueing message to register with account "
252+ b"'account_name' and tags prova\xc4\xb5o "
253+ b"with a password.")
254
255 def test_queue_message_on_exchange_with_access_group(self):
256 """
257@@ -541,7 +552,7 @@
258 the registration message.
259 """
260 self.mstore.set_accepted_types(["register"])
261- self.mstore.set_server_api("3.3")
262+ self.mstore.set_server_api(b"3.3")
263 self.config.account_name = "account_name"
264 self.reactor.fire("run")
265 self.reactor.fire("pre-exchange")
266@@ -559,7 +570,7 @@
267 isn't included in the message.
268 """
269 self.mstore.set_accepted_types(["register"])
270- self.mstore.set_server_api("3.2")
271+ self.mstore.set_server_api(b"3.2")
272 self.config.account_name = "account_name"
273 self.reactor.fire("run")
274 self.reactor.fire("pre-exchange")
275
276=== modified file 'landscape/lib/tests/test_vm_info.py'
277--- landscape/lib/tests/test_vm_info.py 2016-09-23 12:51:20 +0000
278+++ landscape/lib/tests/test_vm_info.py 2017-03-23 12:58:06 +0000
279@@ -25,7 +25,7 @@
280 """
281 L{get_vm_info} should be empty when there's no virtualisation.
282 """
283- self.assertEqual(u"", get_vm_info(root_path=self.root_path))
284+ self.assertEqual(b"", get_vm_info(root_path=self.root_path))
285
286 def test_get_vm_info_is_openvz_when_proc_vz_exists(self):
287 """
288@@ -34,7 +34,7 @@
289 proc_vz_path = os.path.join(self.proc_path, "vz")
290 self.makeFile(path=proc_vz_path, content="foo")
291
292- self.assertEqual("openvz", get_vm_info(root_path=self.root_path))
293+ self.assertEqual(b"openvz", get_vm_info(root_path=self.root_path))
294
295 def test_get_vm_info_is_xen_when_sys_bus_xen_is_non_empty(self):
296 """
297@@ -46,7 +46,7 @@
298 foo_devices_path = os.path.join(devices_xen_path, "foo")
299 self.makeFile(path=foo_devices_path, content="bar")
300
301- self.assertEqual("xen", get_vm_info(root_path=self.root_path))
302+ self.assertEqual(b"xen", get_vm_info(root_path=self.root_path))
303
304 def test_get_vm_info_is_empty_without_xen_devices(self):
305 """
306@@ -56,7 +56,7 @@
307 devices_xen_path = os.path.join(self.sys_path, "bus/xen/devices")
308 self.makeDir(path=devices_xen_path)
309
310- self.assertEqual("", get_vm_info(root_path=self.root_path))
311+ self.assertEqual(b"", get_vm_info(root_path=self.root_path))
312
313 def test_get_vm_info_with_bochs_sys_vendor(self):
314 """
315@@ -64,7 +64,7 @@
316 Bochs.
317 """
318 self.make_sys_vendor("Bochs")
319- self.assertEqual("kvm", get_vm_info(root_path=self.root_path))
320+ self.assertEqual(b"kvm", get_vm_info(root_path=self.root_path))
321
322 def test_get_vm_info_with_openstack_sys_vendor(self):
323 """
324@@ -72,7 +72,7 @@
325 Openstack.
326 """
327 self.make_sys_vendor("OpenStack Foundation")
328- self.assertEqual("kvm", get_vm_info(root_path=self.root_path))
329+ self.assertEqual(b"kvm", get_vm_info(root_path=self.root_path))
330
331 def test_get_vm_info_with_qemu_sys_vendor(self):
332 """
333@@ -80,7 +80,7 @@
334 QEMU.
335 """
336 self.make_sys_vendor("QEMU")
337- self.assertEqual("kvm", get_vm_info(root_path=self.root_path))
338+ self.assertEqual(b"kvm", get_vm_info(root_path=self.root_path))
339
340 def test_get_vm_info_with_vmware_sys_vendor(self):
341 """
342@@ -88,7 +88,7 @@
343 VMware Inc.
344 """
345 self.make_sys_vendor("VMware, Inc.")
346- self.assertEqual("vmware", get_vm_info(root_path=self.root_path))
347+ self.assertEqual(b"vmware", get_vm_info(root_path=self.root_path))
348
349 def test_get_vm_info_with_virtualbox_sys_vendor(self):
350 """
351@@ -96,28 +96,28 @@
352 is innotek. GmbH.
353 """
354 self.make_sys_vendor("innotek GmbH")
355- self.assertEqual("virtualbox", get_vm_info(root_path=self.root_path))
356+ self.assertEqual(b"virtualbox", get_vm_info(root_path=self.root_path))
357
358 def test_get_vm_info_with_microsoft_sys_vendor(self):
359 """
360 L{get_vm_info} returns "hyperv" if the sys_vendor is Microsoft.
361 """
362 self.make_sys_vendor("Microsoft Corporation")
363- self.assertEqual("hyperv", get_vm_info(root_path=self.root_path))
364+ self.assertEqual(b"hyperv", get_vm_info(root_path=self.root_path))
365
366 def test_get_vm_info_with_google_sys_vendor(self):
367 """
368 L{get_vm_info} returns "gce" if the sys_vendor is Google.
369 """
370 self.make_sys_vendor("Google")
371- self.assertEqual("gce", get_vm_info(root_path=self.root_path))
372+ self.assertEqual(b"gce", get_vm_info(root_path=self.root_path))
373
374 def test_get_vm_info_matches_insensitive(self):
375 """
376 L{get_vm_info} matches the vendor string in a case-insentive way.
377 """
378 self.make_sys_vendor("openstack foundation")
379- self.assertEqual("kvm", get_vm_info(root_path=self.root_path))
380+ self.assertEqual(b"kvm", get_vm_info(root_path=self.root_path))
381
382 def test_get_vm_info_with_kvm_on_other_architecture(self):
383 """
384@@ -130,7 +130,7 @@
385 "model : Some CPU (emulated by qemu)\n"
386 "machine : Some Machine (emulated by qemu)\n")
387 self.makeFile(path=cpuinfo_path, content=cpuinfo)
388- self.assertEqual("kvm", get_vm_info(root_path=self.root_path))
389+ self.assertEqual(b"kvm", get_vm_info(root_path=self.root_path))
390
391 def test_get_vm_info_with_other_vendor(self):
392 """
393@@ -138,7 +138,7 @@
394 unknown.
395 """
396 self.make_sys_vendor("Some other vendor")
397- self.assertEqual("", get_vm_info(root_path=self.root_path))
398+ self.assertEqual(b"", get_vm_info(root_path=self.root_path))
399
400
401 class GetContainerInfoTest(LandscapeTest):
402
403=== modified file 'landscape/lib/vm_info.py'
404--- landscape/lib/vm_info.py 2017-03-13 15:38:09 +0000
405+++ landscape/lib/vm_info.py 2017-03-23 12:58:06 +0000
406@@ -8,16 +8,16 @@
407
408 def get_vm_info(root_path="/"):
409 """
410- Return a string with the virtualization type if it's known, an empty string
411- otherwise.
412+ Return a bytestring with the virtualization type if it's known, an empty
413+ bytestring otherwise.
414
415- It loops through some possible configurations and return a string with
416+ It loops through some possible configurations and return a bytestring with
417 the name of the technology being used or None if there's no match
418 """
419 if _is_vm_openvz(root_path):
420- return "openvz"
421+ return b"openvz"
422 if _is_vm_xen(root_path):
423- return "xen"
424+ return b"xen"
425
426 sys_vendor_path = os.path.join(root_path, "sys/class/dmi/id/sys_vendor")
427 if os.path.exists(sys_vendor_path):
428@@ -51,22 +51,23 @@
429
430
431 def _get_vm_by_vendor(sys_vendor_path):
432- """Return the VM type string (possibly empty) based on the vendor."""
433+ """Return the VM type byte string (possibly empty) based on the vendor."""
434 vendor = read_text_file(sys_vendor_path).lower()
435 # Use lower-key string for vendors, since we do case-insentive match.
436+ # We need bytes here as required by the message schema.
437 content_vendors_map = (
438- ("bochs", "kvm"),
439- ("google", "gce"),
440- ("innotek", "virtualbox"),
441- ("microsoft", "hyperv"),
442- ("openstack", "kvm"),
443- ("qemu", "kvm"),
444- ("vmware", "vmware"))
445+ ("bochs", b"kvm"),
446+ ("google", b"gce"),
447+ ("innotek", b"virtualbox"),
448+ ("microsoft", b"hyperv"),
449+ ("openstack", b"kvm"),
450+ ("qemu", b"kvm"),
451+ ("vmware", b"vmware"))
452 for name, vm_type in content_vendors_map:
453 if name in vendor:
454 return vm_type
455
456- return ""
457+ return b""
458
459
460 def _get_vm_legacy(root_path):
461@@ -74,9 +75,9 @@
462 try:
463 cpuinfo = read_text_file(os.path.join(root_path, "proc/cpuinfo"))
464 except (IOError, OSError):
465- return ""
466+ return b""
467
468 if "qemu" in cpuinfo:
469- return "kvm"
470+ return b"kvm"
471
472- return ""
473+ return b""
474
475=== modified file 'landscape/message_schemas.py'
476--- landscape/message_schemas.py 2017-03-17 10:08:19 +0000
477+++ landscape/message_schemas.py 2017-03-23 12:58:06 +0000
478@@ -199,7 +199,7 @@
479 "api-addresses": List(Unicode()),
480 "machine-id": Unicode()}),
481 "access_group": Unicode()},
482- api="3.3",
483+ api=b"3.3",
484 optional=["registration_password", "hostname", "tags", "vm-info",
485 "container-info", "access_group", "juju-info"])
486
487
488=== modified file 'landscape/tests/helpers.py'
489--- landscape/tests/helpers.py 2017-03-21 17:57:12 +0000
490+++ landscape/tests/helpers.py 2017-03-23 12:58:06 +0000
491@@ -148,6 +148,11 @@
492 else:
493 return result[0]
494
495+ if not _PY3:
496+ def assertCountEqual(self, *args, **kwargs):
497+ """An alias for C{TestCase.assertItemsEqual} for py2/3 compat."""
498+ self.assertItemsEqual(*args, **kwargs)
499+
500 def assertNoResult(self, deferred):
501 """See C{twisted.trial._synctest._Assertions.assertNoResult}.
502
503
504=== modified file 'py3_ready_tests'
505--- py3_ready_tests 2017-03-23 07:28:20 +0000
506+++ py3_ready_tests 2017-03-23 12:58:06 +0000
507@@ -7,6 +7,6 @@
508
509
510
511-
512-
513+landscape.broker.tests.test_registration
514+landscape.broker.tests.test_exchange
515

Subscribers

People subscribed via source and target branches

to all changes: