Merge lp:~james-page/charm-helpers/multiple-https-networks into lp:charm-helpers

Proposed by James Page
Status: Merged
Merged at revision: 221
Proposed branch: lp:~james-page/charm-helpers/multiple-https-networks
Merge into: lp:charm-helpers
Diff against target: 638 lines (+313/-85)
7 files modified
charmhelpers/contrib/hahelpers/apache.py (+10/-3)
charmhelpers/contrib/hahelpers/cluster.py (+1/-2)
charmhelpers/contrib/openstack/context.py (+70/-24)
charmhelpers/contrib/openstack/templates/openstack_https_frontend (+9/-8)
tests/contrib/hahelpers/test_apache_utils.py (+34/-8)
tests/contrib/hahelpers/test_cluster_utils.py (+2/-2)
tests/contrib/openstack/test_os_contexts.py (+187/-38)
To merge this branch: bzr merge lp:~james-page/charm-helpers/multiple-https-networks
Reviewer Review Type Date Requested Status
Liam Young (community) Approve
Review via email: mp+235676@code.launchpad.net

Description of the change

This branch adds support for keystone signed certificates for multiple endpoints IP's for a single service.

It also retains backwards compatibility so that existing deploys don't break!

To post a comment you must log in.
220. By James Page

Fixup backwards compat, tidy lint

221. By James Page

Support unset cn, add test case

Revision history for this message
Liam Young (gnuoy) wrote :

