diff --git a/requirements.txt b/requirements.txt index 56cbcc9..f0aa163 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ ops >= 1.2.0 +tenacity git+https://github.com/openstack/charms.ceph#egg=charms_ceph git+https://opendev.org/openstack/charm-ops-openstack#egg=ops_openstack #git+https://opendev.org/openstack/charm-ops-interface-tls-certificates#egg=interface_tls_certificates diff --git a/src/charm.py b/src/charm.py index a413ada..42b3d70 100755 --- a/src/charm.py +++ b/src/charm.py @@ -22,6 +22,7 @@ import secrets import socket import string import subprocess +import tenacity import ops_openstack.plugins.classes import interface_dashboard import interface_api_endpoints @@ -67,7 +68,7 @@ class CephDashboardCharm(ops_openstack.core.OSBaseCharm): def minimum_supported(self, supported_version: str) -> bool: """Check if installed Ceph release is >= to supported_version""" - return ch_host.cmp_pkgrevno('ceph-common', supported_version) < 1 + return ch_host.cmp_pkgrevno('ceph-common', supported_version) >= 0 def convert_option(self, value: Union[bool, str, int]) -> List[str]: """Convert a value to the corresponding value part of the ceph @@ -190,20 +191,46 @@ class CephDashboardCharm(ops_openstack.core.OSBaseCharm): sans.append(self.config.get('public-hostname')) self.ca_client.request_server_certificate(socket.getfqdn(), sans) + def _check_for_certs(self) -> bool: + """Check that charm has TLS data it needs""" + # Check charm config for TLS data + key, cert, _ = self._get_tls_from_config() + if key and cert: + return True + # Check relation for TLS data + try: + self.ca_client.server_key + return True + except ca_client.CAClientError: + return False + + def _check_dashboard_responding(self) -> bool: + """Check the dashboard port is open""" + + @tenacity.retry(wait=tenacity.wait_fixed(2), + stop=tenacity.stop_after_attempt(30), reraise=True) + def _check_port(ip, port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + result = sock.connect_ex((ip, port)) + assert result == 0 + + try: + _check_port(self._get_bind_ip(), self.TLS_PORT) + return True + except AssertionError: + return False + def check_dashboard(self) -> StatusBase: """Check status of dashboard""" - self._stored.is_started = ceph_utils.is_dashboard_enabled() - if self._stored.is_started: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - result = sock.connect_ex((self._get_bind_ip(), self.TLS_PORT)) - if result == 0: - return ActiveStatus() - else: - return BlockedStatus( - 'Dashboard not responding') - else: - return BlockedStatus( - 'Dashboard is not enabled') + checks = [ + (ceph_utils.is_dashboard_enabled, 'Dashboard is not enabled'), + (self._check_for_certs, ('No certificates found. Please add a ' + 'certifcates relation or provide via ' + 'charm config')), + (self._check_dashboard_responding, 'Dashboard not responding')] + for check_f, msg in checks: + if not check_f(): + return BlockedStatus(msg) return ActiveStatus() def kick_dashboard(self) -> None: @@ -251,6 +278,7 @@ class CephDashboardCharm(ops_openstack.core.OSBaseCharm): 'mgr/dashboard/{hostname}/server_addr'.format( hostname=socket.gethostname()), str(self._get_bind_ip())) + self._stored.is_started = True self.update_status() def _get_bind_ip(self) -> str: diff --git a/tests/tests.yaml b/tests/tests.yaml index b6df5e8..e1e1bda 100644 --- a/tests/tests.yaml +++ b/tests/tests.yaml @@ -13,4 +13,4 @@ target_deploy_status: workload-status-message: Vault needs to be initialized ceph-dashboard: workload-status: blocked - workload-status-message-prefix: Dashboard not responding + workload-status-message-prefix: No certificates found diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py index 577ab7e..adce0b6 100644 --- a/unit_tests/__init__.py +++ b/unit_tests/__init__.py @@ -17,3 +17,8 @@ import mock # Mock out secrets to make py35 happy. sys.modules['secrets'] = mock.MagicMock() + +# Tenacity decorators need to be mocked before import +tenacity = mock.MagicMock() +tenacity.retry.side_effect = lambda *args, **kwargs: lambda x: x +sys.modules['tenacity'] = tenacity diff --git a/unit_tests/test_ceph_dashboard_charm.py b/unit_tests/test_ceph_dashboard_charm.py index 2414b1a..fbeb8d3 100644 --- a/unit_tests/test_ceph_dashboard_charm.py +++ b/unit_tests/test_ceph_dashboard_charm.py @@ -279,6 +279,15 @@ class TestCephDashboardCharmBase(CharmTestCase): socket_mock.connect_ex.return_value = 0 self.ceph_utils.is_dashboard_enabled.return_value = True self.harness.begin() + self.assertEqual( + self.harness.charm.check_dashboard(), + BlockedStatus('No certificates found. Please add a certifcates ' + 'relation or provide via charm config')) + self.harness.update_config( + key_values={ + 'ssl_key': base64.b64encode(TEST_KEY.encode("utf-8")), + 'ssl_cert': base64.b64encode(TEST_CERT.encode("utf-8")), + 'ssl_ca': base64.b64encode(TEST_CA.encode("utf-8"))}) self.assertEqual( self.harness.charm.check_dashboard(), ActiveStatus())