Merge lp:~bigkevmcd/landscape-client/bootstrap-with-userdata-ca into lp:~landscape/landscape-client/trunk

Proposed by Kevin McDermott on 2010-07-19
Status: Merged
Approved by: Sidnei da Silva on 2010-07-19
Approved revision: 276
Merged at revision: 273
Proposed branch: lp:~bigkevmcd/landscape-client/bootstrap-with-userdata-ca
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 530 lines (+148/-60)
5 files modified
landscape/broker/registration.py (+14/-4)
landscape/broker/tests/helpers.py (+3/-1)
landscape/broker/tests/test_registration.py (+82/-47)
landscape/configuration.py (+25/-7)
landscape/tests/test_configuration.py (+24/-1)
To merge this branch: bzr merge lp:~bigkevmcd/landscape-client/bootstrap-with-userdata-ca
Reviewer Review Type Date Requested Status
Sidnei da Silva (community) 2010-07-19 Approve on 2010-07-19
Thomas Herve (community) 2010-07-19 Approve on 2010-07-19
Review via email: mp+30247@code.launchpad.net

Description of the change

This adds support for writing out the ssl public key, and setting up the transport to use it when doing cloud registrations.

To post a comment you must log in.
Thomas Herve (therve) wrote :

[1] Pep8

landscape/tests/test_configuration.py:1492:1: E302 expected 2 blank lines, found 3
landscape/tests/test_configuration.py:1522:9: E301 expected 1 blank line, found 2
landscape/tests/test_configuration.py:1572:9: E301 expected 1 blank line, found 2
landscape/tests/test_configuration.py:1820:1: W391 blank line at end of file
landscape/configuration.py:515:5: E301 expected 1 blank line, found 2

[2] Pyflakes
landscape/broker/registration.py:1: 'os' imported but unused
landscape/broker/tests/test_registration.py:1: 'os' imported but unused

[3]
+ self.assertEqual(new_config.url, "https://example.com/message-system")
+ self.assertEqual(new_config.ping_url, "http://example.com/ping")

+ self.assertEquals(open(expected_filename, "r").read(),
+ "1234567890")

Can you invert those checks please (and use assertEquals for the last one)?

[4] test_store_public_key_data is missing a docstring.

[5] s/ssl-certificate-ca/ssl-ca-certificate

+1!

review: Approve
276. By Kevin McDermott on 2010-07-19

Address Thomas' review comments.

Sidnei da Silva (sidnei) wrote :

[1] PEP8 still there:

landscape/configuration.py:515:5: E301 expected 1 blank line, found 2
landscape/tests/test_configuration.py:1492:1: E302 expected 2 blank lines, found 3
landscape/tests/test_configuration.py:1522:9: E301 expected 1 blank line, found 2
landscape/tests/test_configuration.py:1573:9: E301 expected 1 blank line, found 2