Approve

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/contrib/hahelpers/apache.py'
2--- charmhelpers/contrib/hahelpers/apache.py 2014-02-26 21:48:09 +0000
3+++ charmhelpers/contrib/hahelpers/apache.py 2014-09-24 12:45:35 +0000
4@@ -20,20 +20,27 @@
5 )
6
7
8-def get_cert():
9+def get_cert(cn=None):
10+ # TODO: deal with multiple https endpoints via charm config
11 cert = config_get('ssl_cert')
12 key = config_get('ssl_key')
13 if not (cert and key):
14 log("Inspecting identity-service relations for SSL certificate.",
15 level=INFO)
16 cert = key = None
17+ if cn:
18+ ssl_cert_attr = 'ssl_cert_{}'.format(cn)
19+ ssl_key_attr = 'ssl_key_{}'.format(cn)
20+ else:
21+ ssl_cert_attr = 'ssl_cert'
22+ ssl_key_attr = 'ssl_key'
23 for r_id in relation_ids('identity-service'):
24 for unit in relation_list(r_id):
25 if not cert:
26- cert = relation_get('ssl_cert',
27+ cert = relation_get(ssl_cert_attr,
28 rid=r_id, unit=unit)
29 if not key:
30- key = relation_get('ssl_key',
31+ key = relation_get(ssl_key_attr,
32 rid=r_id, unit=unit)
33 return (cert, key)
34
35
36=== modified file 'charmhelpers/contrib/hahelpers/cluster.py'
37--- charmhelpers/contrib/hahelpers/cluster.py 2014-08-07 10:25:37 +0000
38+++ charmhelpers/contrib/hahelpers/cluster.py 2014-09-24 12:45:35 +0000
39@@ -139,10 +139,9 @@
40 return True
41 for r_id in relation_ids('identity-service'):
42 for unit in relation_list(r_id):
43+ # TODO - needs fixing for new helper as ssl_cert/key suffixes with CN
44 rel_state = [
45 relation_get('https_keystone', rid=r_id, unit=unit),
46- relation_get('ssl_cert', rid=r_id, unit=unit),
47- relation_get('ssl_key', rid=r_id, unit=unit),
48 relation_get('ca_cert', rid=r_id, unit=unit),
49 ]
50 # NOTE: works around (LP: #1203241)
51
52=== modified file 'charmhelpers/contrib/openstack/context.py'
53--- charmhelpers/contrib/openstack/context.py 2014-08-06 03:02:27 +0000
54+++ charmhelpers/contrib/openstack/context.py 2014-09-24 12:45:35 +0000
55@@ -8,7 +8,6 @@
56 check_call
57 )
58
59-
60 from charmhelpers.fetch import (
61 apt_install,
62 filter_installed_packages,
63@@ -28,6 +27,11 @@
64 INFO
65 )
66
67+from charmhelpers.core.host import (
68+ mkdir,
69+ write_file
70+)
71+
72 from charmhelpers.contrib.hahelpers.cluster import (
73 determine_apache_port,
74 determine_api_port,
75@@ -38,6 +42,7 @@
76 from charmhelpers.contrib.hahelpers.apache import (
77 get_cert,
78 get_ca_cert,
79+ install_ca_cert,
80 )
81
82 from charmhelpers.contrib.openstack.neutron import (
83@@ -47,6 +52,7 @@
84 from charmhelpers.contrib.network.ip import (
85 get_address_in_network,
86 get_ipv6_addr,
87+ is_address_in_network
88 )
89
90 CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
91@@ -490,22 +496,36 @@
92 cmd = ['a2enmod', 'ssl', 'proxy', 'proxy_http']
93 check_call(cmd)
94
95- def configure_cert(self):
96- if not os.path.isdir('/etc/apache2/ssl'):
97- os.mkdir('/etc/apache2/ssl')
98+ def configure_cert(self, cn=None):
99 ssl_dir = os.path.join('/etc/apache2/ssl/', self.service_namespace)
100- if not os.path.isdir(ssl_dir):
101- os.mkdir(ssl_dir)
102- cert, key = get_cert()
103- with open(os.path.join(ssl_dir, 'cert'), 'w') as cert_out:
104- cert_out.write(b64decode(cert))
105- with open(os.path.join(ssl_dir, 'key'), 'w') as key_out:
106- key_out.write(b64decode(key))
107+ mkdir(path=ssl_dir)
108+ cert, key = get_cert(cn)
109+ if cn:
110+ cert_filename = 'cert_{}'.format(cn)
111+ key_filename = 'key_{}'.format(cn)
112+ else:
113+ cert_filename = 'cert'
114+ key_filename = 'key'
115+ write_file(path=os.path.join(ssl_dir, cert_filename),
116+ content=b64decode(cert))
117+ write_file(path=os.path.join(ssl_dir, key_filename),
118+ content=b64decode(key))
119+
120+ def configure_ca(self):
121 ca_cert = get_ca_cert()
122 if ca_cert:
123- with open(CA_CERT_PATH, 'w') as ca_out:
124- ca_out.write(b64decode(ca_cert))
125- check_call(['update-ca-certificates'])
126+ install_ca_cert(b64decode(ca_cert))
127+
128+ def canonical_names(self):
129+ '''Figure out which canonical names clients will access this service'''
130+ cns = []
131+ for r_id in relation_ids('identity-service'):
132+ for unit in related_units(r_id):
133+ rdata = relation_get(rid=r_id, unit=unit)
134+ for k in rdata:
135+ if k.startswith('ssl_key_'):
136+ cns.append(k.lstrip('ssl_key_'))
137+ return list(set(cns))
138
139 def __call__(self):
140 if isinstance(self.external_ports, basestring):
141@@ -513,21 +533,47 @@
142 if (not self.external_ports or not https()):
143 return {}
144
145- self.configure_cert()
146+ self.configure_ca()
147 self.enable_modules()
148
149 ctxt = {
150 'namespace': self.service_namespace,
151- 'private_address': unit_get('private-address'),
152- 'endpoints': []
153+ 'endpoints': [],
154+ 'ext_ports': []
155 }
156- if is_clustered():
157- ctxt['private_address'] = config('vip')
158- for api_port in self.external_ports:
159- ext_port = determine_apache_port(api_port)
160- int_port = determine_api_port(api_port)
161- portmap = (int(ext_port), int(int_port))
162- ctxt['endpoints'].append(portmap)
163+
164+ for cn in self.canonical_names():
165+ self.configure_cert(cn)
166+
167+ addresses = []
168+ vips = []
169+ if config('vip'):
170+ vips = config('vip').split()
171+
172+ for network_type in ['os-internal-network',
173+ 'os-admin-network',
174+ 'os-public-network']:
175+ address = get_address_in_network(config(network_type),
176+ unit_get('private-address'))
177+ if len(vips) > 0 and is_clustered():
178+ for vip in vips:
179+ if is_address_in_network(config(network_type),
180+ vip):
181+ addresses.append((address, vip))
182+ break
183+ elif is_clustered():
184+ addresses.append((address, config('vip')))
185+ else:
186+ addresses.append((address, address))
187+
188+ for address, endpoint in set(addresses):
189+ for api_port in self.external_ports:
190+ ext_port = determine_apache_port(api_port)
191+ int_port = determine_api_port(api_port)
192+ portmap = (address, endpoint, int(ext_port), int(int_port))
193+ ctxt['endpoints'].append(portmap)
194+ ctxt['ext_ports'].append(int(ext_port))
195+ ctxt['ext_ports'] = list(set(ctxt['ext_ports']))
196 return ctxt
197
198
199
200=== modified file 'charmhelpers/contrib/openstack/templates/openstack_https_frontend'
201--- charmhelpers/contrib/openstack/templates/openstack_https_frontend 2013-07-19 23:31:35 +0000
202+++ charmhelpers/contrib/openstack/templates/openstack_https_frontend 2014-09-24 12:45:35 +0000
203@@ -1,16 +1,18 @@
204 {% if endpoints -%}
205-{% for ext, int in endpoints -%}
206-Listen {{ ext }}
207-NameVirtualHost *:{{ ext }}
208-<VirtualHost *:{{ ext }}>
209- ServerName {{ private_address }}
210+{% for ext_port in ext_ports -%}
211+Listen {{ ext_port }}
212+{% endfor -%}
213+{% for address, endpoint, ext, int in endpoints -%}
214+<VirtualHost {{ address }}:{{ ext }}>
215+ ServerName {{ endpoint }}
216 SSLEngine on
217- SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert
218- SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key
219+ SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }}
220+ SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key_{{ endpoint }}
221 ProxyPass / http://localhost:{{ int }}/
222 ProxyPassReverse / http://localhost:{{ int }}/
223 ProxyPreserveHost on
224 </VirtualHost>
225+{% endfor -%}
226 <Proxy *>
227 Order deny,allow
228 Allow from all
229@@ -19,5 +21,4 @@
230 Order allow,deny
231 Allow from all
232 </Location>
233-{% endfor -%}
234 {% endif -%}
235
236=== modified file 'tests/contrib/hahelpers/test_apache_utils.py'
237--- tests/contrib/hahelpers/test_apache_utils.py 2014-04-22 15:42:41 +0000
238+++ tests/contrib/hahelpers/test_apache_utils.py 2014-09-24 12:45:35 +0000
239@@ -1,7 +1,7 @@
240 from mock import patch
241
242 from testtools import TestCase
243-from tests.helpers import patch_open
244+from tests.helpers import patch_open, FakeRelation
245
246 import charmhelpers.contrib.hahelpers.apache as apache_utils
247
248@@ -29,6 +29,24 @@
249 -----END CERTIFICATE-----
250 '''
251
252+IDENTITY_NEW_STYLE_CERTS = {
253+ 'identity:0': {
254+ 'keystone/0': {
255+ 'ssl_cert_test-cn': 'keystone_provided_cert',
256+ 'ssl_key_test-cn': 'keystone_provided_key',
257+ }
258+ }
259+}
260+
261+IDENTITY_OLD_STYLE_CERTS = {
262+ 'identity:0': {
263+ 'keystone/0': {
264+ 'ssl_cert': 'keystone_provided_cert',
265+ 'ssl_key': 'keystone_provided_key',
266+ }
267+ }
268+}
269+
270
271 class ApacheUtilsTests(TestCase):
272 def setUp(self):
273@@ -54,7 +72,7 @@
274 'some_ca_cert', # config_get('ssl_cert')
275 'some_ca_key', # config_Get('ssl_key')
276 ]
277- result = apache_utils.get_cert()
278+ result = apache_utils.get_cert('test-cn')
279 self.assertEquals(('some_ca_cert', 'some_ca_key'), result)
280
281 def test_get_ca_cert_from_config(self):
282@@ -63,12 +81,20 @@
283
284 def test_get_cert_from_relation(self):
285 self.config_get.return_value = None
286- self.relation_ids.return_value = 'identity-service:0'
287- self.relation_list.return_value = 'keystone/0'
288- self.relation_get.side_effect = [
289- 'keystone_provided_cert',
290- 'keystone_provided_key',
291- ]
292+ rel = FakeRelation(IDENTITY_NEW_STYLE_CERTS)
293+ self.relation_ids.side_effect = rel.relation_ids
294+ self.relation_list.side_effect = rel.relation_units
295+ self.relation_get.side_effect = rel.get
296+ result = apache_utils.get_cert('test-cn')
297+ self.assertEquals(('keystone_provided_cert', 'keystone_provided_key'),
298+ result)
299+
300+ def test_get_cert_from_relation_deprecated(self):
301+ self.config_get.return_value = None
302+ rel = FakeRelation(IDENTITY_OLD_STYLE_CERTS)
303+ self.relation_ids.side_effect = rel.relation_ids
304+ self.relation_list.side_effect = rel.relation_units
305+ self.relation_get.side_effect = rel.get
306 result = apache_utils.get_cert()
307 self.assertEquals(('keystone_provided_cert', 'keystone_provided_key'),
308 result)
309
310=== modified file 'tests/contrib/hahelpers/test_cluster_utils.py'
311--- tests/contrib/hahelpers/test_cluster_utils.py 2014-08-07 10:25:37 +0000
312+++ tests/contrib/hahelpers/test_cluster_utils.py 2014-09-24 12:45:35 +0000
313@@ -117,7 +117,7 @@
314 @patch.object(cluster_utils, 'peer_units')
315 @patch.object(cluster_utils, 'is_clustered')
316 def test_is_is_elected_leader_unclustered(self, is_clustered,
317- peer_units, oldest_peer):
318+ peer_units, oldest_peer):
319 '''It detects it is the eligible leader in non-clustered peer group'''
320 is_clustered.return_value = False
321 oldest_peer.return_value = True
322@@ -127,7 +127,7 @@
323 @patch.object(cluster_utils, 'peer_units')
324 @patch.object(cluster_utils, 'is_clustered')
325 def test_not_is_elected_leader_unclustered(self, is_clustered,
326- peer_units, oldest_peer):
327+ peer_units, oldest_peer):
328 '''It detects it is not the eligible leader in non-clustered group'''
329 is_clustered.return_value = False
330 oldest_peer.return_value = False
331
332=== modified file 'tests/contrib/openstack/test_os_contexts.py'
333--- tests/contrib/openstack/test_os_contexts.py 2014-08-04 07:47:26 +0000
334+++ tests/contrib/openstack/test_os_contexts.py 2014-09-24 12:45:35 +0000
335@@ -59,6 +59,8 @@
336 relation = self.relation_data[rid][unit]
337 except KeyError:
338 return None
339+ if attribute is None:
340+ return relation
341 if attribute in relation:
342 return relation[attribute]
343 return None
344@@ -228,6 +230,39 @@
345 }
346 }
347
348+IDENTITY_RELATION_NO_CERT = {
349+ 'identity-service:0': {
350+ 'keystone/0': {
351+ 'private-address': 'keystone1',
352+ },
353+ }
354+}
355+
356+IDENTITY_RELATION_SINGLE_CERT = {
357+ 'identity-service:0': {
358+ 'keystone/0': {
359+ 'private-address': 'keystone1',
360+ 'ssl_cert_cinderhost1': 'certa',
361+ 'ssl_key_cinderhost1': 'keya',
362+ },
363+ }
364+}
365+
366+IDENTITY_RELATION_MULTIPLE_CERT = {
367+ 'identity-service:0': {
368+ 'keystone/0': {
369+ 'private-address': 'keystone1',
370+ 'ssl_cert_cinderhost1-int-network': 'certa',
371+ 'ssl_key_cinderhost1-int-network': 'keya',
372+ 'ssl_cert_cinderhost1-pub-network': 'certa',
373+ 'ssl_key_cinderhost1-pub-network': 'keya',
374+ 'ssl_cert_cinderhost1-adm-network': 'certa',
375+ 'ssl_key_cinderhost1-adm-network': 'keya',
376+ },
377+ }
378+}
379+
380+
381 SUB_CONFIG = """
382 nova:
383 /etc/nova/nova.conf:
384@@ -294,12 +329,27 @@
385 },
386 }
387
388+NONET_CONFIG = {
389+ 'vip': 'cinderhost1vip',
390+ 'os-internal-network': None,
391+ 'os-admin-network': None,
392+ 'os-public-network': None
393+}
394+
395+FULLNET_CONFIG = {
396+ 'vip': '10.5.1.1 10.5.2.1 10.5.3.1',
397+ 'os-internal-network': "10.5.1.0/24",
398+ 'os-admin-network': "10.5.2.0/24",
399+ 'os-public-network': "10.5.3.0/24"
400+}
401+
402 # Imported in contexts.py and needs patching in setUp()
403 TO_PATCH = [
404 'b64decode',
405 'check_call',
406 'get_cert',
407 'get_ca_cert',
408+ 'install_ca_cert',
409 'log',
410 'config',
411 'relation_get',
412@@ -315,8 +365,11 @@
413 'time',
414 'https',
415 'get_address_in_network',
416+ 'is_address_in_network',
417 'local_unit',
418- 'get_ipv6_addr'
419+ 'get_ipv6_addr',
420+ 'mkdir',
421+ 'write_file'
422 ]
423
424
425@@ -937,41 +990,100 @@
426 self.https.return_value = False
427 self.assertEquals({}, apache())
428
429- def _test_https_context(self, apache, is_clustered, peer_units):
430+ def _test_https_context(self, apache, is_clustered, peer_units,
431+ network_config=NONET_CONFIG, multinet=False):
432 self.https.return_value = True
433+ vips = network_config['vip'].split()
434+ if multinet:
435+ self.get_address_in_network.side_effect = ['10.5.1.100',
436+ '10.5.2.100',
437+ '10.5.3.100']
438+ else:
439+ self.get_address_in_network.return_value = 'cinderhost1'
440+
441+ config = {}
442+ config.update(network_config)
443+ self.config.side_effect = lambda key: config[key]
444+
445+ self.unit_get.return_value = 'cinderhost1'
446+ self.is_clustered.return_value = is_clustered
447+
448+ apache = context.ApacheSSLContext()
449+ apache.configure_cert = MagicMock()
450+ apache.enable_modules = MagicMock()
451+ apache.configure_ca = MagicMock()
452+ apache.canonical_names = MagicMock()
453
454 if is_clustered:
455+ apache.canonical_names.return_value = \
456+ network_config['vip'].split()
457 self.determine_api_port.return_value = 8756
458 self.determine_apache_port.return_value = 8766
459+ if len(vips) > 1:
460+ self.is_address_in_network.side_effect = [
461+ True, False, True, False, False, True
462+ ]
463+ else:
464+ self.is_address_in_network.return_value = True
465 else:
466+ apache.canonical_names.return_value = ['cinderhost1']
467 self.determine_api_port.return_value = 8766
468 self.determine_apache_port.return_value = 8776
469
470- config = {'vip': 'cinderhost1vip'}
471- self.config.side_effect = lambda key: config[key]
472- self.unit_get.return_value = 'cinderhost1'
473- self.is_clustered.return_value = is_clustered
474- apache = context.ApacheSSLContext()
475- apache.configure_cert = MagicMock
476- apache.enable_modules = MagicMock
477 apache.external_ports = '8776'
478 apache.service_namespace = 'cinder'
479
480 if is_clustered:
481- ex = {
482- 'private_address': 'cinderhost1vip',
483- 'namespace': 'cinder',
484- 'endpoints': [(8766, 8756)],
485- }
486+ if len(vips) > 1:
487+ ex = {
488+ 'namespace': 'cinder',
489+ 'endpoints': [('10.5.1.100', '10.5.1.1',
490+ 8766, 8756),
491+ ('10.5.2.100', '10.5.2.1',
492+ 8766, 8756),
493+ ('10.5.3.100', '10.5.3.1',
494+ 8766, 8756)],
495+ 'ext_ports': [8766]
496+ }
497+ else:
498+ ex = {
499+ 'namespace': 'cinder',
500+ 'endpoints': [('cinderhost1', 'cinderhost1vip',
501+ 8766, 8756)],
502+ 'ext_ports': [8766]
503+ }
504 else:
505- ex = {
506- 'private_address': 'cinderhost1',
507- 'namespace': 'cinder',
508- 'endpoints': [(8776, 8766)],
509- }
510+ if multinet:
511+ ex = {
512+ 'namespace': 'cinder',
513+ 'endpoints': [('10.5.3.100', '10.5.3.100',
514+ 8776, 8766),
515+ ('10.5.2.100', '10.5.2.100',
516+ 8776, 8766),
517+ ('10.5.1.100', '10.5.1.100',
518+ 8776, 8766)],
519+ 'ext_ports': [8776]
520+ }
521+ else:
522+ ex = {
523+ 'namespace': 'cinder',
524+ 'endpoints': [('cinderhost1', 'cinderhost1', 8776, 8766)],
525+ 'ext_ports': [8776]
526+ }
527
528 self.assertEquals(ex, apache())
529- self.assertTrue(apache.configure_cert.called)
530+ if is_clustered:
531+ if len(vips) > 1:
532+ apache.configure_cert.assert_has_calls([
533+ call('10.5.1.1'),
534+ call('10.5.2.1'),
535+ call('10.5.3.1')
536+ ])
537+ else:
538+ apache.configure_cert.assert_called_with('cinderhost1vip')
539+ else:
540+ apache.configure_cert.assert_called_with('cinderhost1')
541+ self.assertTrue(apache.configure_ca.called)
542 self.assertTrue(apache.enable_modules.called)
543
544 def test_https_context_no_peers_no_cluster(self):
545@@ -979,6 +1091,16 @@
546 apache = context.ApacheSSLContext()
547 self._test_https_context(apache, is_clustered=False, peer_units=None)
548
549+ def test_https_context_multinetwork(self):
550+ apache = context.ApacheSSLContext()
551+ self._test_https_context(apache, is_clustered=False, peer_units=None,
552+ network_config=FULLNET_CONFIG, multinet=True)
553+
554+ def test_https_context_multinetwork_cluster(self):
555+ apache = context.ApacheSSLContext()
556+ self._test_https_context(apache, is_clustered=True, peer_units=None,
557+ network_config=FULLNET_CONFIG, multinet=True)
558+
559 def test_https_context_wth_peers_no_cluster(self):
560 '''Test apache2 https on a unclustered unit with peers'''
561 apache = context.ApacheSSLContext()
562@@ -996,31 +1118,58 @@
563 ex_cmd = ['a2enmod', 'ssl', 'proxy', 'proxy_http']
564 self.check_call.assert_called_with(ex_cmd)
565
566- @patch('__builtin__.open')
567- @patch('os.mkdir')
568- @patch('os.path.isdir')
569- def test_https_configure_cert(self, isdir, mkdir, _open):
570- '''Test apache2 properly installs certs and keys to disk'''
571- isdir.return_value = False
572- self.get_cert.return_value = ('SSL_CERT', 'SSL_KEY')
573- self.get_ca_cert.return_value = 'CA_CERT'
574+ def test_https_configure_cert(self):
575+ '''Test apache2 properly installs certs and keys to disk'''
576+ self.get_cert.return_value = ('SSL_CERT', 'SSL_KEY')
577+ self.b64decode.side_effect = ['SSL_CERT', 'SSL_KEY']
578+ apache = context.ApacheSSLContext()
579+ apache.service_namespace = 'cinder'
580+ apache.configure_cert('test-cn')
581+ # appropriate directories are created.
582+ self.mkdir.assert_called_with(path='/etc/apache2/ssl/cinder')
583+ # appropriate files are written.
584+ files = [call(path='/etc/apache2/ssl/cinder/cert_test-cn',
585+ content='SSL_CERT'),
586+ call(path='/etc/apache2/ssl/cinder/key_test-cn',
587+ content='SSL_KEY')]
588+ self.write_file.assert_has_calls(files)
589+ # appropriate bits are b64decoded.
590+ decode = [call('SSL_CERT'), call('SSL_KEY')]
591+ self.assertEquals(decode, self.b64decode.call_args_list)
592+
593+ def test_https_configure_cert_deprecated(self):
594+ '''Test apache2 properly installs certs and keys to disk'''
595+ self.get_cert.return_value = ('SSL_CERT', 'SSL_KEY')
596+ self.b64decode.side_effect = ['SSL_CERT', 'SSL_KEY']
597 apache = context.ApacheSSLContext()
598 apache.service_namespace = 'cinder'
599 apache.configure_cert()
600 # appropriate directories are created.
601- dirs = [call('/etc/apache2/ssl'), call('/etc/apache2/ssl/cinder')]
602- self.assertEquals(dirs, mkdir.call_args_list)
603- # appropriate files are opened for writing.
604- _ca = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
605- files = [call('/etc/apache2/ssl/cinder/cert', 'w'),
606- call('/etc/apache2/ssl/cinder/key', 'w'),
607- call(_ca, 'w')]
608- for f in files:
609- self.assertIn(f, _open.call_args_list)
610+ self.mkdir.assert_called_with(path='/etc/apache2/ssl/cinder')
611+ # appropriate files are written.
612+ files = [call(path='/etc/apache2/ssl/cinder/cert',
613+ content='SSL_CERT'),
614+ call(path='/etc/apache2/ssl/cinder/key',
615+ content='SSL_KEY')]
616+ self.write_file.assert_has_calls(files)
617 # appropriate bits are b64decoded.
618- decode = [call('SSL_CERT'), call('SSL_KEY'), call('CA_CERT')]
619+ decode = [call('SSL_CERT'), call('SSL_KEY')]
620 self.assertEquals(decode, self.b64decode.call_args_list)
621
622+ def test_https_canonical_names(self):
623+ rel = FakeRelation(IDENTITY_RELATION_SINGLE_CERT)
624+ self.relation_ids.side_effect = rel.relation_ids
625+ self.related_units.side_effect = rel.relation_units
626+ self.relation_get.side_effect = rel.get
627+ apache = context.ApacheSSLContext()
628+ self.assertEquals(apache.canonical_names(), ['cinderhost1'])
629+ rel.relation_data = IDENTITY_RELATION_MULTIPLE_CERT
630+ self.assertEquals(apache.canonical_names(), ['cinderhost1-adm-network',
631+ 'cinderhost1-int-network',
632+ 'cinderhost1-pub-network'])
633+ rel.relation_data = IDENTITY_RELATION_NO_CERT
634+ self.assertEquals(apache.canonical_names(), [])
635+
636 def test_image_service_context_missing_data(self):
637 '''Test image-service with missing relation and missing data'''
638 image_service = context.ImageServiceContext()

Subscribers

People subscribed via source and target branches