diff --git a/interfaces/cos_agent/interface/v0/schema.py b/interfaces/cos_agent/interface/v0/schema.py index 2cf3b75c9..7cb502a22 100644 --- a/interfaces/cos_agent/interface/v0/schema.py +++ b/interfaces/cos_agent/interface/v0/schema.py @@ -2,11 +2,9 @@ # See LICENSE file for licensing details. """This file defines the schemas for the provider and requirer sides of the cos_agent interface. -It exposes two interfaces.schema_base.DataBagSchema subclasses called: -- ProviderSchema -- RequirerSchema + Examples: - ProviderSchema: + Provider: app: # The value of `config` key is a json-dumped data. unit: { @@ -49,7 +47,7 @@ "metrics_scrape_jobs": [{"job_name": "kafka_0", "path": "/metrics", "port": 9101}], } } - RequirerSchema: + Requirer: unit: app: """ @@ -59,12 +57,10 @@ import lzma from typing import Annotated -from interface_tester.schema_base import DataBagSchema from pydantic import ( AfterValidator, BaseModel, Field, - Json, PlainSerializer, WithJsonSchema, ) @@ -114,16 +110,11 @@ class Config: class ProviderUnitData(BaseModel): """Unit databag model for `cos-agent` relation.""" - config: Json[NestedDataModel] = Field( + config: NestedDataModel = Field( description='When dumped into a databag in JSON format, this configuration data will be nested under the key defaulted to "config".' ) -class ProviderSchema(DataBagSchema): - """Provider schema for CosAgent.""" - - unit: ProviderUnitData - - -class RequirerSchema(DataBagSchema): - """Requirer schema for CosAgent.""" +ProviderAppData = None +RequirerAppData = None +RequirerUnitData = None diff --git a/interfaces/cos_agent/interface/v0/tests/.disable b/interfaces/cos_agent/interface/v0/tests/.disable deleted file mode 100644 index e69de29bb..000000000 diff --git a/interfaces/cos_agent/interface/v0/tests/test_provider.py b/interfaces/cos_agent/interface/v0/tests/test_provider.py deleted file mode 100644 index 8b94fefa8..000000000 --- a/interfaces/cos_agent/interface/v0/tests/test_provider.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. - -import json - -from interface_tester import Tester -from scenario import Relation, State -from scenario.context import CharmEvents - - -def test_no_data_on_created(): - t = Tester() - state_out = t.run("cos-agent-relation-created") - t.assert_relation_data_empty() - - -def test_no_data_on_joined(): - t = Tester() - state_out = t.run("cos-agent-relation-joined") - t.assert_schema_valid() - - -def test_no_data_on_changed(): - t = Tester() - state_out = t.run("cos-agent-relation-changed") - t.assert_schema_valid() - - -valid_unit_data = { - "config": { - "metrics_alert_rules": { - "groups": [ - { - "name": "welcome-lxd_f8768e19_kafka_jmx_missing_alerts", - "rules": [ - { - "alert": "KafkaMissing", - "expr": "up == 0", - "for": "0m", - "labels": { - "severity": "critical", - "juju_model": "welcome-lxd", - "juju_model_uuid": "f8768e19-066a-449a-825b-20f060b99025", - "juju_application": "kafka", - "juju_charm": "kafka", - }, - "annotations": { - "summary": "Prometheus target missing (instance {{ $labels.instance }})", - "description": "Kafka target has disappeared. An exporter might be crashed.\n VALUE = {{ $value }}\n LABELS = {{ $labels}}", - }, - } - ], - }, - { - "name": "welcome-lxd_f8768e19_kafka_jvm_filling_alerts", - "rules": [ - { - "alert": "JvmMemoryFillingUp", - "expr": '(sum by (instance)(jvm_memory_bytes_used{area="heap"}) / sum by (instance)(jvm_memory_bytes_max{area="heap"})) * 100 > 80', - "for": "2m", - "labels": { - "severity": "warning", - "juju_model": "welcome-lxd", - "juju_model_uuid": "f8768e19-066a-449a-825b-20f060b99025", - "juju_application": "kafka", - "juju_charm": "kafka", - }, - "annotations": { - "summary": "JVM memory filling up (instance {{ $labels.instance}})", - "description": "JVM memory is filling up (> 80%)\n VALUE = {{ $value }}\n LABELS ={{ $labels }}", - }, - } - ], - }, - ] - }, - "log_alert_rules": {}, - "dashboards": [ - "/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4af1GDxdAD2CgBccJ1Wse0YL0FXaPTB5Bgw6u7FoNcUn99tIdLOGVyat", - ], - "metrics_scrape_jobs": [{"job_name": "kafka_0", "path": "/metrics", "port": 9101}], - "log_slots": ["charmed-kafka:logs"], - } -} - -valid_unit_data["config"] = json.dumps(valid_unit_data["config"]) - - -def test_on_changed_with_existing_valid_data(): - relation = Relation( - endpoint="cos-agent", - interface="cos-agent", - local_unit_data=valid_unit_data, - ) - t = Tester(State(relations=[relation])) - state_out = t.run(CharmEvents.relation_changed(relation)) - t.assert_schema_valid() diff --git a/interfaces/cos_agent/ruff.toml b/interfaces/cos_agent/ruff.toml index efbd8c5cc..79b043b3f 100644 --- a/interfaces/cos_agent/ruff.toml +++ b/interfaces/cos_agent/ruff.toml @@ -9,10 +9,3 @@ quote-style = "preserve" "D", # docs "E501", # line too long ] -"./interface/v*/tests/*.py" = [ - "CPY", # copyright - "D", # docs - "S", # security - "E501", # line too long - "F841", # assignment to unused variable -] diff --git a/interfaces/grafana_datasource/interface/v0/interface.yaml b/interfaces/grafana_datasource/interface/v0/interface.yaml index 56042f9c6..b220c0cc7 100644 --- a/interfaces/grafana_datasource/interface/v0/interface.yaml +++ b/interfaces/grafana_datasource/interface/v0/interface.yaml @@ -12,16 +12,10 @@ description: | providers: - name: tempo-coordinator-k8s url: https://github.com/canonical/tempo-coordinator-k8s-operator - test_setup: - location: tests/interface/conftest.py - identifier: grafana_datasource_tester requirers: - name: grafana-k8s url: https://github.com/canonical/grafana-k8s-operator - test_setup: - location: tests/interface/conftest.py - identifier: grafana_source_tester maintainer: observability diff --git a/interfaces/grafana_datasource/interface/v0/schema.py b/interfaces/grafana_datasource/interface/v0/schema.py index a9b970305..13e830f05 100644 --- a/interfaces/grafana_datasource/interface/v0/schema.py +++ b/interfaces/grafana_datasource/interface/v0/schema.py @@ -1,7 +1,6 @@ from typing import Any -from interface_tester.schema_base import DataBagSchema -from pydantic import BaseModel, Field, Json +from pydantic import BaseModel, Field class GrafanaSourceData(BaseModel): @@ -19,21 +18,21 @@ class GrafanaSourceData(BaseModel): type: str = Field( description="Type of the datasource.", examples=['tempo', 'loki', 'prometheus'] ) - extra_fields: Json[Any] | None = Field( + extra_fields: Any | None = Field( description="Any datasource-type-specific additional configuration." ) - secure_extra_fields: Json[Any] | None = Field( + secure_extra_fields: Any | None = Field( description="Any secure datasource-type-specific additional configuration." ) -class GrafanaSourceProviderAppData(BaseModel): +class ProviderAppData(BaseModel): """Application databag model for the requirer side of this interface.""" - grafana_source_data: Json[GrafanaSourceData] + grafana_source_data: GrafanaSourceData -class GrafanaSourceProviderUnitData(BaseModel): +class ProviderUnitData(BaseModel): """Application databag model for the requirer side of this interface.""" grafana_source_host: str = Field( @@ -41,24 +40,14 @@ class GrafanaSourceProviderUnitData(BaseModel): ) -class ProviderSchema(DataBagSchema): - """The schemas for the requirer side of this interface.""" - - app: GrafanaSourceProviderAppData - unit: GrafanaSourceProviderUnitData - - -class GrafanaSourceRequirerAppData(BaseModel): +class RequirerAppData(BaseModel): """Application databag model for the requirer side of this interface.""" - datasource_uids: Json[dict[str, str]] + datasource_uids: dict[str, str] grafana_uid: str = Field( description="UID of the requirer application.", examples=['foo-0000-0000-0000-0000-grafana-1'], ) -class RequirerSchema(DataBagSchema): - """The schema for the provider side of this interface.""" - - app: GrafanaSourceRequirerAppData +RequirerUnitData = None diff --git a/interfaces/grafana_datasource/interface/v0/tests/.disable b/interfaces/grafana_datasource/interface/v0/tests/.disable deleted file mode 100644 index e69de29bb..000000000 diff --git a/interfaces/grafana_datasource/interface/v0/tests/test_provider.py b/interfaces/grafana_datasource/interface/v0/tests/test_provider.py deleted file mode 100644 index ee06d3a0a..000000000 --- a/interfaces/grafana_datasource/interface/v0/tests/test_provider.py +++ /dev/null @@ -1,23 +0,0 @@ -from interface_tester import Tester -from scenario import Relation, State - - -def test_share_datasource_on_remote_joined(): - # GIVEN the remote side hasn't sent anything - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='grafana-source', - interface='grafana_datasource', - remote_app_name='foo', - remote_app_data={}, - remote_units_data={0: {}}, - ) - ] - ) - ) - # WHEN the provider processes a relation-joined event - tester.run('grafana-source-relation-joined') - # THEN the provider publishes valid datasource data - tester.assert_schema_valid() diff --git a/interfaces/grafana_datasource/interface/v0/tests/test_requirer.py b/interfaces/grafana_datasource/interface/v0/tests/test_requirer.py deleted file mode 100644 index 5a5978d1b..000000000 --- a/interfaces/grafana_datasource/interface/v0/tests/test_requirer.py +++ /dev/null @@ -1,82 +0,0 @@ -import json - -from interface_tester import Tester -from scenario import Relation, State - - -def test_nothing_happens_on_no_remote_data(): - # GIVEN the remote side hasn't shared their datasource endpoint yet - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='grafana-source', - interface='grafana_datasource', - remote_app_name='foo', - remote_app_data={}, - remote_units_data={0: {}}, - ) - ] - ) - ) - # WHEN the requirer processes a relation-joined event - tester.run('grafana-source-relation-joined') - # THEN nothing is written to the databags - tester.assert_relation_data_empty() - - -def test_nothing_happens_on_invalid_remote_data(): - # GIVEN the remote side has shared gibberish - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='grafana-source', - interface='grafana_datasource', - remote_app_name='foo', - remote_app_data={"foo": "bar"}, - remote_units_data={0: {"baz": "qux"}}, - ) - ] - ) - ) - # WHEN the requirer processes a relation-changed event - tester.run('grafana-source-relation-changed') - # THEN nothing is written to the databags - tester.assert_relation_data_empty() - - -def test_datasource_uid_shared_if_remote_data_valid(): - # GIVEN the remote side has shared a valid datasource endpoint - relation_in = Relation( - endpoint='grafana-source', - interface='grafana_datasource', - remote_app_name='foo', - remote_app_data={ - "grafana_source_data": json.dumps({ - "model": "somemodel", - "model_uuid": "0000-0000-0000-0042", - "application": "myapp", - "type": "prometheus", - }) - }, - remote_units_data={ - 0: {"grafana_source_host": "somehost:80"}, - 42: {"grafana_source_host": "someotherhost:80"}, - }, - ) - tester = Tester(state_in=State(relations=[relation_in])) - - # WHEN the requirer processes a relation-changed event - state_out = tester.run('grafana-source-relation-changed') - - # THEN the schema is valid - tester.assert_schema_valid() - - # AND THEN the requirer has shared a datasource UID - rel_out = [r for r in state_out.relations if r.id == relation_in.id][0] - ds_uids = json.loads(rel_out.local_app_data['datasource_uids']) - - # each requirer unit has received a datasource uid - assert ds_uids['foo/0'] - assert ds_uids['foo/42'] diff --git a/interfaces/grafana_datasource/ruff.toml b/interfaces/grafana_datasource/ruff.toml index 71c58f21b..79b043b3f 100644 --- a/interfaces/grafana_datasource/ruff.toml +++ b/interfaces/grafana_datasource/ruff.toml @@ -9,11 +9,3 @@ quote-style = "preserve" "D", # docs "E501", # line too long ] -"./interface/v*/tests/*.py" = [ - "CPY", # copyright - "D", # docs - "S", # security - "E501", # line too long - "F841", # assignment to unused variable - "RUF015", # single element slice -] diff --git a/interfaces/grafana_datasource_exchange/interface/v0/interface.yaml b/interfaces/grafana_datasource_exchange/interface/v0/interface.yaml index 8358ee5a4..85fea76ea 100644 --- a/interfaces/grafana_datasource_exchange/interface/v0/interface.yaml +++ b/interfaces/grafana_datasource_exchange/interface/v0/interface.yaml @@ -13,16 +13,10 @@ description: | providers: - name: tempo-coordinator-k8s url: https://github.com/canonical/tempo-coordinator-k8s-operator - test_setup: - location: tests/interface/conftest.py - identifier: grafana_datasource_exchange_tester requirers: - name: tempo-coordinator-k8s url: https://github.com/canonical/tempo-coordinator-k8s-operator - test_setup: - location: tests/interface/conftest.py - identifier: grafana_datasource_exchange_tester maintainer: observability diff --git a/interfaces/grafana_datasource_exchange/interface/v0/schema.py b/interfaces/grafana_datasource_exchange/interface/v0/schema.py index c76d8e833..6f46f1611 100644 --- a/interfaces/grafana_datasource_exchange/interface/v0/schema.py +++ b/interfaces/grafana_datasource_exchange/interface/v0/schema.py @@ -1,5 +1,4 @@ -from interface_tester.schema_base import DataBagSchema -from pydantic import BaseModel, Field, Json +from pydantic import BaseModel, Field class GrafanaDatasource(BaseModel): @@ -15,16 +14,10 @@ class GrafanaDatasource(BaseModel): class GrafanaSourceAppData(BaseModel): """Application databag model for the requirer side of this interface.""" - datasources: Json[list[GrafanaDatasource]] + datasources: list[GrafanaDatasource] -class ProviderSchema(DataBagSchema): - """The schemas for the requirer side of this interface.""" - - app: GrafanaSourceAppData - - -class RequirerSchema(DataBagSchema): - """The schemas for the provider side of this interface.""" - - app: GrafanaSourceAppData +ProviderAppData = GrafanaSourceAppData +ProviderUnitData = None +RequirerAppData = GrafanaSourceAppData +RequirerUnitData = None diff --git a/interfaces/grafana_datasource_exchange/interface/v0/tests/.disable b/interfaces/grafana_datasource_exchange/interface/v0/tests/.disable deleted file mode 100644 index e69de29bb..000000000 diff --git a/interfaces/grafana_datasource_exchange/interface/v0/tests/test_provider.py b/interfaces/grafana_datasource_exchange/interface/v0/tests/test_provider.py deleted file mode 100644 index 1a269eb60..000000000 --- a/interfaces/grafana_datasource_exchange/interface/v0/tests/test_provider.py +++ /dev/null @@ -1,16 +0,0 @@ -from interface_tester import Tester -from scenario import Relation, State - - -def test_datasource_exchange(): - # GIVEN the grafana_datasource interface has shared one or more source UIDs - source_exchange = Relation( - endpoint='grafana-source-exchange', - interface='grafana_datasource_exchange', - remote_app_name='bar', - ) - tester = Tester(state_in=State(relations=[source_exchange])) - # WHEN the provider processes any relation event - tester.run('grafana-source-exchange-relation-changed') - # THEN the provider publishes valid data - tester.assert_schema_valid() diff --git a/interfaces/grafana_datasource_exchange/interface/v0/tests/test_requirer.py b/interfaces/grafana_datasource_exchange/interface/v0/tests/test_requirer.py deleted file mode 100644 index 98ae77cbc..000000000 --- a/interfaces/grafana_datasource_exchange/interface/v0/tests/test_requirer.py +++ /dev/null @@ -1,16 +0,0 @@ -from interface_tester import Tester -from scenario import Relation, State - - -def test_datasource_exchange(): - # GIVEN the grafana_datasource interface has shared one or more source UIDs - source_exchange = Relation( - endpoint='grafana-source-exchange', - interface='grafana_datasource_exchange', - remote_app_name='bar', - ) - tester = Tester(state_in=State(relations=[source_exchange])) - # WHEN the requirer processes any relation event - tester.run('grafana-source-exchange-relation-changed') - # THEN the requirer publishes valid data - tester.assert_schema_valid() diff --git a/interfaces/grafana_datasource_exchange/ruff.toml b/interfaces/grafana_datasource_exchange/ruff.toml index efbd8c5cc..79b043b3f 100644 --- a/interfaces/grafana_datasource_exchange/ruff.toml +++ b/interfaces/grafana_datasource_exchange/ruff.toml @@ -9,10 +9,3 @@ quote-style = "preserve" "D", # docs "E501", # line too long ] -"./interface/v*/tests/*.py" = [ - "CPY", # copyright - "D", # docs - "S", # security - "E501", # line too long - "F841", # assignment to unused variable -] diff --git a/interfaces/litmus_auth/interface/v0/interface.yaml b/interfaces/litmus_auth/interface/v0/interface.yaml index e2735741a..e3b6a4d12 100644 --- a/interfaces/litmus_auth/interface/v0/interface.yaml +++ b/interfaces/litmus_auth/interface/v0/interface.yaml @@ -10,19 +10,9 @@ description: | providers: - name: litmus-auth-k8s url: https://github.com/canonical/litmus-operators - test_setup: - charm_root: auth - location: tests/interface/conftest.py - identifier: litmus_auth_tester - pre_run: uv pip compile pyproject.toml --quiet --output-file requirements.txt requirers: - name: litmus-backend-k8s url: https://github.com/canonical/litmus-operators - test_setup: - charm_root: backend - location: tests/interface/conftest.py - identifier: litmus_auth_tester - pre_run: uv pip compile pyproject.toml --quiet --output-file requirements.txt maintainer: observability diff --git a/interfaces/litmus_auth/interface/v0/schema.py b/interfaces/litmus_auth/interface/v0/schema.py index 10d26f821..f219a6ae5 100644 --- a/interfaces/litmus_auth/interface/v0/schema.py +++ b/interfaces/litmus_auth/interface/v0/schema.py @@ -1,10 +1,5 @@ -"""This file defines the schemas for the provider and requirer sides of this relation interface. -It must expose two interfaces.schema_base.DataBagSchema subclasses called: -- ProviderSchema -- RequirerSchema -""" +"""This file defines the schemas for the provider and requirer sides of this relation interface.""" -from interface_tester.schema_base import DataBagSchema from pydantic import BaseModel, Field @@ -29,13 +24,7 @@ class AppSchema(BaseModel): ) -class ProviderSchema(DataBagSchema): - """The schema for the provider side of this interface.""" - - app: AppSchema - - -class RequirerSchema(DataBagSchema): - """The schema for the requirer side of this interface.""" - - app: AppSchema +ProviderAppData = AppSchema +ProviderUnitData = None +RequirerAppData = AppSchema +RequirerUnitData = None diff --git a/interfaces/litmus_auth/interface/v0/tests/.disable b/interfaces/litmus_auth/interface/v0/tests/.disable deleted file mode 100644 index e69de29bb..000000000 diff --git a/interfaces/litmus_auth/interface/v0/tests/test_provider.py b/interfaces/litmus_auth/interface/v0/tests/test_provider.py deleted file mode 100644 index 714fa8425..000000000 --- a/interfaces/litmus_auth/interface/v0/tests/test_provider.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2025 Canonical -# See LICENSE file for licensing details. -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - - -def test_validation_succeeds_on_joining_with_endpoint(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="litmus_auth", - interface="litmus_auth", - remote_app_name="backend", - remote_app_data={ - "grpc_server_host": json.dumps("192.0.2.1"), - "grpc_server_port": json.dumps(8080), - "insecure": json.dumps(False), - "version": json.dumps(0), - }, - ), - ] - ) - ) - tester.run("litmus-auth-relation-joined") - tester.assert_schema_valid() diff --git a/interfaces/litmus_auth/interface/v0/tests/test_requirer.py b/interfaces/litmus_auth/interface/v0/tests/test_requirer.py deleted file mode 100644 index 9fa2853d1..000000000 --- a/interfaces/litmus_auth/interface/v0/tests/test_requirer.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2025 Canonical -# See LICENSE file for licensing details. -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - - -def test_validation_succeeds_on_joining_with_endpoint(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="litmus_auth", - interface="litmus_auth", - remote_app_name="auth", - remote_app_data={ - "grpc_server_host": json.dumps("192.0.2.1"), - "grpc_server_port": json.dumps(8080), - "insecure": json.dumps(False), - "version": json.dumps(0), - }, - ), - ] - ) - ) - tester.run("litmus-auth-relation-joined") - tester.assert_schema_valid() diff --git a/interfaces/litmus_auth/ruff.toml b/interfaces/litmus_auth/ruff.toml index efbd8c5cc..79b043b3f 100644 --- a/interfaces/litmus_auth/ruff.toml +++ b/interfaces/litmus_auth/ruff.toml @@ -9,10 +9,3 @@ quote-style = "preserve" "D", # docs "E501", # line too long ] -"./interface/v*/tests/*.py" = [ - "CPY", # copyright - "D", # docs - "S", # security - "E501", # line too long - "F841", # assignment to unused variable -] diff --git a/interfaces/mimir_cluster/interface/v0/schema.py b/interfaces/mimir_cluster/interface/v0/schema.py index 61f69595f..1985177e2 100644 --- a/interfaces/mimir_cluster/interface/v0/schema.py +++ b/interfaces/mimir_cluster/interface/v0/schema.py @@ -1,15 +1,9 @@ -"""This file defines the schemas for the provider and requirer sides of this relation interface. - -It must expose two interfaces.schema_base.DataBagSchema subclasses called: -- ProviderSchema -- RequirerSchema -""" +"""This file defines the schemas for the provider and requirer sides of this relation interface.""" import enum import typing import pydantic -from interface_tester.schema_base import DataBagSchema class MimirRole(str, enum.Enum): @@ -41,16 +35,10 @@ class Scheme(str, enum.Enum): https = "https" -class MimirClusterProviderAppData(pydantic.BaseModel): +class ProviderAppData(pydantic.BaseModel): mimir_config: dict[str, typing.Any] -class ProviderSchema(DataBagSchema): - """The schema for the provider side of this interface.""" - - app: MimirClusterProviderAppData - - class JujuTopology(pydantic.BaseModel): unit: str app: str @@ -60,19 +48,15 @@ class JujuTopology(pydantic.BaseModel): juju_model_uuid: str = pydantic.Field(description="Juju model UUID.", alias="model_uuid") -class MimirClusterRequirerUnitData(pydantic.BaseModel): +class RequirerUnitData(pydantic.BaseModel): juju_topology: JujuTopology address: str port: int scheme: Scheme -class MimirClusterRequirerAppData(pydantic.BaseModel): +class RequirerAppData(pydantic.BaseModel): roles: list[MimirRole] -class RequirerSchema(DataBagSchema): - """The schema for the requirer side of this interface.""" - - unit: MimirClusterRequirerUnitData - app: MimirClusterRequirerAppData +ProviderUnitData = None diff --git a/interfaces/profiling/interface/v0/interface.yaml b/interfaces/profiling/interface/v0/interface.yaml index 494f7c1f7..bfe3c33fb 100644 --- a/interfaces/profiling/interface/v0/interface.yaml +++ b/interfaces/profiling/interface/v0/interface.yaml @@ -9,16 +9,9 @@ status: draft providers: - name: pyroscope-coordinator-k8s url: https://github.com/canonical/pyroscope-operators - test_setup: - charm_root: coordinator - location: tests/interface/conftest.py - identifier: profiling_tester - pre_run: uv pip compile pyproject.toml --quiet --output-file requirements.txt requirers: - name: opentelemetry-collector-k8s url: https://github.com/canonical/opentelemetry-collector-k8s-operator - test_setup: - pre_run: uv pip compile pyproject.toml --quiet --output-file requirements.txt maintainer: "observability" diff --git a/interfaces/profiling/interface/v0/schema.py b/interfaces/profiling/interface/v0/schema.py index 66acfb855..446762711 100644 --- a/interfaces/profiling/interface/v0/schema.py +++ b/interfaces/profiling/interface/v0/schema.py @@ -1,15 +1,9 @@ -"""This file defines the schemas for the provider and requirer sides of this relation interface. +"""This file defines the schemas for the provider and requirer sides of this relation interface.""" -It must expose two interfaces.schema_base.DataBagSchema subclasses called: -- ProviderSchema -- RequirerSchema -""" - -from interface_tester.schema_base import DataBagSchema from pydantic import BaseModel, Field -class ProviderAppSchema(BaseModel): +class ProviderAppData(BaseModel): """Application databag schema for the provider side of the profiling interface.""" otlp_grpc_endpoint_url: str = Field( @@ -22,11 +16,6 @@ class ProviderAppSchema(BaseModel): ) -class ProviderSchema(DataBagSchema): - """The schema for the provider side of this interface.""" - - app: ProviderAppSchema - - -class RequirerSchema(DataBagSchema): - """The schema for the requirer side of this interface.""" +ProviderUnitData = None +RequirerAppData = None +RequirerUnitData = None diff --git a/interfaces/profiling/interface/v0/tests/.disable b/interfaces/profiling/interface/v0/tests/.disable deleted file mode 100644 index e69de29bb..000000000 diff --git a/interfaces/profiling/interface/v0/tests/test_provider.py b/interfaces/profiling/interface/v0/tests/test_provider.py deleted file mode 100644 index 479d2e7ae..000000000 --- a/interfaces/profiling/interface/v0/tests/test_provider.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. - -from interface_tester.interface_test import Tester - - -# on created, joined, changed: the provider is expected to publish all data -def test_data_on_created(): - tester = Tester() - tester.run('profiling-relation-created') - tester.assert_schema_valid() - - -def test_data_on_joined(): - tester = Tester() - tester.run('profiling-relation-joined') - tester.assert_schema_valid() - - -def test_data_on_changed(): - tester = Tester() - tester.run('profiling-relation-changed') - tester.assert_schema_valid() diff --git a/interfaces/profiling/interface/v0/tests/test_requirer.py b/interfaces/profiling/interface/v0/tests/test_requirer.py deleted file mode 100644 index dac18bca7..000000000 --- a/interfaces/profiling/interface/v0/tests/test_requirer.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - - -# on created, joined, changed: the requirer is expected to publish no data -def test_no_data_on_created(): - tester = Tester() - tester.run('tracing-relation-created') - tester.assert_relation_data_empty() - - -def test_no_data_on_joined(): - tester = Tester() - tester.run('tracing-relation-joined') - tester.assert_relation_data_empty() - - -def test_no_data_on_changed(): - tester = Tester() - tester.run('tracing-relation-changed') - tester.assert_relation_data_empty() - - -# if the remote end has sent their side of the deal, we're happy -def test_data_on_changed(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='profiling', - interface='profiling', - remote_app_name='remote', - remote_app_data={ - "otlp_grpc_endpoint_url": json.dumps("my.fqdn.cluster.local:1234"), - "insecure": json.dumps(False), - }, - ) - ] - ) - ) - tester.run('tracing-relation-changed') - tester.assert_schema_valid() diff --git a/interfaces/profiling/ruff.toml b/interfaces/profiling/ruff.toml index efbd8c5cc..79b043b3f 100644 --- a/interfaces/profiling/ruff.toml +++ b/interfaces/profiling/ruff.toml @@ -9,10 +9,3 @@ quote-style = "preserve" "D", # docs "E501", # line too long ] -"./interface/v*/tests/*.py" = [ - "CPY", # copyright - "D", # docs - "S", # security - "E501", # line too long - "F841", # assignment to unused variable -] diff --git a/interfaces/prometheus_scrape/interface/v0/schema.py b/interfaces/prometheus_scrape/interface/v0/schema.py index 9257892b8..1202aed6b 100644 --- a/interfaces/prometheus_scrape/interface/v0/schema.py +++ b/interfaces/prometheus_scrape/interface/v0/schema.py @@ -1,12 +1,7 @@ -""" -This file defines the schemas for the provider and requirer sides of the `prometheus_scrape` interface. - -It exposes two interfaces.schema_base.DataBagSchema subclasses called: -- ProviderSchema -- RequirerSchema +"""This file defines the schemas for the ``prometheus_scrape`` interface. Examples: - ProviderSchema: + Provider: app: { "alert_rules": { "groups": [ @@ -56,8 +51,7 @@ } """ -from interface_tester.schema_base import DataBagSchema -from pydantic import BaseModel, Field, Json +from pydantic import BaseModel, Field class AlertRuleModel(BaseModel): @@ -115,20 +109,20 @@ class Config: unit: str = Field(description="Juju unit name.") -class ApplicationDataModel(BaseModel): - alert_rules: Json[AlertRulesModel] = Field( +class ProviderAppData(BaseModel): + alert_rules: AlertRulesModel = Field( description="Alert rules provided by the charm. By default, loaded from " "`/prometheus_alert_rules`." ) - scrape_jobs: Json[list[ScrapeJobModel]] = Field( + scrape_jobs: list[ScrapeJobModel] = Field( description="List of Prometheus scrape job configurations specifying metrics scraping targets." ) - scrape_metadata: Json[ScrapeMetadataModel] = Field( + scrape_metadata: ScrapeMetadataModel = Field( description="Metadata providing information about the Juju topology." ) -class UnitDataModel(BaseModel): +class ProviderUnitData(BaseModel): class Config: extra = "allow" @@ -146,12 +140,5 @@ class Config: ) -class ProviderSchema(DataBagSchema): - """Provider schema for Prometheus Scrape.""" - - app: ApplicationDataModel - unit: UnitDataModel - - -class RequirerSchema(DataBagSchema): - """Requirer schema for Prometheus Scrape.""" +RequirerAppData = None +RequirerUnitData = None diff --git a/interfaces/prometheus_scrape/interface/v0/tests/.disable b/interfaces/prometheus_scrape/interface/v0/tests/.disable deleted file mode 100644 index e69de29bb..000000000 diff --git a/interfaces/prometheus_scrape/interface/v0/tests/test_provider.py b/interfaces/prometheus_scrape/interface/v0/tests/test_provider.py deleted file mode 100644 index e3e4ed52f..000000000 --- a/interfaces/prometheus_scrape/interface/v0/tests/test_provider.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. - -import json - -from interface_tester import Tester -from scenario import Relation, State -from scenario.context import CharmEvents - - -def test_no_data_on_created(): - t = Tester() - state_out = t.run("prometheus-scrape-relation-created") - t.assert_relation_data_empty() - - -def test_no_data_on_joined(): - t = Tester() - state_out = t.run("prometheus-scrape-relation-joined") - t.assert_schema_valid() - - -def test_no_data_on_changed(): - t = Tester() - state_out = t.run("prometheus-scrape-relation-changed") - t.assert_relation_data_empty() - - -valid_app_data = { - "alert_rules": { - "groups": [ - { - "name": "critical_alerts", - "rules": [ - { - "alert": "DiskSpaceExceeded", - "expr": "disk_usage > 90", - "for": "5m", - "labels": {"severity": "critical", "team": "ops"}, - "annotations": { - "summary": "Disk space exceeded alert", - "description": "The disk usage on the server has exceeded 90% for the last 5 minutes.", - }, - } - ], - } - ] - }, - "scrape_jobs": [ - { - "metrics_path": "/metrics", - "static_configs": [ - { - "targets": ["server1:8080", "server2:8080"], - "labels": {"job": "server_metrics"}, - } - ], - } - ], - "scrape_metadata": { - "model": "example-model", - "model_uuid": "12345678-abcd-1234-efgh-1234567890ab", - "application": "example-app", - "unit": "example-app/0", - "charm_name": "example-charm", - }, -} -valid_unit_data = { - "prometheus_scrape_unit_address": "example-app-0.example-model-endpoints.default.svc.cluster.local", - "prometheus_scrape_unit_name": "example-app/0", - "prometheus_scrape_unit_path": '', -} -valid_app_data["scrape_metadata"] = json.dumps(valid_app_data["scrape_metadata"]) -valid_app_data["scrape_jobs"] = json.dumps(valid_app_data["scrape_jobs"]) -valid_app_data["alert_rules"] = json.dumps(valid_app_data["alert_rules"]) - - -def test_on_changed_with_existing_valid_data(): - relation = Relation( - endpoint="prometheus_scrape", - interface="prometheus_scrape", - local_app_data=valid_app_data, - local_unit_data=valid_unit_data, - ) - t = Tester(State(relations=[relation])) - state_out = t.run(CharmEvents.relation_changed(relation)) - t.assert_schema_valid() diff --git a/interfaces/prometheus_scrape/ruff.toml b/interfaces/prometheus_scrape/ruff.toml index efbd8c5cc..79b043b3f 100644 --- a/interfaces/prometheus_scrape/ruff.toml +++ b/interfaces/prometheus_scrape/ruff.toml @@ -9,10 +9,3 @@ quote-style = "preserve" "D", # docs "E501", # line too long ] -"./interface/v*/tests/*.py" = [ - "CPY", # copyright - "D", # docs - "S", # security - "E501", # line too long - "F841", # assignment to unused variable -] diff --git a/interfaces/pyroscope_cluster/interface/v0/interface.yaml b/interfaces/pyroscope_cluster/interface/v0/interface.yaml index 3a54afea1..f00152de6 100644 --- a/interfaces/pyroscope_cluster/interface/v0/interface.yaml +++ b/interfaces/pyroscope_cluster/interface/v0/interface.yaml @@ -9,20 +9,10 @@ description: | providers: - name: pyroscope-coordinator-k8s url: https://github.com/canonical/pyroscope-k8s-operator - test_setup: - charm_root: coordinator - location: tests/interface/conftest.py - identifier: cluster_tester - pre_run: uv pip compile pyproject.toml --quiet --output-file requirements.txt requirers: - name: pyroscope-worker-k8s url: https://github.com/canonical/pyroscope-k8s-operator - test_setup: - charm_root: worker - location: tests/interface/conftest.py - identifier: cluster_tester - pre_run: uv pip compile pyproject.toml --quiet --output-file requirements.txt maintainer: observability diff --git a/interfaces/pyroscope_cluster/interface/v0/schema.py b/interfaces/pyroscope_cluster/interface/v0/schema.py index 39d07587d..0f0f50b31 100644 --- a/interfaces/pyroscope_cluster/interface/v0/schema.py +++ b/interfaces/pyroscope_cluster/interface/v0/schema.py @@ -1,14 +1,8 @@ -"""This file defines the schemas for the provider and requirer sides of this relation interface. - -It must expose two interfaces.schema_base.DataBagSchema subclasses called: -- ProviderSchema -- RequirerSchema -""" +"""This file defines the schemas for the provider and requirer sides of this relation interface.""" from enum import Enum -from interface_tester.schema_base import DataBagSchema -from pydantic import BaseModel, Field, Json +from pydantic import BaseModel, Field from typing_extensions import TypedDict @@ -18,43 +12,39 @@ class RemoteWriteEndpoint(TypedDict): url: str -class PyroscopeClusterProviderAppData(BaseModel): - """PyroscopeClusterProviderAppData.""" - - worker_config: Json[str] = Field( +class ProviderAppData(BaseModel): + worker_config: str = Field( description="The pyroscope configuration that the requirer should run with." "Yaml-encoded. Must conform to the schema that the presently deployed " "workload version supports; for example see: " "https://grafana.com/docs/pyroscope/latest/configuration/#configure-pyroscope." ) - loki_endpoints: Json[dict[str, str]] | None = Field( + loki_endpoints: dict[str, str] | None = Field( default=None, description="List of loki-push-api endpoints to which the worker node can push any logs it generates.", ) - charm_tracing_receivers: Json[dict[str, str]] | None = Field( + charm_tracing_receivers: dict[str, str] | None = Field( default=None, description="Endpoints to which the the worker can push charm traces to.", ) - workload_tracing_receivers: Json[dict[str, str]] | None = Field( + workload_tracing_receivers: dict[str, str] | None = Field( default=None, description="Endpoints to which the the worker can push workload traces to.", ) - remote_write_endpoints: Json[list[RemoteWriteEndpoint]] | None = Field( + remote_write_endpoints: list[RemoteWriteEndpoint] | None = Field( default=None, description="Endpoints to which the workload (and the worker charm) can push metrics to.", ) - worker_ports: Json[list[int]] | None = Field( + worker_ports: list[int] | None = Field( default=None, description="Ports that the worker should open. " "If not provided, the worker will open all the legacy ones.", ) - ca_cert: Json[str] | None = Field( - default=None, description="CA certificate for tls encryption." - ) - server_cert: Json[str] | None = Field( + ca_cert: str | None = Field(default=None, description="CA certificate for tls encryption.") + server_cert: str | None = Field( default=None, description="Server certificate for tls encryption." ) - privkey_secret_id: Json[str] | None = Field( + privkey_secret_id: str | None = Field( default=None, description="Private key used by the coordinator, for tls encryption.", ) @@ -72,11 +62,9 @@ class _Topology(BaseModel): unit: str | None -class PyroscopeClusterRequirerUnitData(BaseModel): - """PyroscopeClusterRequirerUnitData.""" - - juju_topology: Json[_Topology] - address: Json[str] +class RequirerUnitData(BaseModel): + juju_topology: _Topology + address: str class PyroscopeRole(str, Enum): @@ -100,20 +88,8 @@ class PyroscopeRole(str, Enum): store_gateway = "store-gateway" -class PyroscopeClusterRequirerAppData(BaseModel): - """PyroscopeClusterRequirerAppData.""" - - role: Json[PyroscopeRole] - - -class ProviderSchema(DataBagSchema): - """The schema for the provider side of this interface.""" - - app: PyroscopeClusterProviderAppData - +class RequirerAppData(BaseModel): + role: PyroscopeRole -class RequirerSchema(DataBagSchema): - """The schema for the requirer side of this interface.""" - app: PyroscopeClusterRequirerAppData - unit: PyroscopeClusterRequirerUnitData +ProviderUnitData = None diff --git a/interfaces/pyroscope_cluster/interface/v0/tests/.disable b/interfaces/pyroscope_cluster/interface/v0/tests/.disable deleted file mode 100644 index e69de29bb..000000000 diff --git a/interfaces/pyroscope_cluster/interface/v0/tests/test_provider.py b/interfaces/pyroscope_cluster/interface/v0/tests/test_provider.py deleted file mode 100644 index 45a6089d1..000000000 --- a/interfaces/pyroscope_cluster/interface/v0/tests/test_provider.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - - -def test_validation_fails_with_missing_role(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="pyroscope_cluster", - interface="pyroscope_cluster", - remote_app_name="worker", - remote_app_data={}, - remote_units_data={ - 0: { - "juju_topology": json.dumps({ - "application": "worker", - "unit": "worker/0", - "charm_name": "worker", - }), - "address": json.dumps("192.0.2.1"), - } - }, - ) - ] - ) - ) - tester.run("pyroscope-cluster-relation-created") - tester.assert_relation_data_empty() - - -def test_validation_succeeds_on_joining_with_role(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="pyroscope_cluster", - interface="pyroscope_cluster", - remote_app_name="worker", - remote_app_data={ - "role": json.dumps("all"), - }, - remote_units_data={ - 0: { - "juju_topology": json.dumps({ - "application": "worker", - "unit": "worker/0", - "charm_name": "worker", - }), - "address": json.dumps("192.0.2.1"), - } - }, - ), - ] - ) - ) - tester.run("pyroscope-cluster-relation-joined") - tester.assert_schema_valid() - - -def test_validation_fails_on_joining_with_invalid_role(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="pyroscope_cluster", - interface="pyroscope_cluster", - remote_app_name="worker", - remote_app_data={"role": json.dumps("imposter")}, - remote_units_data={ - 0: { - "juju_topology": json.dumps({ - "application": "worker", - "unit": "worker/0", - "charm_name": "worker", - }), - "address": json.dumps("192.0.2.1"), - } - }, - ) - ] - ) - ) - tester.run("pyroscope-cluster-relation-joined") - tester.assert_relation_data_empty() diff --git a/interfaces/pyroscope_cluster/interface/v0/tests/test_requirer.py b/interfaces/pyroscope_cluster/interface/v0/tests/test_requirer.py deleted file mode 100644 index fbe4edea6..000000000 --- a/interfaces/pyroscope_cluster/interface/v0/tests/test_requirer.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. - -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - - -def test_data_on_created(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="pyroscope_cluster", - interface="pyroscope_cluster", - remote_app_name="coordinator", - remote_app_data={"worker_config": json.dumps("foo: bar")}, - ) - ] - ) - ) - tester.run("pyroscope-cluster-relation-created") - tester.assert_schema_valid() diff --git a/interfaces/pyroscope_cluster/ruff.toml b/interfaces/pyroscope_cluster/ruff.toml index efbd8c5cc..79b043b3f 100644 --- a/interfaces/pyroscope_cluster/ruff.toml +++ b/interfaces/pyroscope_cluster/ruff.toml @@ -9,10 +9,3 @@ quote-style = "preserve" "D", # docs "E501", # line too long ] -"./interface/v*/tests/*.py" = [ - "CPY", # copyright - "D", # docs - "S", # security - "E501", # line too long - "F841", # assignment to unused variable -] diff --git a/interfaces/tempo_cluster/interface/v0/interface.yaml b/interfaces/tempo_cluster/interface/v0/interface.yaml index 7f1f9c7b6..5c901329f 100644 --- a/interfaces/tempo_cluster/interface/v0/interface.yaml +++ b/interfaces/tempo_cluster/interface/v0/interface.yaml @@ -6,9 +6,6 @@ status: draft providers: - name: tempo-coordinator-k8s url: https://github.com/canonical/tempo-coordinator-k8s-operator - test_setup: - location: tests/interface/conftest.py - identifier: cluster_tester requirers: - name: tempo-worker-k8s diff --git a/interfaces/tempo_cluster/interface/v0/tests/.disable b/interfaces/tempo_cluster/interface/v0/tests/.disable deleted file mode 100644 index e69de29bb..000000000 diff --git a/interfaces/tempo_cluster/interface/v0/tests/test_provider.py b/interfaces/tempo_cluster/interface/v0/tests/test_provider.py deleted file mode 100644 index 177889464..000000000 --- a/interfaces/tempo_cluster/interface/v0/tests/test_provider.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - - -def test_validation_fails_with_missing_role(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="tempo_cluster", - interface="tempo_cluster", - remote_app_name="worker", - remote_app_data={}, - remote_units_data={ - 0: { - "juju_topology": json.dumps({ - "application": "worker", - "unit": "worker/0", - "charm_name": "worker", - }), - "address": json.dumps("192.0.2.1"), - } - }, - ) - ] - ) - ) - tester.run("tempo-cluster-relation-created") - tester.assert_relation_data_empty() - - -def test_validation_succeeds_on_joining_with_role(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="tempo_cluster", - interface="tempo_cluster", - remote_app_name="worker", - remote_app_data={ - "role": json.dumps("all"), - }, - remote_units_data={ - 0: { - "juju_topology": json.dumps({ - "application": "worker", - "unit": "worker/0", - "charm_name": "worker", - }), - "address": json.dumps("192.0.2.1"), - } - }, - ), - ] - ) - ) - tester.run("tempo-cluster-relation-joined") - tester.assert_schema_valid() - - -def test_validation_fails_on_joining_with_invalid_role(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="tempo_cluster", - interface="tempo_cluster", - remote_app_name="worker", - remote_app_data={"role": json.dumps("imposter")}, - remote_units_data={ - 0: { - "juju_topology": json.dumps({ - "application": "worker", - "unit": "worker/0", - "charm_name": "worker", - }), - "address": json.dumps("192.0.2.1"), - } - }, - ) - ] - ) - ) - tester.run("tempo-cluster-relation-joined") - tester.assert_relation_data_empty() diff --git a/interfaces/tempo_cluster/interface/v0/tests/test_requirer.py b/interfaces/tempo_cluster/interface/v0/tests/test_requirer.py deleted file mode 100644 index 3a89a6aa1..000000000 --- a/interfaces/tempo_cluster/interface/v0/tests/test_requirer.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. - -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - - -def test_data_on_created(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="tempo_cluster", - interface="tempo_cluster", - remote_app_name="coordinator", - remote_app_data={"worker_config": json.dumps("foo: bar")}, - ) - ] - ) - ) - tester.run("tempo-cluster-relation-created") - tester.assert_schema_valid() diff --git a/interfaces/tempo_cluster/interface/v1/interface.yaml b/interfaces/tempo_cluster/interface/v1/interface.yaml index 4425e8e81..956c5a65d 100644 --- a/interfaces/tempo_cluster/interface/v1/interface.yaml +++ b/interfaces/tempo_cluster/interface/v1/interface.yaml @@ -9,9 +9,6 @@ description: | providers: - name: tempo-coordinator-k8s url: https://github.com/canonical/tempo-coordinator-k8s-operator - test_setup: - location: tests/interface/conftest.py - identifier: cluster_tester requirers: - name: tempo-worker-k8s diff --git a/interfaces/tempo_cluster/interface/v1/schema.py b/interfaces/tempo_cluster/interface/v1/schema.py index 37ee5b748..70e5817b2 100644 --- a/interfaces/tempo_cluster/interface/v1/schema.py +++ b/interfaces/tempo_cluster/interface/v1/schema.py @@ -1,57 +1,47 @@ -"""This file defines the schemas for the provider and requirer sides of this relation interface. - -It must expose two interfaces.schema_base.DataBagSchema subclasses called: -- ProviderSchema -- RequirerSchema -""" +"""This file defines the schemas for the provider and requirer sides of this relation interface.""" from enum import Enum -from interface_tester.schema_base import DataBagSchema -from pydantic import BaseModel, Field, Json - +from pydantic import BaseModel, Field -class TempoClusterProviderAppData(BaseModel): - """TempoClusterProviderAppData.""" - worker_config: Json[str] = Field( +class ProviderAppData(BaseModel): + worker_config: str = Field( description="The tempo configuration that the requirer should run with." "Yaml-encoded. Must conform to the schema that the presently deployed " "workload version supports; for example see: " "https://grafana.com/docs/tempo/latest/configuration/#configure-tempo." ) - loki_endpoints: Json[dict[str, str]] | None = Field( + loki_endpoints: dict[str, str] | None = Field( default=None, description="List of loki-push-api endpoints to which the worker node can push any logs it generates.", ) - ca_cert: Json[str] | None = Field( - default=None, description="CA certificate for tls encryption." - ) - server_cert: Json[str] | None = Field( + ca_cert: str | None = Field(default=None, description="CA certificate for tls encryption.") + server_cert: str | None = Field( default=None, description="Server certificate for tls encryption." ) - s3_tls_ca_cert: Json[str] | None = Field( + s3_tls_ca_cert: str | None = Field( default=None, description="CA certificate for the s3 bucket API." ) - privkey_secret_id: Json[str] | None = Field( + privkey_secret_id: str | None = Field( default=None, description="ID of a Juju secret that holds the private key used by the coordinator for TLS encryption.", ) - remote_write_endpoints: Json[list[dict[str, str]]] | None = Field( + remote_write_endpoints: list[dict[str, str]] | None = Field( default=None, description="Endpoints to which the workload (and the worker charm) can push metrics to.", ) - charm_tracing_receivers: Json[dict[str, str]] | None = Field( + charm_tracing_receivers: dict[str, str] | None = Field( default=None, description="Endpoints to which the worker node can push its charm traces to." "It is a mapping from protocol names such as `zipkin`, `otlp_grpc`, `otlp_http`.", ) - workload_tracing_receivers: Json[dict[str, str]] | None = Field( + workload_tracing_receivers: dict[str, str] | None = Field( default=None, description="Endpoints to which the worker node can push its workload traces to." "It is a mapping from protocol names such as `zipkin`, `otlp_grpc`, `otlp_http`.", ) - worker_ports: Json[list[int]] | None = Field( + worker_ports: list[int] | None = Field( default=None, description="Ports that the worker should open on its pod.", ) @@ -65,11 +55,9 @@ class _Topology(BaseModel): unit: str | None -class TempoClusterRequirerUnitData(BaseModel): - """TempoClusterRequirerUnitData.""" - - juju_topology: Json[_Topology] - address: Json[str] +class RequirerUnitData(BaseModel): + juju_topology: _Topology + address: str class TempoRole(str, Enum): @@ -91,20 +79,8 @@ class TempoRole(str, Enum): METRICS_GENERATOR = "metrics-generator" -class TempoClusterRequirerAppData(BaseModel): - """TempoClusterRequirerAppData.""" - - role: Json[TempoRole] - - -class ProviderSchema(DataBagSchema): - """The schema for the provider side of this interface.""" - - app: TempoClusterProviderAppData - +class RequirerAppData(BaseModel): + role: TempoRole -class RequirerSchema(DataBagSchema): - """The schema for the requirer side of this interface.""" - app: TempoClusterRequirerAppData - unit: TempoClusterRequirerUnitData +ProviderUnitData = None diff --git a/interfaces/tempo_cluster/interface/v1/tests/.disable b/interfaces/tempo_cluster/interface/v1/tests/.disable deleted file mode 100644 index e69de29bb..000000000 diff --git a/interfaces/tempo_cluster/interface/v1/tests/test_provider.py b/interfaces/tempo_cluster/interface/v1/tests/test_provider.py deleted file mode 100644 index 177889464..000000000 --- a/interfaces/tempo_cluster/interface/v1/tests/test_provider.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - - -def test_validation_fails_with_missing_role(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="tempo_cluster", - interface="tempo_cluster", - remote_app_name="worker", - remote_app_data={}, - remote_units_data={ - 0: { - "juju_topology": json.dumps({ - "application": "worker", - "unit": "worker/0", - "charm_name": "worker", - }), - "address": json.dumps("192.0.2.1"), - } - }, - ) - ] - ) - ) - tester.run("tempo-cluster-relation-created") - tester.assert_relation_data_empty() - - -def test_validation_succeeds_on_joining_with_role(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="tempo_cluster", - interface="tempo_cluster", - remote_app_name="worker", - remote_app_data={ - "role": json.dumps("all"), - }, - remote_units_data={ - 0: { - "juju_topology": json.dumps({ - "application": "worker", - "unit": "worker/0", - "charm_name": "worker", - }), - "address": json.dumps("192.0.2.1"), - } - }, - ), - ] - ) - ) - tester.run("tempo-cluster-relation-joined") - tester.assert_schema_valid() - - -def test_validation_fails_on_joining_with_invalid_role(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="tempo_cluster", - interface="tempo_cluster", - remote_app_name="worker", - remote_app_data={"role": json.dumps("imposter")}, - remote_units_data={ - 0: { - "juju_topology": json.dumps({ - "application": "worker", - "unit": "worker/0", - "charm_name": "worker", - }), - "address": json.dumps("192.0.2.1"), - } - }, - ) - ] - ) - ) - tester.run("tempo-cluster-relation-joined") - tester.assert_relation_data_empty() diff --git a/interfaces/tempo_cluster/interface/v1/tests/test_requirer.py b/interfaces/tempo_cluster/interface/v1/tests/test_requirer.py deleted file mode 100644 index 603a48db8..000000000 --- a/interfaces/tempo_cluster/interface/v1/tests/test_requirer.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. - -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - - -def test_data_on_created(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint="tempo_cluster", - interface="tempo_cluster", - remote_app_name="coordinator", - remote_app_data={ - "worker_config": json.dumps("foo: bar"), - "charm_tracing_receivers": json.dumps({ - "otlp_http": "http://192.0.2.1:4318", - }), - "workload_tracing_receivers": json.dumps({ - "otlp_http": "http://192.0.2.2:4318", - "otlp_grpc": "192.0.2.2:4317", - }), - }, - ) - ] - ) - ) - tester.run("tempo-cluster-relation-created") - tester.assert_schema_valid() diff --git a/interfaces/tempo_cluster/ruff.toml b/interfaces/tempo_cluster/ruff.toml index efbd8c5cc..79b043b3f 100644 --- a/interfaces/tempo_cluster/ruff.toml +++ b/interfaces/tempo_cluster/ruff.toml @@ -9,10 +9,3 @@ quote-style = "preserve" "D", # docs "E501", # line too long ] -"./interface/v*/tests/*.py" = [ - "CPY", # copyright - "D", # docs - "S", # security - "E501", # line too long - "F841", # assignment to unused variable -] diff --git a/interfaces/tracing/interface/v0/tests/test_provider.py b/interfaces/tracing/interface/v0/tests/test_provider.py deleted file mode 100644 index 451931367..000000000 --- a/interfaces/tracing/interface/v0/tests/test_provider.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2023 Canonical -# See LICENSE file for licensing details. - -from interface_tester.interface_test import Tester -from scenario import Relation, State - -# No matter what's in the remote databags, -# the tracing v1 provider will always populate with valid data. - - -def test_data_on_created(): - tester = Tester() - tester.run('foo-relation-created') - tester.assert_schema_valid() - - -def test_data_on_joined(): - tester = Tester() - tester.run('foo-relation-joined') - tester.assert_schema_valid() - - -def test_data_on_changed(): - tester = Tester() - tester.run('foo-relation-changed') - tester.assert_schema_valid() - - -# also if the remote side sends rubbish -def test_no_data_on_changed_bad_remote_data(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='foo', - interface='tracing', - remote_app_name='remote', - remote_app_data={"bubble": "rubble"}, - ) - ] - ) - ) - tester.run('foo-relation-changed') - tester.assert_relation_data_empty() diff --git a/interfaces/tracing/interface/v1/tests/test_provider.py b/interfaces/tracing/interface/v1/tests/test_provider.py deleted file mode 100644 index 95745b724..000000000 --- a/interfaces/tracing/interface/v1/tests/test_provider.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - - -def test_no_response_on_bad_data(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='tracing', interface='tracing', remote_app_data={"bubble": "rubble"} - ) - ] - ) - ) - tester.run('tracing-relation-changed') - tester.assert_relation_data_empty() - - -def test_data_on_created(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='tracing', - interface='tracing', - remote_app_name='remote', - remote_app_data={"receivers": json.dumps(["otlp_grpc"])}, - ) - ] - ) - ) - tester.run('tracing-relation-created') - tester.assert_schema_valid() - - -def test_data_on_joined(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='tracing', - interface='tracing', - remote_app_name='remote', - remote_app_data={"receivers": json.dumps(["otlp_grpc"])}, - ) - ] - ) - ) - tester.run('tracing-relation-joined') - tester.assert_schema_valid() - - -def test_data_on_changed(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='tracing', - interface='tracing', - remote_app_name='remote', - remote_app_data={"receivers": json.dumps(["otlp_grpc"])}, - ) - ] - ) - ) - tester.run('tracing-relation-changed') - tester.assert_schema_valid() diff --git a/interfaces/tracing/interface/v1/tests/test_requirer.py b/interfaces/tracing/interface/v1/tests/test_requirer.py deleted file mode 100644 index 5848d148e..000000000 --- a/interfaces/tracing/interface/v1/tests/test_requirer.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. - -from interface_tester.interface_test import Tester - - -# on created, joined, changed: the requirer is expected to publish a list of requested receivers -def test_data_on_created(): - tester = Tester() - tester.run('tracing-relation-created') - tester.assert_schema_valid() - - -def test_data_on_joined(): - tester = Tester() - tester.run('tracing-relation-joined') - tester.assert_schema_valid() - - -def test_data_on_changed(): - tester = Tester() - tester.run('tracing-relation-changed') - tester.assert_schema_valid() diff --git a/interfaces/tracing/interface/v2/interface.yaml b/interfaces/tracing/interface/v2/interface.yaml index c7b6b124d..6b62d84e7 100644 --- a/interfaces/tracing/interface/v2/interface.yaml +++ b/interfaces/tracing/interface/v2/interface.yaml @@ -11,19 +11,9 @@ description: | providers: - name: tempo-coordinator-k8s url: https://github.com/canonical/tempo-operators - test_setup: - charm_root: coordinator - pre_run: uv export --frozen --format requirements.txt --output-file requirements.txt - location: tests/interface/conftest.py - identifier: tracing_tester requirers: - name: pyroscope-coordinator-k8s url: https://github.com/canonical/pyroscope-operators - test_setup: - charm_root: coordinator - pre_run: uv export --frozen --format requirements.txt --output-file requirements.txt - location: tests/interface/conftest.py - identifier: tracing_tester maintainer: observability diff --git a/interfaces/tracing/interface/v2/schema.py b/interfaces/tracing/interface/v2/schema.py index af22ee547..6bbfcfc4a 100644 --- a/interfaces/tracing/interface/v2/schema.py +++ b/interfaces/tracing/interface/v2/schema.py @@ -2,19 +2,15 @@ # See LICENSE file for licensing details. """This file defines the schemas for the provider and requirer sides of the tracing interface. -It exposes two interfaces.schema_base.DataBagSchema subclasses called: -- ProviderSchema -- RequirerSchema - Examples: - RequirerSchema: + Requirer: unit_data: application_data: receivers: - otlp_grpc - otlp_http - ProviderSchema: + Provider: # unit_data: application_data: receivers: @@ -31,8 +27,7 @@ import enum -from interface_tester.schema_base import DataBagSchema -from pydantic import BaseModel, ConfigDict, Field, Json +from pydantic import BaseModel, ConfigDict, Field class TransportProtocolType(str, enum.Enum): @@ -83,26 +78,18 @@ class Receiver(BaseModel): ) -class TracingProviderData(BaseModel): - receivers: Json[list[Receiver]] = Field( +class ProviderAppData(BaseModel): + receivers: list[Receiver] = Field( ..., description="A list of enabled receivers in the form of the protocol they use and their resolvable server url.", ) -class TracingRequirerData(BaseModel): - receivers: Json[list[str]] = Field( +class RequirerAppData(BaseModel): + receivers: list[str] = Field( ..., description="List of protocols that the requirer wishes to use." ) -class ProviderSchema(DataBagSchema): - """Provider schema for Tracing.""" - - app: TracingProviderData - - -class RequirerSchema(DataBagSchema): - """Requirer schema for Tracing.""" - - app: TracingRequirerData +ProviderUnitData = None +RequirerUnitData = None diff --git a/interfaces/tracing/interface/v2/tests/test_provider.py b/interfaces/tracing/interface/v2/tests/test_provider.py deleted file mode 100644 index a91e57f6b..000000000 --- a/interfaces/tracing/interface/v2/tests/test_provider.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. -import json - -from interface_tester.interface_test import Tester -from scenario import Relation, State - -_VALID_REQUIRER_APP_DATA = { - "receivers": json.dumps([ - {"protocol": {"name": "otlp_grpc", "type": "grpc"}, "url": "http://192.0.2.0/24"} - ]) -} - - -def test_data_on_created(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='tracing', - interface='tracing', - remote_app_name='remote', - remote_app_data=_VALID_REQUIRER_APP_DATA, - ) - ] - ) - ) - tester.run('tracing-relation-created') - tester.assert_schema_valid() - - -def test_data_on_joined(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='tracing', - interface='tracing', - remote_app_name='remote', - remote_app_data=_VALID_REQUIRER_APP_DATA, - ) - ] - ) - ) - tester.run('tracing-relation-joined') - tester.assert_schema_valid() - - -def test_data_on_changed(): - tester = Tester( - state_in=State( - relations=[ - Relation( - endpoint='tracing', - interface='tracing', - remote_app_name='remote', - remote_app_data=_VALID_REQUIRER_APP_DATA, - ) - ] - ) - ) - tester.run('tracing-relation-changed') - tester.assert_schema_valid() diff --git a/interfaces/tracing/interface/v2/tests/test_requirer.py b/interfaces/tracing/interface/v2/tests/test_requirer.py deleted file mode 100644 index 5848d148e..000000000 --- a/interfaces/tracing/interface/v2/tests/test_requirer.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2024 Canonical -# See LICENSE file for licensing details. - -from interface_tester.interface_test import Tester - - -# on created, joined, changed: the requirer is expected to publish a list of requested receivers -def test_data_on_created(): - tester = Tester() - tester.run('tracing-relation-created') - tester.assert_schema_valid() - - -def test_data_on_joined(): - tester = Tester() - tester.run('tracing-relation-joined') - tester.assert_schema_valid() - - -def test_data_on_changed(): - tester = Tester() - tester.run('tracing-relation-changed') - tester.assert_schema_valid()