Merge lp:~james-page/charm-helpers/multiple-https-networks into lp:charm-helpers
- multiple-https-networks
- Merge into devel
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Liam Young (community) | Approve | ||
Review via email: mp+235676@code.launchpad.net |
Commit message
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
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() |
Approve