Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DPE-2780] Test shard for unpermitted relations #293

Merged
merged 8 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ jobs:
- backup-integration
- metric-integration
- sharding-integration
- sharding-relation-integration
name: ${{ matrix.tox-environments }}
needs:
- lint
Expand Down
11 changes: 11 additions & 0 deletions tests/integration/dummy_legacy_app/charmcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.

type: charm
bases:
- build-on:
- name: "ubuntu"
channel: "20.04"
run-on:
- name: "ubuntu"
channel: "20.04"
14 changes: 14 additions & 0 deletions tests/integration/dummy_legacy_app/metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.
name: legacy-application
description: |
Dummy legacy application charm used in integration tests.
summary: |
Dummy legacy application charm meant only to be used in non-legacy integration tests. Legacy
integration tests should use a production legacy application.
series:
- focal

requires:
obsolete:
interface: mongodb
1 change: 1 addition & 0 deletions tests/integration/dummy_legacy_app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ops >= 1.4.0
38 changes: 38 additions & 0 deletions tests/integration/dummy_legacy_app/src/charm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.

"""Application charm that connects to database charms.

This charm is meant to be used only for testing
of the libraries in this repository.
"""

import logging

from ops.charm import CharmBase
from ops.main import main
from ops.model import ActiveStatus

logger = logging.getLogger(__name__)

# Extra roles that this application needs when interacting with the database.
EXTRA_USER_ROLES = "admin"


class ApplicationCharm(CharmBase):
"""Application charm that connects to database charms."""

def __init__(self, *args):
super().__init__(*args)

# Default charm events.
self.framework.observe(self.on.start, self._on_start)

def _on_start(self, _) -> None:
"""Only sets an Active status."""
self.unit.status = ActiveStatus()


if __name__ == "__main__":
main(ApplicationCharm)
33 changes: 33 additions & 0 deletions tests/integration/sharding_tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.

import pytest
from pytest_operator.plugin import OpsTest


@pytest.fixture(scope="module")
async def legacy_charm(ops_test: OpsTest):
"""Build an application charm that uses the legacy interface.

Note: this should only be used outside of the legacy integration tests, as those tests should
test a real legacy application.
"""
charm_path = "tests/integration/dummy_legacy_app"
charm = await ops_test.build_charm(charm_path)
return charm


@pytest.fixture(scope="module")
async def application_charm(ops_test: OpsTest):
"""Build the application charm."""
charm_path = "tests/integration/relation_tests/new_relations/application-charm"
charm = await ops_test.build_charm(charm_path)
return charm


@pytest.fixture(scope="module")
async def database_charm(ops_test: OpsTest):
"""Build the database charm."""
charm = await ops_test.build_charm(".")
return charm
MiaAltieri marked this conversation as resolved.
Show resolved Hide resolved
140 changes: 140 additions & 0 deletions tests/integration/sharding_tests/test_sharding_relations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/env python3
# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.
import pytest
from juju.errors import JujuAPIError
from pytest_operator.plugin import OpsTest

SHARD_ONE_APP_NAME = "shard-one"
CONFIG_SERVER_ONE_APP_NAME = "config-server-one"
CONFIG_SERVER_TWO_APP_NAME = "config-server-two"
APP_CHARM_NAME = "application"
LEGACY_APP_CHARM_NAME = "legacy-application"

CONFIG_SERVER_REL_NAME = "config-server"
SHARD_REL_NAME = "sharding"
DATABASE_REL_NAME = "first-database"
LEGACY_RELATION_NAME = "obsolete"

RELATION_LIMIT_MESSAGE = 'cannot add relation "shard-one:sharding config-server-two:config-server": establishing a new relation for shard-one:sharding would exceed its maximum relation limit of 1'
# for now we have a large timeout due to the slow drainage of the `config.system.sessions`
# collection. More info here:
# https://stackoverflow.com/questions/77364840/mongodb-slow-chunk-migration-for-collection-config-system-sessions-with-remov
TIMEOUT = 30 * 60


@pytest.mark.abort_on_fail
async def test_build_and_deploy(
ops_test: OpsTest, application_charm, legacy_charm, database_charm
) -> None:
"""Build and deploy a sharded cluster."""
await ops_test.model.deploy(
database_charm,
config={"role": "config-server"},
application_name=CONFIG_SERVER_ONE_APP_NAME,
)
await ops_test.model.deploy(
database_charm,
config={"role": "config-server"},
application_name=CONFIG_SERVER_TWO_APP_NAME,
)
await ops_test.model.deploy(
database_charm, num_units=1, config={"role": "shard"}, application_name=SHARD_ONE_APP_NAME
)
await ops_test.model.deploy(application_charm, application_name=APP_CHARM_NAME)
await ops_test.model.deploy(legacy_charm, application_name=LEGACY_APP_CHARM_NAME)

