From 5d80747a3467367268959156320aa46d3423d913 Mon Sep 17 00:00:00 2001 From: Simon Aronsson Date: Thu, 5 Sep 2024 14:35:51 +0200 Subject: [PATCH 1/5] Create README.md --- lib/charms/interfaces/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 lib/charms/interfaces/README.md diff --git a/lib/charms/interfaces/README.md b/lib/charms/interfaces/README.md new file mode 100644 index 00000000..e98ca48f --- /dev/null +++ b/lib/charms/interfaces/README.md @@ -0,0 +1,4 @@ +### ⚠️ Warning + +The move of the library has not been fully done yet. Do not use yet. For now, it still lives in https://github.com/canonical/traefik-k8s-operator + From fa0526bf9482a6927b0c475e8d8c01fe26ff569c Mon Sep 17 00:00:00 2001 From: Tony Meyer Date: Thu, 26 Sep 2024 17:29:57 +1200 Subject: [PATCH 2/5] chore: bump to Scenario 7 (#177) * Remove the restriction on ops and ops-scenario for building JSON schemas. * Fix typo. * Bump ops-scenario and pytest-interface-tester versions. * Remove unused import. * Adjust to the Scenario 7 API. * Bump the generated docs. --- README.md | 2 +- docs/json_schemas/dns_record/v0/provider.json | 6 +----- docs/json_schemas/dns_record/v0/requirer.json | 12 ++---------- .../kratos_external_idp/v0/provider.json | 6 +----- docs/json_schemas/smtp/v0/provider.json | 12 ++---------- docs/json_schemas/tracing/v2/provider.json | 12 ++---------- interfaces/__template__/v0/interface_tests/README.md | 3 ++- .../cos_agent/v0/interface_tests/test_provider.py | 3 ++- .../ingress/v1/interface_tests/test_provider.py | 3 ++- .../ingress/v2/interface_tests/test_provider.py | 7 ++++--- .../v0/interface_tests/test_provider.py | 5 +++-- pyproject.toml | 8 ++++---- tox.ini | 4 ++-- utils/interface-validator.py | 2 +- 14 files changed, 29 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 6f658c8b..dcb80a7c 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,6 @@ For the time being, to see available interfaces, their statuses, and schemas, br # Relation interface testers -In order to automatically validate whether a charm satisfies a given relation interface, the relation interface maintainer(s) need to write one or more **relation interface tests**. A relation interface test is a [scenario-based test case](https://github.com/canonical/ops-scenario) which checks that, given an intitial context, when a relation event is triggered, the charm will do what the interface specifies. For example, most interface testers will check that, on relation changed, the charm will write a certain value into its (app/unit) databag and that that value matches a certain (Pydantic) schema. +In order to automatically validate whether a charm satisfies a given relation interface, the relation interface maintainer(s) need to write one or more **relation interface tests**. A relation interface test is a [scenario-based test case](https://github.com/canonical/ops-scenario) which checks that, given an initial context, when a relation event is triggered, the charm will do what the interface specifies. For example, most interface testers will check that, on relation changed, the charm will write a certain value into its (app/unit) databag and that that value matches a certain (Pydantic) schema. See [the tester documentation](https://github.com/canonical/interface-tester-pytest) for more. diff --git a/docs/json_schemas/dns_record/v0/provider.json b/docs/json_schemas/dns_record/v0/provider.json index ec7064db..4e979c17 100644 --- a/docs/json_schemas/dns_record/v0/provider.json +++ b/docs/json_schemas/dns_record/v0/provider.json @@ -35,11 +35,7 @@ "type": "string" }, "status": { - "allOf": [ - { - "$ref": "#/$defs/Status" - } - ], + "$ref": "#/$defs/Status", "description": "Status for the domain request.", "examples": [ "approved", diff --git a/docs/json_schemas/dns_record/v0/requirer.json b/docs/json_schemas/dns_record/v0/requirer.json index fa6f4ed2..91825545 100644 --- a/docs/json_schemas/dns_record/v0/requirer.json +++ b/docs/json_schemas/dns_record/v0/requirer.json @@ -101,11 +101,7 @@ "type": "integer" }, "record_class": { - "allOf": [ - { - "$ref": "#/$defs/RecordClass" - } - ], + "$ref": "#/$defs/RecordClass", "default": null, "description": "The DNS record class.", "examples": [ @@ -114,11 +110,7 @@ "name": "Record class" }, "record_type": { - "allOf": [ - { - "$ref": "#/$defs/RecordType" - } - ], + "$ref": "#/$defs/RecordType", "default": null, "description": "The DNS record type.", "examples": [ diff --git a/docs/json_schemas/kratos_external_idp/v0/provider.json b/docs/json_schemas/kratos_external_idp/v0/provider.json index 2d1f47ad..5aa8040a 100644 --- a/docs/json_schemas/kratos_external_idp/v0/provider.json +++ b/docs/json_schemas/kratos_external_idp/v0/provider.json @@ -16,11 +16,7 @@ "type": "string" }, "secret_backend": { - "allOf": [ - { - "$ref": "#/$defs/SecretBackend" - } - ], + "$ref": "#/$defs/SecretBackend", "default": "relation" }, "provider": { diff --git a/docs/json_schemas/smtp/v0/provider.json b/docs/json_schemas/smtp/v0/provider.json index 351b4f26..1f35daee 100644 --- a/docs/json_schemas/smtp/v0/provider.json +++ b/docs/json_schemas/smtp/v0/provider.json @@ -84,11 +84,7 @@ "title": "Password ID" }, "auth_type": { - "allOf": [ - { - "$ref": "#/$defs/AuthType" - } - ], + "$ref": "#/$defs/AuthType", "description": "The type used to authenticate with the SMTP relay.", "examples": [ "none" @@ -96,11 +92,7 @@ "title": "Auth type" }, "transport_security": { - "allOf": [ - { - "$ref": "#/$defs/TransportSecurity" - } - ], + "$ref": "#/$defs/TransportSecurity", "description": "The security protocol to use for the SMTP relay.", "examples": [ "none" diff --git a/docs/json_schemas/tracing/v2/provider.json b/docs/json_schemas/tracing/v2/provider.json index c89c72c2..85dbdfbe 100644 --- a/docs/json_schemas/tracing/v2/provider.json +++ b/docs/json_schemas/tracing/v2/provider.json @@ -20,11 +20,7 @@ "type": "string" }, "type": { - "allOf": [ - { - "$ref": "#/$defs/TransportProtocolType" - } - ], + "$ref": "#/$defs/TransportProtocolType", "description": "The transport protocol used by this receiver.", "examples": [ "http", @@ -43,11 +39,7 @@ "description": "Specification of an active receiver.", "properties": { "protocol": { - "allOf": [ - { - "$ref": "#/$defs/ProtocolType" - } - ], + "$ref": "#/$defs/ProtocolType", "description": "Receiver protocol name and type." }, "url": { diff --git a/interfaces/__template__/v0/interface_tests/README.md b/interfaces/__template__/v0/interface_tests/README.md index 00ed852c..ee5cd2d6 100644 --- a/interfaces/__template__/v0/interface_tests/README.md +++ b/interfaces/__template__/v0/interface_tests/README.md @@ -10,6 +10,7 @@ Copy this test to `test_provider.py` and fill in the blanks however appropriate ```python from scenario import Relation, State +from scenario.context import CharmEvents from interface_tester import Tester def test_data_published_on_changed_remote_valid(): @@ -25,7 +26,7 @@ def test_data_published_on_changed_remote_valid(): ) # WHEN the receives a event: - t.run(relation.changed_event) + t.run(CharmEvents.relation_changed(relation)) # THEN the also publishes valid data to its side of the relation # (if applicable) diff --git a/interfaces/cos_agent/v0/interface_tests/test_provider.py b/interfaces/cos_agent/v0/interface_tests/test_provider.py index 1c9966aa..4891b4f5 100644 --- a/interfaces/cos_agent/v0/interface_tests/test_provider.py +++ b/interfaces/cos_agent/v0/interface_tests/test_provider.py @@ -4,6 +4,7 @@ import json from interface_tester import Tester from scenario import State, Relation +from scenario.context import CharmEvents def test_no_data_on_created(): @@ -90,5 +91,5 @@ def test_on_changed_with_existing_valid_data(): local_unit_data=valid_unit_data, ) t = Tester(State(relations=[relation])) - state_out = t.run(relation.changed_event) + state_out = t.run(CharmEvents.relation_changed(relation)) t.assert_schema_valid() diff --git a/interfaces/ingress/v1/interface_tests/test_provider.py b/interfaces/ingress/v1/interface_tests/test_provider.py index b38ed0fb..c0d73faf 100644 --- a/interfaces/ingress/v1/interface_tests/test_provider.py +++ b/interfaces/ingress/v1/interface_tests/test_provider.py @@ -4,6 +4,7 @@ from interface_tester import Tester from scenario import State, Relation +from scenario.context import CharmEvents def test_no_data_on_created(): @@ -43,7 +44,7 @@ def test_data_published_on_changed_remote_valid(): remote_app_data={'host': '"0.0.0.42"', 'model': '"bar"', 'name': '"remote/0"', 'port': '42'} ) t = Tester(State(leader=True, relations=[ingress])) - state_out = t.run(ingress.changed_event) + state_out = t.run(CharmEvents.relation_changed(ingress)) t.assert_schema_valid() diff --git a/interfaces/ingress/v2/interface_tests/test_provider.py b/interfaces/ingress/v2/interface_tests/test_provider.py index 4eb9b49d..d27417b2 100644 --- a/interfaces/ingress/v2/interface_tests/test_provider.py +++ b/interfaces/ingress/v2/interface_tests/test_provider.py @@ -3,6 +3,7 @@ from interface_tester import Tester from scenario import State, Relation +from scenario.context import CharmEvents def test_no_data_on_created(): @@ -45,7 +46,7 @@ def test_data_published_on_changed_remote_valid(): relations=[relation] ) ) - state_out = t.run(relation.changed_event) + state_out = t.run(CharmEvents.relation_changed(relation)) t.assert_schema_valid() @@ -59,7 +60,7 @@ def test_data_published_on_changed_remote_invalid_json(): relations=[ingress] ) ) - state_out = t.run(ingress.changed_event) + state_out = t.run(CharmEvents.relation_changed(ingress)) t.assert_relation_data_empty() @@ -73,6 +74,6 @@ def test_data_published_on_changed_remote_invalid(): relations=[ingress] ) ) - state_out = t.run(ingress.changed_event) + state_out = t.run(CharmEvents.relation_changed(ingress)) t.assert_relation_data_empty() diff --git a/interfaces/prometheus_scrape/v0/interface_tests/test_provider.py b/interfaces/prometheus_scrape/v0/interface_tests/test_provider.py index 5c3fc84a..adde825a 100644 --- a/interfaces/prometheus_scrape/v0/interface_tests/test_provider.py +++ b/interfaces/prometheus_scrape/v0/interface_tests/test_provider.py @@ -4,6 +4,7 @@ import json from interface_tester import Tester from scenario import Relation, State +from scenario.context import CharmEvents def test_no_data_on_created(): @@ -80,5 +81,5 @@ def test_on_changed_with_existing_valid_data(): local_unit_data=valid_unit_data, ) t = Tester(State(relations=[relation])) - state_out = t.run(relation.changed_event) - t.assert_schema_valid() \ No newline at end of file + state_out = t.run(CharmEvents.relation_changed(relation)) + t.assert_schema_valid() diff --git a/pyproject.toml b/pyproject.toml index 4a1ef801..07218709 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ packages = [ 'interfaces'] [project] name = "charm-relation-interfaces" -version = "0.1" +version = "0.1.1" authors = [ { name = "Pietro Pasotti", email = "pietro.pasotti@canonical.com" }, { name = "Simon Aronsson", email = "simon.aronsson@canonical.com" }, @@ -23,7 +23,7 @@ keywords = ["juju", "relation interfaces"] dependencies = [ "pydantic>2", - "pytest-interface-tester>=3.1.0", + "pytest-interface-tester>=3.1.2", "PyGithub>=2.3.0" ] @@ -41,7 +41,7 @@ unit_tests = [ "ops>=2.9", "PyYAML>=6.0", "pytest>=7.3.1", - "ops-scenario>=5", + "ops-scenario>=7.0.5", ] json_schemas = [ @@ -51,7 +51,7 @@ json_schemas = [ interface_tests = [ "PyYAML>=6.0", "pytest>=7.3.1", - "ops-scenario>=5", + "ops-scenario>=7.0.5", "requests==2.28.1", "virtualenv==20.21.0" ] diff --git a/tox.ini b/tox.ini index 7e620894..c400cb65 100644 --- a/tox.ini +++ b/tox.ini @@ -63,8 +63,8 @@ commands = [testenv:build-json-schemas] description = build json schemas in docs/ deps = - ops==2.6.0 - ops-scenario<6.0.4 #schema build fails with missing 'CloudCredential' in ops module + ops + ops-scenario .[json_schemas] setenv = PYTHONPATH={toxinidir} diff --git a/utils/interface-validator.py b/utils/interface-validator.py index f3d4f9ed..f3d5baa0 100644 --- a/utils/interface-validator.py +++ b/utils/interface-validator.py @@ -8,7 +8,7 @@ from enum import Enum from typing import List, Optional -from pydantic import AnyHttpUrl, BaseModel, Field, ValidationError, ConfigDict +from pydantic import AnyHttpUrl, BaseModel, ValidationError, ConfigDict class text: BOLD = '\033[1m' From 2c9617e9070c6f7e1e62df67455b9484e8ae630f Mon Sep 17 00:00:00 2001 From: Mateusz Kulewicz Date: Thu, 26 Sep 2024 08:08:06 +0200 Subject: [PATCH 3/5] test: update models and interface tests for tracing and tempo_cluster Update tempo_cluster tests. --- .../tempo_cluster/v0/provider.json | 65 ++++++++++++-- .../tempo_cluster/v0/requirer.json | 90 +++++++++++-------- docs/json_schemas/tracing/v2/requirer.json | 45 +++++----- interfaces/tempo_cluster/v0/interface.yaml | 3 + .../v0/interface_tests/test_provider.py | 88 ++++++++++++++++++ .../v0/interface_tests/test_requirer.py | 23 +++++ interfaces/tempo_cluster/v0/schema.py | 34 +++---- interfaces/tracing/v0/interface.yaml | 4 +- interfaces/tracing/v1/interface.yaml | 6 +- .../v1/interface_tests/test_provider.py | 6 +- interfaces/tracing/v2/interface.yaml | 9 ++ .../v2/interface_tests/test_provider.py | 18 +--- interfaces/tracing/v2/schema.py | 10 ++- 13 files changed, 290 insertions(+), 111 deletions(-) create mode 100644 interfaces/tempo_cluster/v0/interface_tests/test_provider.py create mode 100644 interfaces/tempo_cluster/v0/interface_tests/test_requirer.py diff --git a/docs/json_schemas/tempo_cluster/v0/provider.json b/docs/json_schemas/tempo_cluster/v0/provider.json index aad7e783..e311f3fa 100644 --- a/docs/json_schemas/tempo_cluster/v0/provider.json +++ b/docs/json_schemas/tempo_cluster/v0/provider.json @@ -8,18 +8,26 @@ "TempoClusterProviderAppData": { "description": "TempoClusterProviderAppData.", "properties": { - "tempo_config": { + "worker_config": { + "contentMediaType": "application/json", + "contentSchema": { + "type": "string" + }, "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.", - "title": "Tempo Config", + "title": "Worker Config", "type": "string" }, "loki_endpoints": { "anyOf": [ { - "additionalProperties": { - "type": "string" + "contentMediaType": "application/json", + "contentSchema": { + "additionalProperties": { + "type": "string" + }, + "type": "object" }, - "type": "object" + "type": "string" }, { "type": "null" @@ -32,6 +40,10 @@ "ca_cert": { "anyOf": [ { + "contentMediaType": "application/json", + "contentSchema": { + "type": "string" + }, "type": "string" }, { @@ -45,6 +57,10 @@ "server_cert": { "anyOf": [ { + "contentMediaType": "application/json", + "contentSchema": { + "type": "string" + }, "type": "string" }, { @@ -58,6 +74,10 @@ "privkey_secret_id": { "anyOf": [ { + "contentMediaType": "application/json", + "contentSchema": { + "type": "string" + }, "type": "string" }, { @@ -68,13 +88,40 @@ "description": "Private key used by the coordinator, for tls encryption.", "title": "Privkey Secret Id" }, + "remote_write_endpoints": { + "anyOf": [ + { + "contentMediaType": "application/json", + "contentSchema": { + "items": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Endpoints to which the workload (and the worker charm) can push metrics to.", + "title": "Remote Write Endpoints" + }, "tempo_receiver": { "anyOf": [ { - "additionalProperties": { - "type": "string" + "contentMediaType": "application/json", + "contentSchema": { + "additionalProperties": { + "type": "string" + }, + "type": "object" }, - "type": "object" + "type": "string" }, { "type": "null" @@ -86,7 +133,7 @@ } }, "required": [ - "tempo_config" + "worker_config" ], "title": "TempoClusterProviderAppData", "type": "object" diff --git a/docs/json_schemas/tempo_cluster/v0/requirer.json b/docs/json_schemas/tempo_cluster/v0/requirer.json index 1cf68f53..857fed5d 100644 --- a/docs/json_schemas/tempo_cluster/v0/requirer.json +++ b/docs/json_schemas/tempo_cluster/v0/requirer.json @@ -1,44 +1,15 @@ { "$defs": { - "JujuTopology": { - "description": "JujuTopology as defined by cos-lib.", - "properties": { - "model": { - "title": "Model", - "type": "string" - }, - "model_uuid": { - "title": "Model Uuid", - "type": "string" - }, - "application": { - "title": "Application", - "type": "string" - }, - "charm_name": { - "title": "Charm Name", - "type": "string" - }, - "unit": { - "title": "Unit", - "type": "string" - } - }, - "required": [ - "model", - "model_uuid", - "application", - "charm_name", - "unit" - ], - "title": "JujuTopology", - "type": "object" - }, "TempoClusterRequirerAppData": { "description": "TempoClusterRequirerAppData.", "properties": { "role": { - "$ref": "#/$defs/TempoRole" + "contentMediaType": "application/json", + "contentSchema": { + "$ref": "#/$defs/TempoRole" + }, + "title": "Role", + "type": "string" } }, "required": [ @@ -51,9 +22,18 @@ "description": "TempoClusterRequirerUnitData.", "properties": { "juju_topology": { - "$ref": "#/$defs/JujuTopology" + "contentMediaType": "application/json", + "contentSchema": { + "$ref": "#/$defs/_Topology" + }, + "title": "Juju Topology", + "type": "string" }, "address": { + "contentMediaType": "application/json", + "contentSchema": { + "type": "string" + }, "title": "Address", "type": "string" } @@ -78,6 +58,44 @@ ], "title": "TempoRole", "type": "string" + }, + "_Topology": { + "description": "JujuTopology as defined by cos-lib.", + "properties": { + "application": { + "title": "Application", + "type": "string" + }, + "charm_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Charm Name" + }, + "unit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Unit" + } + }, + "required": [ + "application", + "charm_name", + "unit" + ], + "title": "_Topology", + "type": "object" } }, "description": "The schema for the requirer side of this interface.", diff --git a/docs/json_schemas/tracing/v2/requirer.json b/docs/json_schemas/tracing/v2/requirer.json index 194e9244..c4691125 100644 --- a/docs/json_schemas/tracing/v2/requirer.json +++ b/docs/json_schemas/tracing/v2/requirer.json @@ -4,6 +4,27 @@ "properties": {}, "title": "BaseModel", "type": "object" + }, + "TracingRequirerData": { + "properties": { + "receivers": { + "contentMediaType": "application/json", + "contentSchema": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "List of protocols that the requirer wishes to use.", + "title": "Receivers", + "type": "string" + } + }, + "required": [ + "receivers" + ], + "title": "TracingRequirerData", + "type": "object" } }, "description": "Requirer schema for Tracing.", @@ -20,31 +41,11 @@ "default": null }, "app": { - "anyOf": [ - { - "$ref": "#/$defs/BaseModel" - }, - { - "type": "null" - } - ], - "default": null - }, - "receivers": { - "contentMediaType": "application/json", - "contentSchema": { - "items": { - "type": "string" - }, - "type": "array" - }, - "description": "List of protocols that the requirer wishes to use.", - "title": "Receivers", - "type": "string" + "$ref": "#/$defs/TracingRequirerData" } }, "required": [ - "receivers" + "app" ], "title": "RequirerSchema", "type": "object" diff --git a/interfaces/tempo_cluster/v0/interface.yaml b/interfaces/tempo_cluster/v0/interface.yaml index e1b6bc0e..169a5015 100644 --- a/interfaces/tempo_cluster/v0/interface.yaml +++ b/interfaces/tempo_cluster/v0/interface.yaml @@ -6,6 +6,9 @@ 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/v0/interface_tests/test_provider.py b/interfaces/tempo_cluster/v0/interface_tests/test_provider.py new file mode 100644 index 00000000..a977ccea --- /dev/null +++ b/interfaces/tempo_cluster/v0/interface_tests/test_provider.py @@ -0,0 +1,88 @@ +# Copyright 2024 Canonical +# See LICENSE file for licensing details. +import json + +from interface_tester.interface_test import Tester +from scenario import State, Relation + + + +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/v0/interface_tests/test_requirer.py b/interfaces/tempo_cluster/v0/interface_tests/test_requirer.py new file mode 100644 index 00000000..791f0a49 --- /dev/null +++ b/interfaces/tempo_cluster/v0/interface_tests/test_requirer.py @@ -0,0 +1,23 @@ +# Copyright 2024 Canonical +# See LICENSE file for licensing details. + +from interface_tester.interface_test import Tester +from scenario import State, Relation +import json + + +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/v0/schema.py b/interfaces/tempo_cluster/v0/schema.py index 86d0774d..c8504c0c 100644 --- a/interfaces/tempo_cluster/v0/schema.py +++ b/interfaces/tempo_cluster/v0/schema.py @@ -5,30 +5,34 @@ - RequirerSchema """ from enum import Enum -from typing import Optional, Dict, Any, Literal +from typing import Optional, Dict, List from interface_tester.schema_base import DataBagSchema -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, Json class TempoClusterProviderAppData(BaseModel): """TempoClusterProviderAppData.""" - tempo_config: str = Field( + worker_config: Json[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: Optional[Dict[str, str]] = Field( + loki_endpoints: Optional[Json[Dict[str, str]]] = Field( default=None, description="List of loki-push-api endpoints to which the worker node can push any logs it generates.") - ca_cert: Optional[str] = Field(default=None, description="CA certificate for tls encryption.") - server_cert: Optional[str] = Field(default=None, description="Server certificate for tls encryption.") - privkey_secret_id: Optional[str] = Field( + ca_cert: Optional[Json[str]] = Field(default=None, description="CA certificate for tls encryption.") + server_cert: Optional[Json[str]] = Field(default=None, description="Server certificate for tls encryption.") + privkey_secret_id: Optional[Json[str]] = Field( default=None, description="Private key used by the coordinator, for tls encryption." ) - tempo_receiver: Optional[Dict[str, str]] = Field( + remote_write_endpoints: Optional[Json[List[Dict[str, str]]]] = Field( + default=None, + description="Endpoints to which the workload (and the worker charm) can push metrics to." + ) + tempo_receiver: Optional[Json[Dict[str, str]]] = Field( default=None, description="Tempo receiver protocols to which the worker node can push any traces it generates." "It is a mapping from protocol names such as `zipkin`, `otlp_grpc`, `otlp_http`." @@ -38,20 +42,18 @@ class TempoClusterProviderAppData(BaseModel): ) -class JujuTopology(BaseModel): +class _Topology(BaseModel): """JujuTopology as defined by cos-lib.""" - model: str - model_uuid: str application: str - charm_name: str - unit: str + charm_name: Optional[str] + unit: Optional[str] class TempoClusterRequirerUnitData(BaseModel): """TempoClusterRequirerUnitData.""" - juju_topology: JujuTopology - address: str + juju_topology: Json[_Topology] + address: Json[str] class TempoRole(str, Enum): @@ -76,7 +78,7 @@ class TempoRole(str, Enum): class TempoClusterRequirerAppData(BaseModel): """TempoClusterRequirerAppData.""" - role: TempoRole + role: Json[TempoRole] class ProviderSchema(DataBagSchema): diff --git a/interfaces/tracing/v0/interface.yaml b/interfaces/tracing/v0/interface.yaml index 452ce07c..82491493 100644 --- a/interfaces/tracing/v0/interface.yaml +++ b/interfaces/tracing/v0/interface.yaml @@ -3,8 +3,6 @@ name: tracing version: 0 status: retired -providers: - - name: tempo-k8s - url: https://github.com/canonical/tempo-k8s-operator +providers: [] requirers: [] \ No newline at end of file diff --git a/interfaces/tracing/v1/interface.yaml b/interfaces/tracing/v1/interface.yaml index 473a558c..c387257e 100644 --- a/interfaces/tracing/v1/interface.yaml +++ b/interfaces/tracing/v1/interface.yaml @@ -1,10 +1,8 @@ name: tracing version: 1 -status: published +status: retired -providers: - - name: tempo-k8s - url: https://github.com/canonical/tempo-k8s-operator +providers: [] requirers: [] \ No newline at end of file diff --git a/interfaces/tracing/v1/interface_tests/test_provider.py b/interfaces/tracing/v1/interface_tests/test_provider.py index 7afa62bd..bccbb52c 100644 --- a/interfaces/tracing/v1/interface_tests/test_provider.py +++ b/interfaces/tracing/v1/interface_tests/test_provider.py @@ -27,7 +27,7 @@ def test_data_on_created(): interface='tracing', remote_app_name='remote', remote_app_data={ - "receivers": json.dumps(["otlp_grpc", "tempo_http", "tempo_grpc"]) + "receivers": json.dumps(["otlp_grpc"]) } ) ] @@ -46,7 +46,7 @@ def test_data_on_joined(): interface='tracing', remote_app_name='remote', remote_app_data={ - "receivers": json.dumps(["otlp_grpc", "tempo_http", "tempo_grpc"]) + "receivers": json.dumps(["otlp_grpc"]) } ) ] @@ -65,7 +65,7 @@ def test_data_on_changed(): interface='tracing', remote_app_name='remote', remote_app_data={ - "receivers": json.dumps(["otlp_grpc", "tempo_http", "tempo_grpc"]) + "receivers": json.dumps(["otlp_grpc"]) } ) ] diff --git a/interfaces/tracing/v2/interface.yaml b/interfaces/tracing/v2/interface.yaml index 5330a29d..8e551dc5 100644 --- a/interfaces/tracing/v2/interface.yaml +++ b/interfaces/tracing/v2/interface.yaml @@ -6,5 +6,14 @@ status: published providers: - name: tempo-k8s url: https://github.com/canonical/tempo-k8s-operator + - name: tempo-coordinator-k8s + url: https://github.com/canonical/tempo-coordinator-k8s-operator + test_setup: + location: tests/interface/conftest.py + identifier: tracing_tester +# Grafana-agent-k8s is disabled at the moment as interface-tester doesn't allow for vroot definition +# see https://github.com/canonical/pytest-interface-tester/issues/20 for updates on the issue +# - name: grafana-agent-k8s +# url: https://github.com/canonical/grafana-agent-k8s-operator requirers: [] \ No newline at end of file diff --git a/interfaces/tracing/v2/interface_tests/test_provider.py b/interfaces/tracing/v2/interface_tests/test_provider.py index 7afa62bd..8a065c00 100644 --- a/interfaces/tracing/v2/interface_tests/test_provider.py +++ b/interfaces/tracing/v2/interface_tests/test_provider.py @@ -6,18 +6,6 @@ from scenario import State, Relation -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( @@ -27,7 +15,7 @@ def test_data_on_created(): interface='tracing', remote_app_name='remote', remote_app_data={ - "receivers": json.dumps(["otlp_grpc", "tempo_http", "tempo_grpc"]) + "receivers": json.dumps(["otlp_grpc"]) } ) ] @@ -46,7 +34,7 @@ def test_data_on_joined(): interface='tracing', remote_app_name='remote', remote_app_data={ - "receivers": json.dumps(["otlp_grpc", "tempo_http", "tempo_grpc"]) + "receivers": json.dumps(["otlp_grpc"]) } ) ] @@ -65,7 +53,7 @@ def test_data_on_changed(): interface='tracing', remote_app_name='remote', remote_app_data={ - "receivers": json.dumps(["otlp_grpc", "tempo_http", "tempo_grpc"]) + "receivers": json.dumps(["otlp_grpc"]) } ) ] diff --git a/interfaces/tracing/v2/schema.py b/interfaces/tracing/v2/schema.py index 4f792b64..f41cab93 100644 --- a/interfaces/tracing/v2/schema.py +++ b/interfaces/tracing/v2/schema.py @@ -90,6 +90,12 @@ class TracingProviderData(BaseModel): ) +class TracingRequirerData(BaseModel): + receivers: Json[List[str]] = Field( + ..., description="List of protocols that the requirer wishes to use." + ) + + class ProviderSchema(DataBagSchema): """Provider schema for Tracing.""" @@ -99,6 +105,4 @@ class ProviderSchema(DataBagSchema): class RequirerSchema(DataBagSchema): """Requirer schema for Tracing.""" - receivers: Json[List[str]] = Field( - ..., description="List of protocols that the requirer wishes to use." - ) + app: TracingRequirerData From 25373b1978464b062620e5d2f1fa0fe8a48cce2b Mon Sep 17 00:00:00 2001 From: Gulsum Atici Date: Tue, 8 Oct 2024 03:04:58 +0300 Subject: [PATCH 4/5] feat: adding fiveg_rfsim interface (#175) * feat: adding fiveg_rfsim interface Signed-off-by: gatici * use python 3.11 Signed-off-by: gatici * revert python version Signed-off-by: gatici * fix charm urls Signed-off-by: gatici * Remove port number as it is added by DU upstream Signed-off-by: gatici * fix: address review comments Signed-off-by: gatici --------- Signed-off-by: gatici --- .../json_schemas/fiveg_rfsim/v0/provider.json | 48 +++++++++++++++++++ .../json_schemas/fiveg_rfsim/v0/requirer.json | 36 ++++++++++++++ interfaces/fiveg_rfsim/v0/README.md | 47 ++++++++++++++++++ interfaces/fiveg_rfsim/v0/interface.yaml | 12 +++++ interfaces/fiveg_rfsim/v0/schema.py | 33 +++++++++++++ 5 files changed, 176 insertions(+) create mode 100644 docs/json_schemas/fiveg_rfsim/v0/provider.json create mode 100644 docs/json_schemas/fiveg_rfsim/v0/requirer.json create mode 100644 interfaces/fiveg_rfsim/v0/README.md create mode 100644 interfaces/fiveg_rfsim/v0/interface.yaml create mode 100644 interfaces/fiveg_rfsim/v0/schema.py diff --git a/docs/json_schemas/fiveg_rfsim/v0/provider.json b/docs/json_schemas/fiveg_rfsim/v0/provider.json new file mode 100644 index 00000000..80a64032 --- /dev/null +++ b/docs/json_schemas/fiveg_rfsim/v0/provider.json @@ -0,0 +1,48 @@ +{ + "$defs": { + "BaseModel": { + "properties": {}, + "title": "BaseModel", + "type": "object" + }, + "FivegRFSIMProviderAppData": { + "properties": { + "rfsim_address": { + "description": "RF simulator service ip", + "examples": [ + "192.168.70.130" + ], + "title": "Rfsim Address", + "type": "string" + } + }, + "required": [ + "rfsim_address" + ], + "title": "FivegRFSIMProviderAppData", + "type": "object" + } + }, + "description": "Provider schema for the fiveg_rfsim interface.", + "properties": { + "unit": { + "anyOf": [ + { + "$ref": "#/$defs/BaseModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "app": { + "$ref": "#/$defs/FivegRFSIMProviderAppData" + } + }, + "required": [ + "app" + ], + "title": "ProviderSchema", + "type": "object" +} \ No newline at end of file diff --git a/docs/json_schemas/fiveg_rfsim/v0/requirer.json b/docs/json_schemas/fiveg_rfsim/v0/requirer.json new file mode 100644 index 00000000..9358a221 --- /dev/null +++ b/docs/json_schemas/fiveg_rfsim/v0/requirer.json @@ -0,0 +1,36 @@ +{ + "$defs": { + "BaseModel": { + "properties": {}, + "title": "BaseModel", + "type": "object" + } + }, + "description": "Requirer schema for the fiveg_rfsim interface.", + "properties": { + "unit": { + "anyOf": [ + { + "$ref": "#/$defs/BaseModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "app": { + "anyOf": [ + { + "$ref": "#/$defs/BaseModel" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "title": "RequirerSchema", + "type": "object" +} \ No newline at end of file diff --git a/interfaces/fiveg_rfsim/v0/README.md b/interfaces/fiveg_rfsim/v0/README.md new file mode 100644 index 00000000..926c053e --- /dev/null +++ b/interfaces/fiveg_rfsim/v0/README.md @@ -0,0 +1,47 @@ +# `fiveg_rfsim` + +## Usage + +Within 5G RAN (Radio Access Network) architecture, the OAI DU charm can be started to act as both the DU and the RU through its RF simulator functionality. + +The OAI UE charm requires RF simulator address in order to connect. Hence, the provider of this interface would be a OAI DU charm and the requirer of this interface would be the OAI UE charm. + +This relation interface describes the expected behavior of charms claiming to be able to provide or consume information on connectivity over the fiveg_rfsim interface. + +## Direction + +```mermaid +flowchart TD + Provider -- rfsim_address --> Requirer +``` + +As with all Juju relations, the `fiveg_rfsim` interface consists of two parties: a Provider and a Requirer. + +## Behavior + +Both the Requirer and the Provider need to adhere to criteria to be considered compatible with the interface. + +### Provider + +- Is expected to provide the DU's `rfsim` service ip. + +### Requirer + +- Is expected to use the `rfsim` service address passed by the provider. + +## Relation Data + +[\[Pydantic Schema\]](./schema.py) + +#### Example + +```yaml +provider: + app: { + "rfsim_address": "192.168.70.130", + } + unit: {} +requirer: + app: {} + unit: {} +``` diff --git a/interfaces/fiveg_rfsim/v0/interface.yaml b/interfaces/fiveg_rfsim/v0/interface.yaml new file mode 100644 index 00000000..e4ae0695 --- /dev/null +++ b/interfaces/fiveg_rfsim/v0/interface.yaml @@ -0,0 +1,12 @@ +name: fiveg_rfsim +internal: true +version: 0 +status: draft + +providers: + - name: oai-ran-du-k8s + url: https://github.com/canonical/oai-ran-du-k8s-operator + +requirers: + - name: oai-ran-ue-k8s + url: https://github.com/canonical/oai-ran-ue-k8s-operator diff --git a/interfaces/fiveg_rfsim/v0/schema.py b/interfaces/fiveg_rfsim/v0/schema.py new file mode 100644 index 00000000..c53c2700 --- /dev/null +++ b/interfaces/fiveg_rfsim/v0/schema.py @@ -0,0 +1,33 @@ +"""This file defines the schemas for the provider and requirer sides of the `fiveg_rfsim` interface. +It exposes two interface_tester.schema_base.DataBagSchema subclasses called: +- ProviderSchema +- RequirerSchema +Examples: + ProviderSchema: + unit: + app: { + "rfsim_address": "192.168.70.130", + } + RequirerSchema: + unit: + app: +""" + +from pydantic import BaseModel, Field + +from interface_tester.schema_base import DataBagSchema + + +class FivegRFSIMProviderAppData(BaseModel): + rfsim_address: str = Field( + description="RF simulator service ip", + examples=["192.168.70.130"] + ) + +class ProviderSchema(DataBagSchema): + """Provider schema for the fiveg_rfsim interface.""" + app: FivegRFSIMProviderAppData + + +class RequirerSchema(DataBagSchema): + """Requirer schema for the fiveg_rfsim interface.""" \ No newline at end of file From 81cd7c58736c2856a8cf8842fb08efe77a64b04a Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Wed, 9 Oct 2024 20:29:05 -0400 Subject: [PATCH 5/5] feat: add tls certificates per app support to tls certificates interface (#172) Signed-off-by: guillaume --- .../tls_certificates/v1/provider.json | 122 ++++++++++++++++++ .../tls_certificates/v1/requirer.json | 69 ++++++++++ interfaces/tls_certificates/v1/README.md | 5 +- interfaces/tls_certificates/v1/schema.py | 101 +++++++++++++++ .../tls_certificates/v1/schemas/provider.json | 81 ------------ .../tls_certificates/v1/schemas/requirer.json | 45 ------- 6 files changed, 293 insertions(+), 130 deletions(-) create mode 100644 docs/json_schemas/tls_certificates/v1/provider.json create mode 100644 docs/json_schemas/tls_certificates/v1/requirer.json create mode 100644 interfaces/tls_certificates/v1/schema.py delete mode 100644 interfaces/tls_certificates/v1/schemas/provider.json delete mode 100644 interfaces/tls_certificates/v1/schemas/requirer.json diff --git a/docs/json_schemas/tls_certificates/v1/provider.json b/docs/json_schemas/tls_certificates/v1/provider.json new file mode 100644 index 00000000..5840d2e2 --- /dev/null +++ b/docs/json_schemas/tls_certificates/v1/provider.json @@ -0,0 +1,122 @@ +{ + "$defs": { + "BaseModel": { + "properties": {}, + "title": "BaseModel", + "type": "object" + }, + "Certificate": { + "description": "Certificate model.", + "properties": { + "ca": { + "description": "The signing certificate authority.", + "title": "Ca", + "type": "string" + }, + "certificate_signing_request": { + "description": "Certificate signing request.", + "title": "Certificate Signing Request", + "type": "string" + }, + "certificate": { + "description": "Certificate.", + "title": "Certificate", + "type": "string" + }, + "chain": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "List of certificates in the chain.", + "title": "Chain" + }, + "recommended_expiry_notification_time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Recommended expiry notification time in seconds.", + "title": "Recommended Expiry Notification Time" + }, + "revoked": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "Whether the certificate is revoked.", + "title": "Revoked" + } + }, + "required": [ + "ca", + "certificate_signing_request", + "certificate", + "chain", + "recommended_expiry_notification_time", + "revoked" + ], + "title": "Certificate", + "type": "object" + }, + "ProviderApplicationData": { + "description": "Provider application data model.", + "properties": { + "certificates": { + "contentMediaType": "application/json", + "contentSchema": { + "items": { + "$ref": "#/$defs/Certificate" + }, + "type": "array" + }, + "description": "List of certificates.", + "title": "Certificates", + "type": "string" + } + }, + "required": [ + "certificates" + ], + "title": "ProviderApplicationData", + "type": "object" + } + }, + "description": "Provider schema for TLS Certificates.", + "properties": { + "unit": { + "anyOf": [ + { + "$ref": "#/$defs/BaseModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "app": { + "$ref": "#/$defs/ProviderApplicationData" + } + }, + "required": [ + "app" + ], + "title": "ProviderSchema", + "type": "object" +} \ No newline at end of file diff --git a/docs/json_schemas/tls_certificates/v1/requirer.json b/docs/json_schemas/tls_certificates/v1/requirer.json new file mode 100644 index 00000000..4f4a11a6 --- /dev/null +++ b/docs/json_schemas/tls_certificates/v1/requirer.json @@ -0,0 +1,69 @@ +{ + "$defs": { + "CertificateSigningRequest": { + "description": "Certificate signing request model.", + "properties": { + "certificate_signing_request": { + "description": "Certificate signing request.", + "title": "Certificate Signing Request", + "type": "string" + }, + "ca": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "Whether the certificate is a CA.", + "title": "Ca" + } + }, + "required": [ + "certificate_signing_request", + "ca" + ], + "title": "CertificateSigningRequest", + "type": "object" + }, + "RequirerData": { + "description": "Requirer data model.\n\nThe same model is used for the unit and application data.", + "properties": { + "certificate_signing_requests": { + "contentMediaType": "application/json", + "contentSchema": { + "items": { + "$ref": "#/$defs/CertificateSigningRequest" + }, + "type": "array" + }, + "description": "List of certificate signing requests.", + "title": "Certificate Signing Requests", + "type": "string" + } + }, + "required": [ + "certificate_signing_requests" + ], + "title": "RequirerData", + "type": "object" + } + }, + "description": "Requirer schema for TLS Certificates.", + "properties": { + "unit": { + "$ref": "#/$defs/RequirerData" + }, + "app": { + "$ref": "#/$defs/RequirerData" + } + }, + "required": [ + "unit", + "app" + ], + "title": "RequirerSchema", + "type": "object" +} \ No newline at end of file diff --git a/interfaces/tls_certificates/v1/README.md b/interfaces/tls_certificates/v1/README.md index c6598853..26bd04d4 100644 --- a/interfaces/tls_certificates/v1/README.md +++ b/interfaces/tls_certificates/v1/README.md @@ -27,6 +27,7 @@ compatible with the interface. - Is expected to generate (or use an existing) private-key - Is expected to provide a list of CSR's for which it requires certificates - Is expected to specify whether the certificate request is for a Certificate Authority (CA) or not +- Is expected to use the appropriate databag depending on whether the Certificate is meant to be used by the unit or by the application. - Is expected to stop using a certificate when revoked by the Provider ### Provider @@ -40,8 +41,6 @@ compatible with the interface. ### Requirer -[\[JSON Schema\]](./schemas/requirer.json) - The requirer specifies a set of certificate signing requests (CSR's). #### Example @@ -64,8 +63,6 @@ The requirer specifies a set of certificate signing requests (CSR's). ### Provider -[\[JSON Schema\]](./schemas/provider.json) - The provider replies with a certificate, a CA Certificate and a CA chain for each of the Certificate Signing Requests requested by the requirer. diff --git a/interfaces/tls_certificates/v1/schema.py b/interfaces/tls_certificates/v1/schema.py new file mode 100644 index 00000000..31daa9fc --- /dev/null +++ b/interfaces/tls_certificates/v1/schema.py @@ -0,0 +1,101 @@ +""" +This file defines the schemas for the provider and requirer sides of the `tls_certificates` interface. + +It exposes two interfaces.schema_base.DataBagSchema subclasses called: +- ProviderSchema +- RequirerSchema + +Examples: + ProviderSchema: + unit: + app: { + "certificates": [ + { + "ca": "-----BEGIN CERTIFICATE----- ...", + "chain": [ + "-----BEGIN CERTIFICATE----- ...", + "-----BEGIN CERTIFICATE----- ..." + ], + "certificate_signing_request": "-----BEGIN CERTIFICATE REQUEST----- ...", + "certificate": "-----BEGIN CERTIFICATE----- ..." + } + ] + } + RequirerSchema: + unit: { + "certificate_signing_requests": [ + { + "certificate_signing_request": "-----BEGIN CERTIFICATE REQUEST----- ...", + "ca": true + } + ] + } + app: +""" + +from typing import List, Optional +from pydantic import BaseModel, Field, Json +from interface_tester.schema_base import DataBagSchema + + +class Certificate(BaseModel): + """Certificate model.""" + ca: str = Field( + description="The signing certificate authority." + ) + certificate_signing_request: str = Field( + description="Certificate signing request." + ) + certificate: str = Field( + description="Certificate." + ) + chain: Optional[List[str]] = Field( + description="List of certificates in the chain." + ) + recommended_expiry_notification_time: Optional[int] = Field( + description="Recommended expiry notification time in seconds." + ) + revoked: Optional[bool] = Field( + description="Whether the certificate is revoked." + ) + + +class CertificateSigningRequest(BaseModel): + """Certificate signing request model.""" + certificate_signing_request: str = Field( + description="Certificate signing request." + ) + ca: Optional[bool] = Field( + description="Whether the certificate is a CA." + ) + + +class ProviderApplicationData(BaseModel): + """Provider application data model.""" + certificates: Json[List[Certificate]] = Field( + description="List of certificates." + ) + + +class RequirerData(BaseModel): + """Requirer data model. + + The same model is used for the unit and application data. + """ + + certificate_signing_requests: Json[List[CertificateSigningRequest]] = Field( + description="List of certificate signing requests." + ) + + +class ProviderSchema(DataBagSchema): + """Provider schema for TLS Certificates.""" + + app: ProviderApplicationData + + +class RequirerSchema(DataBagSchema): + """Requirer schema for TLS Certificates.""" + + app: RequirerData + unit: RequirerData \ No newline at end of file diff --git a/interfaces/tls_certificates/v1/schemas/provider.json b/interfaces/tls_certificates/v1/schemas/provider.json deleted file mode 100644 index 19f5b41a..00000000 --- a/interfaces/tls_certificates/v1/schemas/provider.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "$id": "https://canonical.github.io/charm-relation-interfaces/interfaces/tls_certificates/v1/schemas/provider.json", - "type": "object", - "title": "`tls_certificates` provider root schema", - "description": "The `tls_certificates` root schema comprises the entire provider databag for this interface.", - "examples": [ - { - "certificates": [ - { - "ca": "-----BEGIN CERTIFICATE-----\\nMIIDJTCCAg2gAwIBAgIUMsSK+4FGCjW6sL/EXMSxColmKw8wDQYJKoZIhvcNAQEL\\nBQAwIDELMAkGA1UEBhMCVVMxETAPBgNVBAMMCHdoYXRldmVyMB4XDTIyMDcyOTIx\\nMTgyN1oXDTIzMDcyOTIxMTgyN1owIDELMAkGA1UEBhMCVVMxETAPBgNVBAMMCHdo\\nYXRldmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA55N9DkgFWbJ/\\naqcdQhso7n1kFvt6j/fL1tJBvRubkiFMQJnZFtekfalN6FfRtA3jq+nx8o49e+7t\\nLCKT0xQ+wufXfOnxv6/if6HMhHTiCNPOCeztUgQ2+dfNwRhYYgB1P93wkUVjwudK\\n13qHTTZ6NtEF6EzOqhOCe6zxq6wrr422+ZqCvcggeQ5tW9xSd/8O1vNID/0MTKpy\\nET3drDtBfHmiUEIBR3T3tcy6QsIe4Rz/2sDinAcM3j7sG8uY6drh8jY3PWar9til\\nv2l4qDYSU8Qm5856AB1FVZRLRJkLxZYZNgreShAIYgEd0mcyI2EO/UvKxsIcxsXc\\nd45GhGpKkwIDAQABo1cwVTAfBgNVHQ4EGAQWBBRXBrXKh3p/aFdQjUcT/UcvICBL\\nODAhBgNVHSMEGjAYgBYEFFcGtcqHen9oV1CNRxP9Ry8gIEs4MA8GA1UdEwEB/wQF\\nMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGmCEvcoFUrT9e133SHkgF/ZAgzeIziO\\nBjfAdU4fvAVTVfzaPm0yBnGqzcHyacCzbZjKQpaKVgc5e6IaqAQtf6cZJSCiJGhS\\nJYeosWrj3dahLOUAMrXRr8G/Ybcacoqc+osKaRa2p71cC3V6u2VvcHRV7HDFGJU7\\noijbdB+WhqET6Txe67rxZCJG9Ez3EOejBJBl2PJPpy7m1Ml4RR+E8YHNzB0lcBzc\\nEoiJKlDfKSO14E2CPDonnUoWBJWjEvJys3tbvKzsRj2fnLilytPFU0gH3cEjCopi\\nzFoWRdaRuNHYCqlBmso1JFDl8h4fMmglxGNKnKRar0WeGyxb4xXBGpI=\\n-----END CERTIFICATE-----\\n", - "chain": [ - "-----BEGIN CERTIFICATE-----\\nMIIDJTCCAg2gAwIBAgIUMsSK+4FGCjW6sL/EXMSxColmKw8wDQYJKoZIhvcNAQEL\\nBQAwIDELMAkGA1UEBhMCVVMxETAPBgNVBAMMCHdoYXRldmVyMB4XDTIyMDcyOTIx\\nMTgyN1oXDTIzMDcyOTIxMTgyN1owIDELMAkGA1UEBhMCVVMxETAPBgNVBAMMCHdo\\nYXRldmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA55N9DkgFWbJ/\\naqcdQhso7n1kFvt6j/fL1tJBvRubkiFMQJnZFtekfalN6FfRtA3jq+nx8o49e+7t\\nLCKT0xQ+wufXfOnxv6/if6HMhHTiCNPOCeztUgQ2+dfNwRhYYgB1P93wkUVjwudK\\n13qHTTZ6NtEF6EzOqhOCe6zxq6wrr422+ZqCvcggeQ5tW9xSd/8O1vNID/0MTKpy\\nET3drDtBfHmiUEIBR3T3tcy6QsIe4Rz/2sDinAcM3j7sG8uY6drh8jY3PWar9til\\nv2l4qDYSU8Qm5856AB1FVZRLRJkLxZYZNgreShAIYgEd0mcyI2EO/UvKxsIcxsXc\\nd45GhGpKkwIDAQABo1cwVTAfBgNVHQ4EGAQWBBRXBrXKh3p/aFdQjUcT/UcvICBL\\nODAhBgNVHSMEGjAYgBYEFFcGtcqHen9oV1CNRxP9Ry8gIEs4MA8GA1UdEwEB/wQF\\nMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGmCEvcoFUrT9e133SHkgF/ZAgzeIziO\\nBjfAdU4fvAVTVfzaPm0yBnGqzcHyacCzbZjKQpaKVgc5e6IaqAQtf6cZJSCiJGhS\\nJYeosWrj3dahLOUAMrXRr8G/Ybcacoqc+osKaRa2p71cC3V6u2VvcHRV7HDFGJU7\\noijbdB+WhqET6Txe67rxZCJG9Ez3EOejBJBl2PJPpy7m1Ml4RR+E8YHNzB0lcBzc\\nEoiJKlDfKSO14E2CPDonnUoWBJWjEvJys3tbvKzsRj2fnLilytPFU0gH3cEjCopi\\nzFoWRdaRuNHYCqlBmso1JFDl8h4fMmglxGNKnKRar0WeGyxb4xXBGpI=\\n-----END CERTIFICATE-----\\n" - ], - "certificate_signing_request": "-----BEGIN CERTIFICATE REQUEST-----\nMIICWjCCAUICAQAwFTETMBEGA1UEAwwKYmFuYW5hLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBANWlx9wE6cW7Jkb4DZZDOZoEjk1eDBMJ+8R4pyKp\nFBeHMl1SQSDt6rAWsrfL3KOGiIHqrRY0B5H6c51L8LDuVrJG0bPmyQ6rsBo3gVke\nDSivfSLtGvHtp8lwYnIunF8r858uYmblAR0tdXQNmnQvm+6GERvURQ6sxpgZ7iLC\npPKDoPt+4GKWL10FWf0i82FgxWC2KqRZUtNbgKETQuARLig7etBmCnh20zmynorA\ncY7vrpTPAaeQpGLNqqYvKV9W6yWVY08V+nqARrFrjk3vSioZSu8ZJUdZ4d9++SGl\nbH7A6e77YDkX9i/dQ3Pa/iDtWO3tXS2MvgoxX1iSWlGNOHcCAwEAAaAAMA0GCSqG\nSIb3DQEBCwUAA4IBAQCW1fKcHessy/ZhnIwAtSLznZeZNH8LTVOzkhVd4HA7EJW+\nKVLBx8DnN7L3V2/uPJfHiOg4Rx7fi7LkJPegl3SCqJZ0N5bQS/KvDTCyLG+9E8Y+\n7wqCmWiXaH1devimXZvazilu4IC2dSks2D8DPWHgsOdVks9bme8J3KjdNMQudegc\newWZZ1Dtbd+Rn7cpKU3jURMwm4fRwGxbJ7iT5fkLlPBlyM/yFEik4SmQxFYrZCQg\n0f3v4kBefTh5yclPy5tEH+8G0LMsbbo3dJ5mPKpAShi0QEKDLd7eR1R/712lYTK4\ndi4XaEfqERgy68O4rvb4PGlJeRGS7AmL7Ss8wfAq\n-----END CERTIFICATE REQUEST-----\n", - "certificate": "-----BEGIN CERTIFICATE-----\nMIICvDCCAaQCFFPAOD7utDTsgFrm0vS4We18OcnKMA0GCSqGSIb3DQEBCwUAMCAx\nCzAJBgNVBAYTAlVTMREwDwYDVQQDDAh3aGF0ZXZlcjAeFw0yMjA3MjkyMTE5Mzha\nFw0yMzA3MjkyMTE5MzhaMBUxEzARBgNVBAMMCmJhbmFuYS5jb20wggEiMA0GCSqG\nSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVpcfcBOnFuyZG+A2WQzmaBI5NXgwTCfvE\neKciqRQXhzJdUkEg7eqwFrK3y9yjhoiB6q0WNAeR+nOdS/Cw7layRtGz5skOq7Aa\nN4FZHg0or30i7Rrx7afJcGJyLpxfK/OfLmJm5QEdLXV0DZp0L5vuhhEb1EUOrMaY\nGe4iwqTyg6D7fuBili9dBVn9IvNhYMVgtiqkWVLTW4ChE0LgES4oO3rQZgp4dtM5\nsp6KwHGO766UzwGnkKRizaqmLylfVusllWNPFfp6gEaxa45N70oqGUrvGSVHWeHf\nfvkhpWx+wOnu+2A5F/Yv3UNz2v4g7Vjt7V0tjL4KMV9YklpRjTh3AgMBAAEwDQYJ\nKoZIhvcNAQELBQADggEBAChjRzuba8zjQ7NYBVas89Oy7u++MlS8xWxh++yiUsV6\nWMk3ZemsPtXc1YmXorIQohtxLxzUPm2JhyzFzU/sOLmJQ1E/l+gtZHyRCwsb20fX\nmphuJsMVd7qv/GwEk9PBsk2uDqg4/Wix0Rx5lf95juJP7CPXQJl5FQauf3+LSz0y\nwF/j+4GqvrwsWr9hKOLmPdkyKkR6bHKtzzsxL9PM8GnElk2OpaPMMnzbL/vt2IAt\nxK01ZzPxCQCzVwHo5IJO5NR/fIyFbEPhxzG17QsRDOBR9fl9cOIvDeSO04vyZ+nz\n+kA2c3fNrZFAtpIlOOmFh8Q12rVL4sAjI5mVWnNEgvI=\n-----END CERTIFICATE-----\n" - } - ] - }, - { - "certificates": [ - { - "ca": "-----BEGIN CERTIFICATE-----\\nMIIDJTCCAg2gAwIBAgIUMsSK+4FGCjW6sL/EXMSxColmKw8wDQYJKoZIhvcNAQEL\\nBQAwIDELMAkGA1UEBhMCVVMxETAPBgNVBAMMCHdoYXRldmVyMB4XDTIyMDcyOTIx\\nMTgyN1oXDTIzMDcyOTIxMTgyN1owIDELMAkGA1UEBhMCVVMxETAPBgNVBAMMCHdo\\nYXRldmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA55N9DkgFWbJ/\\naqcdQhso7n1kFvt6j/fL1tJBvRubkiFMQJnZFtekfalN6FfRtA3jq+nx8o49e+7t\\nLCKT0xQ+wufXfOnxv6/if6HMhHTiCNPOCeztUgQ2+dfNwRhYYgB1P93wkUVjwudK\\n13qHTTZ6NtEF6EzOqhOCe6zxq6wrr422+ZqCvcggeQ5tW9xSd/8O1vNID/0MTKpy\\nET3drDtBfHmiUEIBR3T3tcy6QsIe4Rz/2sDinAcM3j7sG8uY6drh8jY3PWar9til\\nv2l4qDYSU8Qm5856AB1FVZRLRJkLxZYZNgreShAIYgEd0mcyI2EO/UvKxsIcxsXc\\nd45GhGpKkwIDAQABo1cwVTAfBgNVHQ4EGAQWBBRXBrXKh3p/aFdQjUcT/UcvICBL\\nODAhBgNVHSMEGjAYgBYEFFcGtcqHen9oV1CNRxP9Ry8gIEs4MA8GA1UdEwEB/wQF\\nMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGmCEvcoFUrT9e133SHkgF/ZAgzeIziO\\nBjfAdU4fvAVTVfzaPm0yBnGqzcHyacCzbZjKQpaKVgc5e6IaqAQtf6cZJSCiJGhS\\nJYeosWrj3dahLOUAMrXRr8G/Ybcacoqc+osKaRa2p71cC3V6u2VvcHRV7HDFGJU7\\noijbdB+WhqET6Txe67rxZCJG9Ez3EOejBJBl2PJPpy7m1Ml4RR+E8YHNzB0lcBzc\\nEoiJKlDfKSO14E2CPDonnUoWBJWjEvJys3tbvKzsRj2fnLilytPFU0gH3cEjCopi\\nzFoWRdaRuNHYCqlBmso1JFDl8h4fMmglxGNKnKRar0WeGyxb4xXBGpI=\\n-----END CERTIFICATE-----\\n", - "chain": [ - "-----BEGIN CERTIFICATE-----\\nMIIDJTCCAg2gAwIBAgIUMsSK+4FGCjW6sL/EXMSxColmKw8wDQYJKoZIhvcNAQEL\\nBQAwIDELMAkGA1UEBhMCVVMxETAPBgNVBAMMCHdoYXRldmVyMB4XDTIyMDcyOTIx\\nMTgyN1oXDTIzMDcyOTIxMTgyN1owIDELMAkGA1UEBhMCVVMxETAPBgNVBAMMCHdo\\nYXRldmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA55N9DkgFWbJ/\\naqcdQhso7n1kFvt6j/fL1tJBvRubkiFMQJnZFtekfalN6FfRtA3jq+nx8o49e+7t\\nLCKT0xQ+wufXfOnxv6/if6HMhHTiCNPOCeztUgQ2+dfNwRhYYgB1P93wkUVjwudK\\n13qHTTZ6NtEF6EzOqhOCe6zxq6wrr422+ZqCvcggeQ5tW9xSd/8O1vNID/0MTKpy\\nET3drDtBfHmiUEIBR3T3tcy6QsIe4Rz/2sDinAcM3j7sG8uY6drh8jY3PWar9til\\nv2l4qDYSU8Qm5856AB1FVZRLRJkLxZYZNgreShAIYgEd0mcyI2EO/UvKxsIcxsXc\\nd45GhGpKkwIDAQABo1cwVTAfBgNVHQ4EGAQWBBRXBrXKh3p/aFdQjUcT/UcvICBL\\nODAhBgNVHSMEGjAYgBYEFFcGtcqHen9oV1CNRxP9Ry8gIEs4MA8GA1UdEwEB/wQF\\nMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGmCEvcoFUrT9e133SHkgF/ZAgzeIziO\\nBjfAdU4fvAVTVfzaPm0yBnGqzcHyacCzbZjKQpaKVgc5e6IaqAQtf6cZJSCiJGhS\\nJYeosWrj3dahLOUAMrXRr8G/Ybcacoqc+osKaRa2p71cC3V6u2VvcHRV7HDFGJU7\\noijbdB+WhqET6Txe67rxZCJG9Ez3EOejBJBl2PJPpy7m1Ml4RR+E8YHNzB0lcBzc\\nEoiJKlDfKSO14E2CPDonnUoWBJWjEvJys3tbvKzsRj2fnLilytPFU0gH3cEjCopi\\nzFoWRdaRuNHYCqlBmso1JFDl8h4fMmglxGNKnKRar0WeGyxb4xXBGpI=\\n-----END CERTIFICATE-----\\n" - ], - "certificate_signing_request": "-----BEGIN CERTIFICATE REQUEST-----\nMIICWjCCAUICAQAwFTETMBEGA1UEAwwKYmFuYW5hLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBANWlx9wE6cW7Jkb4DZZDOZoEjk1eDBMJ+8R4pyKp\nFBeHMl1SQSDt6rAWsrfL3KOGiIHqrRY0B5H6c51L8LDuVrJG0bPmyQ6rsBo3gVke\nDSivfSLtGvHtp8lwYnIunF8r858uYmblAR0tdXQNmnQvm+6GERvURQ6sxpgZ7iLC\npPKDoPt+4GKWL10FWf0i82FgxWC2KqRZUtNbgKETQuARLig7etBmCnh20zmynorA\ncY7vrpTPAaeQpGLNqqYvKV9W6yWVY08V+nqARrFrjk3vSioZSu8ZJUdZ4d9++SGl\nbH7A6e77YDkX9i/dQ3Pa/iDtWO3tXS2MvgoxX1iSWlGNOHcCAwEAAaAAMA0GCSqG\nSIb3DQEBCwUAA4IBAQCW1fKcHessy/ZhnIwAtSLznZeZNH8LTVOzkhVd4HA7EJW+\nKVLBx8DnN7L3V2/uPJfHiOg4Rx7fi7LkJPegl3SCqJZ0N5bQS/KvDTCyLG+9E8Y+\n7wqCmWiXaH1devimXZvazilu4IC2dSks2D8DPWHgsOdVks9bme8J3KjdNMQudegc\newWZZ1Dtbd+Rn7cpKU3jURMwm4fRwGxbJ7iT5fkLlPBlyM/yFEik4SmQxFYrZCQg\n0f3v4kBefTh5yclPy5tEH+8G0LMsbbo3dJ5mPKpAShi0QEKDLd7eR1R/712lYTK4\ndi4XaEfqERgy68O4rvb4PGlJeRGS7AmL7Ss8wfAq\n-----END CERTIFICATE REQUEST-----\n", - "certificate": "-----BEGIN CERTIFICATE-----\nMIICvDCCAaQCFFPAOD7utDTsgFrm0vS4We18OcnKMA0GCSqGSIb3DQEBCwUAMCAx\nCzAJBgNVBAYTAlVTMREwDwYDVQQDDAh3aGF0ZXZlcjAeFw0yMjA3MjkyMTE5Mzha\nFw0yMzA3MjkyMTE5MzhaMBUxEzARBgNVBAMMCmJhbmFuYS5jb20wggEiMA0GCSqG\nSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVpcfcBOnFuyZG+A2WQzmaBI5NXgwTCfvE\neKciqRQXhzJdUkEg7eqwFrK3y9yjhoiB6q0WNAeR+nOdS/Cw7layRtGz5skOq7Aa\nN4FZHg0or30i7Rrx7afJcGJyLpxfK/OfLmJm5QEdLXV0DZp0L5vuhhEb1EUOrMaY\nGe4iwqTyg6D7fuBili9dBVn9IvNhYMVgtiqkWVLTW4ChE0LgES4oO3rQZgp4dtM5\nsp6KwHGO766UzwGnkKRizaqmLylfVusllWNPFfp6gEaxa45N70oqGUrvGSVHWeHf\nfvkhpWx+wOnu+2A5F/Yv3UNz2v4g7Vjt7V0tjL4KMV9YklpRjTh3AgMBAAEwDQYJ\nKoZIhvcNAQELBQADggEBAChjRzuba8zjQ7NYBVas89Oy7u++MlS8xWxh++yiUsV6\nWMk3ZemsPtXc1YmXorIQohtxLxzUPm2JhyzFzU/sOLmJQ1E/l+gtZHyRCwsb20fX\nmphuJsMVd7qv/GwEk9PBsk2uDqg4/Wix0Rx5lf95juJP7CPXQJl5FQauf3+LSz0y\nwF/j+4GqvrwsWr9hKOLmPdkyKkR6bHKtzzsxL9PM8GnElk2OpaPMMnzbL/vt2IAt\nxK01ZzPxCQCzVwHo5IJO5NR/fIyFbEPhxzG17QsRDOBR9fl9cOIvDeSO04vyZ+nz\n+kA2c3fNrZFAtpIlOOmFh8Q12rVL4sAjI5mVWnNEgvI=\n-----END CERTIFICATE-----\n", - "revoked": true - } - ] - } - ], - "properties": { - "certificates": { - "$id": "#/properties/certificates", - "type": "array", - "items": { - "$id": "#/properties/certificates/items", - "type": "object", - "required": [ - "certificate_signing_request", - "certificate", - "ca", - "chain" - ], - "properties": { - "certificate_signing_request": { - "$id": "#/properties/certificates/items/certificate_signing_request", - "type": "string" - }, - "certificate": { - "$id": "#/properties/certificates/items/certificate", - "type": "string" - }, - "ca": { - "$id": "#/properties/certificates/items/ca", - "type": "string" - }, - "chain": { - "$id": "#/properties/certificates/items/chain", - "type": "array", - "items": { - "type": "string", - "$id": "#/properties/certificates/items/chain/items" - } - }, - "revoked": { - "$id": "#/properties/certificates/items/revoked", - "type": "boolean" - } - }, - "additionalProperties": true - } - } - }, - "required": [ - "certificates" - ], - "additionalProperties": true -} diff --git a/interfaces/tls_certificates/v1/schemas/requirer.json b/interfaces/tls_certificates/v1/schemas/requirer.json deleted file mode 100644 index 8f9514d6..00000000 --- a/interfaces/tls_certificates/v1/schemas/requirer.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "$id": "https://canonical.github.io/charm-relation-interfaces/interfaces/tls_certificates/v1/schemas/requirer.json", - "type": "object", - "title": "`tls_certificates` requirer root schema", - "description": "The `tls_certificates` root schema comprises the entire requirer databag for this interface.", - "examples": [ - { - "certificate_signing_requests": [ - { - "certificate_signing_request": "-----BEGIN CERTIFICATE REQUEST-----\\nMIICWjCCAUICAQAwFTETMBEGA1UEAwwKYmFuYW5hLmNvbTCCASIwDQYJKoZIhvcN\\nAQEBBQADggEPADCCAQoCggEBANWlx9wE6cW7Jkb4DZZDOZoEjk1eDBMJ+8R4pyKp\\nFBeHMl1SQSDt6rAWsrfL3KOGiIHqrRY0B5H6c51L8LDuVrJG0bPmyQ6rsBo3gVke\\nDSivfSLtGvHtp8lwYnIunF8r858uYmblAR0tdXQNmnQvm+6GERvURQ6sxpgZ7iLC\\npPKDoPt+4GKWL10FWf0i82FgxWC2KqRZUtNbgKETQuARLig7etBmCnh20zmynorA\\ncY7vrpTPAaeQpGLNqqYvKV9W6yWVY08V+nqARrFrjk3vSioZSu8ZJUdZ4d9++SGl\\nbH7A6e77YDkX9i/dQ3Pa/iDtWO3tXS2MvgoxX1iSWlGNOHcCAwEAAaAAMA0GCSqG\\nSIb3DQEBCwUAA4IBAQCW1fKcHessy/ZhnIwAtSLznZeZNH8LTVOzkhVd4HA7EJW+\\nKVLBx8DnN7L3V2/uPJfHiOg4Rx7fi7LkJPegl3SCqJZ0N5bQS/KvDTCyLG+9E8Y+\\n7wqCmWiXaH1devimXZvazilu4IC2dSks2D8DPWHgsOdVks9bme8J3KjdNMQudegc\\newWZZ1Dtbd+Rn7cpKU3jURMwm4fRwGxbJ7iT5fkLlPBlyM/yFEik4SmQxFYrZCQg\\n0f3v4kBefTh5yclPy5tEH+8G0LMsbbo3dJ5mPKpAShi0QEKDLd7eR1R/712lYTK4\\ndi4XaEfqERgy68O4rvb4PGlJeRGS7AmL7Ss8wfAq\\n-----END CERTIFICATE REQUEST-----\\n" - }, - { - "certificate_signing_request": "-----BEGIN CERTIFICATE REQUEST-----\\nMIICWjCCAUICAQAwFTETMBEGA1UEAwwKYmFuYW5hLmNvbTCCASIwDQYJKoZIhvcN\\nAQEBBQADggEPADCCAQoCggEBAMk3raaX803cHvzlBF9LC7KORT46z4VjyU5PIaMb\\nQLIDgYKFYI0n5hf2Ra4FAHvOvEmW7bjNlHORFEmvnpcU5kPMNUyKFMTaC8LGmN8z\\nUBH3aK+0+FRvY4afn9tgj5435WqOG9QdoDJ0TJkjJbJI9M70UOgL711oU7ql6HxU\\n4d2ydFK9xAHrBwziNHgNZ72L95s4gLTXf0fAHYf15mDA9U5yc+YDubCKgTXzVySQ\\nUx73VCJLfC/XkZIh559IrnRv5G9fu6BMLEuBwAz6QAO4+/XidbKWN4r2XSq5qX4n\\n6EPQQWP8/nd4myq1kbg6Q8w68L/0YdfjCmbyf2TuoWeImdUCAwEAAaAAMA0GCSqG\\nSIb3DQEBCwUAA4IBAQBIdwraBvpYo/rl5MH1+1Um6HRg4gOdQPY5WcJy9B9tgzJz\\nittRSlRGTnhyIo6fHgq9KHrmUthNe8mMTDailKFeaqkVNVvk7l0d1/B90Kz6OfmD\\nxN0qjW53oP7y3QB5FFBM8DjqjmUnz5UePKoX4AKkDyrKWxMwGX5RoET8c/y0y9jp\\nvSq3Wh5UpaZdWbe1oVY8CqMVUEVQL2DPjtopxXFz2qACwsXkQZxWmjvZnRiP8nP8\\nbdFaEuh9Q6rZ2QdZDEtrU4AodPU3NaukFr5KlTUQt3w/cl+5//zils6G5zUWJ2pN\\ng7+t9PTvXHRkH+LnwaVnmsBFU2e05qADQbfIn7JA\\n-----END CERTIFICATE REQUEST-----\\n", - "ca": true - } - ] - } - ], - "properties": { - "certificate_signing_requests": { - "type": "array", - "items": { - "type": "object", - "properties": { - "certificate_signing_request": { - "type": "string" - }, - "ca": { - "type": "boolean", - "default": false - } - }, - "required": [ - "certificate_signing_request" - ], - "additionalProperties": true - } - } - }, - "required": [ - "certificate_signing_requests" - ], - "additionalProperties": true -}