From 87e814ce535b05e120f103fd0a63253cceedbe4b Mon Sep 17 00:00:00 2001 From: Daniela Plascencia Date: Wed, 17 Apr 2024 12:13:12 +0200 Subject: [PATCH 1/7] refactor: use k8s_service_info lib instead of SDI Use the k8s_service_info for receiving the MLMD GRPC Service info instead of using the SDI, as it will stop being supported soon. This commit also ensures that mlmd runs with trust=True in the integration tests. Fixes #413 --- charms/kfp-metadata-writer/charmcraft.yaml | 10 +- .../charms/mlops_libs/v0/k8s_service_info.py | 385 ++++++++++++++++++ charms/kfp-metadata-writer/metadata.yaml | 16 +- .../kfp-metadata-writer/requirements-unit.in | 17 +- .../kfp-metadata-writer/requirements-unit.txt | 117 ++++-- charms/kfp-metadata-writer/requirements.in | 3 + charms/kfp-metadata-writer/requirements.txt | 15 +- charms/kfp-metadata-writer/src/charm.py | 19 +- .../components/k8s_service_info_component.py | 54 +++ .../tests/unit/test_operator.py | 64 ++- .../bundles/kfp_latest_edge.yaml.j2 | 2 +- 11 files changed, 612 insertions(+), 90 deletions(-) create mode 100644 charms/kfp-metadata-writer/lib/charms/mlops_libs/v0/k8s_service_info.py create mode 100644 charms/kfp-metadata-writer/src/components/k8s_service_info_component.py diff --git a/charms/kfp-metadata-writer/charmcraft.yaml b/charms/kfp-metadata-writer/charmcraft.yaml index 24d0fec5..459ec101 100644 --- a/charms/kfp-metadata-writer/charmcraft.yaml +++ b/charms/kfp-metadata-writer/charmcraft.yaml @@ -1,6 +1,7 @@ -# Learn more about charmcraft.yaml configuration at: -# https://juju.is/docs/sdk/charmcraft-config -type: "charm" +# Copyright 2024 Canonical Ltd. +# See LICENSE file for licensing details. + +type: charm bases: - build-on: - name: "ubuntu" @@ -10,4 +11,5 @@ bases: channel: "20.04" parts: charm: - charm-python-packages: [setuptools, pip] # Fixes install of some packages + charm-python-packages: [setuptools, pip] + build-packages: [cargo, rustc, pkg-config, libffi-dev, libssl-dev] diff --git a/charms/kfp-metadata-writer/lib/charms/mlops_libs/v0/k8s_service_info.py b/charms/kfp-metadata-writer/lib/charms/mlops_libs/v0/k8s_service_info.py new file mode 100644 index 00000000..81784778 --- /dev/null +++ b/charms/kfp-metadata-writer/lib/charms/mlops_libs/v0/k8s_service_info.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python3 +# Copyright 2024 Canonical Ltd. +# See LICENSE file for licensing details. + +"""Library for sharing Kubernetes Services information. + +This library offers a Python API for providing and requesting information about +any Kubernetes Service resource. +The default relation name is `k8s-svc-info` and it's recommended to use that name, +though if changed, you must ensure to pass the correct name when instantiating the +provider and requirer classes, as well as in `metadata.yaml`. + +## Getting Started + +### Fetching the library with charmcraft + +Using charmcraft you can: +```shell +charmcraft fetch-lib charms.mlops_libs.v0.k8s_service_info +``` + +## Using the library as requirer + +### Add relation to metadata.yaml +```yaml +requires: + k8s-svc-info: + interface: k8s-service + limit: 1 +``` + +### Instantiate the KubernetesServiceInfoRequirer class in charm.py + +```python +from ops.charm import CharmBase +from charms.mlops_libs.v0.kubernetes_service_info import KubernetesServiceInfoRequirer, KubernetesServiceInfoRelationError + +class RequirerCharm(CharmBase): + def __init__(self, *args): + self._k8s_svc_info_requirer = KubernetesServiceInfoRequirer(self) + self.framework.observe(self.on.some_event_emitted, self.some_event_function) + + def some_event_function(): + # use the getter function wherever the info is needed + try: + k8s_svc_info_data = self._k8s_svc_info_requirer.get_data() + except KubernetesServiceInfoRelationError as error: + "your error handler goes here" +``` + +## Using the library as provider + +### Add relation to metadata.yaml +```yaml +provides: + k8s-svc-info: + interface: k8s-service +``` + +### Instantiate the KubernetesServiceInfoProvider class in charm.py + +```python +from ops.charm import CharmBase +from charms.mlops_libs.v0.kubernetes_service_info import KubernetesServiceInfoProvider, KubernetesServiceInfoRelationError + +class ProviderCharm(CharmBase): + def __init__(self, *args, **kwargs): + ... + self._k8s_svc_info_provider = KubernetesServiceInfoProvider(self) + self.observe(self.on.some_event, self._some_event_handler) + def _some_event_handler(self, ...): + # This will update the relation data bag with the Service name and port + try: + self._k8s_svc_info_provider.send_data(name, port) + except KubernetesServiceInfoRelationError as error: + "your error handler goes here" +``` + +## Relation data + +The data shared by this library is: +* name: the name of the Kubernetes Service + as it appears in the resource metadata, e.g. "metadata-grpc-service". +* port: the port of the Kubernetes Service +""" + +import logging +from typing import List, Optional, Union + +from ops.charm import CharmBase, RelationEvent +from ops.framework import BoundEvent, EventSource, Object, ObjectEvents +from ops.model import Relation +from pydantic import BaseModel + +# The unique Charmhub library identifier, never change it +LIBID = "f5c3f6cc023e40468d6f9a871e8afcd0" + +# 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 = 1 + +# Default relation and interface names. If changed, consistency must be kept +# across the provider and requirer. +DEFAULT_RELATION_NAME = "k8s-service-info" +DEFAULT_INTERFACE_NAME = "k8s-service" +REQUIRED_ATTRIBUTES = ["name", "port"] + +logger = logging.getLogger(__name__) + + +class KubernetesServiceInfoRelationError(Exception): + """Base exception class for any relation error handled by this library.""" + + pass + + +class KubernetesServiceInfoRelationMissingError(KubernetesServiceInfoRelationError): + """Exception to raise when the relation is missing on either end.""" + + def __init__(self): + self.message = "Missing relation with a k8s service info provider." + super().__init__(self.message) + + +class KubernetesServiceInfoRelationDataMissingError(KubernetesServiceInfoRelationError): + """Exception to raise when there is missing data in the relation data bag.""" + + def __init__(self, message): + self.message = message + super().__init__(self.message) + + +class KubernetesServiceInfoUpdatedEvent(RelationEvent): + """Indicates the Kubernetes Service Info data was updated.""" + + +class KubernetesServiceInfoEvents(ObjectEvents): + """Events for the Kubernetes Service Info library.""" + + updated = EventSource(KubernetesServiceInfoUpdatedEvent) + + +class KubernetesServiceInfoObject(BaseModel): + """Representation of a Kubernetes Service info object. + + Args: + name: The name of the Service + port: The port of the Service + """ + + name: str + port: str + + +class KubernetesServiceInfoRequirer(Object): + """Implement the Requirer end of the Kubernetes Service Info relation. + + Observes the relation events and get data of a related application. + + This library emits: + * KubernetesServiceInfoUpdatedEvent: when data received on the relation is updated. + + Args: + charm (CharmBase): the provider application + refresh_event: (list, optional): list of BoundEvents that this manager should handle. + Use this to update the data sent on this relation on demand. + relation_name (str, optional): the name of the relation + + Attributes: + charm (CharmBase): variable for storing the requirer application + relation_name (str): variable for storing the name of the relation + """ + + on = KubernetesServiceInfoEvents() + + def __init__( + self, + charm: CharmBase, + refresh_event: Optional[Union[BoundEvent, List[BoundEvent]]] = None, + relation_name: Optional[str] = DEFAULT_RELATION_NAME, + ): + super().__init__(charm, relation_name) + self._charm = charm + self._relation_name = relation_name + self._requirer_wrapper = KubernetesServiceInfoRequirerWrapper( + self._charm, self._relation_name + ) + + self.framework.observe( + self._charm.on[self._relation_name].relation_changed, self._on_relation_changed + ) + + self.framework.observe( + self._charm.on[self._relation_name].relation_broken, self._on_relation_broken + ) + + if refresh_event: + if not isinstance(refresh_event, (tuple, list)): + refresh_event = [refresh_event] + for evt in refresh_event: + self.framework.observe(evt, self._on_relation_changed) + + def get_data(self) -> KubernetesServiceInfoObject: + """Return a KubernetesServiceInfoObject.""" + return self._requirer_wrapper.get_data() + + def _on_relation_changed(self, event: BoundEvent) -> None: + """Handle relation-changed event for this relation.""" + self.on.updated.emit(event.relation) + + def _on_relation_broken(self, event: BoundEvent) -> None: + """Handle relation-broken event for this relation.""" + self.on.updated.emit(event.relation) + + +class KubernetesServiceInfoRequirerWrapper(Object): + """Wrapper for the relation data getting logic. + + Args: + charm (CharmBase): the requirer application + relation_name (str, optional): the name of the relation + + Attributes: + relation_name (str): variable for storing the name of the relation + """ + + def __init__(self, charm, relation_name: Optional[str] = DEFAULT_RELATION_NAME): + super().__init__(charm, relation_name) + self.relation_name = relation_name + + @staticmethod + def _validate_relation(relation: Relation) -> None: + """Series of checks for the relation and relation data. + + Args: + relation (Relation): the relation object to run the checks on + + Raises: + KubernetesServiceInfoRelationDataMissingError if data is missing or incomplete + KubernetesServiceInfoRelationMissingError: if there is no related application + """ + # Raise if there is no related application + if not relation: + raise KubernetesServiceInfoRelationMissingError() + + # Extract remote app information from relation + remote_app = relation.app + # Get relation data from remote app + relation_data = relation.data[remote_app] + + # Raise if there is no data found in the relation data bag + if not relation_data: + raise KubernetesServiceInfoRelationDataMissingError( + f"No data found in relation {relation.name} data bag." + ) + + # Check if the relation data contains the expected attributes + missing_attributes = [ + attribute for attribute in REQUIRED_ATTRIBUTES if attribute not in relation_data + ] + if missing_attributes: + raise KubernetesServiceInfoRelationDataMissingError( + f"Missing attributes: {missing_attributes} in relation {relation.name}" + ) + + def get_data(self) -> KubernetesServiceInfoObject: + """Return a KubernetesServiceInfoObject containing Kubernetes Service information. + + Raises: + KubernetesServiceInfoRelationDataMissingError: if data is missing entirely or some attributes + KubernetesServiceInfoRelationMissingError: if there is no related application + ops.model.TooManyRelatedAppsError: if there is more than one related application + """ + # Validate relation data + # Raises TooManyRelatedAppsError if related to more than one app + relation = self.model.get_relation(self.relation_name) + + self._validate_relation(relation=relation) + + # Get relation data from remote app + relation_data = relation.data[relation.app] + + return KubernetesServiceInfoObject(name=relation_data["name"], port=relation_data["port"]) + + +class KubernetesServiceInfoProvider(Object): + """Implement the Provider end of the Kubernetes Service Info relation. + + Observes relation events to send data to related applications. + + Args: + charm (CharmBase): the provider application + name (str): the name of the Kubernetes Service the provider knows about + port (str): the port number of the Kubernetes Service the provider knows about + refresh_event: (list, optional): list of BoundEvents that this manager should handle. Use this to update + the data sent on this relation on demand. + relation_name (str, optional): the name of the relation + + Attributes: + charm (CharmBase): variable for storing the provider application + relation_name (str): variable for storing the name of the relation + """ + + def __init__( + self, + charm: CharmBase, + name: str, + port: str, + refresh_event: Optional[Union[BoundEvent, List[BoundEvent]]] = None, + relation_name: Optional[str] = DEFAULT_RELATION_NAME, + ): + super().__init__(charm, relation_name) + self.charm = charm + self.relation_name = relation_name + self._provider_wrapper = KubernetesServiceInfoProviderWrapper( + self.charm, self.relation_name + ) + self._svc_name = name + self._svc_port = port + + self.framework.observe(self.charm.on.leader_elected, self._send_data) + + self.framework.observe(self.charm.on[self.relation_name].relation_created, self._send_data) + + if refresh_event: + if not isinstance(refresh_event, (tuple, list)): + refresh_event = [refresh_event] + for evt in refresh_event: + self.framework.observe(evt, self._send_data) + + def _send_data(self, _) -> None: + """Serve as an event handler for sending the Kubernetes Service information.""" + self._provider_wrapper.send_data(self._svc_name, self._svc_port) + + +class KubernetesServiceInfoProviderWrapper(Object): + """Wrapper for the relation data sending logic. + + Args: + charm (CharmBase): the provider application + relation_name (str, optional): the name of the relation + + Attributes: + charm (CharmBase): variable for storing the provider application + relation_name (str): variable for storing the name of the relation + """ + + def __init__(self, charm: CharmBase, relation_name: Optional[str] = DEFAULT_RELATION_NAME): + super().__init__(charm, relation_name) + self.charm = charm + self.relation_name = relation_name + + def send_data( + self, + name: str, + port: str, + ) -> None: + """Update the relation data bag with data from a Kubernetes Service. + + This method will complete successfully even if there are no related applications. + + Args: + name (str): the name of the Kubernetes Service the provider knows about + port (str): the port number of the Kubernetes Service the provider knows about + """ + # Validate unit is leader to send data; otherwise return + if not self.charm.model.unit.is_leader(): + logger.info( + "KubernetesServiceInfoProvider handled send_data event when it is not the leader." + "Skipping event - no data sent." + ) + # Update the relation data bag with a Kubernetes Service information + relations = self.charm.model.relations[self.relation_name] + + # Update relation data + for relation in relations: + relation.data[self.charm.app].update( + { + "name": name, + "port": port, + } + ) diff --git a/charms/kfp-metadata-writer/metadata.yaml b/charms/kfp-metadata-writer/metadata.yaml index 3b8c0b25..30cc9c6b 100755 --- a/charms/kfp-metadata-writer/metadata.yaml +++ b/charms/kfp-metadata-writer/metadata.yaml @@ -16,18 +16,4 @@ resources: upstream-source: gcr.io/ml-pipeline/metadata-writer:2.0.3 requires: grpc: - interface: grpc - schema: - v1: - provides: - type: object - properties: - service: - type: string - port: - type: string - required: - - service - - port - versions: [v1] - __schema_source: https://raw.githubusercontent.com/canonical/operator-schemas/master/grpc.yaml + interface: k8s-service diff --git a/charms/kfp-metadata-writer/requirements-unit.in b/charms/kfp-metadata-writer/requirements-unit.in index 82a8e551..d6873440 100644 --- a/charms/kfp-metadata-writer/requirements-unit.in +++ b/charms/kfp-metadata-writer/requirements-unit.in @@ -1,21 +1,8 @@ -# Copyright 2023 Canonical Ltd. +# Copyright 2024 Canonical Ltd. # See LICENSE file for licensing details. -# Please note this file introduces dependencies from the charm's requirements.in, -# special attention must be taken when updating this or the other .in file to try -# to avoid incompatibilities. -# Rules for editing this file: -# * Removing a dependency that is no longer used in the unit test file(s) -# is allowed, and should not represent any risk. -# * Adding a dependency in this file means the dependency is directly used -# in the unit test files(s). -# * ALL python packages/libs used directly in the unit test file(s) must be -# listed here even if requirements.in is already adding them. This will -# add clarity to the dependency list. -# * Pinning a version of a python package/lib shared with requirements.in -# must not introduce any incompatibilities. coverage ops pytest pytest-mock pyyaml --r requirements.in +-r requirements.txt diff --git a/charms/kfp-metadata-writer/requirements-unit.txt b/charms/kfp-metadata-writer/requirements-unit.txt index 7fd4bc63..cbb5f08a 100644 --- a/charms/kfp-metadata-writer/requirements-unit.txt +++ b/charms/kfp-metadata-writer/requirements-unit.txt @@ -4,70 +4,113 @@ # # pip-compile requirements-unit.in # +annotated-types==0.6.0 + # via + # -r requirements.txt + # pydantic anyio==4.0.0 - # via httpcore + # via + # -r requirements.txt + # httpcore attrs==23.1.0 - # via jsonschema + # via + # -r requirements.txt + # jsonschema certifi==2023.7.22 # via + # -r requirements.txt # httpcore # httpx # requests charmed-kubeflow-chisme==0.2.0 - # via -r requirements.in + # via -r requirements.txt charset-normalizer==3.3.0 - # via requests + # via + # -r requirements.txt + # requests coverage==7.3.2 # via -r requirements-unit.in deepdiff==6.2.1 - # via charmed-kubeflow-chisme + # via + # -r requirements.txt + # charmed-kubeflow-chisme exceptiongroup==1.1.3 # via + # -r requirements.txt # anyio # pytest h11==0.14.0 - # via httpcore + # via + # -r requirements.txt + # httpcore httpcore==0.18.0 - # via httpx + # via + # -r requirements.txt + # httpx httpx==0.25.0 - # via lightkube + # via + # -r requirements.txt + # lightkube idna==3.4 # via + # -r requirements.txt # anyio # httpx # requests importlib-resources==6.1.0 - # via jsonschema + # via + # -r requirements.txt + # jsonschema iniconfig==2.0.0 # via pytest jinja2==3.1.2 - # via charmed-kubeflow-chisme + # via + # -r requirements.txt + # charmed-kubeflow-chisme jsonschema==4.17.3 - # via serialized-data-interface + # via + # -r requirements.txt + # serialized-data-interface lightkube==0.14.0 # via - # -r requirements.in + # -r requirements.txt # charmed-kubeflow-chisme lightkube-models==1.28.1.4 - # via lightkube + # via + # -r requirements.txt + # lightkube markupsafe==2.1.3 - # via jinja2 + # via + # -r requirements.txt + # jinja2 ops==2.7.0 # via # -r requirements-unit.in - # -r requirements.in + # -r requirements.txt # charmed-kubeflow-chisme # serialized-data-interface ordered-set==4.1.0 - # via deepdiff + # via + # -r requirements.txt + # deepdiff packaging==23.2 # via pytest pkgutil-resolve-name==1.3.10 - # via jsonschema + # via + # -r requirements.txt + # jsonschema pluggy==1.3.0 # via pytest +pydantic==2.6.4 + # via -r requirements.txt +pydantic-core==2.16.3 + # via + # -r requirements.txt + # pydantic pyrsistent==0.19.3 - # via jsonschema + # via + # -r requirements.txt + # jsonschema pytest==7.4.2 # via # -r requirements-unit.in @@ -77,31 +120,53 @@ pytest-mock==3.11.1 pyyaml==6.0.1 # via # -r requirements-unit.in + # -r requirements.txt # lightkube # ops # serialized-data-interface requests==2.31.0 - # via serialized-data-interface + # via + # -r requirements.txt + # serialized-data-interface ruamel-yaml==0.17.35 - # via charmed-kubeflow-chisme + # via + # -r requirements.txt + # charmed-kubeflow-chisme ruamel-yaml-clib==0.2.8 - # via ruamel-yaml + # via + # -r requirements.txt + # ruamel-yaml serialized-data-interface==0.7.0 # via - # -r requirements.in + # -r requirements.txt # charmed-kubeflow-chisme sniffio==1.3.0 # via + # -r requirements.txt # anyio # httpcore # httpx tenacity==8.2.3 - # via charmed-kubeflow-chisme + # via + # -r requirements.txt + # charmed-kubeflow-chisme tomli==2.0.1 # via pytest +typing-extensions==4.11.0 + # via + # -r requirements.txt + # annotated-types + # pydantic + # pydantic-core urllib3==2.0.6 - # via requests + # via + # -r requirements.txt + # requests websocket-client==1.6.4 - # via ops + # via + # -r requirements.txt + # ops zipp==3.17.0 - # via importlib-resources + # via + # -r requirements.txt + # importlib-resources diff --git a/charms/kfp-metadata-writer/requirements.in b/charms/kfp-metadata-writer/requirements.in index 5d329801..ffa7c62c 100644 --- a/charms/kfp-metadata-writer/requirements.in +++ b/charms/kfp-metadata-writer/requirements.in @@ -4,4 +4,7 @@ charmed-kubeflow-chisme >= 0.2.0 lightkube ops +# pydantic greater or equal to 2.7 yields issues with the +# rustc version available for this ubuntu version +pydantic==2.6.4 serialized-data-interface diff --git a/charms/kfp-metadata-writer/requirements.txt b/charms/kfp-metadata-writer/requirements.txt index de6c5187..e512f44e 100644 --- a/charms/kfp-metadata-writer/requirements.txt +++ b/charms/kfp-metadata-writer/requirements.txt @@ -4,6 +4,8 @@ # # pip-compile requirements.in # +annotated-types==0.6.0 + # via pydantic anyio==4.0.0 # via httpcore attrs==23.1.0 @@ -55,6 +57,10 @@ ordered-set==4.1.0 # via deepdiff pkgutil-resolve-name==1.3.10 # via jsonschema +pydantic==2.6.4 + # via -r requirements.in +pydantic-core==2.16.3 + # via pydantic pyrsistent==0.19.3 # via jsonschema pyyaml==6.0.1 @@ -69,9 +75,7 @@ ruamel-yaml==0.17.35 ruamel-yaml-clib==0.2.8 # via ruamel-yaml serialized-data-interface==0.7.0 - # via - # -r requirements.in - # charmed-kubeflow-chisme + # via charmed-kubeflow-chisme sniffio==1.3.0 # via # anyio @@ -79,6 +83,11 @@ sniffio==1.3.0 # httpx tenacity==8.2.3 # via charmed-kubeflow-chisme +typing-extensions==4.11.0 + # via + # annotated-types + # pydantic + # pydantic-core urllib3==2.0.6 # via requests websocket-client==1.6.4 diff --git a/charms/kfp-metadata-writer/src/charm.py b/charms/kfp-metadata-writer/src/charm.py index a5617132..fd1d4690 100755 --- a/charms/kfp-metadata-writer/src/charm.py +++ b/charms/kfp-metadata-writer/src/charm.py @@ -13,19 +13,18 @@ from charmed_kubeflow_chisme.components.charm_reconciler import CharmReconciler from charmed_kubeflow_chisme.components.kubernetes_component import KubernetesComponent from charmed_kubeflow_chisme.components.leadership_gate_component import LeadershipGateComponent -from charmed_kubeflow_chisme.components.serialised_data_interface_components import ( - SdiRelationDataReceiverComponent, -) from charmed_kubeflow_chisme.kubernetes import create_charm_default_labels from lightkube.resources.core_v1 import ServiceAccount from lightkube.resources.rbac_authorization_v1 import ClusterRole, ClusterRoleBinding from ops.charm import CharmBase from ops.main import main +from components.k8s_service_info_component import K8sServiceInfoComponent from components.pebble_component import KfpMetadataWriterInputs, KfpMetadataWriterPebbleService logger = logging.getLogger(__name__) +GRPC_RELATION_NAME = "grpc" K8S_RESOURCE_FILES = ["src/templates/auth_manifests.yaml.j2"] @@ -46,8 +45,10 @@ def __init__(self, *args): ) self.grpc_relation = self.charm_reconciler.add( - component=SdiRelationDataReceiverComponent( - charm=self, name="relation:grpc", relation_name="grpc" + component=K8sServiceInfoComponent( + charm=self, + relation_name=GRPC_RELATION_NAME, + refresh_event=self.on[GRPC_RELATION_NAME].relation_changed, ), depends_on=[self.leadership_gate], ) @@ -75,12 +76,8 @@ def __init__(self, *args): service_name="kfp-metadata-writer", namespace_to_watch="", inputs_getter=lambda: KfpMetadataWriterInputs( - METADATA_GRPC_SERVICE_SERVICE_HOST=self.grpc_relation.component.get_data()[ - "service" - ], - METADATA_GRPC_SERVICE_SERVICE_PORT=self.grpc_relation.component.get_data()[ - "port" - ], + METADATA_GRPC_SERVICE_SERVICE_HOST=self.grpc_relation.component.get_service_info().name, # noqa + METADATA_GRPC_SERVICE_SERVICE_PORT=self.grpc_relation.component.get_service_info().port, # noqa ), ), depends_on=[self.kubernetes_resources, self.grpc_relation], diff --git a/charms/kfp-metadata-writer/src/components/k8s_service_info_component.py b/charms/kfp-metadata-writer/src/components/k8s_service_info_component.py new file mode 100644 index 00000000..ceb51eb7 --- /dev/null +++ b/charms/kfp-metadata-writer/src/components/k8s_service_info_component.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# Copyright 2024 Ubuntu +# See LICENSE file for licensing details. + +from typing import List, Optional, Union + +from charmed_kubeflow_chisme.components.component import Component +from charms.mlops_libs.v0.k8s_service_info import ( + KubernetesServiceInfoObject, + KubernetesServiceInfoRelationDataMissingError, + KubernetesServiceInfoRelationMissingError, + KubernetesServiceInfoRequirer, +) +from ops import ActiveStatus, BlockedStatus, CharmBase, StatusBase +from ops.framework import BoundEvent + + +class K8sServiceInfoComponent(Component): + """A Component that wraps the requirer side of the k8s_service_info charm library. + + Args: + charm(CharmBase): the requirer charm + relation_name(str, Optional): name of the relation that uses the k8s-service interface + """ + + def __init__( + self, + charm: CharmBase, + relation_name: Optional[str] = "k8s-service-info", + refresh_event: Optional[Union[BoundEvent, List[BoundEvent]]] = None, + ): + super().__init__(charm, relation_name) + self.relation_name = relation_name + self.charm = charm + self.refresh_event = refresh_event + + self._k8s_service_info_requirer = KubernetesServiceInfoRequirer( + charm=self.charm, relation_name=self.relation_name, refresh_event=self.refresh_event + ) + + def get_service_info(self) -> KubernetesServiceInfoObject: + """Wrap the get_data method and return a KubernetesServiceInfoObject.""" + return self._k8s_service_info_requirer.get_data() + + def get_status(self) -> StatusBase: + """Return this component's status based on the presence of the relation and its data.""" + try: + self.get_service_info() + except KubernetesServiceInfoRelationMissingError as rel_error: + return BlockedStatus(f"{rel_error.message} Please add the missing relation.") + except KubernetesServiceInfoRelationDataMissingError as data_error: + # Nothing can be done, just re-raise + raise data_error + return ActiveStatus() diff --git a/charms/kfp-metadata-writer/tests/unit/test_operator.py b/charms/kfp-metadata-writer/tests/unit/test_operator.py index 7229deec..736dc69a 100644 --- a/charms/kfp-metadata-writer/tests/unit/test_operator.py +++ b/charms/kfp-metadata-writer/tests/unit/test_operator.py @@ -4,13 +4,13 @@ from unittest.mock import MagicMock import pytest -from charmed_kubeflow_chisme.testing import add_sdi_relation_to_harness +from charms.mlops_libs.v0.k8s_service_info import KubernetesServiceInfoRelationDataMissingError from ops.model import ActiveStatus, BlockedStatus, WaitingStatus from ops.testing import Harness -from charm import KfpMetadataWriter +from charm import GRPC_RELATION_NAME, KfpMetadataWriter -MOCK_GRPC_DATA = {"service": "service-name", "port": "1234"} +MOCK_GRPC_DATA = {"name": "service-name", "port": "1234"} def test_not_leader( @@ -54,14 +54,22 @@ def test_grpc_relation_with_data(harness, mocked_lightkube_client): harness.charm.leadership_gate.get_status = MagicMock(return_value=ActiveStatus()) # Add relation with data. This should trigger a charm reconciliation due to relation-changed. - add_sdi_relation_to_harness(harness, "grpc", data=MOCK_GRPC_DATA) + rel_id = harness.add_relation( + relation_name=GRPC_RELATION_NAME, remote_app="other-app", app_data=MOCK_GRPC_DATA + ) # Assert - assert isinstance(harness.charm.grpc_relation.status, ActiveStatus) + assert harness.charm.grpc_relation.component.get_service_info().name == MOCK_GRPC_DATA["name"] + assert harness.charm.grpc_relation.component.get_service_info().port == MOCK_GRPC_DATA["port"] + + # Duplicity to check the same data in the relation is in the grpc_relation component + rel_data = harness.get_relation_data(relation_id=rel_id, app_or_unit="other-app") + assert harness.charm.grpc_relation.component.get_service_info().name == rel_data["name"] + assert harness.charm.grpc_relation.component.get_service_info().port == rel_data["port"] -def test_grpc_relation_without_data(harness, mocked_lightkube_client): - """Test that the grpc relation goes Blocked if no data is available.""" +def test_grpc_relation_with_no_data(harness, mocked_lightkube_client): + """Test the grpc relation raises when data is missing.""" # Arrange harness.begin() @@ -69,11 +77,29 @@ def test_grpc_relation_without_data(harness, mocked_lightkube_client): # * leadership_gate to be active and executed harness.charm.leadership_gate.get_status = MagicMock(return_value=ActiveStatus()) - # Add relation with data. This should trigger a charm reconciliation due to relation-changed. - add_sdi_relation_to_harness(harness, "grpc", data={}) + # Add relation without data. + harness.add_relation(relation_name=GRPC_RELATION_NAME, remote_app="other-app", app_data={}) - # Assert - assert isinstance(harness.charm.grpc_relation.status, BlockedStatus) + with pytest.raises(KubernetesServiceInfoRelationDataMissingError): + harness.charm.grpc_relation.component.get_service_info() + + +def test_grpc_relation_with_missing_data(harness, mocked_lightkube_client): + """Test the grpc relation raises when data is missing an attribute.""" + # Arrange + harness.begin() + + # Mock: + # * leadership_gate to be active and executed + harness.charm.leadership_gate.get_status = MagicMock(return_value=ActiveStatus()) + + # Add relation without data. + harness.add_relation( + relation_name=GRPC_RELATION_NAME, remote_app="other-app", app_data={"name": "some-name"} + ) + + with pytest.raises(KubernetesServiceInfoRelationDataMissingError): + harness.charm.grpc_relation.component.get_service_info() def test_grpc_relation_without_relation(harness, mocked_lightkube_client): @@ -90,6 +116,10 @@ def test_grpc_relation_without_relation(harness, mocked_lightkube_client): # Assert assert isinstance(harness.charm.grpc_relation.status, BlockedStatus) + assert ( + harness.charm.grpc_relation.status.message + == "Missing relation with a k8s service info provider. Please add the missing relation." + ) def test_pebble_service_container_running(harness, mocked_lightkube_client): @@ -99,7 +129,9 @@ def test_pebble_service_container_running(harness, mocked_lightkube_client): harness.set_can_connect("kfp-metadata-writer", True) harness.charm.kubernetes_resources.get_status = MagicMock(return_value=ActiveStatus()) - add_sdi_relation_to_harness(harness, "grpc", data=MOCK_GRPC_DATA) + harness.add_relation( + relation_name=GRPC_RELATION_NAME, remote_app="other-app", app_data=MOCK_GRPC_DATA + ) harness.charm.on.install.emit() @@ -114,11 +146,11 @@ def test_pebble_service_container_running(harness, mocked_lightkube_client): assert environment["NAMESPACE_TO_WATCH"] == "" assert ( environment["METADATA_GRPC_SERVICE_SERVICE_HOST"] - == harness.charm.grpc_relation.component.get_data()["service"] + == harness.charm.grpc_relation.component.get_service_info().name ) assert ( environment["METADATA_GRPC_SERVICE_SERVICE_PORT"] - == harness.charm.grpc_relation.component.get_data()["port"] + == harness.charm.grpc_relation.component.get_service_info().port ) @@ -128,7 +160,9 @@ def test_install_before_pebble_service_container(harness, mocked_lightkube_clien harness.begin() harness.charm.kubernetes_resources.get_status = MagicMock(return_value=ActiveStatus()) - add_sdi_relation_to_harness(harness, "grpc", data=MOCK_GRPC_DATA) + harness.add_relation( + relation_name=GRPC_RELATION_NAME, remote_app="other-app", app_data=MOCK_GRPC_DATA + ) harness.charm.on.install.emit() diff --git a/tests/integration/bundles/kfp_latest_edge.yaml.j2 b/tests/integration/bundles/kfp_latest_edge.yaml.j2 index abeb0c40..63e91c97 100644 --- a/tests/integration/bundles/kfp_latest_edge.yaml.j2 +++ b/tests/integration/bundles/kfp_latest_edge.yaml.j2 @@ -6,7 +6,7 @@ applications: minio: { charm: ch:minio, channel: latest/edge, scale: 1 } # We should use `8.0/stable` once changes for https://github.com/canonical/mysql-k8s-operator/issues/337 are published there. kfp-db: { charm: ch:mysql-k8s, channel: 8.0/edge, scale: 1, constraints: mem=2G, trust: true } - mlmd: { charm: ch:mlmd, channel: latest/edge, scale: 1 } + mlmd: { charm: ch:mlmd, channel: latest/edge, scale: 1, trust: true} envoy: { charm: ch:envoy, channel: latest/edge, scale: 1 } kubeflow-profiles: { charm: ch:kubeflow-profiles, channel: latest/edge, scale: 1, trust: true } istio-ingressgateway: From 62af86b575500732a2af67825c8a767f689ef8ed Mon Sep 17 00:00:00 2001 From: Daniela Plascencia Date: Wed, 17 Apr 2024 17:06:24 +0200 Subject: [PATCH 2/7] debug: remove destructive mode --- .github/workflows/integrate.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index cb2248f7..7824690f 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -144,7 +144,7 @@ jobs: # Run integration tests against the 1.7 generic install bundle definition # Using destructive mode because of https://github.com/canonical/charmcraft/issues/1132 # and https://github.com/canonical/charmcraft/issues/1138 - sg snap_microk8s -c "tox -e bundle-integration-${{ matrix.sdk }} -- --model kubeflow --bundle=./tests/integration/bundles/kfp_latest_edge.yaml.j2 --destructive-mode" + sg snap_microk8s -c "tox -e bundle-integration-${{ matrix.sdk }} -- --model kubeflow --bundle=./tests/integration/bundles/kfp_latest_edge.yaml.j2" - name: Get all run: kubectl get all -A From d5ee17b72eb20d013da0e3894f2ddbf233ae96a5 Mon Sep 17 00:00:00 2001 From: Daniela Plascencia Date: Thu, 18 Apr 2024 14:35:00 +0200 Subject: [PATCH 3/7] skip: update based on feedback --- .github/workflows/integrate.yaml | 2 +- charms/kfp-metadata-writer/charmcraft.yaml | 3 +++ charms/kfp-metadata-writer/requirements.in | 9 ++++++--- charms/kfp-metadata-writer/requirements.txt | 4 +++- charms/kfp-metadata-writer/src/charm.py | 1 - .../src/components/k8s_service_info_component.py | 2 ++ .../tests/unit/test_operator.py | 15 +++++---------- 7 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index 7824690f..cb2248f7 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -144,7 +144,7 @@ jobs: # Run integration tests against the 1.7 generic install bundle definition # Using destructive mode because of https://github.com/canonical/charmcraft/issues/1132 # and https://github.com/canonical/charmcraft/issues/1138 - sg snap_microk8s -c "tox -e bundle-integration-${{ matrix.sdk }} -- --model kubeflow --bundle=./tests/integration/bundles/kfp_latest_edge.yaml.j2" + sg snap_microk8s -c "tox -e bundle-integration-${{ matrix.sdk }} -- --model kubeflow --bundle=./tests/integration/bundles/kfp_latest_edge.yaml.j2 --destructive-mode" - name: Get all run: kubectl get all -A diff --git a/charms/kfp-metadata-writer/charmcraft.yaml b/charms/kfp-metadata-writer/charmcraft.yaml index 459ec101..c4accd0f 100644 --- a/charms/kfp-metadata-writer/charmcraft.yaml +++ b/charms/kfp-metadata-writer/charmcraft.yaml @@ -12,4 +12,7 @@ bases: parts: charm: charm-python-packages: [setuptools, pip] + # These build-packages are defined here because pydantic needs them + # at build time. As long as pydantic is listed in requirements.in, this + # list cannot be removed/changed. build-packages: [cargo, rustc, pkg-config, libffi-dev, libssl-dev] diff --git a/charms/kfp-metadata-writer/requirements.in b/charms/kfp-metadata-writer/requirements.in index ffa7c62c..0d3542eb 100644 --- a/charms/kfp-metadata-writer/requirements.in +++ b/charms/kfp-metadata-writer/requirements.in @@ -4,7 +4,10 @@ charmed-kubeflow-chisme >= 0.2.0 lightkube ops -# pydantic greater or equal to 2.7 yields issues with the -# rustc version available for this ubuntu version -pydantic==2.6.4 +# pydantic>=2.7 requires rustc v1.76 or newer, +# which is not available in the base OS this charm has at the moment (Ubuntu 20.04). +# To avoid build-time errors, pydantic has to be pinned to a version that can be built +# with the rustc version that the OS can provide. +# Remove this pin when the base OS can install rustc v1.76 or newer. +pydantic>=2.6,<2.7 serialized-data-interface diff --git a/charms/kfp-metadata-writer/requirements.txt b/charms/kfp-metadata-writer/requirements.txt index e512f44e..0d1768c4 100644 --- a/charms/kfp-metadata-writer/requirements.txt +++ b/charms/kfp-metadata-writer/requirements.txt @@ -75,7 +75,9 @@ ruamel-yaml==0.17.35 ruamel-yaml-clib==0.2.8 # via ruamel-yaml serialized-data-interface==0.7.0 - # via charmed-kubeflow-chisme + # via + # -r requirements.in + # charmed-kubeflow-chisme sniffio==1.3.0 # via # anyio diff --git a/charms/kfp-metadata-writer/src/charm.py b/charms/kfp-metadata-writer/src/charm.py index fd1d4690..f1455c57 100755 --- a/charms/kfp-metadata-writer/src/charm.py +++ b/charms/kfp-metadata-writer/src/charm.py @@ -48,7 +48,6 @@ def __init__(self, *args): component=K8sServiceInfoComponent( charm=self, relation_name=GRPC_RELATION_NAME, - refresh_event=self.on[GRPC_RELATION_NAME].relation_changed, ), depends_on=[self.leadership_gate], ) diff --git a/charms/kfp-metadata-writer/src/components/k8s_service_info_component.py b/charms/kfp-metadata-writer/src/components/k8s_service_info_component.py index ceb51eb7..c5d5aa00 100644 --- a/charms/kfp-metadata-writer/src/components/k8s_service_info_component.py +++ b/charms/kfp-metadata-writer/src/components/k8s_service_info_component.py @@ -38,6 +38,8 @@ def __init__( charm=self.charm, relation_name=self.relation_name, refresh_event=self.refresh_event ) + self._events_to_observe = [self._k8s_service_info_requirer.on.updated] + def get_service_info(self) -> KubernetesServiceInfoObject: """Wrap the get_data method and return a KubernetesServiceInfoObject.""" return self._k8s_service_info_requirer.get_data() diff --git a/charms/kfp-metadata-writer/tests/unit/test_operator.py b/charms/kfp-metadata-writer/tests/unit/test_operator.py index 736dc69a..8745819d 100644 --- a/charms/kfp-metadata-writer/tests/unit/test_operator.py +++ b/charms/kfp-metadata-writer/tests/unit/test_operator.py @@ -54,7 +54,7 @@ def test_grpc_relation_with_data(harness, mocked_lightkube_client): harness.charm.leadership_gate.get_status = MagicMock(return_value=ActiveStatus()) # Add relation with data. This should trigger a charm reconciliation due to relation-changed. - rel_id = harness.add_relation( + harness.add_relation( relation_name=GRPC_RELATION_NAME, remote_app="other-app", app_data=MOCK_GRPC_DATA ) @@ -62,13 +62,8 @@ def test_grpc_relation_with_data(harness, mocked_lightkube_client): assert harness.charm.grpc_relation.component.get_service_info().name == MOCK_GRPC_DATA["name"] assert harness.charm.grpc_relation.component.get_service_info().port == MOCK_GRPC_DATA["port"] - # Duplicity to check the same data in the relation is in the grpc_relation component - rel_data = harness.get_relation_data(relation_id=rel_id, app_or_unit="other-app") - assert harness.charm.grpc_relation.component.get_service_info().name == rel_data["name"] - assert harness.charm.grpc_relation.component.get_service_info().port == rel_data["port"] - -def test_grpc_relation_with_no_data(harness, mocked_lightkube_client): +def test_grpc_relation_with_empty_data(harness, mocked_lightkube_client): """Test the grpc relation raises when data is missing.""" # Arrange harness.begin() @@ -85,7 +80,7 @@ def test_grpc_relation_with_no_data(harness, mocked_lightkube_client): def test_grpc_relation_with_missing_data(harness, mocked_lightkube_client): - """Test the grpc relation raises when data is missing an attribute.""" + """Test the grpc relation raises when data is incomplete.""" # Arrange harness.begin() @@ -128,13 +123,13 @@ def test_pebble_service_container_running(harness, mocked_lightkube_client): harness.begin() harness.set_can_connect("kfp-metadata-writer", True) + harness.charm.on.install.emit() + harness.charm.kubernetes_resources.get_status = MagicMock(return_value=ActiveStatus()) harness.add_relation( relation_name=GRPC_RELATION_NAME, remote_app="other-app", app_data=MOCK_GRPC_DATA ) - harness.charm.on.install.emit() - assert isinstance(harness.charm.unit.status, ActiveStatus) container = harness.charm.unit.get_container("kfp-metadata-writer") From de7ace1699ebe7e06b84c4fdc9e62ef4d049e1d9 Mon Sep 17 00:00:00 2001 From: Daniela Plascencia Date: Fri, 19 Apr 2024 09:37:06 +0200 Subject: [PATCH 4/7] skip: rename component and update based on feedback --- charms/kfp-metadata-writer/src/charm.py | 4 ++-- ...=> k8s_service_info_requirer_component.py} | 21 ++++++++++++------- .../tests/unit/test_operator.py | 15 ++++++------- 3 files changed, 23 insertions(+), 17 deletions(-) rename charms/kfp-metadata-writer/src/components/{k8s_service_info_component.py => k8s_service_info_requirer_component.py} (77%) diff --git a/charms/kfp-metadata-writer/src/charm.py b/charms/kfp-metadata-writer/src/charm.py index f1455c57..a9c7d90c 100755 --- a/charms/kfp-metadata-writer/src/charm.py +++ b/charms/kfp-metadata-writer/src/charm.py @@ -19,7 +19,7 @@ from ops.charm import CharmBase from ops.main import main -from components.k8s_service_info_component import K8sServiceInfoComponent +from components.k8s_service_info_requirer_component import K8sServiceInfoRequirerComponent from components.pebble_component import KfpMetadataWriterInputs, KfpMetadataWriterPebbleService logger = logging.getLogger(__name__) @@ -45,7 +45,7 @@ def __init__(self, *args): ) self.grpc_relation = self.charm_reconciler.add( - component=K8sServiceInfoComponent( + component=K8sServiceInfoRequirerComponent( charm=self, relation_name=GRPC_RELATION_NAME, ), diff --git a/charms/kfp-metadata-writer/src/components/k8s_service_info_component.py b/charms/kfp-metadata-writer/src/components/k8s_service_info_requirer_component.py similarity index 77% rename from charms/kfp-metadata-writer/src/components/k8s_service_info_component.py rename to charms/kfp-metadata-writer/src/components/k8s_service_info_requirer_component.py index c5d5aa00..84c4ba1c 100644 --- a/charms/kfp-metadata-writer/src/components/k8s_service_info_component.py +++ b/charms/kfp-metadata-writer/src/components/k8s_service_info_requirer_component.py @@ -2,7 +2,8 @@ # Copyright 2024 Ubuntu # See LICENSE file for licensing details. -from typing import List, Optional, Union +import logging +from typing import Optional from charmed_kubeflow_chisme.components.component import Component from charms.mlops_libs.v0.k8s_service_info import ( @@ -11,11 +12,12 @@ KubernetesServiceInfoRelationMissingError, KubernetesServiceInfoRequirer, ) -from ops import ActiveStatus, BlockedStatus, CharmBase, StatusBase -from ops.framework import BoundEvent +from ops import ActiveStatus, BlockedStatus, CharmBase, StatusBase, WaitingStatus +logger = logging.getLogger(__name__) -class K8sServiceInfoComponent(Component): + +class K8sServiceInfoRequirerComponent(Component): """A Component that wraps the requirer side of the k8s_service_info charm library. Args: @@ -27,15 +29,14 @@ def __init__( self, charm: CharmBase, relation_name: Optional[str] = "k8s-service-info", - refresh_event: Optional[Union[BoundEvent, List[BoundEvent]]] = None, ): super().__init__(charm, relation_name) self.relation_name = relation_name self.charm = charm - self.refresh_event = refresh_event self._k8s_service_info_requirer = KubernetesServiceInfoRequirer( - charm=self.charm, relation_name=self.relation_name, refresh_event=self.refresh_event + charm=self.charm, + relation_name=self.relation_name, ) self._events_to_observe = [self._k8s_service_info_requirer.on.updated] @@ -52,5 +53,9 @@ def get_status(self) -> StatusBase: return BlockedStatus(f"{rel_error.message} Please add the missing relation.") except KubernetesServiceInfoRelationDataMissingError as data_error: # Nothing can be done, just re-raise - raise data_error + logger.error(f"Empty or missing data. Got: {data_error.message}") + return WaitingStatus( + f"Empty or missing data in {self.relation_name} relation." + " This may be transient, but if it persists it is likely an error." + ) return ActiveStatus() diff --git a/charms/kfp-metadata-writer/tests/unit/test_operator.py b/charms/kfp-metadata-writer/tests/unit/test_operator.py index 8745819d..fb37902a 100644 --- a/charms/kfp-metadata-writer/tests/unit/test_operator.py +++ b/charms/kfp-metadata-writer/tests/unit/test_operator.py @@ -4,7 +4,6 @@ from unittest.mock import MagicMock import pytest -from charms.mlops_libs.v0.k8s_service_info import KubernetesServiceInfoRelationDataMissingError from ops.model import ActiveStatus, BlockedStatus, WaitingStatus from ops.testing import Harness @@ -64,7 +63,7 @@ def test_grpc_relation_with_data(harness, mocked_lightkube_client): def test_grpc_relation_with_empty_data(harness, mocked_lightkube_client): - """Test the grpc relation raises when data is missing.""" + """Test the grpc relation component returns WaitingStatus when data is missing.""" # Arrange harness.begin() @@ -72,15 +71,16 @@ def test_grpc_relation_with_empty_data(harness, mocked_lightkube_client): # * leadership_gate to be active and executed harness.charm.leadership_gate.get_status = MagicMock(return_value=ActiveStatus()) + harness.charm.on.install.emit() + # Add relation without data. harness.add_relation(relation_name=GRPC_RELATION_NAME, remote_app="other-app", app_data={}) - with pytest.raises(KubernetesServiceInfoRelationDataMissingError): - harness.charm.grpc_relation.component.get_service_info() + assert isinstance(harness.charm.grpc_relation.get_status(), WaitingStatus) def test_grpc_relation_with_missing_data(harness, mocked_lightkube_client): - """Test the grpc relation raises when data is incomplete.""" + """Test the grpc relation component returns WaitingStatus when data is incomplete.""" # Arrange harness.begin() @@ -88,13 +88,14 @@ def test_grpc_relation_with_missing_data(harness, mocked_lightkube_client): # * leadership_gate to be active and executed harness.charm.leadership_gate.get_status = MagicMock(return_value=ActiveStatus()) + harness.charm.on.install.emit() + # Add relation without data. harness.add_relation( relation_name=GRPC_RELATION_NAME, remote_app="other-app", app_data={"name": "some-name"} ) - with pytest.raises(KubernetesServiceInfoRelationDataMissingError): - harness.charm.grpc_relation.component.get_service_info() + assert isinstance(harness.charm.grpc_relation.component.get_status(), WaitingStatus) def test_grpc_relation_without_relation(harness, mocked_lightkube_client): From 3d74efd70fb3807addd7298cb69a864b45de4d60 Mon Sep 17 00:00:00 2001 From: Daniela Plascencia Date: Fri, 19 Apr 2024 09:39:51 +0200 Subject: [PATCH 5/7] skip: remove comment --- .../src/components/k8s_service_info_requirer_component.py | 1 - 1 file changed, 1 deletion(-) diff --git a/charms/kfp-metadata-writer/src/components/k8s_service_info_requirer_component.py b/charms/kfp-metadata-writer/src/components/k8s_service_info_requirer_component.py index 84c4ba1c..5767f35f 100644 --- a/charms/kfp-metadata-writer/src/components/k8s_service_info_requirer_component.py +++ b/charms/kfp-metadata-writer/src/components/k8s_service_info_requirer_component.py @@ -52,7 +52,6 @@ def get_status(self) -> StatusBase: except KubernetesServiceInfoRelationMissingError as rel_error: return BlockedStatus(f"{rel_error.message} Please add the missing relation.") except KubernetesServiceInfoRelationDataMissingError as data_error: - # Nothing can be done, just re-raise logger.error(f"Empty or missing data. Got: {data_error.message}") return WaitingStatus( f"Empty or missing data in {self.relation_name} relation." From f3446362ecf420ee338e2b7b467673bc2853db19 Mon Sep 17 00:00:00 2001 From: Daniela Plascencia Date: Mon, 22 Apr 2024 11:58:15 +0200 Subject: [PATCH 6/7] debug: remove destructive mode for debugging purposes --- .github/workflows/integrate.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index cb2248f7..7824690f 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -144,7 +144,7 @@ jobs: # Run integration tests against the 1.7 generic install bundle definition # Using destructive mode because of https://github.com/canonical/charmcraft/issues/1132 # and https://github.com/canonical/charmcraft/issues/1138 - sg snap_microk8s -c "tox -e bundle-integration-${{ matrix.sdk }} -- --model kubeflow --bundle=./tests/integration/bundles/kfp_latest_edge.yaml.j2 --destructive-mode" + sg snap_microk8s -c "tox -e bundle-integration-${{ matrix.sdk }} -- --model kubeflow --bundle=./tests/integration/bundles/kfp_latest_edge.yaml.j2" - name: Get all run: kubectl get all -A From 7ca431ffc9dbd176aecea0919c2e5eb15bea6844 Mon Sep 17 00:00:00 2001 From: Daniela Plascencia Date: Mon, 22 Apr 2024 12:09:11 +0200 Subject: [PATCH 7/7] revert prev commit --- .github/workflows/integrate.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index 7824690f..cb2248f7 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -144,7 +144,7 @@ jobs: # Run integration tests against the 1.7 generic install bundle definition # Using destructive mode because of https://github.com/canonical/charmcraft/issues/1132 # and https://github.com/canonical/charmcraft/issues/1138 - sg snap_microk8s -c "tox -e bundle-integration-${{ matrix.sdk }} -- --model kubeflow --bundle=./tests/integration/bundles/kfp_latest_edge.yaml.j2" + sg snap_microk8s -c "tox -e bundle-integration-${{ matrix.sdk }} -- --model kubeflow --bundle=./tests/integration/bundles/kfp_latest_edge.yaml.j2 --destructive-mode" - name: Get all run: kubectl get all -A