Merge lp:~ack/landscape-client/max-pending-computers-message into lp:~landscape/landscape-client/trunk
- max-pending-computers-message
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Alberto Donato |
Approved revision: | 948 |
Merged at revision: | 935 |
Proposed branch: | lp:~ack/landscape-client/max-pending-computers-message |
Merge into: | lp:~landscape/landscape-client/trunk |
Diff against target: |
608 lines (+194/-83) 6 files modified
debian/changelog (+6/-0) landscape/__init__.py (+10/-9) landscape/broker/registration.py (+10/-9) landscape/broker/tests/test_registration.py (+61/-12) landscape/configuration.py (+45/-33) landscape/tests/test_configuration.py (+62/-20) |
To merge this branch: | bzr merge lp:~ack/landscape-client/max-pending-computers-message |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Chad Smith | Approve | ||
🤖 Landscape Builder | test results | Approve | |
Free Ekanayaka (community) | Approve | ||
Review via email:
|
Commit message
This adds support in the client to handle a new error condition when trying to register a client, when there are too many pending computers already.
To do that, the client API version has been bumped, since the server needs to know if the client knows about this new error code.
Description of the change
This adds support in the client to handle a new error condition when trying to register a client, when there are too many pending computers already.
To do that, the client API version has been bumped, since the server needs to know if the client knows about this new error code.
The server side of this change is lp:~ack/landscape/max-pending-computers-message (which needs this branch landed because of the version bump).
Testing instructions:
I tested the changes as follows:
- deploy an LDS from lp:~ack/landscape/max-pending-computers-message (make stage-landscape
- create a LXD and install landscape-client, configure it to point to the LDS.
- register the client 20 times (without accepting it) to create pending computers with: landscape-client --silent
- try to register again, get the "Invalid account name or registration key." error (current client still works as previously)
- make package from the client branch, install those packages in the LXD
- the error message will be "Maximum number of computers pending approval reached."
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
🤖 Landscape Builder (landscape-builder) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
🤖 Landscape Builder (landscape-builder) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Chad Smith (chad.smith) wrote : | # |
just a couple nits so far. Going through deploy testing now.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Chad Smith (chad.smith) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Free Ekanayaka (free.ekanayaka) wrote : | # |
+1 with a few nits
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Alberto Donato (ack) : | # |
- 947. By Alberto Donato
-
Address review comments.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
🤖 Landscape Builder (landscape-builder) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
🤖 Landscape Builder (landscape-builder) wrote : | # |
Command: TRIAL_ARGS=-j4 make check
Result: Fail
Revno: 947
Branch: lp:~ack/landscape-client/max-pending-computers-message
Jenkins: https:/
- 948. By Alberto Donato
-
Fix test.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
🤖 Landscape Builder (landscape-builder) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
🤖 Landscape Builder (landscape-builder) wrote : | # |
Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 948
Branch: lp:~ack/landscape-client/max-pending-computers-message
Jenkins: https:/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Chad Smith (chad.smith) : | # |
Preview Diff
1 | === modified file 'debian/changelog' |
2 | --- debian/changelog 2017-01-25 19:23:36 +0000 |
3 | +++ debian/changelog 2017-02-24 10:09:35 +0000 |
4 | @@ -1,3 +1,9 @@ |
5 | +landscape-client (17.01+bzr946-0ubuntu0) xenial; urgency=medium |
6 | + |
7 | + * New local test build |
8 | + |
9 | + -- Ubuntu <alberto.donato@gmail.com> Wed, 22 Feb 2017 12:04:19 +0000 |
10 | + |
11 | landscape-client (17.01-0ubuntu1) UNRELEASED; urgency=medium |
12 | |
13 | * New trunk build |
14 | |
15 | === modified file 'landscape/__init__.py' |
16 | --- landscape/__init__.py 2017-01-25 19:23:36 +0000 |
17 | +++ landscape/__init__.py 2017-02-24 10:09:35 +0000 |
18 | @@ -1,5 +1,5 @@ |
19 | -DEBIAN_REVISION = "" |
20 | -UPSTREAM_VERSION = "17.01" |
21 | +DEBIAN_REVISION = "-0ubuntu0" |
22 | +UPSTREAM_VERSION = "17.01+bzr946" |
23 | VERSION = "%s%s" % (UPSTREAM_VERSION, DEBIAN_REVISION) |
24 | |
25 | # The minimum server API version that all Landscape servers are known to speak |
26 | @@ -17,17 +17,13 @@ |
27 | # |
28 | # Changelog: |
29 | # |
30 | -# 3.3: |
31 | -# * Add new schema for the "registration" message, providing Juju information |
32 | # 3.2: |
33 | # * Add new "eucalyptus-info" and "eucalyptus-info-error" messages. |
34 | +# 3.3: |
35 | +# * Add new schema for the "registration" message, providing Juju information |
36 | # |
37 | SERVER_API = "3.3" |
38 | |
39 | -# XXX This is needed for backward compatibility in the server code importing |
40 | -# the API variable. We should eventually replace it in the server code. |
41 | -API = SERVER_API |
42 | - |
43 | # The "client-api" field of outgoing messages will be set to this value, and |
44 | # used by the server to know which schema do the message types accepted by the |
45 | # client support. Bump it when the schema of an accepted message type changes |
46 | @@ -53,4 +49,9 @@ |
47 | # 3.7: |
48 | # * Server returns 402 Payment Required if the computer has no valid license. |
49 | # |
50 | -CLIENT_API = "3.7" |
51 | +# 3.8: |
52 | +# * Handle the new "max-pending-computers" key returned by the server in the |
53 | +# "info" field of the response payload for a fail registration, in case the |
54 | +# account has too many pending computers. |
55 | +# |
56 | +CLIENT_API = "3.8" |
57 | |
58 | === modified file 'landscape/broker/registration.py' |
59 | --- landscape/broker/registration.py 2015-11-09 21:10:59 +0000 |
60 | +++ landscape/broker/registration.py 2017-02-24 10:09:35 +0000 |
61 | @@ -19,10 +19,11 @@ |
62 | from landscape.lib.versioning import is_version_higher |
63 | |
64 | |
65 | -class InvalidCredentialsError(Exception): |
66 | +class RegistrationError(Exception): |
67 | """ |
68 | - Raised when an invalid account title and/or registration key |
69 | - is used with L{RegistrationManager.register}. |
70 | + Raised when the registration failed because an invalid account title |
71 | + and/or registration key are used with RegistrationManager.register, |
72 | + or the server has too many pending computers already. |
73 | """ |
74 | |
75 | |
76 | @@ -116,7 +117,7 @@ |
77 | |
78 | @return: A L{Deferred} which will either be fired with None if |
79 | registration was successful or will fail with an |
80 | - L{InvalidCredentialsError} if not. |
81 | + RegistrationError if not. |
82 | """ |
83 | self._identity.secure_id = None |
84 | self._identity.insecure_id = None |
85 | @@ -163,7 +164,7 @@ |
86 | account_name = identity.account_name |
87 | |
88 | if not account_name: |
89 | - self._reactor.fire("registration-failed") |
90 | + self._reactor.fire("registration-failed", reason="unknown-account") |
91 | return |
92 | |
93 | tags = identity.tags |
94 | @@ -233,8 +234,8 @@ |
95 | self._reactor.fire("resynchronize-clients") |
96 | |
97 | def _handle_registration(self, message): |
98 | - if message["info"] == "unknown-account": |
99 | - self._reactor.fire("registration-failed") |
100 | + if message["info"] in ("unknown-account", "max-pending-computers"): |
101 | + self._reactor.fire("registration-failed", reason=message["info"]) |
102 | |
103 | def _handle_unknown_id(self, message): |
104 | id = self._identity |
105 | @@ -279,6 +280,6 @@ |
106 | self.deferred.callback(None) |
107 | self._cancel_calls() |
108 | |
109 | - def _failed(self): |
110 | - self.deferred.errback(InvalidCredentialsError()) |
111 | + def _failed(self, reason=None): |
112 | + self.deferred.errback(RegistrationError(reason)) |
113 | self._cancel_calls() |
114 | |
115 | === modified file 'landscape/broker/tests/test_registration.py' |
116 | --- landscape/broker/tests/test_registration.py 2016-06-16 16:17:53 +0000 |
117 | +++ landscape/broker/tests/test_registration.py 2017-02-24 10:09:35 +0000 |
118 | @@ -3,8 +3,7 @@ |
119 | import socket |
120 | import mock |
121 | |
122 | -from landscape.broker.registration import ( |
123 | - InvalidCredentialsError, Identity) |
124 | +from landscape.broker.registration import RegistrationError, Identity |
125 | from landscape.tests.helpers import LandscapeTest |
126 | from landscape.broker.tests.helpers import ( |
127 | BrokerConfigurationHelper, RegistrationHelper) |
128 | @@ -343,16 +342,29 @@ |
129 | self.assertMessages(self.mstore.get_pending_messages(), []) |
130 | handler_mock.assert_called_once_with() |
131 | |
132 | - def test_registration_failed_event(self): |
133 | + def test_registration_failed_event_unknown_account(self): |
134 | """ |
135 | The deferred returned by a registration request should fail |
136 | - with L{InvalidCredentialsError} if the server responds with a |
137 | - failure message. |
138 | + if the server responds with a failure message because credentials are |
139 | + wrong. |
140 | """ |
141 | reactor_fire_mock = self.reactor.fire = mock.Mock() |
142 | self.exchanger.handle_message( |
143 | {"type": "registration", "info": "unknown-account"}) |
144 | - reactor_fire_mock.assert_called_with("registration-failed") |
145 | + reactor_fire_mock.assert_called_with( |
146 | + "registration-failed", reason="unknown-account") |
147 | + |
148 | + def test_registration_failed_event_max_pending_computers(self): |
149 | + """ |
150 | + The deferred returned by a registration request should fail |
151 | + if the server responds with a failure message because the max number of |
152 | + pending computers have been reached. |
153 | + """ |
154 | + reactor_fire_mock = self.reactor.fire = mock.Mock() |
155 | + self.exchanger.handle_message( |
156 | + {"type": "registration", "info": "max-pending-computers"}) |
157 | + reactor_fire_mock.assert_called_with( |
158 | + "registration-failed", reason="max-pending-computers") |
159 | |
160 | def test_registration_failed_event_not_fired_when_uncertain(self): |
161 | """ |
162 | @@ -424,17 +436,22 @@ |
163 | |
164 | self.assertEqual(results, [None]) |
165 | |
166 | - def test_register_deferred_called_on_failed(self): |
167 | + def test_register_deferred_called_on_failed_unknown_account(self): |
168 | + """ |
169 | + The registration errback is called on failures when credentials are |
170 | + invalid. |
171 | + """ |
172 | # We don't want informational messages. |
173 | self.logger.setLevel(logging.WARNING) |
174 | |
175 | - calls = [0] |
176 | + calls = [] |
177 | d = self.handler.register() |
178 | |
179 | def add_call(failure): |
180 | exception = failure.value |
181 | - self.assertTrue(isinstance(exception, InvalidCredentialsError)) |
182 | - calls[0] += 1 |
183 | + self.assertTrue(isinstance(exception, RegistrationError)) |
184 | + self.assertEqual("unknown-account", str(exception)) |
185 | + calls.append(True) |
186 | |
187 | d.addErrback(add_call) |
188 | |
189 | @@ -442,13 +459,45 @@ |
190 | self.exchanger.handle_message( |
191 | {"type": "registration", "info": "unknown-account"}) |
192 | |
193 | - self.assertEqual(calls, [1]) |
194 | + self.assertEqual(calls, [True]) |
195 | |
196 | # Doing it again to ensure that the deferred isn't called twice. |
197 | self.exchanger.handle_message( |
198 | {"type": "registration", "info": "unknown-account"}) |
199 | |
200 | - self.assertEqual(calls, [1]) |
201 | + self.assertEqual(calls, [True]) |
202 | + |
203 | + self.assertEqual(self.logfile.getvalue(), "") |
204 | + |
205 | + def test_register_deferred_called_on_failed_max_pending_computers(self): |
206 | + """ |
207 | + The registration errback is called on failures when max number of |
208 | + pending computers has been reached. |
209 | + """ |
210 | + # We don't want informational messages. |
211 | + self.logger.setLevel(logging.WARNING) |
212 | + |
213 | + calls = [] |
214 | + d = self.handler.register() |
215 | + |
216 | + def add_call(failure): |
217 | + exception = failure.value |
218 | + self.assertTrue(isinstance(exception, RegistrationError)) |
219 | + self.assertEqual("max-pending-computers", str(exception)) |
220 | + calls.append(True) |
221 | + |
222 | + d.addErrback(add_call) |
223 | + |
224 | + self.exchanger.handle_message( |
225 | + {"type": "registration", "info": "max-pending-computers"}) |
226 | + |
227 | + self.assertEqual(calls, [True]) |
228 | + |
229 | + # Doing it again to ensure that the deferred isn't called twice. |
230 | + self.exchanger.handle_message( |
231 | + {"type": "registration", "info": "max-pending-computers"}) |
232 | + |
233 | + self.assertEqual(calls, [True]) |
234 | |
235 | self.assertEqual(self.logfile.getvalue(), "") |
236 | |
237 | |
238 | === modified file 'landscape/configuration.py' |
239 | --- landscape/configuration.py 2015-11-09 21:10:59 +0000 |
240 | +++ landscape/configuration.py 2017-02-24 10:09:35 +0000 |
241 | @@ -23,7 +23,7 @@ |
242 | from landscape.lib.fetch import fetch, FetchError |
243 | from landscape.lib.bootstrap import BootstrapList, BootstrapDirectory |
244 | from landscape.reactor import LandscapeReactor |
245 | -from landscape.broker.registration import InvalidCredentialsError |
246 | +from landscape.broker.registration import RegistrationError |
247 | from landscape.broker.config import BrokerConfiguration |
248 | from landscape.broker.amp import RemoteBrokerConnector |
249 | |
250 | @@ -104,7 +104,7 @@ |
251 | raise ImportOptionError( |
252 | "Couldn't read configuration from %s." % |
253 | self.import_from) |
254 | - except Exception, error: |
255 | + except Exception as error: |
256 | raise ImportOptionError(str(error)) |
257 | |
258 | # But real command line options have precedence. |
259 | @@ -124,7 +124,7 @@ |
260 | error_message = None |
261 | try: |
262 | content = fetch(url) |
263 | - except FetchError, error: |
264 | + except FetchError as error: |
265 | error_message = str(error) |
266 | if error_message is not None: |
267 | raise ImportOptionError( |
268 | @@ -324,9 +324,9 @@ |
269 | proxies now. If you don't use a proxy, leave these fields empty. |
270 | """) |
271 | |
272 | - if not "http_proxy" in options: |
273 | + if "http_proxy" not in options: |
274 | self.prompt("http_proxy", "HTTP proxy URL") |
275 | - if not "https_proxy" in options: |
276 | + if "https_proxy" not in options: |
277 | self.prompt("https_proxy", "HTTPS proxy URL") |
278 | |
279 | def query_script_plugin(self): |
280 | @@ -413,7 +413,7 @@ |
281 | self.prompt("tags", "Tags", False) |
282 | if self._get_invalid_tags(self.config.tags): |
283 | self.show_help("Tag names may only contain alphanumeric " |
284 | - "characters.") |
285 | + "characters.") |
286 | self.config.tags = None # Reset for the next prompt |
287 | else: |
288 | break |
289 | @@ -588,9 +588,10 @@ |
290 | return key_filename |
291 | |
292 | |
293 | -def failure(add_result): |
294 | +def failure(add_result, reason=None): |
295 | """Handle a failed communication by recording the kind of failure.""" |
296 | - add_result("failure") |
297 | + if reason: |
298 | + add_result(reason) |
299 | |
300 | |
301 | def exchange_failure(add_result, ssl_error=False): |
302 | @@ -601,17 +602,19 @@ |
303 | add_result("non-ssl-error") |
304 | |
305 | |
306 | -def handle_registration_errors(failure, connector): |
307 | - """Handle invalid credentials. |
308 | +def handle_registration_errors(add_result, failure, connector): |
309 | + """Handle registration errors. |
310 | |
311 | The connection to the broker succeeded but the registration itself |
312 | - failed, because of invalid credentials. We need to trap the exceptions |
313 | - so they don't stacktrace (we know what is going on), and try to cleanly |
314 | - disconnect from the broker. |
315 | + failed, because of invalid credentials or excessive pending computers. |
316 | + We need to trap the exceptions so they don't stacktrace (we know what is |
317 | + going on), and try to cleanly disconnect from the broker. |
318 | |
319 | Note: "results" contains a failure indication already (or will shortly) |
320 | since the registration-failed signal will fire.""" |
321 | - failure.trap(InvalidCredentialsError, MethodCallError) |
322 | + error = failure.trap(RegistrationError, MethodCallError) |
323 | + if error is RegistrationError: |
324 | + add_result(str(failure.value)) |
325 | connector.disconnect() |
326 | |
327 | |
328 | @@ -633,7 +636,8 @@ |
329 | "exchange-failed": partial(exchange_failure, add_result)} |
330 | deferreds = [ |
331 | remote.call_on_event(handlers), |
332 | - remote.register().addErrback(handle_registration_errors, connector)] |
333 | + remote.register().addErrback( |
334 | + partial(handle_registration_errors, add_result), connector)] |
335 | results = gather_results(deferreds) |
336 | results.addCallback(done, connector, reactor) |
337 | return results |
338 | @@ -646,8 +650,8 @@ |
339 | |
340 | |
341 | def register(config, reactor=None, connector_factory=RemoteBrokerConnector, |
342 | - got_connection=got_connection, max_retries=14, on_error=None, |
343 | - results=None): |
344 | + got_connection=got_connection, max_retries=14, on_error=None, |
345 | + results=None): |
346 | """Instruct the Landscape Broker to register the client. |
347 | |
348 | The broker will be instructed to reload its configuration and then to |
349 | @@ -694,24 +698,32 @@ |
350 | |
351 | |
352 | def report_registration_outcome(what_happened, print=print): |
353 | - """Report the registrtion interaction outcome to the user in human-readable |
354 | + """Report the registration interaction outcome to the user in human-readable |
355 | form. |
356 | """ |
357 | - if what_happened == "success": |
358 | - print("System successfully registered.") |
359 | - elif what_happened == "failure": |
360 | - print("Invalid account name or registration key.", file=sys.stderr) |
361 | - elif what_happened == "ssl-error": |
362 | - print("\nThe server's SSL information is incorrect, or fails " |
363 | - "signature verification!\n" |
364 | - "If the server is using a self-signed certificate, " |
365 | - "please ensure you supply it with the --ssl-public-key " |
366 | - "parameter.", file=sys.stderr) |
367 | - elif what_happened == "non-ssl-error": |
368 | - print("\nWe were unable to contact the server.\n" |
369 | - "Your internet connection may be down. " |
370 | - "The landscape client will continue to try and contact " |
371 | - "the server periodically.", file=sys.stderr) |
372 | + messages = { |
373 | + "success": "System successfully registered.", |
374 | + "unknown-account": "Invalid account name or registration key.", |
375 | + "max-pending-computers": ( |
376 | + "Maximum number of computers pending approval reached. ", |
377 | + "Login to your Landscape server account page to manage " |
378 | + "pending computer approvals."), |
379 | + "ssl-error": ( |
380 | + "\nThe server's SSL information is incorrect, or fails " |
381 | + "signature verification!\n" |
382 | + "If the server is using a self-signed certificate, " |
383 | + "please ensure you supply it with the --ssl-public-key " |
384 | + "parameter."), |
385 | + "non-ssl-error": ( |
386 | + "\nWe were unable to contact the server.\n" |
387 | + "Your internet connection may be down. " |
388 | + "The landscape client will continue to try and contact " |
389 | + "the server periodically.") |
390 | + } |
391 | + message = messages.get(what_happened) |
392 | + if message: |
393 | + fd = sys.stdout if what_happened == "success" else sys.stderr |
394 | + print(message, file=fd) |
395 | |
396 | |
397 | def determine_exit_code(what_happened): |
398 | |
399 | === modified file 'landscape/tests/test_configuration.py' |
400 | --- landscape/tests/test_configuration.py 2016-12-08 12:48:46 +0000 |
401 | +++ landscape/tests/test_configuration.py 2017-02-24 10:09:35 +0000 |
402 | @@ -1,5 +1,6 @@ |
403 | from __future__ import print_function |
404 | |
405 | +from functools import partial |
406 | from ConfigParser import ConfigParser |
407 | from cStringIO import StringIO |
408 | import os |
409 | @@ -9,7 +10,7 @@ |
410 | import mock |
411 | from twisted.internet.defer import succeed, fail, Deferred |
412 | |
413 | -from landscape.broker.registration import InvalidCredentialsError |
414 | +from landscape.broker.registration import RegistrationError |
415 | from landscape.broker.tests.helpers import RemoteBrokerHelper |
416 | from landscape.configuration import ( |
417 | print_text, LandscapeSetupScript, LandscapeSetupConfiguration, |
418 | @@ -44,6 +45,7 @@ |
419 | |
420 | |
421 | class SuccessTests(unittest.TestCase): |
422 | + |
423 | def test_success(self): |
424 | """The success handler records the success.""" |
425 | results = [] |
426 | @@ -52,11 +54,12 @@ |
427 | |
428 | |
429 | class FailureTests(unittest.TestCase): |
430 | + |
431 | def test_failure(self): |
432 | """The failure handler records the failure and returns non-zero.""" |
433 | results = [] |
434 | - self.assertNotEqual(0, failure(results.append)) |
435 | - self.assertEqual(["failure"], results) |
436 | + self.assertNotEqual(0, failure(results.append, "an-error")) |
437 | + self.assertEqual(["an-error"], results) |
438 | |
439 | |
440 | class ExchangeFailureTests(unittest.TestCase): |
441 | @@ -84,7 +87,7 @@ |
442 | |
443 | def test_handle_registration_errors_traps(self): |
444 | """ |
445 | - The handle_registration_errors() function traps InvalidCredentialsError |
446 | + The handle_registration_errors() function traps RegistrationError |
447 | and MethodCallError errors. |
448 | """ |
449 | class FauxFailure(object): |
450 | @@ -94,10 +97,15 @@ |
451 | faux_connector = FauxConnector() |
452 | faux_failure = FauxFailure() |
453 | |
454 | + results = [] |
455 | + add_result = results.append |
456 | + |
457 | self.assertNotEqual( |
458 | - 0, handle_registration_errors(faux_failure, faux_connector)) |
459 | + 0, |
460 | + handle_registration_errors( |
461 | + add_result, faux_failure, faux_connector)) |
462 | self.assertTrue( |
463 | - [InvalidCredentialsError, MethodCallError], |
464 | + [RegistrationError, MethodCallError], |
465 | faux_failure.trapped_exceptions) |
466 | |
467 | def test_handle_registration_errors_disconnects_cleanly(self): |
468 | @@ -112,8 +120,13 @@ |
469 | faux_connector = FauxConnector() |
470 | faux_failure = FauxFailure() |
471 | |
472 | + results = [] |
473 | + add_result = results.append |
474 | + |
475 | self.assertNotEqual( |
476 | - 0, handle_registration_errors(faux_failure, faux_connector)) |
477 | + 0, |
478 | + handle_registration_errors( |
479 | + add_result, faux_failure, faux_connector)) |
480 | self.assertTrue(faux_connector.was_disconnected) |
481 | |
482 | def test_handle_registration_errors_as_errback(self): |
483 | @@ -128,11 +141,15 @@ |
484 | |
485 | def i_raise(result): |
486 | calls.append(True) |
487 | - return InvalidCredentialsError("Bad mojo") |
488 | + return RegistrationError("Bad mojo") |
489 | + |
490 | + results = [] |
491 | + add_result = results.append |
492 | |
493 | deferred = Deferred() |
494 | deferred.addCallback(i_raise) |
495 | - deferred.addErrback(handle_registration_errors, faux_connector) |
496 | + deferred.addErrback( |
497 | + partial(handle_registration_errors, add_result), faux_connector) |
498 | deferred.callback("") # This kicks off the callback chain. |
499 | |
500 | self.assertEqual([True], calls) |
501 | @@ -1126,7 +1143,8 @@ |
502 | printed) |
503 | |
504 | @mock.patch("__builtin__.raw_input", return_value="y") |
505 | - @mock.patch("landscape.configuration.register", return_value="failure") |
506 | + @mock.patch( |
507 | + "landscape.configuration.register", return_value="unknown-account") |
508 | @mock.patch("landscape.configuration.setup") |
509 | def test_main_user_interaction_failure( |
510 | self, mock_setup, mock_register, mock_raw_input): |
511 | @@ -1177,11 +1195,13 @@ |
512 | printed) |
513 | |
514 | @mock.patch("__builtin__.raw_input") |
515 | - @mock.patch("landscape.configuration.register", return_value="failure") |
516 | + @mock.patch( |
517 | + "landscape.configuration.register", return_value="unknown-account") |
518 | @mock.patch("landscape.configuration.setup") |
519 | def test_main_user_interaction_failure_silent( |
520 | self, mock_setup, mock_register, mock_raw_input): |
521 | - """A failure result is communicated to the user even with --silent. |
522 | + """ |
523 | + A failure result is communicated to the user even with --silent. |
524 | """ |
525 | printed = [] |
526 | |
527 | @@ -1797,13 +1817,13 @@ |
528 | |
529 | def test_register_registration_error(self): |
530 | """ |
531 | - If we get a registration error, the register() function returns |
532 | - "failure". |
533 | + If we get a registration error, the register() function returns the |
534 | + error from the registration response message. |
535 | """ |
536 | self.reactor.call_later(0, self.reactor.fire, "registration-failed") |
537 | |
538 | def fail_register(): |
539 | - return fail(InvalidCredentialsError("Nope.")) |
540 | + return fail(RegistrationError("max-pending-computers")) |
541 | |
542 | self.remote.register = fail_register |
543 | |
544 | @@ -1811,7 +1831,7 @@ |
545 | result = register( |
546 | config=self.config, reactor=self.reactor, |
547 | connector_factory=connector_factory, max_retries=99) |
548 | - self.assertEqual("failure", result) |
549 | + self.assertEqual("max-pending-computers", result) |
550 | |
551 | |
552 | class FauxConnection(object): |
553 | @@ -1981,9 +2001,10 @@ |
554 | for handler in faux_remote.handlers.values()]) |
555 | # We include a single error handler to react to exchange errors. |
556 | self.assertTrue(1, len(faux_remote.register_deferred.errbacks)) |
557 | + # the handle_registration_errors is wrapped in a partial() |
558 | self.assertEqual( |
559 | 'handle_registration_errors', |
560 | - faux_remote.register_deferred.errbacks[0].__name__) |
561 | + faux_remote.register_deferred.errbacks[0].func.__name__) |
562 | |
563 | def test_register_with_on_error_and_an_error(self): |
564 | """A caller-provided on_error callable will be called if errors occur. |
565 | @@ -2070,11 +2091,30 @@ |
566 | self.assertIn("System successfully registered.", self.result) |
567 | self.assertIn(sys.stdout.name, self.output) |
568 | |
569 | - def test_failure_case(self): |
570 | - report_registration_outcome("failure", print=self.record_result) |
571 | + def test_unknown_account_case(self): |
572 | + """ |
573 | + If the unknown-account error is found, an appropriate message is |
574 | + returned. |
575 | + """ |
576 | + report_registration_outcome( |
577 | + "unknown-account", print=self.record_result) |
578 | self.assertIn("Invalid account name or registration key.", self.result) |
579 | self.assertIn(sys.stderr.name, self.output) |
580 | |
581 | + def test_max_pending_computers_case(self): |
582 | + """ |
583 | + If the max-pending-computers error is found, an appropriate message is |
584 | + returned. |
585 | + """ |
586 | + report_registration_outcome( |
587 | + "max-pending-computers", print=self.record_result) |
588 | + messages = ( |
589 | + "Maximum number of computers pending approval reached. ", |
590 | + "Login to your Landscape server account page to manage pending " |
591 | + "computer approvals.") |
592 | + self.assertIn(messages, self.result) |
593 | + self.assertIn(sys.stderr.name, self.output) |
594 | + |
595 | def test_ssl_error_case(self): |
596 | report_registration_outcome("ssl-error", print=self.record_result) |
597 | self.assertIn("\nThe server's SSL information is incorrect, or fails " |
598 | @@ -2107,7 +2147,9 @@ |
599 | When passed a failure result, the determine_exit_code function returns |
600 | 2. |
601 | """ |
602 | - failure_codes = ["failure", "ssl-error", "non-ssl-error"] |
603 | + failure_codes = [ |
604 | + "unknown-account", "max-computers-count", "ssl-error", |
605 | + "non-ssl-error"] |
606 | for code in failure_codes: |
607 | result = determine_exit_code(code) |
608 | self.assertEqual(2, result) |
Command: TRIAL_ARGS=-j4 make check /ci.lscape. net/job/ latch-test- precise/ 867/
Result: Success
Revno: 946
Branch: lp:~ack/landscape-client/max-pending-computers-message
Jenkins: https:/