From 5e6c73d38856e944246b45aef82be8474c5743ef Mon Sep 17 00:00:00 2001 From: Mia Altieri <32723809+MiaAltieri@users.noreply.github.com> Date: Fri, 21 Oct 2022 12:10:10 +0200 Subject: [PATCH] Housekeeping (#118) * add template, update years, remove series * move lib check * update libs --- .github/workflows/ci.yaml | 14 ++ .github/workflows/pull_request_template.md | 18 ++ .github/workflows/release.yaml | 14 -- actions.yaml | 2 +- charmcraft.yaml | 2 +- lib/charms/operator_libs_linux/v0/apt.py | 161 ++++++++++------ lib/charms/operator_libs_linux/v0/systemd.py | 176 ------------------ lib/charms/operator_libs_linux/v1/systemd.py | 8 +- .../v1/tls_certificates.py | 13 +- metadata.yaml | 2 +- pyproject.toml | 2 +- src/charm.py | 2 +- tests/integration/helpers.py | 2 +- tests/integration/test_charm.py | 2 +- tests/unit/helpers.py | 2 +- tests/unit/test_charm.py | 2 +- tests/unit/test_mongodb_lib.py | 2 +- 17 files changed, 162 insertions(+), 262 deletions(-) create mode 100644 .github/workflows/pull_request_template.md delete mode 100644 lib/charms/operator_libs_linux/v0/systemd.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 21c2123f2..643225393 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -105,3 +105,17 @@ jobs: provider: lxd - name: Run tls integration tests run: tox -e tls-integration + + lib-check: + name: Check libraries + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Check libs + uses: canonical/charming-actions/check-libraries@1.0.3 + with: + credentials: "${{ secrets.CHARMHUB_TOKEN }}" # FIXME: current token will expire in 2023-07-04 + github-token: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/.github/workflows/pull_request_template.md b/.github/workflows/pull_request_template.md new file mode 100644 index 000000000..82af2eebb --- /dev/null +++ b/.github/workflows/pull_request_template.md @@ -0,0 +1,18 @@ +### Problem + + + +### Context + + + +### Solution + + + +### Testing + + + +### Release Notes + \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 58f15574d..632357693 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -6,20 +6,6 @@ on: - main jobs: - lib-check: - name: Check libraries - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Check libs - uses: canonical/charming-actions/check-libraries@1.0.3 - with: - credentials: "${{ secrets.CHARMHUB_TOKEN }}" # FIXME: current token will expire in 2023-07-04 - github-token: "${{ secrets.GITHUB_TOKEN }}" - ci-tests: uses: ./.github/workflows/ci.yaml diff --git a/actions.yaml b/actions.yaml index c9a669467..922a63b30 100644 --- a/actions.yaml +++ b/actions.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Canonical Ltd. +# Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. get-primary: diff --git a/charmcraft.yaml b/charmcraft.yaml index cdd506005..6fe7a67c9 100644 --- a/charmcraft.yaml +++ b/charmcraft.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Canonical Ltd. +# Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. type: charm diff --git a/lib/charms/operator_libs_linux/v0/apt.py b/lib/charms/operator_libs_linux/v0/apt.py index 07c72a879..2b5c8f2e7 100644 --- a/lib/charms/operator_libs_linux/v0/apt.py +++ b/lib/charms/operator_libs_linux/v0/apt.py @@ -45,7 +45,7 @@ ```python try: - vim = apt.DebianPackage.from_system("vim" + vim = apt.DebianPackage.from_system("vim") # To find from the apt cache only # apt.DebianPackage.from_apt_cache("vim") @@ -124,7 +124,7 @@ # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 4 +LIBPATCH = 7 VALID_SOURCE_TYPES = ("deb", "deb-src") @@ -136,12 +136,12 @@ class Error(Exception): def __repr__(self): """String representation of Error.""" - return f"<{type(self).__module__}.{type(self).__name__} {self.args}>" + return "<{}.{} {}>".format(type(self).__module__, type(self).__name__, self.args) @property def name(self): """Return a string representation of the model plus class.""" - return f"<{type(self).__module__}.{type(self).__name__}>" + return "<{}.{}>".format(type(self).__module__, type(self).__name__) @property def message(self): @@ -206,10 +206,10 @@ def __eq__(self, other) -> bool: Returns: A boolean reflecting equality """ - return isinstance(other, self.__class__) and (self._name, self._version.number) == ( - other._name, - other._version.number, - ) + return isinstance(other, self.__class__) and ( + self._name, + self._version.number, + ) == (other._name, other._version.number) def __hash__(self): """A basic hash so this class can be used in Mappings and dicts.""" @@ -217,17 +217,23 @@ def __hash__(self): def __repr__(self): """A representation of the package.""" - return f"<{self.__module__}.{self.__class__.__name__}: {self.__dict__}>" + return "<{}.{}: {}>".format(self.__module__, self.__class__.__name__, self.__dict__) def __str__(self): """A human-readable representation of the package.""" return "<{}: {}-{}.{} -- {}>".format( - self.__class__.__name__, self._name, self._version, self._arch, str(self._state) + self.__class__.__name__, + self._name, + self._version, + self._arch, + str(self._state), ) @staticmethod def _apt( - command: str, package_names: Union[str, List], optargs: Optional[List[str]] = None + command: str, + package_names: Union[str, List], + optargs: Optional[List[str]] = None, ) -> None: """Wrap package management commands for Debian/Ubuntu systems. @@ -247,20 +253,20 @@ def _apt( check_call(_cmd, stderr=PIPE, stdout=PIPE) except CalledProcessError as e: raise PackageError( - f"Could not {command} package(s) [{[*package_names]}]: {e.output}" + "Could not {} package(s) [{}]: {}".format(command, [*package_names], e.output) ) from None def _add(self) -> None: """Add a package to the system.""" self._apt( "install", - f"{self.name}={self.version}", + "{}={}".format(self.name, self.version), optargs=["--option=Dpkg::Options::=--force-confold"], ) def _remove(self) -> None: """Removes a package from the system. Implementation-specific.""" - return self._apt("remove", f"{self.name}={self.version}") + return self._apt("remove", "{}={}".format(self.name, self.version)) @property def name(self) -> str: @@ -332,7 +338,7 @@ def arch(self) -> str: @property def fullversion(self) -> str: """Returns the name+epoch for a package.""" - return f"{self._version}.{self._arch}" + return "{}.{}".format(self._version, self._arch) @staticmethod def _get_epoch_from_version(version: str) -> Tuple[str, str]: @@ -369,7 +375,9 @@ def from_system( # This seems unnecessary, but virtually all `apt` commands have a return code of `100`, # and providing meaningful error messages without this is ugly. raise PackageNotFoundError( - f"Package '{package}{f'.{arch}' if arch else ''}' could not be found on the system or in the apt cache!" + "Package '{}{}' could not be found on the system or in the apt cache!".format( + package, ".{}".format(arch) if arch else "" + ) ) from None @classmethod @@ -394,7 +402,7 @@ def from_installed_package( try: output = check_output(["dpkg", "-l", package], stderr=PIPE, universal_newlines=True) except CalledProcessError: - raise PackageNotFoundError(f"Package is not installed: {package}") from None + raise PackageNotFoundError("Package is not installed: {}".format(package)) from None # Pop off the output from `dpkg -l' because there's no flag to # omit it` @@ -414,6 +422,16 @@ def from_installed_package( for line in lines: try: matches = dpkg_matcher.search(line).groupdict() + package_status = matches["package_status"] + + if not package_status.endswith("i"): + logger.debug( + "package '%s' in dpkg output but not installed, status: '%s'", + package, + package_status, + ) + break + epoch, split_version = DebianPackage._get_epoch_from_version(matches["version"]) pkg = DebianPackage( matches["package_name"], @@ -422,13 +440,15 @@ def from_installed_package( matches["arch"], PackageState.Present, ) - if pkg.arch == arch and (version == "" or str(pkg.version) == version): + if (pkg.arch == "all" or pkg.arch == arch) and ( + version == "" or str(pkg.version) == version + ): return pkg except AttributeError: logger.warning("dpkg matcher could not parse line: %s", line) # If we didn't find it, fail through - raise PackageNotFoundError(f"Package {package}.{arch} is not installed!") + raise PackageNotFoundError("Package {}.{} is not installed!".format(package, arch)) @classmethod def from_apt_cache( @@ -455,7 +475,9 @@ def from_apt_cache( ["apt-cache", "show", package], stderr=PIPE, universal_newlines=True ) except CalledProcessError as e: - raise PackageError(f"Could not list packages in apt-cache: {e.output}") from None + raise PackageError( + "Could not list packages in apt-cache: {}".format(e.output) + ) from None pkg_groups = output.strip().split("\n\n") keys = ("Package", "Architecture", "Version") @@ -472,14 +494,20 @@ def from_apt_cache( epoch, split_version = DebianPackage._get_epoch_from_version(vals["Version"]) pkg = DebianPackage( - vals["Package"], split_version, epoch, vals["Architecture"], PackageState.Available + vals["Package"], + split_version, + epoch, + vals["Architecture"], + PackageState.Available, ) - if pkg.arch == arch and (version == "" or str(pkg.version) == version): + if (pkg.arch == "all" or pkg.arch == arch) and ( + version == "" or str(pkg.version) == version + ): return pkg # If we didn't find it, fail through - raise PackageNotFoundError(f"Package {package}.{arch} is not in the apt cache!") + raise PackageNotFoundError("Package {}.{} is not in the apt cache!".format(package, arch)) class Version: @@ -498,11 +526,11 @@ def __init__(self, version: str, epoch: str): def __repr__(self): """A representation of the package.""" - return f"<{self.__module__}.{self.__class__.__name__}: {self.__dict__}>" + return "<{}.{}: {}>".format(self.__module__, self.__class__.__name__, self.__dict__) def __str__(self): """A human-readable representation of the package.""" - return f"{f'{self._epoch}:' if self._epoch else ''}{self._version}" + return "{}{}".format("{}:".format(self._epoch) if self._epoch else "", self._version) @property def epoch(self): @@ -746,13 +774,15 @@ def add_package( packages["failed"].append(p) if packages["failed"]: - raise PackageError(f"Failed to install packages: {', '.join(packages['failed'])}") + raise PackageError("Failed to install packages: {}".format(", ".join(packages["failed"]))) return packages["success"] if len(packages["success"]) > 1 else packages["success"][0] def _add( - name: str, version: Optional[str] = "", arch: Optional[str] = "" + name: str, + version: Optional[str] = "", + arch: Optional[str] = "", ) -> Tuple[Union[DebianPackage, str], bool]: """Adds a package. @@ -797,7 +827,9 @@ def remove_package( except PackageNotFoundError: logger.info("package '%s' was requested for removal, but it was not installed.", p) - return packages if len(packages) > 1 else packages[0] + # the list of packages will be empty when no package is removed + logger.debug("packages: '%s'", packages) + return packages[0] if len(packages) == 1 else packages def update() -> None: @@ -898,7 +930,11 @@ def make_options_string(self) -> str: if self._gpg_key_filename: options["signed-by"] = self._gpg_key_filename - return f"[{' '.join([f'{k}={v}' for k,v in options.items()])}] " if options else "" + return ( + "[{}] ".format(" ".join(["{}={}".format(k, v) for k, v in options.items()])) + if options + else "" + ) @staticmethod def prefix_from_uri(uri: str) -> str: @@ -907,7 +943,7 @@ def prefix_from_uri(uri: str) -> str: path = ( uridetails.path.lstrip("/").replace("/", "-") if uridetails.path else uridetails.netloc ) - return f"/etc/apt/sources.list.d/{path}" + return "/etc/apt/sources.list.d/{}".format(path) @staticmethod def from_repo_line(repo_line: str, write_file: Optional[bool] = True) -> "DebianRepository": @@ -918,8 +954,8 @@ def from_repo_line(repo_line: str, write_file: Optional[bool] = True) -> "Debian write_file: boolean to enable writing the new repo to disk """ repo = RepositoryMapping._parse(repo_line, "UserInput") - fname = ( - f"{DebianRepository.prefix_from_uri(repo.uri)}-{repo.release.replace('/', '-')}.list" + fname = "{}-{}.list".format( + DebianRepository.prefix_from_uri(repo.uri), repo.release.replace("/", "-") ) repo.filename = fname @@ -927,14 +963,22 @@ def from_repo_line(repo_line: str, write_file: Optional[bool] = True) -> "Debian if repo.gpg_key: options["signed-by"] = repo.gpg_key - options_str = f"[{' '.join([f'{k}={v}' for k,v in options.items()])}] " if options else "" + # For Python 3.5 it's required to use sorted in the options dict in order to not have + # different results in the order of the options between executions. + options_str = ( + "[{}] ".format(" ".join(["{}={}".format(k, v) for k, v in sorted(options.items())])) + if options + else "" + ) if write_file: with open(fname, "wb") as f: f.write( - f"{'#' if not repo.enabled else ''}" - f"{repo.repotype} {options_str}{repo.uri} " - f"{repo.release} {' '.join(repo.groups)}\n".encode("utf-8") + ( + "{}".format("#" if not repo.enabled else "") + + "{} {}{} ".format(repo.repotype, options_str, repo.uri) + + "{} {}\n".format(repo.release, " ".join(repo.groups)) + ).encode("utf-8") ) return repo @@ -944,10 +988,12 @@ def disable(self) -> None: Disable it instead of removing from the repository file. """ - searcher = f"{self.repotype} {self.make_options_string()}{self.uri} {self.release}" + searcher = "{} {}{} {}".format( + self.repotype, self.make_options_string(), self.uri, self.release + ) for line in fileinput.input(self._filename, inplace=True): - if re.match(rf"^{re.escape(searcher)}\s", line): - print(f"# {line}", end="") + if re.match(r"^{}\s".format(re.escape(searcher)), line): + print("# {}".format(line), end="") else: print(line, end="") @@ -984,7 +1030,7 @@ def import_key(self, key: str) -> None: key_bytes = key.encode("utf-8") key_name = self._get_keyid_by_gpg_key(key_bytes) key_gpg = self._dearmor_gpg_key(key_bytes) - self._gpg_key_filename = f"/etc/apt/trusted.gpg.d/{key_name}.gpg" + self._gpg_key_filename = "/etc/apt/trusted.gpg.d/{}.gpg".format(key_name) self._write_apt_gpg_keyfile(key_name=self._gpg_key_filename, key_material=key_gpg) else: raise GPGKeyError("ASCII armor markers missing from GPG key") @@ -1002,7 +1048,7 @@ def import_key(self, key: str) -> None: key_asc = self._get_key_by_keyid(key) # write the key in GPG format so that apt-key list shows it key_gpg = self._dearmor_gpg_key(key_asc.encode("utf-8")) - self._gpg_key_filename = f"/etc/apt/trusted.gpg.d/{key}.gpg" + self._gpg_key_filename = "/etc/apt/trusted.gpg.d/{}.gpg".format(key) self._write_apt_gpg_keyfile(key_name=key, key_material=key_gpg) @staticmethod @@ -1015,7 +1061,12 @@ def _get_keyid_by_gpg_key(key_material: bytes) -> str: """ # Use the same gpg command for both Xenial and Bionic cmd = ["gpg", "--with-colons", "--with-fingerprint"] - ps = subprocess.run(cmd, stdout=PIPE, stderr=PIPE, input=key_material) + ps = subprocess.run( + cmd, + stdout=PIPE, + stderr=PIPE, + input=key_material, + ) out, err = ps.stdout.decode(), ps.stderr.decode() if "gpg: no valid OpenPGP data found." in err: raise GPGKeyError("Invalid GPG key material provided") @@ -1162,7 +1213,7 @@ def load(self, filename: str): except InvalidSourceError: skipped.append(n) else: - repo_identifier = f"{repo.repotype}-{repo.uri}-{repo.release}" + repo_identifier = "{}-{}-{}".format(repo.repotype, repo.uri, repo.release) self._repository_map[repo_identifier] = repo parsed.append(n) logger.debug("parsed repo: '%s'", repo_identifier) @@ -1238,8 +1289,8 @@ def add(self, repo: DebianRepository, default_filename: Optional[bool] = False) repo: a `DebianRepository` object default_filename: an (Optional) filename if the default is not desirable """ - new_filename = ( - f"{DebianRepository.prefix_from_uri(repo.uri)}-{repo.release.replace('/', '-')}.list" + new_filename = "{}-{}.list".format( + DebianRepository.prefix_from_uri(repo.uri), repo.release.replace("/", "-") ) fname = repo.filename or new_filename @@ -1250,12 +1301,14 @@ def add(self, repo: DebianRepository, default_filename: Optional[bool] = False) with open(fname, "wb") as f: f.write( - f"{'#' if not repo.enabled else ''}" - f"{repo.repotype} {repo.make_options_string()}{repo.uri} " - f"{repo.release} {' '.join(repo.groups)}\n".encode("utf-8") + ( + "{}".format("#" if not repo.enabled else "") + + "{} {}{} ".format(repo.repotype, repo.make_options_string(), repo.uri) + + "{} {}\n".format(repo.release, " ".join(repo.groups)) + ).encode("utf-8") ) - self._repository_map[f"{repo.repotype}-{repo.uri}-{repo.release}"] = repo + self._repository_map["{}-{}-{}".format(repo.repotype, repo.uri, repo.release)] = repo def disable(self, repo: DebianRepository) -> None: """Remove a repository. Disable by default. @@ -1263,12 +1316,14 @@ def disable(self, repo: DebianRepository) -> None: Args: repo: a `DebianRepository` to disable """ - searcher = f"{repo.repotype} {repo.make_options_string()}{repo.uri} {repo.release}" + searcher = "{} {}{} {}".format( + repo.repotype, repo.make_options_string(), repo.uri, repo.release + ) for line in fileinput.input(repo.filename, inplace=True): - if re.match(rf"^{re.escape(searcher)}\s", line): - print(f"# {line}", end="") + if re.match(r"^{}\s".format(re.escape(searcher)), line): + print("# {}".format(line), end="") else: print(line, end="") - self._repository_map[f"{repo.repotype}-{repo.uri}-{repo.release}"] = repo + self._repository_map["{}-{}-{}".format(repo.repotype, repo.uri, repo.release)] = repo diff --git a/lib/charms/operator_libs_linux/v0/systemd.py b/lib/charms/operator_libs_linux/v0/systemd.py deleted file mode 100644 index 58556b4e6..000000000 --- a/lib/charms/operator_libs_linux/v0/systemd.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright 2021 Canonical Ltd. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Abstractions for stopping, starting and managing system services via systemd. - -This library assumes that your charm is running on a platform that uses systemd. E.g., -Centos 7 or later, Ubuntu Xenial (16.04) or later. - -For the most part, we transparently provide an interface to a commonly used selection of -systemd commands, with a few shortcuts baked in. For example, service_pause and -service_resume with run the mask/unmask and enable/disable invocations. - -Example usage: -```python -from systemd import * - -# Start a service -if not service_running("mysql"): - success = service_start("mysql") - -# Attempt to reload a service, restarting if necessary -success = service_reload("nginx", restart_on_failure=True) - -``` - -""" - -import logging -import subprocess - -__all__ = [ # Don't export `service`. (It's not the intended way of using this lib.) - "service_pause", - "service_reload", - "service_restart", - "service_resume", - "service_running", - "service_start", - "service_stop", -] - -logger = logging.getLogger(__name__) - -# The unique Charmhub library identifier, never change it -LIBID = "045b0d179f6b4514a8bb9b48aee9ebaf" - -# Increment this major API version when introducing breaking changes -LIBAPI = 0 - -# Increment this PATCH version before using `charmcraft publish-lib` or reset -# to 0 if you are raising the major API version -LIBPATCH = 2 - - -def _popen_kwargs(): - return dict( - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - bufsize=1, - universal_newlines=True, - encoding="utf-8", - ) - - -def _service(action: str, service_name: str, now: bool = None, quiet: bool = None) -> bool: - """Control a system service. - - Args: - action: the action to take on the service - service_name: the name of the service to perform the action on - now: passes the --now flag to the shell invocation. - quiet: passes the --quiet flag to the shell invocation. - """ - cmd = ["systemctl", action, service_name] - if now is not None: - cmd.append("--now") - if quiet is not None: - cmd.append("--quiet") - if action != "is-active": - logger.debug("Attempting to {} '{}' with command {}.".format(action, service_name, cmd)) - else: - logger.debug("Checking if '{}' is active".format(service_name)) - - proc = subprocess.Popen(cmd, **_popen_kwargs()) - for line in iter(proc.stdout.readline, ""): - logger.debug(line) - - proc.wait() - return proc.returncode == 0 - - -def service_running(service_name: str) -> bool: - """Determine whether a system service is running. - - Args: - service_name: the name of the service - """ - return _service("is-active", service_name, quiet=True) - - -def service_start(service_name: str) -> bool: - """Start a system service. - - Args: - service_name: the name of the service to stop - """ - return _service("start", service_name) - - -def service_stop(service_name: str) -> bool: - """Stop a system service. - - Args: - service_name: the name of the service to stop - """ - return _service("stop", service_name) - - -def service_restart(service_name: str) -> bool: - """Restart a system service. - - Args: - service_name: the name of the service to restart - """ - return _service("restart", service_name) - - -def service_reload(service_name: str, restart_on_failure: bool = False) -> bool: - """Reload a system service, optionally falling back to restart if reload fails. - - Args: - service_name: the name of the service to reload - restart_on_failure: boolean indicating whether to fallback to a restart if the - reload fails. - """ - service_result = _service("reload", service_name) - if not service_result and restart_on_failure: - service_result = _service("restart", service_name) - return service_result - - -def service_pause(service_name: str) -> bool: - """Pause a system service. - - Stop it, and prevent it from starting again at boot. - - Args: - service_name: the name of the service to pause - """ - _service("disable", service_name, now=True) - _service("mask", service_name) - return not service_running(service_name) - - -def service_resume(service_name: str) -> bool: - """Resume a system service. - - Re-enable starting again at boot. Start the service. - - Args: - service_name: the name of the service to resume - """ - _service("unmask", service_name) - _service("enable", service_name, now=True) - return service_running(service_name) diff --git a/lib/charms/operator_libs_linux/v1/systemd.py b/lib/charms/operator_libs_linux/v1/systemd.py index 7564060ef..5be34c17c 100644 --- a/lib/charms/operator_libs_linux/v1/systemd.py +++ b/lib/charms/operator_libs_linux/v1/systemd.py @@ -102,18 +102,12 @@ def _systemctl( logger.debug("Checking if '{}' is active".format(service_name)) proc = subprocess.Popen(cmd, **_popen_kwargs()) - - proc.wait() - last_line = "" for line in iter(proc.stdout.readline, ""): last_line = line logger.debug(line) - logger.debug("proc.stdin %s", proc.stdin) - logger.debug("proc.stdin %s", proc.stdout) - logger.debug("proc.stderr %s", proc.stderr) - logger.debug("proc.returncode %s", proc.returncode) + proc.wait() if sub_cmd == "is-active": # If we are just checking whether a service is running, return True/False, rather diff --git a/lib/charms/tls_certificates_interface/v1/tls_certificates.py b/lib/charms/tls_certificates_interface/v1/tls_certificates.py index 4ab863016..a03b318eb 100644 --- a/lib/charms/tls_certificates_interface/v1/tls_certificates.py +++ b/lib/charms/tls_certificates_interface/v1/tls_certificates.py @@ -240,7 +240,7 @@ def _on_certificate_expiring(self, event: CertificateExpiringEvent) -> None: # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 8 +LIBPATCH = 9 REQUIRER_JSON_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", @@ -828,6 +828,14 @@ def _relation_data_is_valid(certificates_data: dict) -> bool: except exceptions.ValidationError: return False + def revoke_all_certificates(self) -> None: + """Revokes all certificates of this provider. + + This method is meant to be used when the Root CA has changed. + """ + for relation in self.model.relations[self.relationship_name]: + relation.data[self.model.app]["certificates"] = json.dumps([]) + def set_relation_certificate( self, certificate: str, @@ -895,6 +903,7 @@ def _on_relation_changed(self, event: RelationChangedEvent) -> None: Returns: None """ + assert event.unit is not None requirer_relation_data = _load_relation_data(event.relation.data[event.unit]) provider_relation_data = _load_relation_data(event.relation.data[self.charm.app]) if not self._relation_data_is_valid(requirer_relation_data): @@ -1146,7 +1155,7 @@ def _on_relation_changed(self, event: RelationChangedEvent) -> None: if not self._relation_data_is_valid(provider_relation_data): logger.warning( f"Provider relation data did not pass JSON Schema validation: " - f"{event.relation.data[event.app]}" + f"{event.relation.data[relation.app]}" ) return requirer_csrs = [ diff --git a/metadata.yaml b/metadata.yaml index 32e048360..946f7394f 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -1,4 +1,4 @@ -# Copyright 2021 Canonical Ltd. +# Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. name: mongodb display-name: MongoDB diff --git a/pyproject.toml b/pyproject.toml index f974d2ae7..39befc12e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,4 @@ -# Copyright 2021 Canonical Ltd. +# Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. # Testing tools configuration diff --git a/src/charm.py b/src/charm.py index a7d6140f6..210542d19 100755 --- a/src/charm.py +++ b/src/charm.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """Charm code for MongoDB service.""" -# Copyright 2021 Canonical Ltd. +# Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. import json import logging diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index 79d258781..69a2f62af 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -1,4 +1,4 @@ -# Copyright 2021 Canonical Ltd. +# Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. from pathlib import Path diff --git a/tests/integration/test_charm.py b/tests/integration/test_charm.py index bcf0b8a5b..30d4158a9 100644 --- a/tests/integration/test_charm.py +++ b/tests/integration/test_charm.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright 2021 Canonical Ltd. +# Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. import logging diff --git a/tests/unit/helpers.py b/tests/unit/helpers.py index 613901b16..7347b7c59 100644 --- a/tests/unit/helpers.py +++ b/tests/unit/helpers.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright 2021 Canonical Ltd. +# Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. """Helper functions for writing tests.""" diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index 6a8096dea..41ee9ff2c 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -1,4 +1,4 @@ -# Copyright 2021 Canonical Ltd. +# Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. import unittest diff --git a/tests/unit/test_mongodb_lib.py b/tests/unit/test_mongodb_lib.py index 79c440b00..d65e924d1 100644 --- a/tests/unit/test_mongodb_lib.py +++ b/tests/unit/test_mongodb_lib.py @@ -1,4 +1,4 @@ -# Copyright 2021 Canonical Ltd. +# Copyright 2022 Canonical Ltd. # See LICENSE file for licensing details. import unittest