diff --git a/.github/workflows/integration_test.yaml b/.github/workflows/integration_test.yaml index 0c59fef..3c8f152 100644 --- a/.github/workflows/integration_test.yaml +++ b/.github/workflows/integration_test.yaml @@ -13,8 +13,10 @@ jobs: pre-run-script: | -c "chmod +x tests/integration/pre_run_script.sh ./tests/integration/pre_run_script.sh" - # self-hosted-runner: true - # self-hosted-runner-label: "edge" - python-version: "3.12" - runs-on: "ubuntu-24.04" + self-hosted-runner: true + self-hosted-runner-label: "edge" modules: '["test_charm", "test_proxy"]' + allure-report: + if: always() && !cancelled() + needs: integration-tests + uses: canonical/operator-workflows/.github/workflows/allure_report.yaml@main diff --git a/charmcraft.yaml b/charmcraft.yaml index bb9fd23..72b9786 100644 --- a/charmcraft.yaml +++ b/charmcraft.yaml @@ -2,13 +2,9 @@ # See LICENSE file for licensing details. type: charm -bases: - - build-on: - - name: ubuntu - channel: "22.04" - run-on: - - name: ubuntu - channel: "22.04" +platforms: + ubuntu@22.04:amd64: + ubuntu@24.04:amd64: parts: charm: diff --git a/metadata.yaml b/metadata.yaml index cb4b530..2203e18 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -25,8 +25,7 @@ docs: https://discourse.charmhub.io/t/tmate-ssh-server-documentation-overview/12 issues: https://github.com/canonical/tmate-ssh-server-operator/issues source: https://github.com/canonical/tmate-ssh-server-operator summary: Tmate SSH Relay Server -series: - - jammy + tags: - application_development - ops diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index f1ef059..a16d18f 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -4,6 +4,9 @@ """Fixtures for tmate-ssh-server charm integration tests.""" import logging import secrets + +# Subprocess module is used to check series +import subprocess # nosec B404 import typing from pathlib import Path @@ -29,22 +32,47 @@ def model_fixture(ops_test: OpsTest) -> Model: return ops_test.model +@pytest.fixture(name="codename", scope="module") +def codename_fixture(): + """Series codename for deploying any-charm.""" + return ( + subprocess.check_output(["/usr/bin/lsb_release", "-cs"]) # nosec B603 + .strip() + .decode("utf-8") + ) + + +@pytest.fixture(name="series", scope="module") +def series_fixture(): + """Series version for deploying any-charm.""" + return ( + subprocess.check_output(["/usr/bin/lsb_release", "-rs"]) # nosec B603 + .strip() + .decode("utf-8") + ) + + @pytest_asyncio.fixture(scope="module", name="charm") -async def charm_fixture(request: pytest.FixtureRequest, ops_test: OpsTest) -> str: +async def charm_fixture( + request: pytest.FixtureRequest, ops_test: OpsTest, series: str +) -> str | Path: """The path to charm.""" charm = request.config.getoption("--charm-file") if not charm: charm = await ops_test.build_charm(".") else: - charm = f"./{charm}" + charm_dir = Path(f"./{charm}").parent + charm_matching_series = list(charm_dir.rglob(f"*{series}*.charm")) + assert charm_matching_series, f"No build found for series {series}" + return f"./{charm_matching_series[0]}" return charm @pytest_asyncio.fixture(scope="module", name="tmate_ssh_server") -async def tmate_ssh_server_fixture(model: Model, charm: str): +async def tmate_ssh_server_fixture(model: Model, charm: str, codename: str): """The tmate-ssh-server application fixture.""" - app = await model.deploy(charm) + app = await model.deploy(charm, series=codename) await model.wait_for_idle(apps=[app.name], wait_for_active=True) return app @@ -158,36 +186,3 @@ async def proxy_machine_fixture(ops_test: OpsTest, machine: Machine): ) assert retcode == 0, f"Failed to restart squid service, {stdout} {stderr}" return machine - - -@pytest_asyncio.fixture(scope="module", name="machine_ip") -async def machine_ip_fixture(machine: Machine) -> str: - """The machine public IP address.""" - - def get_machine_ip_address() -> typing.Optional[str]: - """Get latest machine IP address. - - Returns: - The latest machine IP address if ready, None otherwise. - """ - latest_machine = machine.latest() - addresses = latest_machine.data["addresses"] - try: - address = next( - iter( - [ - address["value"] - for address in addresses - if address["scope"] != "local-machine" - ] - ) - ) - except StopIteration: - return None - return address - - await wait_for(get_machine_ip_address) - - # mypy doesn't understand that get_machine_ip_address has to be str from wait_for statement - # above. - return typing.cast(str, get_machine_ip_address()) diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index ce580b8..90ad887 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -7,6 +7,30 @@ import time import typing +from juju.machine import Machine + + +def get_machine_ip_address(machine: Machine) -> typing.Optional[str]: + """Get latest machine IP address. + + Args: + machine: The machine to get the IP address for. + + Returns: + The latest machine IP address if ready, None otherwise. + """ + latest_machine = machine.latest() + addresses = latest_machine.data["addresses"] + try: + address = next( + iter( + [address["value"] for address in addresses if address["scope"] != "local-machine"] + ) + ) + except StopIteration: + return None + return address + async def wait_for( func: typing.Callable[[], typing.Union[typing.Awaitable, typing.Any]], diff --git a/tests/integration/test_proxy.py b/tests/integration/test_proxy.py index 6efb512..3a64c58 100644 --- a/tests/integration/test_proxy.py +++ b/tests/integration/test_proxy.py @@ -9,7 +9,7 @@ from juju.unit import Unit from pytest_operator.plugin import OpsTest -from .helpers import wait_for +from .helpers import get_machine_ip_address, wait_for logger = logging.getLogger(__name__) @@ -19,13 +19,16 @@ async def test_proxy( model: Model, charm: str, proxy_machine: Machine, - machine_ip: str, + codename: str, ): """ arrange: given a model configured with squid proxy ip address. act: when tmate charm is deployed. assert: proxy log contains docker access. """ + machine_ip: str | None = await wait_for(lambda: get_machine_ip_address(machine=proxy_machine)) + assert machine_ip is not None, "Proxy machine IP address not found." + await model.set_config( { "juju-http-proxy": f"http://{machine_ip}:3218", @@ -34,7 +37,7 @@ async def test_proxy( ) logger.info("Deploying tmate charm.") - app = await model.deploy(charm) + app = await model.deploy(charm, series=codename) await model.wait_for_idle(apps=[app.name], wait_for_active=True) unit: Unit = next(iter(app.units)) diff --git a/tox.ini b/tox.ini index 2ac829b..cac9b3e 100644 --- a/tox.ini +++ b/tox.ini @@ -94,14 +94,16 @@ commands = [testenv:integration] description = Run integration tests deps = - pytest + -r{toxinidir}/requirements.txt + allure-pytest>=2.8.18 + git+https://github.com/canonical/data-platform-workflows@v24.0.0\#subdirectory=python/pytest_plugins/allure_pytest_collection_report juju>=3,<4 - ops - pytest-operator - pytest-asyncio # Type error problem with newer version of macaroonbakery macaroonbakery==1.3.2 - -r{toxinidir}/requirements.txt + ops + pytest + pytest-asyncio + pytest-operator commands = pytest --tb native --ignore={[vars]tst_path}unit --log-cli-level=INFO -s {posargs}