await ops_test.model.wait_for_idle(
apps=[
CONFIG_SERVER_ONE_APP_NAME,
CONFIG_SERVER_TWO_APP_NAME,
SHARD_ONE_APP_NAME,
],
idle_period=20,
raise_on_blocked=False,
timeout=TIMEOUT,
)


async def test_only_one_config_server_relation(ops_test: OpsTest) -> None:
"""Verify that a shard can only be related to one config server."""
await ops_test.model.integrate(
f"{SHARD_ONE_APP_NAME}:{SHARD_REL_NAME}",
f"{CONFIG_SERVER_ONE_APP_NAME}:{CONFIG_SERVER_REL_NAME}",
)

with pytest.raises(JujuAPIError) as juju_error:
await ops_test.model.integrate(
f"{SHARD_ONE_APP_NAME}:{SHARD_REL_NAME}",
f"{CONFIG_SERVER_TWO_APP_NAME}:{CONFIG_SERVER_REL_NAME}",
)

assert (
juju_error.value.args[0] == RELATION_LIMIT_MESSAGE
), "Shard can relate to multiple config servers."


async def test_cannot_use_db_relation(ops_test: OpsTest) -> None:
"""Verify that a shard cannot use the DB relation."""
await ops_test.model.integrate(
f"{APP_CHARM_NAME}:{DATABASE_REL_NAME}",
SHARD_ONE_APP_NAME,
)

await ops_test.model.wait_for_idle(
apps=[SHARD_ONE_APP_NAME],
idle_period=20,
raise_on_blocked=False,
timeout=TIMEOUT,
)

shard_unit = ops_test.model.applications[SHARD_ONE_APP_NAME].units[0]
assert (
shard_unit.workload_status_message == "Sharding roles do not support database interface."
), "Shard cannot be related using the database relation"

# clean up relation
await ops_test.model.applications[SHARD_ONE_APP_NAME].remove_relation(
f"{APP_CHARM_NAME}:{DATABASE_REL_NAME}",
SHARD_ONE_APP_NAME,
)

await ops_test.model.wait_for_idle(
apps=[SHARD_ONE_APP_NAME],
idle_period=20,
raise_on_blocked=False,
timeout=TIMEOUT,
)


async def test_cannot_use_legacy_db_relation(ops_test: OpsTest) -> None:
"""Verify that a shard cannot use the legcy DB relation."""
await ops_test.model.integrate(
LEGACY_APP_CHARM_NAME,
SHARD_ONE_APP_NAME,
)

await ops_test.model.wait_for_idle(
apps=[SHARD_ONE_APP_NAME],
idle_period=20,
raise_on_blocked=False,
timeout=TIMEOUT,
)

shard_unit = ops_test.model.applications[SHARD_ONE_APP_NAME].units[0]
assert (
shard_unit.workload_status_message == "Sharding roles do not support obsolete interface."
), "Shard cannot be related using the mongodb relation"

# clean up relation
await ops_test.model.applications[SHARD_ONE_APP_NAME].remove_relation(
f"{SHARD_ONE_APP_NAME}:{LEGACY_RELATION_NAME}",
f"{LEGACY_APP_CHARM_NAME}:{LEGACY_RELATION_NAME}",
)

await ops_test.model.wait_for_idle(
apps=[SHARD_ONE_APP_NAME],
idle_period=20,
raise_on_blocked=False,
timeout=TIMEOUT,
)
MiaAltieri marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 15 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,21 @@ deps =
commands =
pytest -v --tb native --log-cli-level=INFO -s --durations=0 {posargs} {[vars]tests_path}/integration/sharding_tests/test_sharding.py

[testenv:sharding-relation-integration]
description = Run sharding integration tests
pass_env =
{[testenv]pass_env}
CI
deps =
pytest
juju==3.2.0.1
pytest-mock
pytest-operator
git+https://github.com/canonical/[email protected]\#subdirectory=python/pytest_plugins/pytest_operator_cache
-r {tox_root}/requirements.txt
commands =
pytest -v --tb native --log-cli-level=INFO -s --durations=0 {posargs} {[vars]tests_path}/integration/sharding_tests/test_sharding_relations.py


[testenv:integration]
description = Run all integration tests
Expand Down