[2] The lines following the change from assertEquals to assertEqual are now wrongly indented (there's one space too much).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'landscape/broker/registration.py'
2--- landscape/broker/registration.py 2010-05-11 09:39:02 +0000
3+++ landscape/broker/registration.py 2010-07-19 13:17:40 +0000
4@@ -1,4 +1,3 @@
5-
6 import time
7 import logging
8 import socket
9@@ -189,6 +188,14 @@
10 self._pinger.set_url(ping_url)
11 self._config.url = exchange_url
12 self._config.ping_url = ping_url
13+ if "ssl-ca-certificate" in instance_data:
14+ from landscape.configuration import \
15+ store_public_key_data
16+ public_key_file = store_public_key_data(
17+ self._config.get_config_filename(),
18+ instance_data["ssl-ca-certificate"])
19+ self._config.ssl_public_key = public_key_file
20+ self._exchange._transport.pubkey = public_key_file
21 self._config.write()
22
23 def log_error(error):
24@@ -355,9 +362,12 @@
25 logging.debug("user-data %r doesn't have OTP for launch index %d"
26 % (user_data, launch_index))
27 return
28- return {"otp": user_data["otps"][launch_index],
29- "exchange-url": user_data["exchange-url"],
30- "ping-url": user_data["ping-url"]}
31+ instance_data = {"otp": user_data["otps"][launch_index],
32+ "exchange-url": user_data["exchange-url"],
33+ "ping-url": user_data["ping-url"]}
34+ if "ssl-ca-certificate" in user_data:
35+ instance_data["ssl-ca-certificate"] = user_data["ssl-ca-certificate"]
36+ return instance_data
37
38
39 def _wait_for_network():
40
41=== modified file 'landscape/broker/tests/helpers.py'
42--- landscape/broker/tests/helpers.py 2010-05-03 13:08:02 +0000
43+++ landscape/broker/tests/helpers.py 2010-07-19 13:17:40 +0000
44@@ -29,7 +29,9 @@
45 def set_up(self, test_case):
46 data_path = test_case.makeDir()
47 log_dir = test_case.makeDir()
48- test_case.config_filename = test_case.makeFile(
49+ test_case.config_filename = os.path.join(test_case.makeDir(),
50+ "client.conf")
51+ open(test_case.config_filename, "w").write(
52 "[client]\n"
53 "url = http://localhost:91919\n"
54 "computer_title = Some Computer\n"
55
56=== modified file 'landscape/broker/tests/test_registration.py'
57--- landscape/broker/tests/test_registration.py 2010-05-05 07:54:14 +0000
58+++ landscape/broker/tests/test_registration.py 2010-07-19 13:17:40 +0000
59@@ -15,6 +15,7 @@
60 from landscape.lib.bpickle import dumps
61 from landscape.lib.fetch import HTTPCodeError, FetchError
62 from landscape.lib.persist import Persist
63+from landscape.configuration import print_text
64
65
66 class IdentityTest(LandscapeTest):
67@@ -28,21 +29,21 @@
68
69 def check_persist_property(self, attr, persist_name):
70 value = "VALUE"
71- self.assertEquals(getattr(self.identity, attr), None,
72+ self.assertEqual(getattr(self.identity, attr), None,
73 "%r attribute should default to None, not %r" %
74 (attr, getattr(self.identity, attr)))
75 setattr(self.identity, attr, value)
76- self.assertEquals(getattr(self.identity, attr), value,
77+ self.assertEqual(getattr(self.identity, attr), value,
78 "%r attribute should be %r, not %r" %
79 (attr, value, getattr(self.identity, attr)))
80- self.assertEquals(
81+ self.assertEqual(
82 self.persist.get(persist_name), value,
83 "%r not set to %r in persist" % (persist_name, value))
84
85 def check_config_property(self, attr):
86 value = "VALUE"
87 setattr(self.config, attr, value)
88- self.assertEquals(getattr(self.identity, attr), value,
89+ self.assertEqual(getattr(self.identity, attr), value,
90 "%r attribute should be %r, not %r" %
91 (attr, value, getattr(self.identity, attr)))
92
93@@ -88,8 +89,8 @@
94 """
95 self.exchanger.handle_message(
96 {"type": "set-id", "id": "abc", "insecure-id": "def"})
97- self.assertEquals(self.identity.secure_id, "abc")
98- self.assertEquals(self.identity.insecure_id, "def")
99+ self.assertEqual(self.identity.secure_id, "abc")
100+ self.assertEqual(self.identity.insecure_id, "def")
101
102 def test_registration_done_event(self):
103 """
104@@ -107,8 +108,8 @@
105 self.identity.insecure_id = "old_id"
106 self.mstore.set_accepted_types(["register"])
107 self.exchanger.handle_message({"type": "unknown-id"})
108- self.assertEquals(self.identity.secure_id, None)
109- self.assertEquals(self.identity.insecure_id, None)
110+ self.assertEqual(self.identity.secure_id, None)
111+ self.assertEqual(self.identity.insecure_id, None)
112
113 def test_should_register(self):
114 self.mstore.set_accepted_types(["register"])
115@@ -153,7 +154,7 @@
116 "registration_password": None,
117 "hostname": "ooga.local",
118 "tags": None}])
119- self.assertEquals(self.logfile.getvalue().strip(),
120+ self.assertEqual(self.logfile.getvalue().strip(),
121 "INFO: Queueing message to register with account "
122 "'account_name' without a password.")
123
124@@ -171,7 +172,7 @@
125 "registration_password": "SEKRET",
126 "hostname": "ooga.local",
127 "tags": None}])
128- self.assertEquals(self.logfile.getvalue().strip(),
129+ self.assertEqual(self.logfile.getvalue().strip(),
130 "INFO: Queueing message to register with account "
131 "'account_name' with a password.")
132
133@@ -193,7 +194,7 @@
134 "registration_password": "SEKRET",
135 "hostname": "ooga.local",
136 "tags": u"computer,tag"}])
137- self.assertEquals(self.logfile.getvalue().strip(),
138+ self.assertEqual(self.logfile.getvalue().strip(),
139 "INFO: Queueing message to register with account "
140 "'account_name' and tags computer,tag "
141 "with a password.")
142@@ -218,7 +219,7 @@
143 "registration_password": "SEKRET",
144 "hostname": "ooga.local",
145 "tags": None}])
146- self.assertEquals(self.logfile.getvalue().strip(),
147+ self.assertEqual(self.logfile.getvalue().strip(),
148 "ERROR: Invalid tags provided for cloud "
149 "registration.\n "
150 "INFO: Queueing message to register with account "
151@@ -243,7 +244,7 @@
152 "registration_password": "SEKRET",
153 "hostname": "ooga.local",
154 "tags": u"prova\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}o"}])
155- self.assertEquals(self.logfile.getvalue().strip(),
156+ self.assertEqual(self.logfile.getvalue().strip(),
157 "INFO: Queueing message to register with account "
158 "'account_name' and tags prova\xc4\xb5o "
159 "with a password.")
160@@ -260,8 +261,8 @@
161 self.config.account_name = "account_name"
162 self.reactor.fire("pre-exchange")
163 messages = self.mstore.get_pending_messages()
164- self.assertEquals(len(messages), 1)
165- self.assertEquals(messages[0]["type"], "register")
166+ self.assertEqual(len(messages), 1)
167+ self.assertEqual(messages[0]["type"], "register")
168
169 def test_no_message_when_should_register_is_false(self):
170 """If we already have a secure id, do not queue a register message.
171@@ -314,8 +315,8 @@
172 self.identity.secure_id = "foo"
173 self.identity.insecure_id = "bar"
174 self.handler.register()
175- self.assertEquals(self.identity.secure_id, None)
176- self.assertEquals(self.identity.insecure_id, None)
177+ self.assertEqual(self.identity.secure_id, None)
178+ self.assertEqual(self.identity.insecure_id, None)
179
180 def test_register_calls_urgent_exchange(self):
181 exchanger_mock = self.mocker.patch(self.exchanger)
182@@ -332,7 +333,7 @@
183 d = self.handler.register()
184
185 def add_call(result):
186- self.assertEquals(result, None)
187+ self.assertEqual(result, None)
188 calls[0] += 1
189
190 d.addCallback(add_call)
191@@ -341,15 +342,15 @@
192 self.exchanger.handle_message(
193 {"type": "set-id", "id": "abc", "insecure-id": "def"})
194
195- self.assertEquals(calls, [1])
196+ self.assertEqual(calls, [1])
197
198 # Doing it again to ensure that the deferred isn't called twice.
199 self.exchanger.handle_message(
200 {"type": "set-id", "id": "abc", "insecure-id": "def"})
201
202- self.assertEquals(calls, [1])
203+ self.assertEqual(calls, [1])
204
205- self.assertEquals(self.logfile.getvalue(), "")
206+ self.assertEqual(self.logfile.getvalue(), "")
207
208 def test_resynchronize_fired_when_registration_done(self):
209
210@@ -366,7 +367,7 @@
211 self.exchanger.handle_message(
212 {"type": "set-id", "id": "abc", "insecure-id": "def"})
213
214- self.assertEquals(results, [True])
215+ self.assertEqual(results, [True])
216
217 def test_register_deferred_called_on_failed(self):
218 # We don't want informational messages.
219@@ -386,15 +387,15 @@
220 self.exchanger.handle_message(
221 {"type": "registration", "info": "unknown-account"})
222
223- self.assertEquals(calls, [1])
224+ self.assertEqual(calls, [1])
225
226 # Doing it again to ensure that the deferred isn't called twice.
227 self.exchanger.handle_message(
228 {"type": "registration", "info": "unknown-account"})
229
230- self.assertEquals(calls, [1])
231+ self.assertEqual(calls, [1])
232
233- self.assertEquals(self.logfile.getvalue(), "")
234+ self.assertEqual(self.logfile.getvalue(), "")
235
236 def test_exchange_done_calls_exchange(self):
237 exchanger_mock = self.mocker.patch(self.exchanger)
238@@ -453,19 +454,24 @@
239
240 def get_user_data(self, otps=None,
241 exchange_url="https://example.com/message-system",
242- ping_url="http://example.com/ping"):
243+ ping_url="http://example.com/ping",
244+ ssl_ca_certificate=None):
245 if otps is None:
246 otps = ["otp1"]
247- return {"otps": otps, "exchange-url": exchange_url,
248- "ping-url": ping_url}
249+ user_data = {"otps": otps, "exchange-url": exchange_url,
250+ "ping-url": ping_url}
251+ if ssl_ca_certificate is not None:
252+ user_data["ssl-ca-certificate"] = ssl_ca_certificate
253+ return user_data
254
255 def prepare_query_results(
256 self, user_data=None, instance_key="key1", launch_index=0,
257 local_hostname="ooga.local", public_hostname="ooga.amazon.com",
258 reservation_key=u"res1", ramdisk_key=u"ram1", kernel_key=u"kernel1",
259- image_key=u"image1"):
260+ image_key=u"image1", ssl_ca_certificate=None):
261 if user_data is None:
262- user_data = self.get_user_data()
263+ user_data = self.get_user_data(
264+ ssl_ca_certificate=ssl_ca_certificate)
265 if not isinstance(user_data, Exception):
266 user_data = dumps(user_data)
267 api_base = "http://169.254.169.254/latest"
268@@ -537,21 +543,21 @@
269 self.reactor.fire("run")
270
271 # And the metadata returned determines the URLs that are used
272- self.assertEquals(self.transport.get_url(),
273+ self.assertEqual(self.transport.get_url(),
274 "https://example.com/message-system")
275- self.assertEquals(self.pinger.get_url(),
276+ self.assertEqual(self.pinger.get_url(),
277 "http://example.com/ping")
278 # Let's make sure those values were written back to the config file
279 new_config = BrokerConfiguration()
280 new_config.load_configuration_file(self.config_filename)
281- self.assertEquals(new_config.url, "https://example.com/message-system")
282- self.assertEquals(new_config.ping_url, "http://example.com/ping")
283+ self.assertEqual(new_config.url, "https://example.com/message-system")
284+ self.assertEqual(new_config.ping_url, "http://example.com/ping")
285
286 # Okay! Exchange should cause the registration to happen.
287 self.exchanger.exchange()
288 # This *should* be asynchronous, but I think a billion tests are
289 # written like this
290- self.assertEquals(len(self.transport.payloads), 1)
291+ self.assertEqual(len(self.transport.payloads), 1)
292 self.assertMessages(
293 self.transport.payloads[0]["messages"],
294 [self.get_expected_cloud_message(tags=u"server,london")])
295@@ -569,10 +575,10 @@
296 # metadata is fetched and stored at reactor startup:
297 self.reactor.fire("run")
298 self.exchanger.exchange()
299- self.assertEquals(len(self.transport.payloads), 1)
300+ self.assertEqual(len(self.transport.payloads), 1)
301 self.assertMessages(self.transport.payloads[0]["messages"],
302 [self.get_expected_cloud_message(tags=None)])
303- self.assertEquals(self.logfile.getvalue().strip(),
304+ self.assertEqual(self.logfile.getvalue().strip(),
305 "ERROR: Invalid tags provided for cloud "
306 "registration.\n "
307 "INFO: Queueing message to register with OTP\n "
308@@ -580,6 +586,35 @@
309 "https://example.com/message-system.\n "
310 "INFO: Message exchange completed in 0.00s.")
311
312+ def test_cloud_registration_with_ssl_ca_certificate(self):
313+ """
314+ If we have an SSL certificate CA included in the user-data, this should
315+ be written out, and the configuration updated to reflect this.
316+ """
317+ expected_filename = "%s.ssl_public_key" % self.config_filename
318+ print_text_mock = self.mocker.replace(print_text)
319+ print_text_mock("Writing SSL CA certificate to %s..." %
320+ expected_filename)
321+ self.mocker.replay()
322+ self.prepare_query_results(ssl_ca_certificate=u"1234567890")
323+ self.prepare_cloud_registration(tags=u"server,london")
324+ # metadata is fetched and stored at reactor startup:
325+ self.reactor.fire("run")
326+ # And the metadata returned determines the URLs that are used
327+ self.assertEqual("https://example.com/message-system",
328+ self.transport.get_url())
329+ self.assertEqual(expected_filename, self.transport.pubkey)
330+ self.assertEqual("http://example.com/ping",
331+ self.pinger.get_url())
332+ # Let's make sure those values were written back to the config file
333+ new_config = BrokerConfiguration()
334+ new_config.load_configuration_file(self.config_filename)
335+ self.assertEqual("https://example.com/message-system", new_config.url)
336+ self.assertEqual("http://example.com/ping", new_config.ping_url)
337+ self.assertEqual(expected_filename, new_config.ssl_public_key)
338+ self.assertEqual(open(expected_filename, "r").read(),
339+ "1234567890")
340+
341 def test_wrong_user_data(self):
342 self.prepare_query_results(user_data="other stuff, not a bpickle")
343 self.prepare_cloud_registration()
344@@ -642,14 +677,14 @@
345 self.reactor.fire("run")
346 self.exchanger.exchange()
347
348- self.assertEquals(len(self.transport.payloads), 1)
349+ self.assertEqual(len(self.transport.payloads), 1)
350 self.assertMessages(self.transport.payloads[0]["messages"],
351 [self.get_expected_cloud_message(
352 otp=None,
353 account_name=u"onward",
354 registration_password=u"password",
355 tags=u"london,server")])
356- self.assertEquals(self.logfile.getvalue().strip(),
357+ self.assertEqual(self.logfile.getvalue().strip(),
358 "INFO: Queueing message to register with account u'onward' and "
359 "tags london,server as an EC2 instance.\n "
360 "INFO: Starting message exchange with http://localhost:91919.\n "
361@@ -673,8 +708,8 @@
362 self.reactor.fire("pre-exchange")
363
364 messages = self.mstore.get_pending_messages()
365- self.assertEquals(len(messages), 1)
366- self.assertEquals(messages[0]["type"], "register-cloud-vm")
367+ self.assertEqual(len(messages), 1)
368+ self.assertEqual(messages[0]["type"], "register-cloud-vm")
369
370 def test_cloud_registration_fetch_errors(self):
371 """
372@@ -703,7 +738,7 @@
373 self.log_helper.ignore_errors("Got error while fetching meta-data")
374 self.reactor.fire("run")
375 self.exchanger.exchange()
376- self.assertEquals(failed, [True])
377+ self.assertEqual(failed, [True])
378 self.assertIn('error: (7, "couldn\'t connect to host")',
379 self.logfile.getvalue())
380
381@@ -721,7 +756,7 @@
382 self.exchanger.exchange()
383 self.assertIn("HTTPCodeError: Server returned HTTP code 404",
384 self.logfile.getvalue())
385- self.assertEquals(len(self.transport.payloads), 1)
386+ self.assertEqual(len(self.transport.payloads), 1)
387 self.assertMessages(self.transport.payloads[0]["messages"],
388 [self.get_expected_cloud_message(
389 otp=None,
390@@ -741,7 +776,7 @@
391 self.exchanger.exchange()
392 self.assertIn("HTTPCodeError: Server returned HTTP code 404",
393 self.logfile.getvalue())
394- self.assertEquals(len(self.transport.payloads), 1)
395+ self.assertEqual(len(self.transport.payloads), 1)
396 self.assertMessages(self.transport.payloads[0]["messages"],
397 [self.get_expected_cloud_message(
398 ramdisk_key=None)])
399@@ -762,7 +797,7 @@
400 self.exchanger.exchange()
401 self.assertIn("HTTPCodeError: Server returned HTTP code 404",
402 self.logfile.getvalue())
403- self.assertEquals(len(self.transport.payloads), 1)
404+ self.assertEqual(len(self.transport.payloads), 1)
405 self.assertMessages(self.transport.payloads[0]["messages"],
406 [{"type": "register",
407 "computer_title": u"whatever",
408@@ -800,7 +835,7 @@
409
410 self.reactor.fire("run")
411 self.exchanger.exchange()
412- self.assertEquals(len(self.transport.payloads), 1)
413+ self.assertEqual(len(self.transport.payloads), 1)
414 self.assertMessages(self.transport.payloads[0]["messages"],
415 [self.get_expected_cloud_message(otp=otp,
416 launch_index=1)])
417@@ -864,7 +899,7 @@
418 self.mocker.replay()
419
420 self.assertTrue(is_cloud_managed(self.fake_fetch))
421- self.assertEquals(
422+ self.assertEqual(
423 self.urls,
424 [(EC2_API + "/user-data", 5),
425 (EC2_API + "/meta-data/ami-launch-index", 5)])
426
427=== modified file 'landscape/configuration.py'
428--- landscape/configuration.py 2010-05-10 15:01:10 +0000
429+++ landscape/configuration.py 2010-07-19 13:17:40 +0000
430@@ -454,14 +454,12 @@
431 script = LandscapeSetupScript(config)
432 script.run()
433
434+ # WARNING: ssl_public_key is misnamed, it's not the key of the certificate,
435+ # but the actual certificate itself.
436 if config.ssl_public_key and config.ssl_public_key.startswith("base64:"):
437- key_filename = config.get_config_filename() + ".ssl_public_key"
438- print_text("Writing SSL CA certificate to %s..." % key_filename)
439- decoded_key = base64.decodestring(config.ssl_public_key[7:])
440- key_file = open(key_filename, "w")
441- key_file.write(decoded_key)
442- key_file.close()
443- config.ssl_public_key = key_filename
444+ decoded_cert = base64.decodestring(config.ssl_public_key[7:])
445+ config.ssl_public_key = store_public_key_data(
446+ config.get_config_filename(), decoded_cert)
447
448 config.write()
449 # Restart the client to ensure that it's using the new configuration.
450@@ -478,6 +476,26 @@
451 sys.exit(exit_code)
452
453
454+def store_public_key_data(config_filename, certificate_data):
455+ """
456+ Write out the data from the SSL certificate provided to us, either from a
457+ bootstrap.conf file, or from EC2-style user-data.
458+
459+ @param config_filename: This filename is used to generate the filename
460+ we write the certificate data to.
461+ @param certificate_data: a string of data that represents the contents of
462+ the file to be written.
463+ @return the L{BrokerConfiguration} object that was passed in, updated to
464+ reflect the path of the ssl_public_key file.
465+ """
466+ key_filename = config_filename + ".ssl_public_key"
467+ print_text("Writing SSL CA certificate to %s..." % key_filename)
468+ key_file = open(key_filename, "w")
469+ key_file.write(certificate_data)
470+ key_file.close()
471+ return key_filename
472+
473+
474 def register(config, reactor=None):
475 """Instruct the Landscape Broker to register the client.
476
477
478=== modified file 'landscape/tests/test_configuration.py'
479--- landscape/tests/test_configuration.py 2010-05-10 15:01:10 +0000
480+++ landscape/tests/test_configuration.py 2010-07-19 13:17:40 +0000
481@@ -10,7 +10,7 @@
482 print_text, LandscapeSetupScript, LandscapeSetupConfiguration,
483 register, setup, main, setup_init_script_and_start_client,
484 stop_client_and_disable_init_script, ConfigurationError,
485- fetch_import_url, ImportOptionError)
486+ fetch_import_url, ImportOptionError, store_public_key_data)
487 from landscape.broker.registration import InvalidCredentialsError
488 from landscape.sysvconfig import SysVConfig, ProcessError
489 from landscape.tests.helpers import (
490@@ -1521,6 +1521,7 @@
491
492 def register_done():
493 service.reactor.fire("registration-done")
494+
495 registration_mock.register()
496 self.mocker.call(register_done)
497
498@@ -1571,6 +1572,7 @@
499
500 def register_done():
501 service.reactor.fire("registration-failed")
502+
503 registration_mock.register()
504 self.mocker.call(register_done)
505
506@@ -1801,3 +1803,24 @@
507 self.mocker.replay()
508
509 return register(configuration)
510+
511+
512+class StoreSSLCertificateDataTest(LandscapeTest):
513+
514+ def test_store_public_key_data(self):
515+ """
516+ L{store_public_key_data} writes the SSL CA supplied by the server to a
517+ file for later use, this file is called after the name of the
518+ configuration file with .ssl_public_key.
519+ """
520+ config_filename = os.path.join(self.makeDir(), "client.conf")
521+ expected_filename = "%s.ssl_public_key" % config_filename
522+ print_text_mock = self.mocker.replace(print_text)
523+ print_text_mock("Writing SSL CA certificate to %s..." %
524+ expected_filename)
525+ self.mocker.replay()
526+ expected_filename = config_filename + ".ssl_public_key"
527+ self.assertEqual(expected_filename,
528+ store_public_key_data(config_filename, "123456789"))
529+ self.assertEqual("123456789",
530+ open(expected_filename, "r").read())

Subscribers

People subscribed via source and target branches

to all changes: