From 407bc49f133c80c2b888765572bd464129e07a11 Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Tue, 9 Jul 2024 15:55:55 +0000 Subject: [PATCH] Revert "Upgrade external libraries" This reverts commit 6eb7f3b72b08d146df180063c8df4ac81f73a61d. Reason for revert: Kubernetes service patch broke ovn-* relations Upgrade to kubernetes_service_patch changed the way the service loadbalancer is setup. Before, it updated the application service, now it creates a new one. This broke ovn-* relations to outside the cluster because the `ingress-bound-address` is now filled with a k8s svc ip address, not reachable from the outside. Change-Id: I38819cfcf647215e1eec595c2457300cb9058f90 --- .../data_platform_libs/v0/data_interfaces.py | 25 +------ .../grafana_k8s/v0/grafana_dashboard.py | 3 +- .../v1/kubernetes_service_patch.py | 69 ++----------------- .../lib/charms/operator_libs_linux/v2/snap.py | 14 ++-- .../prometheus_k8s/v0/prometheus_scrape.py | 6 +- .../v3/tls_certificates.py | 33 +++++---- .../lib/charms/traefik_k8s/v2/ingress.py | 4 +- .../lib/charms/vault_k8s/v0/vault_kv.py | 43 +++--------- rebuild | 2 +- 9 files changed, 49 insertions(+), 150 deletions(-) diff --git a/libs/external/lib/charms/data_platform_libs/v0/data_interfaces.py b/libs/external/lib/charms/data_platform_libs/v0/data_interfaces.py index a2162aa0..59a97226 100644 --- a/libs/external/lib/charms/data_platform_libs/v0/data_interfaces.py +++ b/libs/external/lib/charms/data_platform_libs/v0/data_interfaces.py @@ -331,7 +331,7 @@ LIBAPI = 0 # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 38 +LIBPATCH = 37 PYDEPS = ["ops>=2.0.0"] @@ -2606,14 +2606,6 @@ class DatabaseProviderData(ProviderData): """ self.update_relation_data(relation_id, {"version": version}) - def set_subordinated(self, relation_id: int) -> None: - """Raises the subordinated flag in the application relation databag. - - Args: - relation_id: the identifier for a particular relation. - """ - self.update_relation_data(relation_id, {"subordinated": "true"}) - class DatabaseProviderEventHandlers(EventHandlers): """Provider-side of the database relation handlers.""" @@ -2850,21 +2842,6 @@ class DatabaseRequirerEventHandlers(RequirerEventHandlers): def _on_relation_changed_event(self, event: RelationChangedEvent) -> None: """Event emitted when the database relation has changed.""" - is_subordinate = False - remote_unit_data = None - for key in event.relation.data.keys(): - if isinstance(key, Unit) and not key.name.startswith(self.charm.app.name): - remote_unit_data = event.relation.data[key] - elif isinstance(key, Application) and key.name != self.charm.app.name: - is_subordinate = event.relation.data[key].get("subordinated") == "true" - - if is_subordinate: - if not remote_unit_data: - return - - if remote_unit_data.get("state") != "ready": - return - # Check which data has changed to emit customs events. diff = self._diff(event) diff --git a/libs/external/lib/charms/grafana_k8s/v0/grafana_dashboard.py b/libs/external/lib/charms/grafana_k8s/v0/grafana_dashboard.py index dfc32ddc..1f1bc4f0 100644 --- a/libs/external/lib/charms/grafana_k8s/v0/grafana_dashboard.py +++ b/libs/external/lib/charms/grafana_k8s/v0/grafana_dashboard.py @@ -219,7 +219,7 @@ LIBAPI = 0 # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 36 +LIBPATCH = 35 logger = logging.getLogger(__name__) @@ -1050,7 +1050,6 @@ class GrafanaDashboardProvider(Object): self.framework.observe(self._charm.on.leader_elected, self._update_all_dashboards_from_dir) self.framework.observe(self._charm.on.upgrade_charm, self._update_all_dashboards_from_dir) - self.framework.observe(self._charm.on.config_changed, self._update_all_dashboards_from_dir) self.framework.observe( self._charm.on[self._relation_name].relation_created, diff --git a/libs/external/lib/charms/observability_libs/v1/kubernetes_service_patch.py b/libs/external/lib/charms/observability_libs/v1/kubernetes_service_patch.py index a2928375..2cce729e 100644 --- a/libs/external/lib/charms/observability_libs/v1/kubernetes_service_patch.py +++ b/libs/external/lib/charms/observability_libs/v1/kubernetes_service_patch.py @@ -6,7 +6,7 @@ This library is designed to enable developers to more simply patch the Kubernetes Service created by Juju during the deployment of a sidecar charm. When sidecar charms are deployed, Juju creates a service named after the application in the namespace (named after the Juju model). This service by -default contains a "placeholder" port, which is 65535/TCP. +default contains a "placeholder" port, which is 65536/TCP. When modifying the default set of resources managed by Juju, one must consider the lifecycle of the charm. In this case, any modifications to the default service (created during deployment), will be @@ -109,26 +109,6 @@ class SomeCharm(CharmBase): # ... ``` -Creating a new k8s lb service instead of patching the one created by juju -Service name is optional. If not provided, it defaults to {app_name}-lb. -If provided and equal to app_name, it also defaults to {app_name}-lb to prevent conflicts with the Juju default service. -```python -from charms.observability_libs.v1.kubernetes_service_patch import KubernetesServicePatch -from lightkube.models.core_v1 import ServicePort - -class SomeCharm(CharmBase): - def __init__(self, *args): - # ... - port = ServicePort(int(self.config["charm-config-port"]), name=f"{self.app.name}") - self.service_patcher = KubernetesServicePatch( - self, - [port], - service_type="LoadBalancer", - service_name="application-lb" - ) - # ... -``` - Additionally, you may wish to use mocks in your charm's unit testing to ensure that the library does not try to make any API calls, or open any files during testing that are unlikely to be present, and could break your tests. The easiest way to do this is during your test `setUp`: @@ -166,7 +146,7 @@ LIBAPI = 1 # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 11 +LIBPATCH = 9 ServiceType = Literal["ClusterIP", "LoadBalancer"] @@ -206,15 +186,10 @@ class KubernetesServicePatch(Object): """ super().__init__(charm, "kubernetes-service-patch") self.charm = charm - self.service_name = service_name or self._app - # To avoid conflicts with the default Juju service, append "-lb" to the service name. - # The Juju application name is retained for the default service created by Juju. - if self.service_name == self._app and service_type == "LoadBalancer": - self.service_name = f"{self._app}-lb" - self.service_type = service_type + self.service_name = service_name if service_name else self._app self.service = self._service_object( ports, - self.service_name, + service_name, service_type, additional_labels, additional_selectors, @@ -227,7 +202,6 @@ class KubernetesServicePatch(Object): self.framework.observe(charm.on.install, self._patch) self.framework.observe(charm.on.upgrade_charm, self._patch) self.framework.observe(charm.on.update_status, self._patch) - self.framework.observe(charm.on.stop, self._remove_service) # apply user defined events if refresh_event: @@ -303,10 +277,7 @@ class KubernetesServicePatch(Object): if self._is_patched(client): return if self.service_name != self._app: - if not self.service_type == "LoadBalancer": - self._delete_and_create_service(client) - else: - self._create_lb_service(client) + self._delete_and_create_service(client) client.patch(Service, self.service_name, self.service, patch_type=PatchType.MERGE) except ApiError as e: if e.status.code == 403: @@ -323,12 +294,6 @@ class KubernetesServicePatch(Object): client.delete(Service, self._app, namespace=self._namespace) client.create(service) - def _create_lb_service(self, client: Client): - try: - client.get(Service, self.service_name, namespace=self._namespace) - except ApiError: - client.create(self.service) - def is_patched(self) -> bool: """Reports if the service patch has been applied. @@ -356,30 +321,6 @@ class KubernetesServicePatch(Object): ] # noqa: E501 return expected_ports == fetched_ports - def _remove_service(self, _): - """Remove a Kubernetes service associated with this charm. - - Specifically designed to delete the load balancer service created by the charm, since Juju only deletes the - default ClusterIP service and not custom services. - - Returns: - None - - Raises: - ApiError: for deletion errors, excluding when the service is not found (404 Not Found). - """ - client = Client() # pyright: ignore - - try: - client.delete(Service, self.service_name, namespace=self._namespace) - except ApiError as e: - if e.status.code == 404: - # Service not found, so no action needed - pass - else: - # Re-raise for other statuses - raise - @property def _app(self) -> str: """Name of the current Juju application. diff --git a/libs/external/lib/charms/operator_libs_linux/v2/snap.py b/libs/external/lib/charms/operator_libs_linux/v2/snap.py index 9d09a78d..ef426775 100644 --- a/libs/external/lib/charms/operator_libs_linux/v2/snap.py +++ b/libs/external/lib/charms/operator_libs_linux/v2/snap.py @@ -83,7 +83,7 @@ LIBAPI = 2 # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 7 +LIBPATCH = 5 # Regex to locate 7-bit C1 ANSI sequences @@ -319,10 +319,7 @@ class Snap(object): Default is to return a string. """ if typed: - args = ["-d"] - if key: - args.append(key) - config = json.loads(self._snap("get", args)) + config = json.loads(self._snap("get", ["-d", key])) if key: return config.get(key) return config @@ -587,16 +584,13 @@ class Snap(object): "Installing snap %s, revision %s, tracking %s", self._name, revision, channel ) self._install(channel, cohort, revision) - logger.info("The snap installation completed successfully") - elif revision is None or revision != self._revision: + else: # The snap is installed, but we are changing it (e.g., switching channels). logger.info( "Refreshing snap %s, revision %s, tracking %s", self._name, revision, channel ) self._refresh(channel=channel, cohort=cohort, revision=revision, devmode=devmode) - logger.info("The snap refresh completed successfully") - else: - logger.info("Refresh of snap %s was unnecessary", self._name) + logger.info("The snap installation completed successfully") self._update_snap_apps() self._state = state diff --git a/libs/external/lib/charms/prometheus_k8s/v0/prometheus_scrape.py b/libs/external/lib/charms/prometheus_k8s/v0/prometheus_scrape.py index e3d35c6f..be967686 100644 --- a/libs/external/lib/charms/prometheus_k8s/v0/prometheus_scrape.py +++ b/libs/external/lib/charms/prometheus_k8s/v0/prometheus_scrape.py @@ -178,7 +178,7 @@ configure the following scrape-related settings, which behave as described by th - `scrape_timeout` - `proxy_url` - `relabel_configs` -- `metric_relabel_configs` +- `metrics_relabel_configs` - `sample_limit` - `label_limit` - `label_name_length_limit` @@ -362,7 +362,7 @@ LIBAPI = 0 # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 47 +LIBPATCH = 46 PYDEPS = ["cosl"] @@ -377,7 +377,7 @@ ALLOWED_KEYS = { "scrape_timeout", "proxy_url", "relabel_configs", - "metric_relabel_configs", + "metrics_relabel_configs", "sample_limit", "label_limit", "label_name_length_limit", diff --git a/libs/external/lib/charms/tls_certificates_interface/v3/tls_certificates.py b/libs/external/lib/charms/tls_certificates_interface/v3/tls_certificates.py index aa4704c7..33f34b62 100644 --- a/libs/external/lib/charms/tls_certificates_interface/v3/tls_certificates.py +++ b/libs/external/lib/charms/tls_certificates_interface/v3/tls_certificates.py @@ -277,13 +277,13 @@ juju relate """ # noqa: D405, D410, D411, D214, D416 import copy -import ipaddress import json import logging import uuid from contextlib import suppress from dataclasses import dataclass from datetime import datetime, timedelta, timezone +from ipaddress import IPv4Address from typing import List, Literal, Optional, Union from cryptography import x509 @@ -317,7 +317,7 @@ LIBAPI = 3 # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 17 +LIBPATCH = 15 PYDEPS = ["cryptography", "jsonschema"] @@ -1077,7 +1077,7 @@ def generate_csr( # noqa: C901 if sans_oid: _sans.extend([x509.RegisteredID(x509.ObjectIdentifier(san)) for san in sans_oid]) if sans_ip: - _sans.extend([x509.IPAddress(ipaddress.ip_address(san)) for san in sans_ip]) + _sans.extend([x509.IPAddress(IPv4Address(san)) for san in sans_ip]) if sans: _sans.extend([x509.DNSName(san) for san in sans]) if sans_dns: @@ -1109,16 +1109,25 @@ def csr_matches_certificate(csr: str, cert: str) -> bool: Returns: bool: True/False depending on whether the CSR matches the certificate. """ - csr_object = x509.load_pem_x509_csr(csr.encode("utf-8")) - cert_object = x509.load_pem_x509_certificate(cert.encode("utf-8")) + try: + csr_object = x509.load_pem_x509_csr(csr.encode("utf-8")) + cert_object = x509.load_pem_x509_certificate(cert.encode("utf-8")) - if csr_object.public_key().public_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) != cert_object.public_key().public_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ): + if csr_object.public_key().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo, + ) != cert_object.public_key().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo, + ): + return False + if ( + csr_object.public_key().public_numbers().n # type: ignore[union-attr] + != cert_object.public_key().public_numbers().n # type: ignore[union-attr] + ): + return False + except ValueError: + logger.warning("Could not load certificate or CSR.") return False return True diff --git a/libs/external/lib/charms/traefik_k8s/v2/ingress.py b/libs/external/lib/charms/traefik_k8s/v2/ingress.py index 407cfb5b..ed464eac 100644 --- a/libs/external/lib/charms/traefik_k8s/v2/ingress.py +++ b/libs/external/lib/charms/traefik_k8s/v2/ingress.py @@ -72,7 +72,7 @@ LIBAPI = 2 # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 13 +LIBPATCH = 12 PYDEPS = ["pydantic"] @@ -590,7 +590,7 @@ class IngressPerAppProvider(_IngressPerAppBase): if PYDANTIC_IS_V1: results[ingress_relation.app.name] = ingress_data.ingress.dict() else: - results[ingress_relation.app.name] = ingress_data.ingress.model_dump(mode="json") + results[ingress_relation.app.name] = ingress_data.ingress.model_dump(mode=json) # type: ignore return results diff --git a/libs/external/lib/charms/vault_k8s/v0/vault_kv.py b/libs/external/lib/charms/vault_k8s/v0/vault_kv.py index 03b9bbd7..99ac0e04 100644 --- a/libs/external/lib/charms/vault_k8s/v0/vault_kv.py +++ b/libs/external/lib/charms/vault_k8s/v0/vault_kv.py @@ -68,7 +68,7 @@ class ExampleRequirerCharm(CharmBase): unit_credentials = self.interface.get_unit_credentials(relation) # unit_credentials is a juju secret id secret = self.model.get_secret(id=unit_credentials) - secret_content = secret.get_content(refresh=True) + secret_content = secret.get_content() role_id = secret_content["role-id"] role_secret_id = secret_content["role-secret-id"] @@ -99,7 +99,7 @@ class ExampleRequirerCharm(CharmBase): def get_nonce(self): secret = self.model.get_secret(label=NONCE_SECRET_LABEL) - nonce = secret.get_content(refresh=True)["nonce"] + nonce = secret.get_content()["nonce"] return nonce @@ -132,7 +132,7 @@ LIBAPI = 0 # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 7 +LIBPATCH = 5 PYDEPS = ["pydantic", "pytest-interface-tester"] @@ -163,9 +163,6 @@ class VaultKvProviderSchema(BaseModel): ca_certificate: str = Field( description="The CA certificate to use when validating the Vault server's certificate." ) - egress_subnet: str = Field( - description="The CIDR allowed by the role." - ) credentials: Json[Mapping[str, str]] = Field( description=( "Mapping of unit name and credentials for that unit." @@ -355,18 +352,7 @@ class VaultKvProvides(ops.Object): relation.data[self.charm.app]["mount"] = mount - def set_egress_subnet(self, relation: ops.Relation, egress_subnet: str): - """Set the egress_subnet on the relation.""" - if not self.charm.unit.is_leader(): - return - relation.data[self.charm.app]["egress_subnet"] = egress_subnet - - def set_unit_credentials( - self, - relation: ops.Relation, - nonce: str, - secret: ops.Secret, - ): + def set_unit_credentials(self, relation: ops.Relation, nonce: str, secret: ops.Secret): """Set the unit credentials on the relation.""" if not self.charm.unit.is_leader(): return @@ -540,11 +526,7 @@ class VaultKvRequires(ops.Object): self.mount_suffix = mount_suffix self.framework.observe( self.charm.on[relation_name].relation_joined, - self._handle_relation, - ) - self.framework.observe( - self.charm.on.config_changed, - self._handle_relation, + self._on_vault_kv_relation_joined, ) self.framework.observe( self.charm.on[relation_name].relation_changed, @@ -563,20 +545,17 @@ class VaultKvRequires(ops.Object): """Set the egress_subnet on the relation.""" relation.data[self.charm.unit]["egress_subnet"] = egress_subnet - def _handle_relation(self, event: ops.EventBase): - """Run when a new unit joins the relation or when the address of the unit changes. + def _on_vault_kv_relation_joined(self, event: ops.RelationJoinedEvent): + """Handle relation joined. Set the secret backend in the application databag if we are the leader. - Emit the connected event. + Always update the egress_subnet in the unit databag. """ - relation = self.model.get_relation(relation_name=self.relation_name) - if not relation: - return if self.charm.unit.is_leader(): - relation.data[self.charm.app]["mount_suffix"] = self.mount_suffix + event.relation.data[self.charm.app]["mount_suffix"] = self.mount_suffix self.on.connected.emit( - relation.id, - relation.name, + event.relation.id, + event.relation.name, ) def _on_vault_kv_relation_changed(self, event: ops.RelationChangedEvent): diff --git a/rebuild b/rebuild index 4bf84b20..14c25454 100644 --- a/rebuild +++ b/rebuild @@ -1,3 +1,3 @@ # This file is used to trigger a build. # Change uuid to trigger a new build on every charms. -32faabc5-4c45-430a-827e-9d917c2a6c3b +03381028-42a3-4a2d-9231-7a2642ede8c7