From a52cf0d8a6f94d6fa2371b5279e03c2c21c8a60e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:12:40 +0200 Subject: [PATCH 01/11] chore: bump the pip_dependencies group with 5 updates (#33) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 31 +++++++++++++++++++++++++++---- test-requirements.txt | 8 ++++---- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index 811910c..fe7e5bf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,19 +6,37 @@ # annotated-types==0.7.0 # via pydantic +anyio==4.4.0 + # via httpx certifi==2024.7.4 - # via requests + # via + # httpcore + # httpx + # requests cffi==1.17.0 # via cryptography charset-normalizer==3.3.2 # via requests -cosl==0.0.23 +cosl==0.0.26 # via -r requirements.in cryptography==43.0.0 # via -r requirements.in +h11==0.14.0 + # via httpcore +httpcore==1.0.5 + # via httpx +httpx==0.27.2 + # via lightkube idna==3.7 - # via requests -ops==2.15.0 + # via + # anyio + # httpx + # requests +lightkube==0.15.4 + # via cosl +lightkube-models==1.30.0.8 + # via lightkube +ops==2.16.0 # via # -r requirements.in # cosl @@ -33,11 +51,16 @@ pydantic-core==2.20.1 pyyaml==6.0.2 # via # cosl + # lightkube # ops requests==2.32.3 # via -r requirements.in rpds-py==0.18.0 # via -r requirements.in +sniffio==1.3.1 + # via + # anyio + # httpx tenacity==9.0.0 # via cosl typing-extensions==4.12.2 diff --git a/test-requirements.txt b/test-requirements.txt index a04131d..5d8a880 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -68,9 +68,9 @@ oauthlib==3.2.2 # via # kubernetes # requests-oauthlib -ops==2.15.0 +ops==2.16.0 # via ops-scenario -ops-scenario==6.1.5 +ops-scenario==6.1.6 # via -r test-requirements.in packaging==24.1 # via @@ -114,7 +114,7 @@ pyrfc3339==1.1 # via # juju # macaroonbakery -pyright==1.1.377 +pyright==1.1.378 # via -r test-requirements.in pytest==8.3.2 # via @@ -146,7 +146,7 @@ requests-oauthlib==2.0.0 # via kubernetes rsa==4.9 # via google-auth -ruff==0.6.2 +ruff==0.6.3 # via -r test-requirements.in six==1.16.0 # via From 4ab6771ca20aa38fa6b80639f97d1053e5bc41dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:12:55 +0200 Subject: [PATCH 02/11] chore: bump canonical/charming-actions from 2.6.2 to 2.6.3 in the github_actions group (#32) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/publish-charm.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-charm.yaml b/.github/workflows/publish-charm.yaml index 7fd25db..50eb9b4 100644 --- a/.github/workflows/publish-charm.yaml +++ b/.github/workflows/publish-charm.yaml @@ -33,7 +33,7 @@ jobs: run: echo "charm_path=$(find . -name '*.charm' -type f -print)" >> $GITHUB_OUTPUT - name: Upload charm to Charmhub - uses: canonical/charming-actions/upload-charm@2.6.2 + uses: canonical/charming-actions/upload-charm@2.6.3 with: built-charm-path: ${{ steps.charm-path.outputs.charm_path }} credentials: "${{ secrets.CHARMCRAFT_AUTH }}" From 8801a4017ae00c64bd6b2c55dbe72c2f45994024 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:08:04 +0300 Subject: [PATCH 03/11] chore: bump the pip_dependencies group with 6 updates (#34) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 12 +++++++----- test-requirements.txt | 8 ++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index fe7e5bf..de7a129 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,9 +17,9 @@ cffi==1.17.0 # via cryptography charset-normalizer==3.3.2 # via requests -cosl==0.0.26 +cosl==0.0.31 # via -r requirements.in -cryptography==43.0.0 +cryptography==43.0.1 # via -r requirements.in h11==0.14.0 # via httpcore @@ -36,17 +36,17 @@ lightkube==0.15.4 # via cosl lightkube-models==1.30.0.8 # via lightkube -ops==2.16.0 +ops==2.16.1 # via # -r requirements.in # cosl pycparser==2.22 # via cffi -pydantic==2.8.2 +pydantic==2.9.0 # via # -r requirements.in # cosl -pydantic-core==2.20.1 +pydantic-core==2.23.2 # via pydantic pyyaml==6.0.2 # via @@ -68,6 +68,8 @@ typing-extensions==4.12.2 # cosl # pydantic # pydantic-core +tzdata==2024.1 + # via pydantic urllib3==2.2.2 # via requests websocket-client==1.8.0 diff --git a/test-requirements.txt b/test-requirements.txt index 5d8a880..496a2c3 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -24,7 +24,7 @@ codespell==2.3.0 # via -r test-requirements.in coverage[toml]==7.6.1 # via -r test-requirements.in -cryptography==43.0.0 +cryptography==43.0.1 # via paramiko decorator==5.1.1 # via @@ -68,7 +68,7 @@ oauthlib==3.2.2 # via # kubernetes # requests-oauthlib -ops==2.16.0 +ops==2.16.1 # via ops-scenario ops-scenario==6.1.6 # via -r test-requirements.in @@ -114,7 +114,7 @@ pyrfc3339==1.1 # via # juju # macaroonbakery -pyright==1.1.378 +pyright==1.1.379 # via -r test-requirements.in pytest==8.3.2 # via @@ -146,7 +146,7 @@ requests-oauthlib==2.0.0 # via kubernetes rsa==4.9 # via google-auth -ruff==0.6.3 +ruff==0.6.4 # via -r test-requirements.in six==1.16.0 # via From 4fae4e4ab6d70704566f89a08364000f1c39e6e1 Mon Sep 17 00:00:00 2001 From: Tony Meyer Date: Tue, 10 Sep 2024 00:44:41 +1200 Subject: [PATCH 04/11] test: update to Scenario 7.0 (#35) --- test-requirements.txt | 2 +- tests/unit/test_charm.py | 613 +++++++++++++++++++-------------------- 2 files changed, 307 insertions(+), 308 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 496a2c3..2e43b71 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -70,7 +70,7 @@ oauthlib==3.2.2 # requests-oauthlib ops==2.16.1 # via ops-scenario -ops-scenario==6.1.6 +ops-scenario==7.0.1 # via -r test-requirements.in packaging==24.1 # via diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index a32f632..aed6d7e 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -5,9 +5,8 @@ from unittest.mock import Mock, patch import ops -import ops.testing import pytest -from scenario import Container, Context, Event, Mount, Network, State, Storage +from scenario import Container, Context, Mount, Network, State, Storage from charm import GocertCharm from lib.charms.tls_certificates_interface.v4.tls_certificates import ( @@ -53,9 +52,9 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -65,15 +64,15 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_not_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -83,15 +82,15 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_cant_connect_network_not_available_gocert_not_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -101,15 +100,15 @@ def test_given_storages_available_container_cant_connect_network_not_available_g **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_config_storage_container_can_connect_network_not_available_gocert_not_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -119,15 +118,15 @@ def test_given_only_config_storage_container_can_connect_network_not_available_g **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_can_connect_network_not_available_gocert_not_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -137,15 +136,15 @@ def test_given_only_database_storage_container_can_connect_network_not_available **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_can_connect_network_not_available_gocert_not_running_when_configure_then_config_file_generated( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -155,21 +154,21 @@ def test_given_storages_available_container_can_connect_network_not_available_go **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("config-changed"), state) - root = out.containers[0].get_filesystem(context) + out = context.run(context.on.config_changed(), state) + root = out.get_container("gocert").get_filesystem(context) assert (root / "etc/gocert/config/config.yaml").open("r") assert not (root / "etc/gocert/config/certificate.pem").exists() assert not ((root / "etc/gocert/config/private_key.pem").exists()) assert len(out.secrets) == 1 - assert out.secrets[0].label == "GoCert Login Details" + assert out.get_secret(label="GoCert Login Details") def test_given_only_config_storage_container_cant_connect_network_available_gocert_not_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -179,15 +178,15 @@ def test_given_only_config_storage_container_cant_connect_network_available_goce **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_cant_connect_network_available_gocert_not_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -197,15 +196,15 @@ def test_given_only_database_storage_container_cant_connect_network_available_go **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_cant_connect_network_available_gocert_not_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -215,15 +214,15 @@ def test_given_storages_available_container_cant_connect_network_available_gocer **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_config_storage_container_can_connect_network_available_gocert_not_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -233,15 +232,15 @@ def test_given_only_config_storage_container_can_connect_network_available_gocer **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_can_connect_network_available_gocert_not_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -251,15 +250,15 @@ def test_given_only_database_storage_container_can_connect_network_available_goc **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_can_connect_network_available_gocert_not_running_when_configure_then_config_and_certificates_generated( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -269,8 +268,8 @@ def test_given_storages_available_container_can_connect_network_available_gocert **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("config-changed"), state) - root = out.containers[0].get_filesystem(context) + out = context.run(context.on.config_changed(), state) + root = out.get_container("gocert").get_filesystem(context) assert (root / "etc/gocert/config/config.yaml").open("r") assert ( (root / "etc/gocert/config/certificate.pem") @@ -289,9 +288,9 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -301,15 +300,15 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -319,15 +318,15 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_cant_connect_network_not_available_gocert_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -337,15 +336,15 @@ def test_given_storages_available_container_cant_connect_network_not_available_g **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_config_storage_container_can_connect_network_not_available_gocert_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -355,15 +354,15 @@ def test_given_only_config_storage_container_can_connect_network_not_available_g **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_can_connect_network_not_available_gocert_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -373,15 +372,15 @@ def test_given_only_database_storage_container_can_connect_network_not_available **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_can_connect_network_not_available_gocert_running_when_configure_then_config_file_generated( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -391,21 +390,21 @@ def test_given_storages_available_container_can_connect_network_not_available_go **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("config-changed"), state) - root = out.containers[0].get_filesystem(context) + out = context.run(context.on.config_changed(), state) + root = out.get_container("gocert").get_filesystem(context) assert (root / "etc/gocert/config/config.yaml").open("r") assert not (root / "etc/gocert/config/certificate.pem").exists() assert not ((root / "etc/gocert/config/private_key.pem").exists()) assert len(out.secrets) == 1 - assert out.secrets[0].label == "GoCert Login Details" + assert out.get_secret(label="GoCert Login Details") def test_given_only_config_storage_container_cant_connect_network_available_gocert_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -415,15 +414,15 @@ def test_given_only_config_storage_container_cant_connect_network_available_goce **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_cant_connect_network_available_gocert_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -433,15 +432,15 @@ def test_given_only_database_storage_container_cant_connect_network_available_go **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_cant_connect_network_available_gocert_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -451,15 +450,15 @@ def test_given_storages_available_container_cant_connect_network_available_gocer **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_config_storage_container_can_connect_network_available_gocert_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -469,15 +468,15 @@ def test_given_only_config_storage_container_can_connect_network_available_gocer **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_can_connect_network_available_gocert_running_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -487,15 +486,15 @@ def test_given_only_database_storage_container_can_connect_network_available_goc **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_can_connect_network_available_gocert_running_when_configure_then_status_is_blocked( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -505,15 +504,15 @@ def test_given_storages_available_container_can_connect_network_available_gocert **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_config_storage_container_cant_connect_network_not_available_gocert_initialized_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -523,15 +522,15 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_initialized_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -541,15 +540,15 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_cant_connect_network_not_available_gocert_initialized_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -559,15 +558,15 @@ def test_given_storages_available_container_cant_connect_network_not_available_g **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_config_storage_container_can_connect_network_not_available_gocert_initialized_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -577,15 +576,15 @@ def test_given_only_config_storage_container_can_connect_network_not_available_g **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_can_connect_network_not_available_gocert_initialized_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -595,15 +594,15 @@ def test_given_only_database_storage_container_can_connect_network_not_available **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_can_connect_network_not_available_gocert_initialized_when_configure_then_config_file_generated( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -613,22 +612,22 @@ def test_given_storages_available_container_can_connect_network_not_available_go **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("config-changed"), state) + out = context.run(context.on.config_changed(), state) - root = out.containers[0].get_filesystem(context) + root = out.get_container("gocert").get_filesystem(context) assert (root / "etc/gocert/config/config.yaml").open("r") assert not (root / "etc/gocert/config/certificate.pem").exists() assert not ((root / "etc/gocert/config/private_key.pem").exists()) assert len(out.secrets) == 1 - assert out.secrets[0].label == "GoCert Login Details" + assert out.get_secret(label="GoCert Login Details") def test_given_only_config_storage_container_cant_connect_network_available_gocert_initialized_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -638,15 +637,15 @@ def test_given_only_config_storage_container_cant_connect_network_available_goce **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_cant_connect_network_available_gocert_initialized_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -656,15 +655,15 @@ def test_given_only_database_storage_container_cant_connect_network_available_go **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_cant_connect_network_available_gocert_initialized_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -674,15 +673,15 @@ def test_given_storages_available_container_cant_connect_network_available_gocer **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_config_storage_container_can_connect_network_available_gocert_initialized_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -692,15 +691,15 @@ def test_given_only_config_storage_container_can_connect_network_available_gocer **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_only_database_storage_container_can_connect_network_available_gocert_initialized_when_configure_then_no_error_raised( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -710,15 +709,15 @@ def test_given_only_database_storage_container_can_connect_network_available_goc **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) def test_given_storages_available_container_can_connect_network_available_gocert_initialized_when_configure_then_status_is_active( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -728,16 +727,16 @@ def test_given_storages_available_container_can_connect_network_available_gocert **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - context.run(Event("config-changed"), state) + context.run(context.on.config_changed(), state) # Unit Status Tests def test_given_only_config_storage_container_cant_connect_network_not_available_gocert_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -747,7 +746,7 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") @@ -755,9 +754,9 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -767,16 +766,16 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_storages_available_container_cant_connect_network_not_available_gocert_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -786,7 +785,7 @@ def test_given_storages_available_container_cant_connect_network_not_available_g **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") @@ -794,9 +793,9 @@ def test_given_only_config_storage_container_can_connect_network_not_available_g self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -806,16 +805,16 @@ def test_given_only_config_storage_container_can_connect_network_not_available_g **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_only_database_storage_container_can_connect_network_not_available_gocert_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -825,16 +824,16 @@ def test_given_only_database_storage_container_can_connect_network_not_available **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_storages_available_container_can_connect_network_not_available_gocert_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -844,16 +843,16 @@ def test_given_storages_available_container_can_connect_network_not_available_go **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") def test_given_only_config_storage_container_cant_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -863,16 +862,16 @@ def test_given_only_config_storage_container_cant_connect_network_available_goce **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_only_database_storage_container_cant_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -882,16 +881,16 @@ def test_given_only_database_storage_container_cant_connect_network_available_go **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_storages_available_container_cant_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -901,16 +900,16 @@ def test_given_storages_available_container_cant_connect_network_available_gocer **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_only_config_storage_container_can_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -920,16 +919,16 @@ def test_given_only_config_storage_container_can_connect_network_available_gocer **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_only_database_storage_container_can_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -939,16 +938,16 @@ def test_given_only_database_storage_container_can_connect_network_available_goc **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_storages_available_container_can_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -958,16 +957,16 @@ def test_given_storages_available_container_can_connect_network_available_gocert **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") def test_given_only_config_storage_container_cant_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -977,16 +976,16 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -996,16 +995,16 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_storages_available_container_cant_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -1015,16 +1014,16 @@ def test_given_storages_available_container_cant_connect_network_not_available_g **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_only_config_storage_container_can_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -1034,16 +1033,16 @@ def test_given_only_config_storage_container_can_connect_network_not_available_g **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_only_database_storage_container_can_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -1053,16 +1052,16 @@ def test_given_only_database_storage_container_can_connect_network_not_available **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_storages_available_container_can_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -1072,16 +1071,16 @@ def test_given_storages_available_container_can_connect_network_not_available_go **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") def test_given_only_config_storage_container_cant_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -1091,16 +1090,16 @@ def test_given_only_config_storage_container_cant_connect_network_available_goce **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_only_database_storage_container_cant_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -1110,16 +1109,16 @@ def test_given_only_database_storage_container_cant_connect_network_available_go **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_storages_available_container_cant_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -1129,16 +1128,16 @@ def test_given_storages_available_container_cant_connect_network_available_gocer **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_only_config_storage_container_can_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -1148,16 +1147,16 @@ def test_given_only_config_storage_container_can_connect_network_available_gocer **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_only_database_storage_container_can_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -1167,16 +1166,16 @@ def test_given_only_database_storage_container_can_connect_network_available_goc **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_storages_available_container_can_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -1186,16 +1185,16 @@ def test_given_storages_available_container_can_connect_network_available_gocert **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") def test_given_only_config_storage_container_cant_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -1205,16 +1204,16 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -1224,16 +1223,16 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_storages_available_container_cant_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -1243,16 +1242,16 @@ def test_given_storages_available_container_cant_connect_network_not_available_g **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_only_config_storage_container_can_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -1262,16 +1261,16 @@ def test_given_only_config_storage_container_can_connect_network_not_available_g **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_only_database_storage_container_can_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -1281,16 +1280,16 @@ def test_given_only_database_storage_container_can_connect_network_not_available **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_storages_available_container_can_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network([], [], [])}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) @@ -1300,16 +1299,16 @@ def test_given_storages_available_container_can_connect_network_not_available_go **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") def test_given_only_config_storage_container_cant_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -1319,16 +1318,16 @@ def test_given_only_config_storage_container_cant_connect_network_available_goce **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_only_database_storage_container_cant_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -1338,16 +1337,16 @@ def test_given_only_database_storage_container_cant_connect_network_available_go **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_storages_available_container_cant_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=False)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=False)}, + networks={Network("juju-info")}, leader=True, ) @@ -1357,16 +1356,16 @@ def test_given_storages_available_container_cant_connect_network_available_gocer **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") def test_given_only_config_storage_container_can_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -1376,16 +1375,16 @@ def test_given_only_config_storage_container_can_connect_network_available_gocer **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_only_database_storage_container_can_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -1395,16 +1394,16 @@ def test_given_only_database_storage_container_can_connect_network_available_goc **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") def test_given_storages_available_container_can_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( - storage=[Storage(name="config"), Storage(name="database")], - containers=[Container(name="gocert", can_connect=True)], - networks={"juju-info": Network.default()}, + storages={Storage(name="config"), Storage(name="database")}, + containers={Container(name="gocert", can_connect=True)}, + networks={Network("juju-info")}, leader=True, ) @@ -1414,20 +1413,20 @@ def test_given_storages_available_container_can_connect_network_available_gocert **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") def test_given_gocert_available_and_initialized_when_collect_status_then_status_is_active( self, context ): with tempfile.TemporaryDirectory() as tempdir: - config_mount = Mount("/etc/gocert/config", tempdir) + config_mount = Mount(location="/etc/gocert/config", source=tempdir) state = State( - storage=[Storage(name="config"), Storage(name="database")], + storages={Storage(name="config"), Storage(name="database")}, containers=[ Container(name="gocert", can_connect=True, mounts={"config": config_mount}) ], - networks={"juju-info": Network.default()}, + networks={Network("juju-info")}, leader=True, ) @@ -1441,20 +1440,20 @@ def test_given_gocert_available_and_initialized_when_collect_status_then_status_ **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): - out = context.run(Event("collect-unit-status"), state) + out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.ActiveStatus() def test_given_gocert_available_and_not_initialized_when_configure_then_admin_user_created( self, context ): with tempfile.TemporaryDirectory() as tempdir: - config_mount = Mount("/etc/gocert/config", tempdir) + config_mount = Mount(location="/etc/gocert/config", source=tempdir) state = State( - storage=[Storage(name="config"), Storage(name="database")], + storages={Storage(name="config"), Storage(name="database")}, containers=[ Container(name="gocert", can_connect=True, mounts={"config": config_mount}) ], - networks={"juju-info": Network.default()}, + networks={Network("juju-info")}, leader=True, ) @@ -1469,7 +1468,7 @@ def test_given_gocert_available_and_not_initialized_when_configure_then_admin_us }, ), ): - out = context.run(Event("update-status"), state) + out = context.run(context.on.update_status(), state) assert len(out.secrets) == 1 - assert out.secrets[0].label == "GoCert Login Details" - assert out.secrets[0].contents[1].get("token") == "example-token" + secret = out.get_secret(label="GoCert Login Details") + assert secret.latest_content.get("token") == "example-token" From c45577667316e35773bf7cab88a4e620d8663902 Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Mon, 9 Sep 2024 09:56:27 -0400 Subject: [PATCH 05/11] chore: change charm name to notary-k8s (#36) Signed-off-by: guillaume Co-authored-by: kayra1 --- CONTRIBUTING.md | 2 +- README.md | 14 +- charmcraft.yaml | 28 +- src/charm.py | 62 +-- src/config/config.yaml | 6 +- .../{gocert.json => notary.json} | 14 +- src/{gocert.py => notary.py} | 26 +- tests/integration/test_charm.py | 4 +- tests/unit/test_charm.py | 496 +++++++++--------- 9 files changed, 326 insertions(+), 326 deletions(-) rename src/grafana_dashboards/{gocert.json => notary.json} (98%) rename src/{gocert.py => notary.py} (82%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f8c192..129bde3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,5 +34,5 @@ charmcraft pack Deploy it by using: ```shell -juju deploy ./gocert-k8s_ubuntu-22.04-amd64.charm --resource gocert-image=ghcr.io/canonical/gocert +juju deploy ./notary-k8s_ubuntu-22.04-amd64.charm --resource notary-image=ghcr.io/canonical/notary ``` diff --git a/README.md b/README.md index f70bd60..ae80158 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ -# gocert-k8s-operator +# notary-k8s-operator Manage your certificates in charms ## OCI Images -- GoCert: [ghcr.io/canonical/gocert](https://github.com/canonical/gocert) +- Notary: [ghcr.io/canonical/notary](https://github.com/canonical/notary) -# gocert +# notary -Charmhub package name: gocert -More information: https://charmhub.io/gocert +Charmhub package name: notary +More information: https://charmhub.io/notary -GoCert helps you manage your certificates, from simple all the way up to enterprise deployments. +Notary helps you manage your certificates, from simple all the way up to enterprise deployments. ## Other resources -- [Read more](https://github.com/canonical/gocert/blob/main/README.md) +- [Read more](https://github.com/canonical/notary/blob/main/README.md) - [Contributing](CONTRIBUTING.md) diff --git a/charmcraft.yaml b/charmcraft.yaml index 82b0e82..1884522 100644 --- a/charmcraft.yaml +++ b/charmcraft.yaml @@ -1,22 +1,22 @@ -name: gocert-k8s +name: notary-k8s type: charm -title: GoCert +title: Notary summary: Certificate management made easy description: | - GoCert helps you manage certificate requests and their associated certificates. - Charmed GoCert helps you automatically receive CSRs and distribute certificates to the applications + Notary helps you manage certificate requests and their associated certificates. + Charmed Notary helps you automatically receive CSRs and distribute certificates to the applications you've deployed in your model. links: documentation: https://discourse.charmhub.io/t/gocert-docs-index/15216 issues: - - https://github.com/canonical/gocert-k8s-operator/issues + - https://github.com/canonical/notary-k8s-operator/issues source: - - https://github.com/canonical/gocert-k8s-operator + - https://github.com/canonical/notary-k8s-operator website: - - https://charmhub.io/gocert-k8s + - https://charmhub.io/notary-k8s provides: @@ -40,13 +40,13 @@ bases: channel: "22.04" containers: - gocert: - resource: gocert-image + notary: + resource: notary-image mounts: - storage: config - location: /etc/gocert/config + location: /etc/notary/config - storage: database - location: /var/lib/gocert/database + location: /var/lib/notary/database storage: config: @@ -57,10 +57,10 @@ storage: minimum-size: 1G resources: - gocert-image: + notary-image: type: oci-image - description: OCI image for the 'GoCert' application - upstream-source: ghcr.io/canonical/gocert:0.0.3 + description: OCI image for the Notary application + upstream-source: ghcr.io/canonical/notary:0.0.3 parts: charm: diff --git a/src/charm.py b/src/charm.py index a598d55..870e38a 100755 --- a/src/charm.py +++ b/src/charm.py @@ -23,7 +23,7 @@ generate_private_key, ) -from gocert import GoCert +from notary import Notary logger = logging.getLogger(__name__) @@ -34,16 +34,16 @@ DB_MOUNT = "database" CONFIG_MOUNT = "config" CHARM_PATH = "/var/lib/juju/storage" -WORKLOAD_CONFIG_PATH = "/etc/gocert" +WORKLOAD_CONFIG_PATH = "/etc/notary" -CERTIFICATE_COMMON_NAME = "GoCert Self Signed Certificate" -SELF_SIGNED_CA_COMMON_NAME = "GoCert Self Signed Root CA" -GOCERT_LOGIN_SECRET_LABEL = "GoCert Login Details" +CERTIFICATE_COMMON_NAME = "Notary Self Signed Certificate" +SELF_SIGNED_CA_COMMON_NAME = "Notary Self Signed Root CA" +NOTARY_LOGIN_SECRET_LABEL = "Notary Login Details" @dataclass class LoginSecret: - """The format of the secret for the login details that are required to login to GoCert.""" + """The format of the secret for the login details that are required to login to Notary.""" username: str password: str @@ -58,15 +58,15 @@ def to_dict(self) -> dict[str, str]: } -class GocertCharm(ops.CharmBase): - """Charmed Gocert.""" +class NotaryCharm(ops.CharmBase): + """Charmed Notary.""" def __init__(self, framework: ops.Framework): super().__init__(framework) self.port = 2111 self.unit.set_ports(self.port) - self.container = self.unit.get_container("gocert") + self.container = self.unit.get_container("notary") self.tls = TLSCertificatesProvidesV4(self, relationship_name="certificates") self.dashboard = GrafanaDashboardProvider(self, relation_name=GRAFANA_RELATION_NAME) self.logs = LogForwarder(charm=self, relation_name=LOGGING_RELATION_NAME) @@ -83,15 +83,15 @@ def __init__(self, framework: ops.Framework): ], ) - self.client = GoCert( + self.client = Notary( f"https://{self._application_bind_address}:{self.port}", f"{CHARM_PATH}/{CONFIG_MOUNT}/0/ca.pem", ) [ framework.observe(event, self.configure) for event in [ - self.on["gocert"].pebble_ready, - self.on["gocert"].pebble_custom_notice, + self.on["notary"].pebble_ready, + self.on["notary"].pebble_custom_notice, self.on["certificates"].relation_changed, self.on.config_storage_attached, self.on.database_storage_attached, @@ -109,7 +109,7 @@ def configure(self, event: ops.EventBase): return if not self.container.can_connect(): return - self._configure_gocert_config_file() + self._configure_notary_config_file() self._configure_access_certificates() self._configure_charm_authorization() @@ -127,15 +127,15 @@ def _on_collect_status(self, event: ops.CollectStatusEvent): event.add_status(ops.WaitingStatus("certificates not yet created")) return if not self.client.is_api_available(): - event.add_status(ops.WaitingStatus("GoCert server not yet available")) + event.add_status(ops.WaitingStatus("Notary server not yet available")) return if not self.client.is_initialized(): - event.add_status(ops.BlockedStatus("Please initialize GoCert")) + event.add_status(ops.BlockedStatus("Please initialize Notary")) return event.add_status(ops.ActiveStatus()) ## Configure Dependencies ## - def _configure_gocert_config_file(self): + def _configure_notary_config_file(self): """Push the config file.""" try: self.container.pull(f"{WORKLOAD_CONFIG_PATH}/config/config.yaml") @@ -149,25 +149,25 @@ def _configure_gocert_config_file(self): logger.info("Config file created.") def _configure_access_certificates(self): - """Update the config files for gocert and replan if required.""" + """Update the config files for notary and replan if required.""" certificates_changed = False if not self._self_signed_certificates_generated(): certificates_changed = True self._generate_self_signed_certificates() logger.info("Certificates configured.") if certificates_changed: - self.container.add_layer("gocert", self._pebble_layer, combine=True) + self.container.add_layer("notary", self._pebble_layer, combine=True) with suppress(ops.pebble.ChangeError): self.container.replan() def _configure_charm_authorization(self): - """Create an admin user to manage GoCert if needed, and acquire a token by logging in if needed.""" + """Create an admin user to manage Notary if needed, and acquire a token by logging in if needed.""" login_details = self._get_or_create_admin_account() if not login_details: return if not login_details.token or not self.client.token_is_valid(login_details.token): login_details.token = self.client.login(login_details.username, login_details.password) - login_details_secret = self.model.get_secret(label=GOCERT_LOGIN_SECRET_LABEL) + login_details_secret = self.model.get_secret(label=NOTARY_LOGIN_SECRET_LABEL) login_details_secret.set_content(login_details.to_dict()) ## Properties ## @@ -175,13 +175,13 @@ def _configure_charm_authorization(self): def _pebble_layer(self) -> ops.pebble.LayerDict: """Return a dictionary representing a Pebble layer.""" return { - "summary": "gocert layer", - "description": "pebble config layer for gocert", + "summary": "notary layer", + "description": "pebble config layer for notary", "services": { - "gocert": { + "notary": { "override": "replace", - "summary": "gocert", - "command": f"gocert -config {WORKLOAD_CONFIG_PATH}/config/config.yaml", + "summary": "notary", + "command": f"notary -config {WORKLOAD_CONFIG_PATH}/config/config.yaml", "startup": "enabled", } }, @@ -260,10 +260,10 @@ def _get_or_create_admin_account(self) -> LoginSecret | None: """Get the first admin user for the charm to use from secrets. Create one if it doesn't exist. Returns: - Login details secret if they exist. None if the related account couldn't be created in GoCert. + Login details secret if they exist. None if the related account couldn't be created in Notary. """ try: - secret = self.model.get_secret(label=GOCERT_LOGIN_SECRET_LABEL) + secret = self.model.get_secret(label=NOTARY_LOGIN_SECRET_LABEL) secret_content = secret.get_content(refresh=True) username = secret_content.get("username", "") password = secret_content.get("password", "") @@ -274,7 +274,7 @@ def _get_or_create_admin_account(self) -> LoginSecret | None: password = _generate_password() account = LoginSecret(username, password, None) self.app.add_secret( - label=GOCERT_LOGIN_SECRET_LABEL, + label=NOTARY_LOGIN_SECRET_LABEL, content=account.to_dict(), ) logger.info("admin account details saved to secrets.") @@ -286,7 +286,7 @@ def _get_or_create_admin_account(self) -> LoginSecret | None: def _generate_password() -> str: - """Generate a password for the GoCert Account.""" + """Generate a password for the Notary Account.""" pw = [] pw.append(random.choice(string.ascii_lowercase)) pw.append(random.choice(string.ascii_uppercase)) @@ -299,10 +299,10 @@ def _generate_password() -> str: def _generate_username() -> str: - """Generate a username for the GoCert Account.""" + """Generate a username for the Notary Account.""" suffix = [random.choice(string.ascii_uppercase) for i in range(4)] return "charm-admin-" + "".join(suffix) if __name__ == "__main__": # pragma: nocover - ops.main(GocertCharm) # type: ignore + ops.main(NotaryCharm) # type: ignore diff --git a/src/config/config.yaml b/src/config/config.yaml index 000eace..5f3165b 100644 --- a/src/config/config.yaml +++ b/src/config/config.yaml @@ -1,5 +1,5 @@ -key_path: "/etc/gocert/config/private_key.pem" -cert_path: "/etc/gocert/config/certificate.pem" -db_path: "/var/lib/gocert/database/certs.db" +key_path: "/etc/notary/config/private_key.pem" +cert_path: "/etc/notary/config/certificate.pem" +db_path: "/var/lib/notary/database/certs.db" port: 2111 pebble_notifications: true \ No newline at end of file diff --git a/src/grafana_dashboards/gocert.json b/src/grafana_dashboards/notary.json similarity index 98% rename from src/grafana_dashboards/gocert.json rename to src/grafana_dashboards/notary.json index 5d4dabf..9db73ab 100644 --- a/src/grafana_dashboards/gocert.json +++ b/src/grafana_dashboards/notary.json @@ -39,7 +39,7 @@ "type": "prometheus", "uid": "${prometheusds}" }, - "description": "The total number of certificate requests in GoCert", + "description": "The total number of certificate requests in Notary", "fieldConfig": { "defaults": { "color": { @@ -443,7 +443,7 @@ "uid": "${prometheusds}" }, "editorMode": "builder", - "expr": "go_goroutines{juju_application=~\"$juju_application\",juju_charm=\"gocert-k8s\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\",juju_unit=~\"$juju_unit\"}", + "expr": "go_goroutines{juju_application=~\"$juju_application\",juju_charm=\"notary-k8s\",juju_model=~\"$juju_model\",juju_model_uuid=~\"$juju_model_uuid\",juju_unit=~\"$juju_unit\"}", "legendFormat": "Goroutines", "range": true, "refId": "A" @@ -457,7 +457,7 @@ "type": "prometheus", "uid": "${prometheusds}" }, - "description": "The total memory usage by GoCert", + "description": "The total memory usage by Notary", "fieldConfig": { "defaults": { "color": { @@ -506,7 +506,7 @@ "uid": "${prometheusds}" }, "editorMode": "builder", - "expr": "go_memstats_alloc_bytes{juju_charm=\"gocert-k8s\"}", + "expr": "go_memstats_alloc_bytes{juju_charm=\"notary-k8s\"}", "legendFormat": "__auto", "range": true, "refId": "A" @@ -557,12 +557,12 @@ "uid": "${lokids}" }, "editorMode": "builder", - "expr": "{charm=\"gocert-k8s\", juju_application=~\"$juju_application\", juju_model=~\"$juju_model\", juju_model_uuid=~\"$juju_model_uuid\", juju_unit=~\"$juju_unit\"} |= \"\"", + "expr": "{charm=\"notary-k8s\", juju_application=~\"$juju_application\", juju_model=~\"$juju_model\", juju_model_uuid=~\"$juju_model_uuid\", juju_unit=~\"$juju_unit\"} |= \"\"", "queryType": "range", "refId": "A" } ], - "title": "GoCert Logs", + "title": "Notary Logs", "type": "logs" } ], @@ -786,7 +786,7 @@ }, "timepicker": {}, "timezone": "", - "title": "GoCert", + "title": "Notary", "uid": "1546e4c5ed06bcba", "version": 1, "weekStart": "" diff --git a/src/gocert.py b/src/notary.py similarity index 82% rename from src/gocert.py rename to src/notary.py index ae09f04..60ed290 100644 --- a/src/gocert.py +++ b/src/notary.py @@ -1,7 +1,7 @@ # Copyright 2024 Canonical Ltd. # See LICENSE file for licensing details. -"""Library for interacting with the GoCert application.""" +"""Library for interacting with the Notary application.""" import logging @@ -10,27 +10,27 @@ logger = logging.getLogger(__name__) -class GoCertClientError(Exception): - """Base class for exceptions raised by the GoCert client.""" +class NotaryClientError(Exception): + """Base class for exceptions raised by the Notary client.""" -class GoCert: - """Class to interact with GoCert.""" +class Notary: + """Class to interact with Notary.""" API_VERSION = "v1" def __init__(self, url: str, ca_path: str) -> None: - """Initialize a client for interacting with GoCert. + """Initialize a client for interacting with Notary. Args: - url: the endpoint that gocert is listening on e.g https://gocert.com:8000 - ca_path: the file path that contains the ca cert that gocert uses for https communication + url: the endpoint that notary is listening on e.g https://notary.com:8000 + ca_path: the file path that contains the ca cert that notary uses for https communication """ self.url = url self.ca_path = ca_path def login(self, username: str, password: str) -> str | None: - """Login to gocert by sending the username and password and return a Token.""" + """Login to notary by sending the username and password and return a Token.""" try: req = requests.post( f"{self.url}/login", @@ -44,7 +44,7 @@ def login(self, username: str, password: str) -> str | None: except requests.HTTPError: logger.error("couldn't log in: code %s, %s", req.status_code, req.text) return - logger.info("logged in to GoCert successfully") + logger.info("logged in to Notary successfully") return req.text def token_is_valid(self, token: str) -> bool: @@ -61,7 +61,7 @@ def token_is_valid(self, token: str) -> bool: return True def is_api_available(self) -> bool: - """Return if the GoCert server is reachable.""" + """Return if the Notary server is reachable.""" try: req = requests.get( f"{self.url}/status", @@ -73,7 +73,7 @@ def is_api_available(self) -> bool: return True def is_initialized(self) -> bool: - """Return if the GoCert server is initialized.""" + """Return if the Notary server is initialized.""" try: req = requests.get( f"{self.url}/status", @@ -110,6 +110,6 @@ def create_first_user(self, username: str, password: str) -> int | None: except requests.HTTPError: logger.warning("couldn't create first user: code %s, %s", req.status_code, req.text) return None - logger.info("created the first user in GoCert.") + logger.info("created the first user in Notary.") id = req.json().get("id") return int(id) if id else None diff --git a/tests/integration/test_charm.py b/tests/integration/test_charm.py index 1e80b70..89f8118 100644 --- a/tests/integration/test_charm.py +++ b/tests/integration/test_charm.py @@ -26,7 +26,7 @@ async def test_build_and_deploy(ops_test: OpsTest, request): Assert on the unit status before any relations/configurations take place. """ charm = Path(request.config.getoption("--charm_path")).resolve() - resources = {"gocert-image": CHARMCRAFT["resources"]["gocert-image"]["upstream-source"]} + resources = {"notary-image": CHARMCRAFT["resources"]["notary-image"]["upstream-source"]} # Deploy the charm and wait for active status await asyncio.gather( @@ -36,7 +36,7 @@ async def test_build_and_deploy(ops_test: OpsTest, request): @pytest.mark.abort_on_fail -async def test_given_loki_and_prometheus_related_to_gocert_all_charm_statuses_active( +async def test_given_loki_and_prometheus_related_to_notary_all_charm_statuses_active( ops_test: OpsTest, ): """Deploy loki and prometheus, and make sure all applications are active.""" diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index aed6d7e..d972db3 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -8,7 +8,7 @@ import pytest from scenario import Container, Context, Mount, Network, State, Storage -from charm import GocertCharm +from charm import NotaryCharm from lib.charms.tls_certificates_interface.v4.tls_certificates import ( Certificate, PrivateKey, @@ -18,14 +18,14 @@ generate_private_key, ) -CERTIFICATE_COMMON_NAME = "GoCert Self Signed Certificate" -SELF_SIGNED_CA_COMMON_NAME = "GoCert Self Signed Root CA" +CERTIFICATE_COMMON_NAME = "Notary Self Signed Certificate" +SELF_SIGNED_CA_COMMON_NAME = "Notary Self Signed Root CA" class TestCharm: @pytest.fixture(scope="function") def context(self): - yield Context(GocertCharm) + yield Context(NotaryCharm) def example_cert_and_key(self) -> tuple[Certificate, PrivateKey]: private_key = generate_private_key() @@ -48,681 +48,681 @@ def example_cert_and_key(self) -> tuple[Certificate, PrivateKey]: return certificate, private_key # Configure tests - def test_given_only_config_storage_container_cant_connect_network_not_available_gocert_not_running_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_cant_connect_network_not_available_notary_not_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_not_running_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_cant_connect_network_not_available_notary_not_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_cant_connect_network_not_available_gocert_not_running_when_configure_then_no_error_raised( + def test_given_storages_available_container_cant_connect_network_not_available_notary_not_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_config_storage_container_can_connect_network_not_available_gocert_not_running_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_can_connect_network_not_available_notary_not_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_can_connect_network_not_available_gocert_not_running_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_can_connect_network_not_available_notary_not_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_can_connect_network_not_available_gocert_not_running_when_configure_then_config_file_generated( + def test_given_storages_available_container_can_connect_network_not_available_notary_not_running_when_configure_then_config_file_generated( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): out = context.run(context.on.config_changed(), state) - root = out.get_container("gocert").get_filesystem(context) - assert (root / "etc/gocert/config/config.yaml").open("r") - assert not (root / "etc/gocert/config/certificate.pem").exists() - assert not ((root / "etc/gocert/config/private_key.pem").exists()) + root = out.get_container("notary").get_filesystem(context) + assert (root / "etc/notary/config/config.yaml").open("r") + assert not (root / "etc/notary/config/certificate.pem").exists() + assert not ((root / "etc/notary/config/private_key.pem").exists()) assert len(out.secrets) == 1 - assert out.get_secret(label="GoCert Login Details") + assert out.get_secret(label="Notary Login Details") - def test_given_only_config_storage_container_cant_connect_network_available_gocert_not_running_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_cant_connect_network_available_notary_not_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_cant_connect_network_available_gocert_not_running_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_cant_connect_network_available_notary_not_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_cant_connect_network_available_gocert_not_running_when_configure_then_no_error_raised( + def test_given_storages_available_container_cant_connect_network_available_notary_not_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_config_storage_container_can_connect_network_available_gocert_not_running_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_can_connect_network_available_notary_not_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_can_connect_network_available_gocert_not_running_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_can_connect_network_available_notary_not_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_can_connect_network_available_gocert_not_running_when_configure_then_config_and_certificates_generated( + def test_given_storages_available_container_can_connect_network_available_notary_not_running_when_configure_then_config_and_certificates_generated( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), ): out = context.run(context.on.config_changed(), state) - root = out.get_container("gocert").get_filesystem(context) - assert (root / "etc/gocert/config/config.yaml").open("r") + root = out.get_container("notary").get_filesystem(context) + assert (root / "etc/notary/config/config.yaml").open("r") assert ( - (root / "etc/gocert/config/certificate.pem") + (root / "etc/notary/config/certificate.pem") .open("r") .read() .startswith("-----BEGIN CERTIFICATE-----") ) assert ( - (root / "etc/gocert/config/private_key.pem") + (root / "etc/notary/config/private_key.pem") .open("r") .read() .startswith("-----BEGIN RSA PRIVATE KEY-----") ) - def test_given_only_config_storage_container_cant_connect_network_not_available_gocert_running_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_cant_connect_network_not_available_notary_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_running_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_cant_connect_network_not_available_notary_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_cant_connect_network_not_available_gocert_running_when_configure_then_no_error_raised( + def test_given_storages_available_container_cant_connect_network_not_available_notary_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_config_storage_container_can_connect_network_not_available_gocert_running_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_can_connect_network_not_available_notary_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_can_connect_network_not_available_gocert_running_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_can_connect_network_not_available_notary_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_can_connect_network_not_available_gocert_running_when_configure_then_config_file_generated( + def test_given_storages_available_container_can_connect_network_not_available_notary_running_when_configure_then_config_file_generated( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): out = context.run(context.on.config_changed(), state) - root = out.get_container("gocert").get_filesystem(context) - assert (root / "etc/gocert/config/config.yaml").open("r") - assert not (root / "etc/gocert/config/certificate.pem").exists() - assert not ((root / "etc/gocert/config/private_key.pem").exists()) + root = out.get_container("notary").get_filesystem(context) + assert (root / "etc/notary/config/config.yaml").open("r") + assert not (root / "etc/notary/config/certificate.pem").exists() + assert not ((root / "etc/notary/config/private_key.pem").exists()) assert len(out.secrets) == 1 - assert out.get_secret(label="GoCert Login Details") + assert out.get_secret(label="Notary Login Details") - def test_given_only_config_storage_container_cant_connect_network_available_gocert_running_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_cant_connect_network_available_notary_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_cant_connect_network_available_gocert_running_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_cant_connect_network_available_notary_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_cant_connect_network_available_gocert_running_when_configure_then_no_error_raised( + def test_given_storages_available_container_cant_connect_network_available_notary_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_config_storage_container_can_connect_network_available_gocert_running_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_can_connect_network_available_notary_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_can_connect_network_available_gocert_running_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_can_connect_network_available_notary_running_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_can_connect_network_available_gocert_running_when_configure_then_status_is_blocked( + def test_given_storages_available_container_can_connect_network_available_notary_running_when_configure_then_status_is_blocked( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_config_storage_container_cant_connect_network_not_available_gocert_initialized_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_cant_connect_network_not_available_notary_initialized_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_initialized_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_cant_connect_network_not_available_notary_initialized_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_cant_connect_network_not_available_gocert_initialized_when_configure_then_no_error_raised( + def test_given_storages_available_container_cant_connect_network_not_available_notary_initialized_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_config_storage_container_can_connect_network_not_available_gocert_initialized_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_can_connect_network_not_available_notary_initialized_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_can_connect_network_not_available_gocert_initialized_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_can_connect_network_not_available_notary_initialized_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_can_connect_network_not_available_gocert_initialized_when_configure_then_config_file_generated( + def test_given_storages_available_container_can_connect_network_not_available_notary_initialized_when_configure_then_config_file_generated( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): out = context.run(context.on.config_changed(), state) - root = out.get_container("gocert").get_filesystem(context) - assert (root / "etc/gocert/config/config.yaml").open("r") - assert not (root / "etc/gocert/config/certificate.pem").exists() - assert not ((root / "etc/gocert/config/private_key.pem").exists()) + root = out.get_container("notary").get_filesystem(context) + assert (root / "etc/notary/config/config.yaml").open("r") + assert not (root / "etc/notary/config/certificate.pem").exists() + assert not ((root / "etc/notary/config/private_key.pem").exists()) assert len(out.secrets) == 1 - assert out.get_secret(label="GoCert Login Details") + assert out.get_secret(label="Notary Login Details") - def test_given_only_config_storage_container_cant_connect_network_available_gocert_initialized_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_cant_connect_network_available_notary_initialized_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_cant_connect_network_available_gocert_initialized_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_cant_connect_network_available_notary_initialized_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_cant_connect_network_available_gocert_initialized_when_configure_then_no_error_raised( + def test_given_storages_available_container_cant_connect_network_available_notary_initialized_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_config_storage_container_can_connect_network_available_gocert_initialized_when_configure_then_no_error_raised( + def test_given_only_config_storage_container_can_connect_network_available_notary_initialized_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): context.run(context.on.config_changed(), state) - def test_given_only_database_storage_container_can_connect_network_available_gocert_initialized_when_configure_then_no_error_raised( + def test_given_only_database_storage_container_can_connect_network_available_notary_initialized_when_configure_then_no_error_raised( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), ): context.run(context.on.config_changed(), state) - def test_given_storages_available_container_can_connect_network_available_gocert_initialized_when_configure_then_status_is_active( + def test_given_storages_available_container_can_connect_network_available_notary_initialized_when_configure_then_status_is_active( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -730,18 +730,18 @@ def test_given_storages_available_container_can_connect_network_available_gocert context.run(context.on.config_changed(), state) # Unit Status Tests - def test_given_only_config_storage_container_cant_connect_network_not_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_cant_connect_network_not_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -750,18 +750,18 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_cant_connect_network_not_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -769,18 +769,18 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_storages_available_container_cant_connect_network_not_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_cant_connect_network_not_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -789,18 +789,18 @@ def test_given_storages_available_container_cant_connect_network_not_available_g assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_config_storage_container_can_connect_network_not_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_can_connect_network_not_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -808,18 +808,18 @@ def test_given_only_config_storage_container_can_connect_network_not_available_g out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_only_database_storage_container_can_connect_network_not_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_can_connect_network_not_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -827,18 +827,18 @@ def test_given_only_database_storage_container_can_connect_network_not_available out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_storages_available_container_can_connect_network_not_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_can_connect_network_not_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -846,18 +846,18 @@ def test_given_storages_available_container_can_connect_network_not_available_go out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") - def test_given_only_config_storage_container_cant_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_cant_connect_network_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -865,18 +865,18 @@ def test_given_only_config_storage_container_cant_connect_network_available_goce out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_database_storage_container_cant_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_cant_connect_network_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -884,18 +884,18 @@ def test_given_only_database_storage_container_cant_connect_network_available_go out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_storages_available_container_cant_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_cant_connect_network_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -903,18 +903,18 @@ def test_given_storages_available_container_cant_connect_network_available_gocer out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_config_storage_container_can_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_can_connect_network_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -922,18 +922,18 @@ def test_given_only_config_storage_container_can_connect_network_available_gocer out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_only_database_storage_container_can_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_can_connect_network_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -941,18 +941,18 @@ def test_given_only_database_storage_container_can_connect_network_available_goc out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_storages_available_container_can_connect_network_available_gocert_not_running_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_can_connect_network_available_notary_not_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": False, "is_initialized.return_value": False}, ), @@ -960,18 +960,18 @@ def test_given_storages_available_container_can_connect_network_available_gocert out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") - def test_given_only_config_storage_container_cant_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_cant_connect_network_not_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -979,18 +979,18 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_cant_connect_network_not_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -998,18 +998,18 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_storages_available_container_cant_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_cant_connect_network_not_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -1017,18 +1017,18 @@ def test_given_storages_available_container_cant_connect_network_not_available_g out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_config_storage_container_can_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_can_connect_network_not_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -1036,18 +1036,18 @@ def test_given_only_config_storage_container_can_connect_network_not_available_g out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_only_database_storage_container_can_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_can_connect_network_not_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -1055,18 +1055,18 @@ def test_given_only_database_storage_container_can_connect_network_not_available out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_storages_available_container_can_connect_network_not_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_can_connect_network_not_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -1074,18 +1074,18 @@ def test_given_storages_available_container_can_connect_network_not_available_go out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") - def test_given_only_config_storage_container_cant_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_cant_connect_network_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -1093,18 +1093,18 @@ def test_given_only_config_storage_container_cant_connect_network_available_goce out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_database_storage_container_cant_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_cant_connect_network_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -1112,18 +1112,18 @@ def test_given_only_database_storage_container_cant_connect_network_available_go out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_storages_available_container_cant_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_cant_connect_network_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -1131,18 +1131,18 @@ def test_given_storages_available_container_cant_connect_network_available_gocer out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_config_storage_container_can_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_can_connect_network_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -1150,18 +1150,18 @@ def test_given_only_config_storage_container_can_connect_network_available_gocer out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_only_database_storage_container_can_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_can_connect_network_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -1169,18 +1169,18 @@ def test_given_only_database_storage_container_can_connect_network_available_goc out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_storages_available_container_can_connect_network_available_gocert_running_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_can_connect_network_available_notary_running_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": False}, ), @@ -1188,18 +1188,18 @@ def test_given_storages_available_container_can_connect_network_available_gocert out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") - def test_given_only_config_storage_container_cant_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_cant_connect_network_not_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1207,18 +1207,18 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_database_storage_container_cant_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_cant_connect_network_not_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1226,18 +1226,18 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_storages_available_container_cant_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_cant_connect_network_not_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1245,18 +1245,18 @@ def test_given_storages_available_container_cant_connect_network_not_available_g out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_config_storage_container_can_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_can_connect_network_not_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1264,18 +1264,18 @@ def test_given_only_config_storage_container_can_connect_network_not_available_g out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_only_database_storage_container_can_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_can_connect_network_not_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1283,18 +1283,18 @@ def test_given_only_database_storage_container_can_connect_network_not_available out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_storages_available_container_can_connect_network_not_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_can_connect_network_not_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1302,18 +1302,18 @@ def test_given_storages_available_container_can_connect_network_not_available_go out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") - def test_given_only_config_storage_container_cant_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_cant_connect_network_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1321,18 +1321,18 @@ def test_given_only_config_storage_container_cant_connect_network_available_goce out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_database_storage_container_cant_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_cant_connect_network_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1340,18 +1340,18 @@ def test_given_only_database_storage_container_cant_connect_network_available_go out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_storages_available_container_cant_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_cant_connect_network_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=False)}, + containers={Container(name="notary", can_connect=False)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1359,18 +1359,18 @@ def test_given_storages_available_container_cant_connect_network_available_gocer out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("container not yet connectable") - def test_given_only_config_storage_container_can_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_only_config_storage_container_can_connect_network_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1378,18 +1378,18 @@ def test_given_only_config_storage_container_can_connect_network_available_gocer out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_only_database_storage_container_can_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_only_database_storage_container_can_connect_network_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1397,18 +1397,18 @@ def test_given_only_database_storage_container_can_connect_network_available_goc out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("storages not yet available") - def test_given_storages_available_container_can_connect_network_available_gocert_initialized_when_collect_status_then_status_is_waiting( + def test_given_storages_available_container_can_connect_network_available_notary_initialized_when_collect_status_then_status_is_waiting( self, context ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="gocert", can_connect=True)}, + containers={Container(name="notary", can_connect=True)}, networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert", + "notary.Notary", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1416,15 +1416,15 @@ def test_given_storages_available_container_can_connect_network_available_gocert out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.WaitingStatus("certificates not yet created") - def test_given_gocert_available_and_initialized_when_collect_status_then_status_is_active( + def test_given_notary_available_and_initialized_when_collect_status_then_status_is_active( self, context ): with tempfile.TemporaryDirectory() as tempdir: - config_mount = Mount(location="/etc/gocert/config", source=tempdir) + config_mount = Mount(location="/etc/notary/config", source=tempdir) state = State( storages={Storage(name="config"), Storage(name="database")}, containers=[ - Container(name="gocert", can_connect=True, mounts={"config": config_mount}) + Container(name="notary", can_connect=True, mounts={"config": config_mount}) ], networks={Network("juju-info")}, leader=True, @@ -1435,7 +1435,7 @@ def test_given_gocert_available_and_initialized_when_collect_status_then_status_ f.write(str(certificate)) with patch( - "gocert.GoCert.__new__", + "notary.Notary.__new__", return_value=Mock( **{"is_api_available.return_value": True, "is_initialized.return_value": True}, ), @@ -1443,22 +1443,22 @@ def test_given_gocert_available_and_initialized_when_collect_status_then_status_ out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.ActiveStatus() - def test_given_gocert_available_and_not_initialized_when_configure_then_admin_user_created( + def test_given_notary_available_and_not_initialized_when_configure_then_admin_user_created( self, context ): with tempfile.TemporaryDirectory() as tempdir: - config_mount = Mount(location="/etc/gocert/config", source=tempdir) + config_mount = Mount(location="/etc/notary/config", source=tempdir) state = State( storages={Storage(name="config"), Storage(name="database")}, containers=[ - Container(name="gocert", can_connect=True, mounts={"config": config_mount}) + Container(name="notary", can_connect=True, mounts={"config": config_mount}) ], networks={Network("juju-info")}, leader=True, ) with patch( - "gocert.GoCert.__new__", + "notary.Notary.__new__", return_value=Mock( **{ "is_api_available.return_value": True, @@ -1470,5 +1470,5 @@ def test_given_gocert_available_and_not_initialized_when_configure_then_admin_us ): out = context.run(context.on.update_status(), state) assert len(out.secrets) == 1 - secret = out.get_secret(label="GoCert Login Details") + secret = out.get_secret(label="Notary Login Details") assert secret.latest_content.get("token") == "example-token" From 116467953d78d553e2fe8201bf99a56987079d1a Mon Sep 17 00:00:00 2001 From: Kayra Date: Fri, 13 Sep 2024 11:03:41 +0300 Subject: [PATCH 06/11] feat: tls provider relation (#6) --- src/charm.py | 75 ++++- src/notary.py | 131 +++++++- tests/integration/test_charm.py | 117 +++++++- tests/unit/test_charm.py | 515 +++++++++++++++++++++++++------- 4 files changed, 711 insertions(+), 127 deletions(-) diff --git a/src/charm.py b/src/charm.py index 870e38a..6cbcbed 100755 --- a/src/charm.py +++ b/src/charm.py @@ -16,6 +16,7 @@ from charms.prometheus_k8s.v0.prometheus_scrape import MetricsEndpointProvider from charms.tls_certificates_interface.v4.tls_certificates import ( Certificate, + ProviderCertificate, TLSCertificatesProvidesV4, generate_ca, generate_certificate, @@ -27,6 +28,8 @@ logger = logging.getLogger(__name__) +CERTIFICATE_PROVIDER_RELATION_NAME = "certificates" + LOGGING_RELATION_NAME = "logging" METRICS_RELATION_NAME = "metrics" GRAFANA_RELATION_NAME = "grafana-dashboard" @@ -67,7 +70,9 @@ def __init__(self, framework: ops.Framework): self.port = 2111 self.unit.set_ports(self.port) self.container = self.unit.get_container("notary") - self.tls = TLSCertificatesProvidesV4(self, relationship_name="certificates") + self.tls = TLSCertificatesProvidesV4( + self, relationship_name=CERTIFICATE_PROVIDER_RELATION_NAME + ) self.dashboard = GrafanaDashboardProvider(self, relation_name=GRAFANA_RELATION_NAME) self.logs = LogForwarder(charm=self, relation_name=LOGGING_RELATION_NAME) self.metrics = MetricsEndpointProvider( @@ -112,6 +117,7 @@ def configure(self, event: ops.EventBase): self._configure_notary_config_file() self._configure_access_certificates() self._configure_charm_authorization() + self._configure_certificate_requirers() def _on_collect_status(self, event: ops.CollectStatusEvent): if not self.unit.is_leader(): @@ -170,6 +176,73 @@ def _configure_charm_authorization(self): login_details_secret = self.model.get_secret(label=NOTARY_LOGIN_SECRET_LABEL) login_details_secret.set_content(login_details.to_dict()) + def _configure_certificate_requirers(self): + """Get all CSR's and certs from databags and Notary, compare differences and update requirers if needed.""" + login_details = self._get_or_create_admin_account() + if not login_details or not login_details.token: + logger.warning("couldn't distribute certificates: not logged in") + return + databag_csrs = self.tls.get_certificate_requests() + notary_table = self.client.get_certificate_requests_table(login_details.token) + if not notary_table: + logger.warning("couldn't distribute certificates: couldn't get table from notary") + return + + for request in databag_csrs: + notary_rows_with_matching_csr = [ + row + for row in notary_table.rows + if row.csr == str(request.certificate_signing_request) + ] + if len(notary_rows_with_matching_csr) < 1: + self.client.post_csr(str(request.certificate_signing_request), login_details.token) + continue + assert len(notary_rows_with_matching_csr) < 2 + request_notary_entry = notary_rows_with_matching_csr[0] + certificates_provided_for_csr = [ + csr + for csr in self.tls.get_issued_certificates(request.relation_id) + if str(csr.certificate_signing_request) == request_notary_entry.csr + ] + if ( + request_notary_entry.certificate_chain == "rejected" + or request_notary_entry.certificate_chain == "" + ): + if len(certificates_provided_for_csr) > 0: + last_provided_certificate = certificates_provided_for_csr[0] + self.tls.set_relation_certificate( + ProviderCertificate( + relation_id=request.relation_id, + certificate_signing_request=request.certificate_signing_request, + certificate=last_provided_certificate.certificate, + ca=last_provided_certificate.ca, + chain=last_provided_certificate.chain, + revoked=True, + ) + ) + continue + certificate_chain = [ + Certificate.from_string(cert) for cert in request_notary_entry.certificate_chain + ] + certificate_not_provided_yet = ( + len(certificate_chain) > 0 and len(certificates_provided_for_csr) == 0 + ) + certificate_provided_is_stale = ( + len(certificate_chain) > 0 + and len(certificates_provided_for_csr) == 1 + and certificate_chain[0] != certificates_provided_for_csr[0].certificate + ) + if certificate_not_provided_yet or certificate_provided_is_stale: + self.tls.set_relation_certificate( + ProviderCertificate( + relation_id=request.relation_id, + certificate_signing_request=request.certificate_signing_request, + certificate=certificate_chain[0], + ca=certificate_chain[-1], + chain=certificate_chain, + ) + ) + ## Properties ## @property def _pebble_layer(self) -> ops.pebble.LayerDict: diff --git a/src/notary.py b/src/notary.py index 60ed290..c7f92f9 100644 --- a/src/notary.py +++ b/src/notary.py @@ -4,6 +4,8 @@ """Library for interacting with the Notary application.""" import logging +from dataclasses import dataclass +from typing import Literal import requests @@ -14,12 +16,28 @@ class NotaryClientError(Exception): """Base class for exceptions raised by the Notary client.""" +@dataclass(frozen=True) +class CertificateRequest: + """The certificate request that's stored in Notary.""" + + id: int + csr: str + certificate_chain: list[str] | Literal["", "rejected"] + + +@dataclass +class CertificateRequests: + """The table of certificate requests in Notary.""" + + rows: list[CertificateRequest] + + class Notary: """Class to interact with Notary.""" API_VERSION = "v1" - def __init__(self, url: str, ca_path: str) -> None: + def __init__(self, url: str, ca_path: str | bool = False) -> None: """Initialize a client for interacting with Notary. Args: @@ -34,7 +52,7 @@ def login(self, username: str, password: str) -> str | None: try: req = requests.post( f"{self.url}/login", - verify=self.ca_path if self.ca_path else None, + verify=self.ca_path, json={"username": username, "password": password}, ) except (requests.RequestException, OSError): @@ -51,8 +69,8 @@ def token_is_valid(self, token: str) -> bool: """Return if the token is still valid by attempting to connect to an endpoint.""" try: req = requests.get( - f"{self.url}/accounts", - verify=self.ca_path if self.ca_path else None, + f"{self.url}/api/{self.API_VERSION}/accounts/me", + verify=self.ca_path, headers={"Authorization": f"Bearer {token}"}, ) req.raise_for_status() @@ -65,7 +83,7 @@ def is_api_available(self) -> bool: try: req = requests.get( f"{self.url}/status", - verify=self.ca_path if self.ca_path else None, + verify=self.ca_path, ) req.raise_for_status() except (requests.RequestException, OSError): @@ -77,7 +95,7 @@ def is_initialized(self) -> bool: try: req = requests.get( f"{self.url}/status", - verify=self.ca_path if self.ca_path else None, + verify=self.ca_path, ) req.raise_for_status() except (requests.RequestException, OSError): @@ -100,7 +118,7 @@ def create_first_user(self, username: str, password: str) -> int | None: try: req = requests.post( f"{self.url}/api/{self.API_VERSION}/accounts", - verify=self.ca_path if self.ca_path else None, + verify=self.ca_path, json={"username": username, "password": password}, ) except (requests.RequestException, OSError): @@ -108,8 +126,105 @@ def create_first_user(self, username: str, password: str) -> int | None: try: req.raise_for_status() except requests.HTTPError: - logger.warning("couldn't create first user: code %s, %s", req.status_code, req.text) + logger.error("couldn't create first user: code %s, %s", req.status_code, req.text) return None logger.info("created the first user in Notary.") id = req.json().get("id") return int(id) if id else None + + def get_certificate_requests_table(self, token: str) -> CertificateRequests | None: + """Get all certificate requests table from Notary. + + Returns: + None if the request fails to go through. The table itself, otherwise. + """ + try: + res = requests.get( + f"{self.url}/api/{self.API_VERSION}/certificate_requests", + verify=self.ca_path, + headers={"Authorization": f"Bearer {token}"}, + ) + res.raise_for_status() + except requests.RequestException as e: + logger.error( + "couldn't retrieve certificate requests table: code %s, %s", + e.response.status_code if e.response else "unknown", + e.response.text if e.response else "unknown", + ) + return None + except OSError: + logger.error("error occurred during HTTP request: TLS file invalid") + return None + table = res.json() + return CertificateRequests( + rows=[ + CertificateRequest( + row.get("id"), + row.get("csr"), + serialize(row.get("certificate")), + ) + for row in table + ] + if table + else [] + ) + + def post_csr(self, csr: str, token: str) -> None: + """Post a new CSR to Notary.""" + try: + res = requests.post( + f"{self.url}/api/{self.API_VERSION}/certificate_requests", + verify=self.ca_path, + headers={"Authorization": f"Bearer {token}"}, + data=csr, + ) + res.raise_for_status() + except requests.RequestException as e: + logger.error( + "couldn't post new certificate requests: code %s, %s", + e.response.status_code if e.response else "unknown", + e.response.text if e.response else "unknown", + ) + except OSError: + logger.error("error occurred during HTTP request: TLS file invalid") + + def post_certificate(self, csr: str, cert_chain: list[str], token: str) -> None: + """Post a certificate chain to an associated csr to Notary.""" + try: + table = self.get_certificate_requests_table(token) + if not table: + return + csr_ids = list(filter(lambda x: x.csr == csr, table.rows)) + if len(csr_ids) != 1: + logger.error("given CSR not found in Notary") + return + res = requests.post( + f"{self.url}/api/{self.API_VERSION}/certificate_requests/{csr_ids[0].id}/certificate", + verify=self.ca_path, + headers={"Authorization": f"Bearer {token}"}, + data="\n".join(cert_chain), + ) + res.raise_for_status() + except requests.RequestException as e: + logger.error( + "couldn't post new certificate: code %s, %s", + e.response.status_code if e.response else "unknown", + e.response.text if e.response else "unknown", + ) + except OSError: + logger.error("error occurred during HTTP request: TLS file invalid") + + +def serialize(pem_string: str) -> list[str] | Literal["", "rejected"]: + """Process the certificate entry coming from Notary. + + Returns: + a list of pem strings, an empty string or a rejected string. + """ + if pem_string != "" and pem_string != "rejected": + return [ + cert.strip() + "-----END CERTIFICATE-----" + for cert in pem_string.split("-----END CERTIFICATE-----") + if cert.strip() + ] + return pem_string diff --git a/tests/integration/test_charm.py b/tests/integration/test_charm.py index 89f8118..c5eca2c 100644 --- a/tests/integration/test_charm.py +++ b/tests/integration/test_charm.py @@ -3,13 +3,25 @@ # See LICENSE file for licensing details. import asyncio +import json import logging +from base64 import b64decode from pathlib import Path import pytest import yaml +from charms.tls_certificates_interface.v4.tls_certificates import ( + CertificateSigningRequest, + generate_ca, + generate_certificate, + generate_private_key, +) +from juju.client.client import SecretsFilter from pytest_operator.plugin import OpsTest +from charm import NOTARY_LOGIN_SECRET_LABEL +from notary import Notary + logger = logging.getLogger(__name__) CHARMCRAFT = yaml.safe_load(Path("./charmcraft.yaml").read_text()) @@ -17,22 +29,77 @@ LOKI_APPLICATION_NAME = "loki-k8s" PROMETHEUS_APPLICATION_NAME = "prometheus-k8s" +TLS_REQUIRER_APPLICATION_NAME = "tls-certificates-requirer" @pytest.mark.abort_on_fail -async def test_build_and_deploy(ops_test: OpsTest, request): +async def test_build_and_deploy(ops_test: OpsTest, request: pytest.FixtureRequest): """Build the charm-under-test and deploy it together with related charms. Assert on the unit status before any relations/configurations take place. """ - charm = Path(request.config.getoption("--charm_path")).resolve() + charm = Path(request.config.getoption("--charm_path")).resolve() # type: ignore resources = {"notary-image": CHARMCRAFT["resources"]["notary-image"]["upstream-source"]} - # Deploy the charm and wait for active status - await asyncio.gather( - ops_test.model.deploy(charm, resources=resources, application_name=APP_NAME), - ops_test.model.wait_for_idle(apps=[APP_NAME], status="active", timeout=1000), + assert ops_test.model + await ops_test.model.deploy(charm, resources=resources, application_name=APP_NAME) + await ops_test.model.wait_for_idle(apps=[APP_NAME], status="active", timeout=1000) + + +@pytest.mark.abort_on_fail +async def test_given_notary_when_tls_requirer_related_then_csr_uploaded_to_notary_and_certificate_provided_to_requirer( + ops_test: OpsTest, +): + assert ops_test.model + admin_credentials = await get_notary_credentials(ops_test) + token = admin_credentials.get("token") + assert token + endpoint = await get_notary_endpoint(ops_test) + client = Notary(url=endpoint) + assert client.token_is_valid(token) + + await ops_test.model.deploy( + "tls-certificates-requirer", + application_name=TLS_REQUIRER_APPLICATION_NAME, + channel="edge", + trust=True, ) + await ops_test.model.integrate( + relation1=f"{APP_NAME}:certificates", + relation2=f"{TLS_REQUIRER_APPLICATION_NAME}", + ) + await ops_test.model.wait_for_idle( + apps=[APP_NAME, TLS_REQUIRER_APPLICATION_NAME], + status="active", + timeout=1000, + raise_on_error=True, + ) + table = client.get_certificate_requests_table(token) + assert table + assert len(table.rows) == 1 + + row = table.rows[0] + ca_pk = generate_private_key() + ca = generate_ca(ca_pk, 365, "integration-test") + cert = generate_certificate(CertificateSigningRequest.from_string(row.csr), ca, ca_pk, 365) + chain = [str(cert), str(ca)] + client.post_certificate(row.csr, chain, token) + + table = client.get_certificate_requests_table(token) + assert table + assert table.rows[0].certificate_chain != "" + assert table.rows[0].certificate_chain != "rejected" + + await ops_test.model.wait_for_idle( + apps=[APP_NAME, TLS_REQUIRER_APPLICATION_NAME], + status="active", + timeout=1000, + raise_on_error=True, + ) + + action_result = await run_get_certificate_action(ops_test) + given_certificate: str = json.loads(action_result)[0].get("certificate", "") + assert given_certificate.replace("\n", "") == str(cert).replace("\n", "") @pytest.mark.abort_on_fail @@ -40,6 +107,7 @@ async def test_given_loki_and_prometheus_related_to_notary_all_charm_statuses_ac ops_test: OpsTest, ): """Deploy loki and prometheus, and make sure all applications are active.""" + assert ops_test.model deploy_prometheus = ops_test.model.deploy( "prometheus-k8s", application_name=PROMETHEUS_APPLICATION_NAME, @@ -69,3 +137,40 @@ async def test_given_loki_and_prometheus_related_to_notary_all_charm_statuses_ac timeout=1000, raise_on_error=True, ) + + +async def get_notary_endpoint(ops_test: OpsTest) -> str: + assert ops_test.model + status = await ops_test.model.get_status() + notary_ip = status.applications[APP_NAME].units[f"{APP_NAME}/0"].address + return f"https://{notary_ip}:2111" + + +async def get_notary_credentials(ops_test: OpsTest) -> dict[str, str]: + assert ops_test.model + secrets = await ops_test.model.list_secrets( + filter=SecretsFilter(label=NOTARY_LOGIN_SECRET_LABEL), show_secrets=True + ) + return { + field: b64decode(secrets[0].value.data[field]).decode("utf-8") + for field in ["username", "password", "token"] + } + + +async def run_get_certificate_action(ops_test: OpsTest) -> str: + """Run `get-certificate` on the `tls-requirer-requirer/0` unit. + + Args: + ops_test (OpsTest): OpsTest + + Returns: + dict: Action output + """ + assert ops_test.model + tls_requirer_unit = ops_test.model.units[f"{TLS_REQUIRER_APPLICATION_NAME}/0"] + assert tls_requirer_unit + action = await tls_requirer_unit.run_action( + action_name="get-certificate", + ) + action_output = await ops_test.model.get_action_output(action_uuid=action.entity_id, wait=30) + return action_output.get("certificates", "") diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index d972db3..f9232a7 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -6,17 +6,22 @@ import ops import pytest -from scenario import Container, Context, Mount, Network, State, Storage +from scenario import Container, Context, Mount, Network, Relation, Secret, State, Storage -from charm import NotaryCharm +from charm import CERTIFICATE_PROVIDER_RELATION_NAME, NOTARY_LOGIN_SECRET_LABEL, NotaryCharm from lib.charms.tls_certificates_interface.v4.tls_certificates import ( Certificate, PrivateKey, + ProviderCertificate, + RequirerCSR, generate_ca, generate_certificate, generate_csr, generate_private_key, ) +from notary import CertificateRequest, CertificateRequests + +TLS_LIB_PATH = "charms.tls_certificates_interface.v4.tls_certificates" CERTIFICATE_COMMON_NAME = "Notary Self Signed Certificate" SELF_SIGNED_CA_COMMON_NAME = "Notary Self Signed Root CA" @@ -54,14 +59,14 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -72,14 +77,14 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -90,14 +95,14 @@ def test_given_storages_available_container_cant_connect_network_not_available_n state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -108,14 +113,14 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -126,14 +131,14 @@ def test_given_only_database_storage_container_can_connect_network_not_available state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -144,21 +149,21 @@ def test_given_storages_available_container_can_connect_network_not_available_no state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.config_changed(), state) root = out.get_container("notary").get_filesystem(context) assert (root / "etc/notary/config/config.yaml").open("r") assert not (root / "etc/notary/config/certificate.pem").exists() - assert not ((root / "etc/notary/config/private_key.pem").exists()) + assert not (root / "etc/notary/config/private_key.pem").exists() assert len(out.secrets) == 1 assert out.get_secret(label="Notary Login Details") @@ -175,7 +180,7 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -193,7 +198,7 @@ def test_given_only_database_storage_container_cant_connect_network_available_no with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -211,7 +216,7 @@ def test_given_storages_available_container_cant_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -229,7 +234,7 @@ def test_given_only_config_storage_container_can_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -247,7 +252,7 @@ def test_given_only_database_storage_container_can_connect_network_available_not with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -265,7 +270,7 @@ def test_given_storages_available_container_can_connect_network_available_notary with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.config_changed(), state) @@ -290,14 +295,14 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -308,14 +313,14 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -326,14 +331,14 @@ def test_given_storages_available_container_cant_connect_network_not_available_n state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -344,14 +349,14 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -362,14 +367,14 @@ def test_given_only_database_storage_container_can_connect_network_not_available state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -380,14 +385,14 @@ def test_given_storages_available_container_can_connect_network_not_available_no state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.config_changed(), state) @@ -411,7 +416,7 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -429,7 +434,7 @@ def test_given_only_database_storage_container_cant_connect_network_available_no with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -447,7 +452,7 @@ def test_given_storages_available_container_cant_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -465,7 +470,7 @@ def test_given_only_config_storage_container_can_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -483,7 +488,7 @@ def test_given_only_database_storage_container_can_connect_network_available_not with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -501,7 +506,7 @@ def test_given_storages_available_container_can_connect_network_available_notary with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -512,14 +517,14 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -530,14 +535,14 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -548,14 +553,14 @@ def test_given_storages_available_container_cant_connect_network_not_available_n state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -566,14 +571,14 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -584,14 +589,14 @@ def test_given_only_database_storage_container_can_connect_network_not_available state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -602,14 +607,14 @@ def test_given_storages_available_container_can_connect_network_not_available_no state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.config_changed(), state) @@ -634,7 +639,7 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -652,7 +657,7 @@ def test_given_only_database_storage_container_cant_connect_network_available_no with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -670,7 +675,7 @@ def test_given_storages_available_container_cant_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -688,7 +693,7 @@ def test_given_only_config_storage_container_can_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -706,7 +711,7 @@ def test_given_only_database_storage_container_can_connect_network_available_not with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -724,7 +729,7 @@ def test_given_storages_available_container_can_connect_network_available_notary with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): context.run(context.on.config_changed(), state) @@ -736,14 +741,14 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -756,14 +761,14 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -775,14 +780,14 @@ def test_given_storages_available_container_cant_connect_network_not_available_n state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -795,14 +800,14 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -814,14 +819,14 @@ def test_given_only_database_storage_container_can_connect_network_not_available state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -833,14 +838,14 @@ def test_given_storages_available_container_can_connect_network_not_available_no state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -859,7 +864,7 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -878,7 +883,7 @@ def test_given_only_database_storage_container_cant_connect_network_available_no with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -897,7 +902,7 @@ def test_given_storages_available_container_cant_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -916,7 +921,7 @@ def test_given_only_config_storage_container_can_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -935,7 +940,7 @@ def test_given_only_database_storage_container_can_connect_network_available_not with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -954,7 +959,7 @@ def test_given_storages_available_container_can_connect_network_available_notary with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": False, "is_initialized.return_value": False}, + **{"is_api_available.return_value": False, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -966,14 +971,14 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -985,14 +990,14 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1004,14 +1009,14 @@ def test_given_storages_available_container_cant_connect_network_not_available_n state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1023,14 +1028,14 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1042,14 +1047,14 @@ def test_given_only_database_storage_container_can_connect_network_not_available state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1061,14 +1066,14 @@ def test_given_storages_available_container_can_connect_network_not_available_no state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1087,7 +1092,7 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1106,7 +1111,7 @@ def test_given_only_database_storage_container_cant_connect_network_available_no with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1125,7 +1130,7 @@ def test_given_storages_available_container_cant_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1144,7 +1149,7 @@ def test_given_only_config_storage_container_can_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1163,7 +1168,7 @@ def test_given_only_database_storage_container_can_connect_network_available_not with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1182,7 +1187,7 @@ def test_given_storages_available_container_can_connect_network_available_notary with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": False}, + **{"is_api_available.return_value": True, "is_initialized.return_value": False}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1194,14 +1199,14 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1213,14 +1218,14 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1232,14 +1237,14 @@ def test_given_storages_available_container_cant_connect_network_not_available_n state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=False)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1251,14 +1256,14 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n state = State( storages={Storage(name="config")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1270,14 +1275,14 @@ def test_given_only_database_storage_container_can_connect_network_not_available state = State( storages={Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1289,14 +1294,14 @@ def test_given_storages_available_container_can_connect_network_not_available_no state = State( storages={Storage(name="config"), Storage(name="database")}, containers={Container(name="notary", can_connect=True)}, - networks={Network("juju-info", [], ingress_addresses=[], egress_subnets=[])}, + networks={Network("juju-info", bind_addresses=[])}, leader=True, ) with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1315,7 +1320,7 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1334,7 +1339,7 @@ def test_given_only_database_storage_container_cant_connect_network_available_no with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1353,7 +1358,7 @@ def test_given_storages_available_container_cant_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1372,7 +1377,7 @@ def test_given_only_config_storage_container_can_connect_network_available_notar with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1391,7 +1396,7 @@ def test_given_only_database_storage_container_can_connect_network_available_not with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1410,7 +1415,7 @@ def test_given_storages_available_container_can_connect_network_available_notary with patch( "notary.Notary", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1437,7 +1442,7 @@ def test_given_notary_available_and_initialized_when_collect_status_then_status_ with patch( "notary.Notary.__new__", return_value=Mock( - **{"is_api_available.return_value": True, "is_initialized.return_value": True}, + **{"is_api_available.return_value": True, "is_initialized.return_value": True}, # type: ignore ), ): out = context.run(context.on.collect_unit_status(), state) @@ -1472,3 +1477,289 @@ def test_given_notary_available_and_not_initialized_when_configure_then_admin_us assert len(out.secrets) == 1 secret = out.get_secret(label="Notary Login Details") assert secret.latest_content.get("token") == "example-token" + + def test_given_tls_requirer_available_when_notary_unreachable_then_no_error_raised( + self, context + ): + state = State( + storages={Storage(name="config"), Storage(name="database")}, + containers=[Container(name="notary", can_connect=True)], + networks={Network("juju-info")}, + leader=True, + relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], + ) + with patch( + "notary.Notary.__new__", + return_value=Mock( + **{ + "is_api_available.return_value": True, + "is_initialized.return_value": False, + "login.return_value": "example-token", + "token_is_valid.return_value": False, + }, + ), + ): + context.run(context.on.update_status(), state) + + @patch(f"{TLS_LIB_PATH}.TLSCertificatesProvidesV4.get_certificate_requests") + def test_given_tls_requirer_available_when_configure_then_csrs_posted_to_notary( + self, mock_get_certificate_requests, context + ): + state = State( + storages={Storage(name="config"), Storage(name="database")}, + containers=[Container(name="notary", can_connect=True)], + networks={Network("juju-info")}, + leader=True, + relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], + secrets={ + Secret( + {"username": "hello", "password": "world", "token": "test-token"}, + id="1", + label=NOTARY_LOGIN_SECRET_LABEL, + owner="app", + ) + }, + ) + csr = generate_csr(private_key=generate_private_key(), common_name="me") + mock_get_certificate_requests.return_value = [ + RequirerCSR( + relation_id=1, + certificate_signing_request=csr, + ) + ] + post_call = Mock() + with patch( + "notary.Notary.__new__", + return_value=Mock( + **{ + "is_api_available.return_value": True, + "is_initialized.return_value": True, + "token_is_valid.return_value": True, + "get_certificate_requests_table.return_value": CertificateRequests(rows=[]), + "post_csr": post_call, + }, + ), + ): + context.run(context.on.update_status(), state) + + post_call.assert_called_once_with(str(csr), "test-token") + + @patch(f"{TLS_LIB_PATH}.TLSCertificatesProvidesV4.get_certificate_requests") + def test_given_tls_requirers_available_when_csrs_already_posted_then_duplicate_csr_not_posted( + self, mock_get_certificate_requests, context + ): + state = State( + storages={Storage(name="config"), Storage(name="database")}, + containers=[Container(name="notary", can_connect=True)], + networks={Network("juju-info")}, + leader=True, + relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], + secrets={ + Secret( + {"username": "hello", "password": "world", "token": "test-token"}, + id="1", + label=NOTARY_LOGIN_SECRET_LABEL, + owner="app", + ) + }, + ) + csr = generate_csr(private_key=generate_private_key(), common_name="me") + mock_get_certificate_requests.return_value = [ + RequirerCSR( + relation_id=1, + certificate_signing_request=csr, + ) + ] + post_call = Mock() + with patch( + "notary.Notary.__new__", + return_value=Mock( + **{ + "is_api_available.return_value": True, + "is_initialized.return_value": True, + "token_is_valid.return_value": True, + "get_certificate_requests_table.return_value": CertificateRequests( + rows=[CertificateRequest(id=1, csr=str(csr), certificate_chain="")] + ), + "post_csr": post_call, + }, + ), + ): + context.run(context.on.update_status(), state) + + post_call.assert_not_called() + + @patch(f"{TLS_LIB_PATH}.TLSCertificatesProvidesV4.set_relation_certificate") + @patch(f"{TLS_LIB_PATH}.TLSCertificatesProvidesV4.get_certificate_requests") + def test_given_tls_requirers_available_when_certificate_available_then_certs_provided_to_requirer( + self, mock_get_certificate_requests, mock_set_relation_certificate, context + ): + state = State( + storages={Storage(name="config"), Storage(name="database")}, + containers=[Container(name="notary", can_connect=True)], + networks={Network("juju-info")}, + leader=True, + relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], + secrets={ + Secret( + {"username": "hello", "password": "world", "token": "test-token"}, + id="1", + label=NOTARY_LOGIN_SECRET_LABEL, + owner="app", + ) + }, + ) + ca_pk = generate_private_key() + ca = generate_ca(ca_pk, 365, "me") + csr = generate_csr(private_key=generate_private_key(), common_name="notary.com") + cert = generate_certificate(csr, ca, ca_pk, 365) + mock_get_certificate_requests.return_value = [ + RequirerCSR( + relation_id=1, + certificate_signing_request=csr, + ) + ] + with patch( + "notary.Notary.__new__", + return_value=Mock( + **{ + "is_api_available.return_value": True, + "is_initialized.return_value": True, + "token_is_valid.return_value": True, + "get_certificate_requests_table.return_value": CertificateRequests( + rows=[ + CertificateRequest( + id=1, csr=str(csr), certificate_chain=[str(cert), str(ca)] + ) + ] + ), + }, + ), + ): + context.run(context.on.update_status(), state) + mock_set_relation_certificate.assert_called_once() + + @patch(f"{TLS_LIB_PATH}.TLSCertificatesProvidesV4.get_issued_certificates") + @patch(f"{TLS_LIB_PATH}.TLSCertificatesProvidesV4.set_relation_certificate") + @patch(f"{TLS_LIB_PATH}.TLSCertificatesProvidesV4.get_certificate_requests") + def test_given_tls_requirers_when_invalid_certificate_available_when_configure_then_new_cert_provided( + self, + mock_get_certificate_requests, + mock_set_relation_certificate, + mock_get_issued_certificates, + context, + ): + state = State( + storages={Storage(name="config"), Storage(name="database")}, + containers=[Container(name="notary", can_connect=True)], + networks={Network("juju-info")}, + leader=True, + relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], + secrets={ + Secret( + {"username": "hello", "password": "world", "token": "test-token"}, + id="1", + label=NOTARY_LOGIN_SECRET_LABEL, + owner="app", + ) + }, + ) + ca_pk = generate_private_key() + ca = generate_ca(ca_pk, 365, "me") + csr = generate_csr(private_key=generate_private_key(), common_name="notary.com") + old_cert = generate_certificate(csr, ca, ca_pk, 365) + new_cert = generate_certificate(csr, ca, ca_pk, 366) + mock_get_certificate_requests.return_value = [ + RequirerCSR( + relation_id=1, + certificate_signing_request=csr, + ) + ] + mock_get_issued_certificates.return_value = [ + ProviderCertificate( + relation_id=1, + certificate_signing_request=csr, + certificate=old_cert, + ca=ca, + chain=[old_cert, ca], + ) + ] + with patch( + "notary.Notary.__new__", + return_value=Mock( + **{ + "is_api_available.return_value": True, + "is_initialized.return_value": True, + "token_is_valid.return_value": True, + "get_certificate_requests_table.return_value": CertificateRequests( + rows=[ + CertificateRequest( + id=1, csr=str(csr), certificate_chain=[str(new_cert), str(ca)] + ) + ] + ), + }, + ), + ): + context.run(context.on.update_status(), state) + mock_set_relation_certificate.assert_called_once() + + @patch(f"{TLS_LIB_PATH}.TLSCertificatesProvidesV4.get_issued_certificates") + @patch(f"{TLS_LIB_PATH}.TLSCertificatesProvidesV4.set_relation_certificate") + @patch(f"{TLS_LIB_PATH}.TLSCertificatesProvidesV4.get_certificate_requests") + def test_given_certificate_rejected_in_notary_when_configure_then_certificate_revoked( + self, + mock_get_certificate_requests, + mock_set_relation_certificate, + mock_get_issued_certificates, + context, + ): + state = State( + storages={Storage(name="config"), Storage(name="database")}, + containers=[Container(name="notary", can_connect=True)], + networks={Network("juju-info")}, + leader=True, + relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], + secrets=[ + Secret( + {"username": "hello", "password": "world", "token": "test-token"}, + id="1", + label=NOTARY_LOGIN_SECRET_LABEL, + owner="app", + ) + ], + ) + ca_pk = generate_private_key() + ca = generate_ca(ca_pk, 365, "me") + csr = generate_csr(private_key=generate_private_key(), common_name="notary.com") + old_cert = generate_certificate(csr, ca, ca_pk, 365) + mock_get_certificate_requests.return_value = [ + RequirerCSR( + relation_id=1, + certificate_signing_request=csr, + ) + ] + mock_get_issued_certificates.return_value = [ + ProviderCertificate( + relation_id=1, + certificate_signing_request=csr, + certificate=old_cert, + ca=ca, + chain=[old_cert, ca], + ) + ] + with patch( + "notary.Notary.__new__", + return_value=Mock( + **{ + "is_api_available.return_value": True, + "is_initialized.return_value": True, + "token_is_valid.return_value": True, + "get_certificate_requests_table.return_value": CertificateRequests( + rows=[CertificateRequest(id=1, csr=str(csr), certificate_chain="rejected")] + ), + }, + ), + ): + context.run(context.on.update_status(), state) + mock_set_relation_certificate.assert_called_once() From 54d63dd57018ec27b84148f516806824819e3201 Mon Sep 17 00:00:00 2001 From: Kayra Date: Fri, 13 Sep 2024 16:14:00 +0300 Subject: [PATCH 07/11] chore: build for arm (#28) --- charmcraft.yaml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/charmcraft.yaml b/charmcraft.yaml index 1884522..e9d887e 100644 --- a/charmcraft.yaml +++ b/charmcraft.yaml @@ -18,6 +18,10 @@ links: website: - https://charmhub.io/notary-k8s +base: ubuntu@24.04 +platforms: + amd64: + arm64: provides: certificates: @@ -31,14 +35,6 @@ requires: logging: interface: loki_push_api -bases: - - build-on: - - name: ubuntu - channel: "22.04" - run-on: - - name: ubuntu - channel: "22.04" - containers: notary: resource: notary-image From e68cc151333e69e170ba27377fbeb94a8572c296 Mon Sep 17 00:00:00 2001 From: Kayra Date: Fri, 13 Sep 2024 16:49:33 +0300 Subject: [PATCH 08/11] chore: simplify config file generation (#37) --- src/charm.py | 14 ++++++++++++-- src/config/config.yaml | 5 ----- 2 files changed, 12 insertions(+), 7 deletions(-) delete mode 100644 src/config/config.yaml diff --git a/src/charm.py b/src/charm.py index 6cbcbed..9fc1621 100755 --- a/src/charm.py +++ b/src/charm.py @@ -11,6 +11,7 @@ from dataclasses import dataclass import ops +import yaml from charms.grafana_k8s.v0.grafana_dashboard import GrafanaDashboardProvider from charms.loki_k8s.v1.loki_push_api import LogForwarder from charms.prometheus_k8s.v0.prometheus_scrape import MetricsEndpointProvider @@ -38,6 +39,7 @@ CONFIG_MOUNT = "config" CHARM_PATH = "/var/lib/juju/storage" WORKLOAD_CONFIG_PATH = "/etc/notary" +WORKLOAD_DB_PATH = "/var/lib" CERTIFICATE_COMMON_NAME = "Notary Self Signed Certificate" SELF_SIGNED_CA_COMMON_NAME = "Notary Self Signed Root CA" @@ -147,10 +149,18 @@ def _configure_notary_config_file(self): self.container.pull(f"{WORKLOAD_CONFIG_PATH}/config/config.yaml") logger.info("Config file already created.") except ops.pebble.PathError: - config_file = open("src/config/config.yaml").read() self.container.make_dir(path=f"{WORKLOAD_CONFIG_PATH}/config", make_parents=True) self.container.push( - path=f"{WORKLOAD_CONFIG_PATH}/config/config.yaml", source=config_file + path=f"{WORKLOAD_CONFIG_PATH}/config/config.yaml", + source=yaml.dump( + data={ + "key_path": f"{WORKLOAD_CONFIG_PATH}/config/private_key.pem", + "cert_path": f"{WORKLOAD_CONFIG_PATH}/config/certificate.pem", + "db_path": f"{WORKLOAD_DB_PATH}/notary/database/certs.db", + "port": self.port, + "pebble_notifications": True, + } + ), ) logger.info("Config file created.") diff --git a/src/config/config.yaml b/src/config/config.yaml deleted file mode 100644 index 5f3165b..0000000 --- a/src/config/config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -key_path: "/etc/notary/config/private_key.pem" -cert_path: "/etc/notary/config/certificate.pem" -db_path: "/var/lib/notary/database/certs.db" -port: 2111 -pebble_notifications: true \ No newline at end of file From f407855482ffe5f6d71bcc76d2119481c7afe742 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 09:52:22 +0300 Subject: [PATCH 09/11] chore: bump the pip_dependencies group with 6 updates (#39) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 8 +++----- test-requirements.txt | 8 ++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index de7a129..b64ba61 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ cffi==1.17.0 # via cryptography charset-normalizer==3.3.2 # via requests -cosl==0.0.31 +cosl==0.0.33 # via -r requirements.in cryptography==43.0.1 # via -r requirements.in @@ -42,11 +42,11 @@ ops==2.16.1 # cosl pycparser==2.22 # via cffi -pydantic==2.9.0 +pydantic==2.9.1 # via # -r requirements.in # cosl -pydantic-core==2.23.2 +pydantic-core==2.23.3 # via pydantic pyyaml==6.0.2 # via @@ -68,8 +68,6 @@ typing-extensions==4.12.2 # cosl # pydantic # pydantic-core -tzdata==2024.1 - # via pydantic urllib3==2.2.2 # via requests websocket-client==1.8.0 diff --git a/test-requirements.txt b/test-requirements.txt index 2e43b71..2e020e7 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -70,7 +70,7 @@ oauthlib==3.2.2 # requests-oauthlib ops==2.16.1 # via ops-scenario -ops-scenario==7.0.1 +ops-scenario==7.0.2 # via -r test-requirements.in packaging==24.1 # via @@ -114,9 +114,9 @@ pyrfc3339==1.1 # via # juju # macaroonbakery -pyright==1.1.379 +pyright==1.1.380 # via -r test-requirements.in -pytest==8.3.2 +pytest==8.3.3 # via # -r test-requirements.in # pytest-asyncio @@ -146,7 +146,7 @@ requests-oauthlib==2.0.0 # via kubernetes rsa==4.9 # via google-auth -ruff==0.6.4 +ruff==0.6.5 # via -r test-requirements.in six==1.16.0 # via From 5a96d1b33fa05ccecb6217eb5e322c287004ab1a Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Mon, 16 Sep 2024 07:07:59 -0400 Subject: [PATCH 10/11] chore: remove broken arm support (#38) Signed-off-by: guillaume --- .github/workflows/build.yaml | 2 -- .github/workflows/publish-charm.yaml | 3 +-- charmcraft.yaml | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 448f159..1d67a58 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -10,8 +10,6 @@ jobs: arch: - arch: amd64 runner: ubuntu-22.04 - - arch: arm64 - runner: [self-hosted, linux, ARM64, medium, jammy] runs-on: ${{ matrix.arch.runner }} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-charm.yaml b/.github/workflows/publish-charm.yaml index 50eb9b4..0e24778 100644 --- a/.github/workflows/publish-charm.yaml +++ b/.github/workflows/publish-charm.yaml @@ -13,8 +13,7 @@ jobs: arch: - arch: amd64 runner: ubuntu-22.04 - - arch: arm64 - runner: [self-hosted, linux, ARM64, medium, jammy] + runs-on: ${{ matrix.arch.runner }} steps: - name: Checkout diff --git a/charmcraft.yaml b/charmcraft.yaml index e9d887e..83f41d9 100644 --- a/charmcraft.yaml +++ b/charmcraft.yaml @@ -21,7 +21,6 @@ links: base: ubuntu@24.04 platforms: amd64: - arm64: provides: certificates: From fc3ee4b7af3885c45045c1a79889b91c585b3cdb Mon Sep 17 00:00:00 2001 From: Kayra Date: Mon, 16 Sep 2024 14:40:07 +0300 Subject: [PATCH 11/11] test: add pebble layer mock to scenario state (#40) --- tests/unit/test_charm.py | 1795 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 1717 insertions(+), 78 deletions(-) diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index f9232a7..c004de2 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -6,6 +6,7 @@ import ops import pytest +from ops.pebble import Layer from scenario import Container, Context, Mount, Network, Relation, Secret, State, Storage from charm import CERTIFICATE_PROVIDER_RELATION_NAME, NOTARY_LOGIN_SECRET_LABEL, NotaryCharm @@ -58,7 +59,28 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -76,7 +98,28 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -94,7 +137,28 @@ def test_given_storages_available_container_cant_connect_network_not_available_n ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -112,7 +176,28 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -130,7 +215,28 @@ def test_given_only_database_storage_container_can_connect_network_not_available ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -148,7 +254,28 @@ def test_given_storages_available_container_can_connect_network_not_available_no ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -172,7 +299,28 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -190,7 +338,28 @@ def test_given_only_database_storage_container_cant_connect_network_available_no ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -208,7 +377,28 @@ def test_given_storages_available_container_cant_connect_network_available_notar ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -226,7 +416,28 @@ def test_given_only_config_storage_container_can_connect_network_available_notar ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -244,7 +455,28 @@ def test_given_only_database_storage_container_can_connect_network_available_not ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -262,7 +494,28 @@ def test_given_storages_available_container_can_connect_network_available_notary ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -294,7 +547,28 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -312,7 +586,28 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -330,7 +625,28 @@ def test_given_storages_available_container_cant_connect_network_not_available_n ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -348,7 +664,28 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -366,7 +703,28 @@ def test_given_only_database_storage_container_can_connect_network_not_available ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -384,7 +742,28 @@ def test_given_storages_available_container_can_connect_network_not_available_no ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -408,7 +787,28 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -426,7 +826,28 @@ def test_given_only_database_storage_container_cant_connect_network_available_no ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -444,7 +865,28 @@ def test_given_storages_available_container_cant_connect_network_available_notar ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -462,7 +904,28 @@ def test_given_only_config_storage_container_can_connect_network_available_notar ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -480,7 +943,28 @@ def test_given_only_database_storage_container_can_connect_network_available_not ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -498,7 +982,28 @@ def test_given_storages_available_container_can_connect_network_available_notary ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -516,7 +1021,28 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -534,7 +1060,28 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -552,7 +1099,28 @@ def test_given_storages_available_container_cant_connect_network_not_available_n ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -570,7 +1138,28 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -588,7 +1177,28 @@ def test_given_only_database_storage_container_can_connect_network_not_available ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -606,7 +1216,28 @@ def test_given_storages_available_container_can_connect_network_not_available_no ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -631,7 +1262,28 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -649,7 +1301,28 @@ def test_given_only_database_storage_container_cant_connect_network_available_no ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -667,7 +1340,28 @@ def test_given_storages_available_container_cant_connect_network_available_notar ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -685,7 +1379,28 @@ def test_given_only_config_storage_container_can_connect_network_available_notar ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -703,7 +1418,28 @@ def test_given_only_database_storage_container_can_connect_network_available_not ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -721,7 +1457,28 @@ def test_given_storages_available_container_can_connect_network_available_notary ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -740,7 +1497,28 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -760,7 +1538,28 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -779,7 +1578,28 @@ def test_given_storages_available_container_cant_connect_network_not_available_n ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -799,7 +1619,28 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -818,7 +1659,28 @@ def test_given_only_database_storage_container_can_connect_network_not_available ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -837,7 +1699,28 @@ def test_given_storages_available_container_can_connect_network_not_available_no ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -856,7 +1739,28 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -875,7 +1779,28 @@ def test_given_only_database_storage_container_cant_connect_network_available_no ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -894,7 +1819,28 @@ def test_given_storages_available_container_cant_connect_network_available_notar ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -913,7 +1859,28 @@ def test_given_only_config_storage_container_can_connect_network_available_notar ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -932,7 +1899,28 @@ def test_given_only_database_storage_container_can_connect_network_available_not ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -951,7 +1939,28 @@ def test_given_storages_available_container_can_connect_network_available_notary ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -970,7 +1979,28 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -989,7 +2019,28 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1008,7 +2059,28 @@ def test_given_storages_available_container_cant_connect_network_not_available_n ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1027,7 +2099,28 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1046,7 +2139,28 @@ def test_given_only_database_storage_container_can_connect_network_not_available ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1065,7 +2179,28 @@ def test_given_storages_available_container_can_connect_network_not_available_no ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1084,7 +2219,28 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1103,7 +2259,28 @@ def test_given_only_database_storage_container_cant_connect_network_available_no ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1122,7 +2299,28 @@ def test_given_storages_available_container_cant_connect_network_available_notar ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1141,7 +2339,28 @@ def test_given_only_config_storage_container_can_connect_network_available_notar ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1160,7 +2379,28 @@ def test_given_only_database_storage_container_can_connect_network_available_not ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1179,7 +2419,28 @@ def test_given_storages_available_container_can_connect_network_available_notary ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1198,7 +2459,28 @@ def test_given_only_config_storage_container_cant_connect_network_not_available_ ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1217,7 +2499,28 @@ def test_given_only_database_storage_container_cant_connect_network_not_availabl ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1236,7 +2539,28 @@ def test_given_storages_available_container_cant_connect_network_not_available_n ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1255,7 +2579,28 @@ def test_given_only_config_storage_container_can_connect_network_not_available_n ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1274,7 +2619,28 @@ def test_given_only_database_storage_container_can_connect_network_not_available ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1293,7 +2659,28 @@ def test_given_storages_available_container_can_connect_network_not_available_no ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info", bind_addresses=[])}, leader=True, ) @@ -1312,7 +2699,28 @@ def test_given_only_config_storage_container_cant_connect_network_available_nota ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1331,7 +2739,28 @@ def test_given_only_database_storage_container_cant_connect_network_available_no ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1350,7 +2779,28 @@ def test_given_storages_available_container_cant_connect_network_available_notar ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=False)}, + containers={ + Container( + name="notary", + can_connect=False, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1369,7 +2819,28 @@ def test_given_only_config_storage_container_can_connect_network_available_notar ): state = State( storages={Storage(name="config")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1388,7 +2859,28 @@ def test_given_only_database_storage_container_can_connect_network_available_not ): state = State( storages={Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1407,7 +2899,28 @@ def test_given_storages_available_container_can_connect_network_available_notary ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers={Container(name="notary", can_connect=True)}, + containers={ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + }, networks={Network("juju-info")}, leader=True, ) @@ -1483,7 +2996,28 @@ def test_given_tls_requirer_available_when_notary_unreachable_then_no_error_rais ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers=[Container(name="notary", can_connect=True)], + containers=[ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + ], networks={Network("juju-info")}, leader=True, relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], @@ -1507,7 +3041,28 @@ def test_given_tls_requirer_available_when_configure_then_csrs_posted_to_notary( ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers=[Container(name="notary", can_connect=True)], + containers=[ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + ], networks={Network("juju-info")}, leader=True, relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], @@ -1550,7 +3105,28 @@ def test_given_tls_requirers_available_when_csrs_already_posted_then_duplicate_c ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers=[Container(name="notary", can_connect=True)], + containers=[ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + ], networks={Network("juju-info")}, leader=True, relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], @@ -1596,7 +3172,28 @@ def test_given_tls_requirers_available_when_certificate_available_then_certs_pro ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers=[Container(name="notary", can_connect=True)], + containers=[ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + ], networks={Network("juju-info")}, leader=True, relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], @@ -1651,7 +3248,28 @@ def test_given_tls_requirers_when_invalid_certificate_available_when_configure_t ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers=[Container(name="notary", can_connect=True)], + containers=[ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + ], networks={Network("juju-info")}, leader=True, relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)], @@ -1716,7 +3334,28 @@ def test_given_certificate_rejected_in_notary_when_configure_then_certificate_re ): state = State( storages={Storage(name="config"), Storage(name="database")}, - containers=[Container(name="notary", can_connect=True)], + containers=[ + Container( + name="notary", + can_connect=True, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + ], networks={Network("juju-info")}, leader=True, relations=[Relation(id=1, endpoint=CERTIFICATE_PROVIDER_RELATION_NAME)],