From bf23c18ad0216e496b18e5bea8186d625cb4d2d0 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Tue, 16 Feb 2021 14:42:19 +0100 Subject: [PATCH] Refactoring around _manage_ca_certs() (#570) * Refactoring around _manage_ca_certs() Needed for https://launchpad.net/bugs/1915504 (cherry picked from commit 5523f95943995734c943a9db1734c58dd71c4acb) --- charmhelpers/contrib/openstack/cert_utils.py | 27 +++++++++++++++++--- charmhelpers/core/host.py | 13 +++++++++- tests/contrib/openstack/test_cert_utils.py | 23 +++++++++++++++++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/charmhelpers/contrib/openstack/cert_utils.py b/charmhelpers/contrib/openstack/cert_utils.py index 248674972..703fc6efe 100644 --- a/charmhelpers/contrib/openstack/cert_utils.py +++ b/charmhelpers/contrib/openstack/cert_utils.py @@ -47,7 +47,7 @@ ) from charmhelpers.core.host import ( - CA_CERT_DIR, + ca_cert_absolute_path, install_ca_cert, mkdir, write_file, @@ -307,6 +307,26 @@ def install_certs(ssl_dir, certs, chain=None, user='root', group='root'): content=bundle['key'], perms=0o640) +def get_cert_relation_ca_name(cert_relation_id=None): + """Determine CA certificate name as provided by relation. + + The filename on disk depends on the name chosen for the application on the + providing end of the certificates relation. + + :param cert_relation_id: (Optional) Relation id providing the certs + :type cert_relation_id: str + :returns: CA certificate filename without path nor extension + :rtype: str + """ + if cert_relation_id is None: + try: + cert_relation_id = relation_ids('certificates')[0] + except IndexError: + return '' + return '{}_juju_ca_cert'.format( + remote_service_name(relid=cert_relation_id)) + + def _manage_ca_certs(ca, cert_relation_id): """Manage CA certs. @@ -316,7 +336,7 @@ def _manage_ca_certs(ca, cert_relation_id): :type cert_relation_id: str """ config_ssl_ca = config('ssl_ca') - config_cert_file = '{}/{}.crt'.format(CA_CERT_DIR, CONFIG_CA_CERT_FILE) + config_cert_file = ca_cert_absolute_path(CONFIG_CA_CERT_FILE) if config_ssl_ca: log("Installing CA certificate from charm ssl_ca config to {}".format( config_cert_file), INFO) @@ -329,8 +349,7 @@ def _manage_ca_certs(ca, cert_relation_id): log("Installing CA certificate from certificate relation", INFO) install_ca_cert( ca.encode(), - name='{}_juju_ca_cert'.format( - remote_service_name(relid=cert_relation_id))) + name=get_cert_relation_ca_name(cert_relation_id)) def process_certificates(service_name, relation_id, unit, diff --git a/charmhelpers/core/host.py b/charmhelpers/core/host.py index f826f6fe3..f1a4a862e 100644 --- a/charmhelpers/core/host.py +++ b/charmhelpers/core/host.py @@ -1068,6 +1068,17 @@ def modulo_distribution(modulo=3, wait=30, non_zero_wait=False): return calculated_wait_time +def ca_cert_absolute_path(basename_without_extension): + """Returns absolute path to CA certificate. + + :param basename_without_extension: Filename without extension + :type basename_without_extension: str + :returns: Absolute full path + :rtype: str + """ + return '{}/{}.crt'.format(CA_CERT_DIR, basename_without_extension) + + def install_ca_cert(ca_cert, name=None): """ Install the given cert as a trusted CA. @@ -1083,7 +1094,7 @@ def install_ca_cert(ca_cert, name=None): ca_cert = ca_cert.encode('utf8') if not name: name = 'juju-{}'.format(charm_name()) - cert_file = '{}/{}.crt'.format(CA_CERT_DIR, name) + cert_file = ca_cert_absolute_path(name) new_hash = hashlib.md5(ca_cert).hexdigest() if file_hash(cert_file) == new_hash: return diff --git a/tests/contrib/openstack/test_cert_utils.py b/tests/contrib/openstack/test_cert_utils.py index 49b0351b3..7ed43c821 100644 --- a/tests/contrib/openstack/test_cert_utils.py +++ b/tests/contrib/openstack/test_cert_utils.py @@ -481,6 +481,29 @@ def test_process_certificates_bindings( custom_hostname_link='funky-name', bindings=['mybinding', 'internal', 'admin', 'public']) + @mock.patch.object(cert_utils, 'remote_service_name') + @mock.patch.object(cert_utils, 'relation_ids') + def test_get_cert_relation_ca_name(self, relation_ids, remote_service_name): + remote_service_name.return_value = 'vault' + + # Test with argument: + self.assertEqual(cert_utils.get_cert_relation_ca_name('certificates:1'), + 'vault_juju_ca_cert') + remote_service_name.assert_called_once_with(relid='certificates:1') + remote_service_name.reset_mock() + + # Test without argument: + relation_ids.return_value = ['certificates:2'] + self.assertEqual(cert_utils.get_cert_relation_ca_name(), + 'vault_juju_ca_cert') + remote_service_name.assert_called_once_with(relid='certificates:2') + remote_service_name.reset_mock() + + # Test without argument nor 'certificates' relation: + relation_ids.return_value = [] + self.assertEqual(cert_utils.get_cert_relation_ca_name(), '') + remote_service_name.assert_not_called() + @mock.patch.object(cert_utils, 'remote_service_name') @mock.patch.object(cert_utils.os, 'remove') @mock.patch.object(cert_utils.os.path, 'exists')