Add support for certs from the tls-certs relation

* Add method for requesting certificates: get_certificate_requests
* Update get_certs_and_keys to process certs from vault relation
* Create a default handler for certificates_available

Change-Id: If24621f2e72b66d58ea15cadeb458cadaf86a483
This commit is contained in:
Liam Young 2018-05-15 07:24:08 +00:00
parent 33fd6a52b5
commit 323edcc8c9
4 changed files with 116 additions and 3 deletions

View File

@ -10,6 +10,7 @@ import subprocess
import charmhelpers.contrib.network.ip as ch_ip
import charmhelpers.contrib.openstack.utils as os_utils
import charmhelpers.contrib.openstack.ha as os_ha
import charmhelpers.contrib.openstack.cert_utils as cert_utils
import charmhelpers.core.hookenv as hookenv
import charmhelpers.core.host as ch_host
import charmhelpers.fetch as fetch
@ -272,6 +273,12 @@ class OpenStackAPICharm(OpenStackCharm):
"get_database_setup() needs to be overriden in the derived "
"class")
def get_certificate_requests(self):
"""Return a dict of certificate requests
"""
return cert_utils.get_certificate_request(
json_encode=False).get('cert_requests', {})
@property
def all_packages(self):
"""List of packages to be installed
@ -563,7 +570,8 @@ class HAOpenStackCharm(OpenStackAPICharm):
addresses.append(laddr)
return sorted(list(set(addresses)))
def get_certs_and_keys(self, keystone_interface=None):
def get_certs_and_keys(self, keystone_interface=None,
certificates_interface=None):
"""Collect SSL config for local endpoints
SSL keys and certs may come from user specified configuration for this
@ -601,6 +609,21 @@ class HAOpenStackCharm(OpenStackAPICharm):
'ca': ca,
'cn': addr})
return keys_and_certs
elif certificates_interface:
keys_and_certs = []
reqs = certificates_interface.get_batch_requests()
ca = certificates_interface.get_ca()
chain = certificates_interface.get_chain()
for cn, data in sorted(reqs.items()):
cert = data['cert']
if chain:
cert = cert + chain
keys_and_certs.append({
'key': data['key'],
'cert': cert,
'ca': ca,
'cn': cn})
return keys_and_certs
else:
return []
@ -645,8 +668,11 @@ class HAOpenStackCharm(OpenStackAPICharm):
relations.endpoint_from_flag('identity-service.available.ssl') or
relations
.endpoint_from_flag('identity-service.available.ssl_legacy'))
certificates_interface = relations.endpoint_from_flag(
'certificates.batch.cert.available')
ssl_objects = self.get_certs_and_keys(
keystone_interface=keystone_interface)
keystone_interface=keystone_interface,
certificates_interface=certificates_interface)
with is_data_changed('configure_ssl.ssl_objects',
ssl_objects) as changed:
if ssl_objects:
@ -656,7 +682,8 @@ class HAOpenStackCharm(OpenStackAPICharm):
self.configure_cert(
ssl['cert'], ssl['key'], cn=ssl['cn'])
self.configure_ca(ssl['ca'])
cert_utils.create_ip_cert_links(
os.path.join('/etc/apache2/ssl/', self.name))
if not os_utils.snap_install_requested():
self.configure_apache()
ch_host.service_reload('apache2')

View File

@ -18,6 +18,7 @@ ALLOWED_DEFAULT_HANDLERS = [
'charm.default-select-package-type',
'update-status',
'upgrade-charm',
'certificates.available',
]
# Where to store the default handler functions for each default state
@ -136,6 +137,16 @@ def make_default_amqp_connection_handler():
reactive.set_state('charms.openstack.do-default-amqp.connected')
@_map_default_handler('certificates.available')
def make_default_certificates_available_handler():
"""Set the default certificates.available state so that the default handler in
layer-openstack can run.
Convoluted, because charms.reactive will only run handlers in the reactive
or hooks directory.
"""
reactive.set_state('charms.openstack.do-default-certificates.available')
@_map_default_handler('shared-db.connected')
def make_default_setup_database_handler():
"""Set the default shared-db.connected state so that the default handler in

View File

@ -31,6 +31,8 @@ sys.modules['charmhelpers.contrib'] = charmhelpers.contrib
sys.modules['charmhelpers.contrib.openstack'] = charmhelpers.contrib.openstack
sys.modules['charmhelpers.contrib.openstack.ha'] = (
charmhelpers.contrib.openstack.ha)
sys.modules['charmhelpers.contrib.openstack.cert_utils'] = (
charmhelpers.contrib.openstack.cert_utils)
sys.modules['charmhelpers.contrib.openstack.utils'] = (
charmhelpers.contrib.openstack.utils)
sys.modules['charmhelpers.contrib.openstack.templating'] = (

View File

@ -344,6 +344,24 @@ class TestOpenStackAPICharm(BaseOpenStackCharmTest):
with self.assertRaises(RuntimeError):
self.target.get_database_setup()
def test_get_certificate_requests(self):
self.patch_object(
chm.cert_utils,
'get_certificate_request',
return_value={'cert_requests': {'test.e.c': {'sans': ['san1']}}})
self.assertEqual(
self.target.get_certificate_requests(),
{'test.e.c': {'sans': ['san1']}})
def test_get_certificate_requests_empty(self):
self.patch_object(
chm.cert_utils,
'get_certificate_request',
return_value={})
self.assertEqual(
self.target.get_certificate_requests(),
{})
def test_all_packages(self):
self.patch_target('enable_memcache')
self.patch_target('packages', new=['pkg1', 'pkg2'])
@ -761,6 +779,61 @@ class TestHAOpenStackCharm(BaseOpenStackCharmTest):
self.target.get_certs_and_keys(keystone_interface=KSInterface()),
expect)
def test_get_certs_and_keys_certs_interface(self):
class CertsInterface(object):
def get_batch_requests(self):
req = {
'int_addr': {
'cert': 'int_cert',
'key': 'int_key'},
'priv_addr': {
'cert': 'priv_cert',
'key': 'priv_key'},
'pub_addr': {
'cert': 'pub_cert',
'key': 'pub_key'},
'admin_addr': {
'cert': 'admin_cert',
'key': 'admin_key'}}
return req
def get_ca(self):
return 'CA'
def get_chain(self):
return 'CHAIN'
self.patch_object(chm.os_utils, 'snap_install_requested',
return_value=False)
expect = [
{
'ca': 'CA',
'cert': 'admin_certCHAIN',
'cn': 'admin_addr',
'key': 'admin_key'},
{
'ca': 'CA',
'cert': 'int_certCHAIN',
'cn': 'int_addr',
'key': 'int_key'},
{
'ca': 'CA',
'cert': 'priv_certCHAIN',
'cn': 'priv_addr',
'key': 'priv_key'},
{
'ca': 'CA',
'cert': 'pub_certCHAIN',
'cn': 'pub_addr',
'key': 'pub_key'},
]
self.assertEqual(
self.target.get_certs_and_keys(
certificates_interface=CertsInterface()),
expect)
def test_config_defined_certs_and_keys(self):
# test that the cached parameters do what we expect
config = {