Skip to content

Commit

Permalink
[DPE-2780] Test shard for unpermitted relations (#293)
Browse files Browse the repository at this point in the history
## Issue
No tests for relations that are not allowed for shards

## Solution
add tests for checking unsupported relations

## extras
create a dummy legacy application. This should NOT be used inside the
legacy integration test suite as those tests should be done with
production legacy charms
  • Loading branch information
MiaAltieri authored Nov 3, 2023
1 parent dcb185a commit a97970f
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 0 deletions.
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
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,
)
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

0 comments on commit a97970f

Please sign in to comment.