diff --git a/charmhelpers/contrib/charmsupport/nrpe.py b/charmhelpers/contrib/charmsupport/nrpe.py index c87cf489..e4cb06bc 100644 --- a/charmhelpers/contrib/charmsupport/nrpe.py +++ b/charmhelpers/contrib/charmsupport/nrpe.py @@ -337,10 +337,8 @@ class NRPE(object): "command": nrpecheck.command, } # If we were passed max_check_attempts, add that to the relation data - try: + if nrpecheck.max_check_attempts is not None: nrpe_monitors[nrpecheck.shortname]['max_check_attempts'] = nrpecheck.max_check_attempts - except AttributeError: - pass # update-status hooks are configured to firing every 5 minutes by # default. When nagios-nrpe-server is restarted, the nagios server diff --git a/charmhelpers/contrib/openstack/cert_utils.py b/charmhelpers/contrib/openstack/cert_utils.py index 24867497..703fc6ef 100644 --- a/charmhelpers/contrib/openstack/cert_utils.py +++ b/charmhelpers/contrib/openstack/cert_utils.py @@ -47,7 +47,7 @@ from charmhelpers.contrib.network.ip import ( ) 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/hookenv.py b/charmhelpers/core/hookenv.py index db7ce728..f6fa7fce 100644 --- a/charmhelpers/core/hookenv.py +++ b/charmhelpers/core/hookenv.py @@ -226,6 +226,17 @@ def relation_id(relation_name=None, service_or_unit=None): raise ValueError('Must specify neither or both of relation_name and service_or_unit') +def departing_unit(): + """The departing unit for the current relation hook. + + Available since juju 2.8. + + :returns: the departing unit, or None if the information isn't available. + :rtype: Optional[str] + """ + return os.environ.get('JUJU_DEPARTING_UNIT', None) + + def local_unit(): """Local unit ID""" return os.environ['JUJU_UNIT_NAME'] diff --git a/charmhelpers/core/host.py b/charmhelpers/core/host.py index f826f6fe..f1a4a862 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/hooks/nova_cc_utils.py b/hooks/nova_cc_utils.py index cfdd2d54..e0ab4bf7 100644 --- a/hooks/nova_cc_utils.py +++ b/hooks/nova_cc_utils.py @@ -24,6 +24,7 @@ import uuid import charmhelpers.contrib.hahelpers.apache as ch_apache import charmhelpers.contrib.hahelpers.cluster as ch_cluster import charmhelpers.contrib.network.ip as ch_ip +import charmhelpers.contrib.openstack.cert_utils as ch_cert_utils import charmhelpers.contrib.openstack.context as ch_context import charmhelpers.contrib.openstack.ip as ch_openstack_ip import charmhelpers.contrib.openstack.templating as ch_templating @@ -1035,36 +1036,15 @@ def auth_token_config(setting): return value -def get_cert_relation_ca_filename(): - """Determine absolute path to CA certificate file as provided by relation. - - The filename on disk depends on the name chosen for the application on the - providing end of the certificates relation. - - :type cert_relation_id: str - :returns: Absolute path to CA certificate file - :rtype: str - """ - # NOTE(fnordahl): this function belongs together with the c-h cert_utils - # module, however given we at the time of this writing are frozen I'll add - # it here as a tactical measure. - # TODO: move to c-h.contrib.openstack.cert_utils - for cert_relation_id in hookenv.relation_ids('certificates'): - return os.path.join( - ch_host.CA_CERT_DIR, - '{}_juju_ca_cert.crt' - .format(hookenv.remote_service_name(relid=cert_relation_id))) - return '' - - def get_ca_cert_b64(): """Retrieve CA-cert as provided by certificates relation or config. :returns: Base64 encoded CA-certificate data :rtype: str """ - ca_cert_file = (get_cert_relation_ca_filename() or + ca_cert_name = (ch_cert_utils.get_cert_relation_ca_name() or ch_apache.CONFIG_CA_CERT_FILE) + ca_cert_file = ch_host.ca_cert_absolute_path(ca_cert_name) try: with open(ca_cert_file, 'rb') as _in: return base64.b64encode(_in.read()).decode('utf-8') diff --git a/unit_tests/test_nova_cc_utils.py b/unit_tests/test_nova_cc_utils.py index 4979f445..e1eff82b 100644 --- a/unit_tests/test_nova_cc_utils.py +++ b/unit_tests/test_nova_cc_utils.py @@ -717,27 +717,58 @@ class NovaCCUtilsTests(CharmTestCase): self.assertFalse(rm.called) _file.write.assert_called_with('|1|= fookey\n') - @patch.object(utils.hookenv, 'remote_service_name') - def test_get_cert_relation_ca_filename(self, mock_remote_service_name): - mock_remote_service_name.return_value = 'namedvault' - self.relation_ids.return_value = ['aRelation'] - self.assertEquals( - utils.get_cert_relation_ca_filename(), - '/usr/local/share/ca-certificates/namedvault_juju_ca_cert.crt') + @patch('charmhelpers.contrib.openstack.cert_utils.' + 'get_cert_relation_ca_name') + def test_get_ca_cert_b64_from_relation(self, get_cert_relation_ca_name): + # Test input simulating the case where a CA certificate has been + # provided by the 'certificates' relation and installed on disk: + get_cert_relation_ca_name.return_value = 'rel_juju_ca_cert' + open_side_effect = None # file is found - def test_get_ca_cert_b64(self): with patch_open() as (_open, _file): _file.readlines = MagicMock() _file.write = MagicMock() _file.read.return_value = b'mycert' - _open.side_effect = OSError - self.assertEqual( - utils.get_ca_cert_b64(), - '') - _open.side_effect = None + _open.side_effect = open_side_effect self.assertEqual( utils.get_ca_cert_b64(), 'bXljZXJ0') + _open.assert_called_once_with( + '/usr/local/share/ca-certificates/rel_juju_ca_cert.crt', 'rb') + + @patch('charmhelpers.contrib.openstack.cert_utils.' + 'get_cert_relation_ca_name') + def test_get_ca_cert_b64_from_option(self, get_cert_relation_ca_name): + # Test input simulating the case where a CA certificate has been + # provided by ssl_* option and installed on disk: + get_cert_relation_ca_name.return_value = '' + open_side_effect = None # file is found + + with patch_open() as (_open, _file): + _file.readlines = MagicMock() + _file.write = MagicMock() + _file.read.return_value = b'mycert' + _open.side_effect = open_side_effect + self.assertEqual( + utils.get_ca_cert_b64(), + 'bXljZXJ0') + _open.assert_called_once_with( + '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt', + 'rb') + + @patch('charmhelpers.contrib.openstack.cert_utils.' + 'get_cert_relation_ca_name') + def test_get_ca_cert_b64_not_found(self, get_cert_relation_ca_name): + # Test input simulating the case where no CA certificate can be found + # on disk: + get_cert_relation_ca_name.return_value = '' + open_side_effect = OSError + + with patch_open() as (_open, _file): + _open.side_effect = open_side_effect + self.assertEqual( + utils.get_ca_cert_b64(), + '') @patch.object(utils, 'known_hosts') @patch('subprocess.check_call')