Skip to content
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
- ubuntu@20.04
- ubuntu@22.04
- ubuntu@24.04
- ubuntu@26.04
name: Integration tests (LXD) | ${{ matrix.bases }}
runs-on: ubuntu-latest
needs:
Expand All @@ -85,5 +86,6 @@ jobs:
with:
provider: lxd
juju-channel: 3/stable
charmcraft-channel: 4.x/stable
- name: Run tests
run: tox run -e integration -- --charm-base=${{ matrix.bases }}
10 changes: 9 additions & 1 deletion charmcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ provides:
subordinate: true

platforms:
ubuntu-26.04-amd64:
build-on: [ubuntu@26.04:amd64]
build-for: [ubuntu@26.04:amd64]
ubuntu-26.04-arm64:
build-on: [ubuntu@26.04:arm64]
build-for: [ubuntu@26.04:arm64]
ubuntu-24.04-amd64:
build-on: [ubuntu@24.04:amd64]
build-for: [ubuntu@24.04:amd64]
Expand All @@ -47,5 +53,7 @@ platforms:

parts:
charm:
plugin: charm
plugin: uv
source: .
build-snaps:
- astral-uv
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[project]
name = "lldpd"
version = "0.0"
requires-python = ">=3.8"
dependencies = ["ops>=1.4.0"]
25 changes: 15 additions & 10 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
"""Configure lldpd operator integration tests."""

import logging
import pathlib
import platform
from pathlib import Path
import subprocess

import jubilant
import pytest
from pytest_operator.plugin import OpsTest


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -49,20 +49,25 @@ def charm_base(request) -> str:


@pytest.fixture(scope="module")
async def lldpd_charm(ops_test: OpsTest, charm_base: str) -> Path:
# Multiple charms will be built, but the build_charm function only returns
# the path to one of the charms. Find the charm that matches the charm_base
# in order to test the right one.
await ops_test.build_charm(".")
def juju():
"""Provide a temporary model, torn down at the end of the module."""
with jubilant.temp_model(config={"update-status-hook-interval": "10s"}) as juju:
yield juju


@pytest.fixture(scope="module")
def lldpd_charm(charm_base: str) -> pathlib.Path:
# charmcraft packs one file per platform. Build and return the charm that
# matches charm_base so the right one is tested.
subprocess.run(["charmcraft", "pack"], check=True)

base = charm_base.replace("@", "-")
arch = platform.machine()
# convert the x86_64 arch into the amd64 arch used by charmcraft.
if arch == "x86_64":
arch = "amd64"

build_dir = (ops_test.tmp_path / "charms").absolute()
charm_file = build_dir / f"lldpd_{base}-{arch}.charm"
charm_file = pathlib.Path(f"lldpd_{base}-{arch}.charm").absolute()
if not charm_file.exists():
raise ValueError(f"Unable to find charm file {charm_file}")

Expand Down
66 changes: 28 additions & 38 deletions tests/integration/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,61 +15,51 @@

"""Test lldpd charm deployment."""

import asyncio
import logging
import pytest

from pytest_operator.plugin import OpsTest
import pathlib

import jubilant
import pytest

logger = logging.getLogger(__name__)

NUM_UNITS = 2


@pytest.mark.abort_on_fail
@pytest.mark.skip_if_deployed
@pytest.mark.order(1)
async def test_build_and_deploy(
ops_test: OpsTest, charm_base: str, lldpd_charm
def test_build_and_deploy(
juju: jubilant.Juju, charm_base: str, lldpd_charm: pathlib.Path
) -> None:
"""Test the lldpd charm builds and deploys."""
logger.info(f"Building and deploying lldp charms for base: {charm_base}")
lldpd = await lldpd_charm
logger.info(f"lldpd charm is located at: {lldpd_charm}")

logger.info(f"lldpd charm is located at: {lldpd}")
# Deploy the ubuntu principal and the lldpd subordinate. The juju CLI
# resolves the ubuntu charm revision for the requested base.
juju.deploy("ubuntu", "ubuntu", num_units=NUM_UNITS, base=charm_base)
# lldpd is a subordinate; it takes no units of its own (they come from the
# principal relation), so num_units is left unset for it.
juju.deploy(lldpd_charm, "lldpd", base=charm_base)

# Deploy ubuntu and lldpd charms.
await asyncio.gather(
ops_test.model.deploy(
"ubuntu",
application_name="ubuntu",
num_units=NUM_UNITS,
base=charm_base,
),
ops_test.model.deploy(
str(lldpd),
application_name="lldpd",
num_units=0,
base=charm_base,
),
juju.integrate("ubuntu:juju-info", "lldpd:juju-info")
juju.wait(
lambda status: jubilant.all_active(status, "ubuntu", "lldpd"),
error=jubilant.any_error,
timeout=1800,
)

# Integrate lldpd with ubuntu
await ops_test.model.integrate("ubuntu:juju-info", "lldpd:juju-info")
async with ops_test.fast_forward():
await ops_test.model.wait_for_idle(
apps=["lldpd", "ubuntu"], status="active", timeout=1800
)
for unit in range(NUM_UNITS):
uname = f"lldpd/{unit}"
assert ops_test.model.units.get(uname).workload_status == "active"


@pytest.mark.order(2)
async def test_lldpd_is_active(ops_test: OpsTest) -> None:
def test_lldpd_is_active(juju: jubilant.Juju) -> None:
"""Test that the lldpd services are active in each juju unit."""
logger.info("Validating that lldpd is active inside each juju unit.")
for unit in ops_test.model.applications["lldpd"].units:
status = (await unit.ssh("systemctl is-active lldpd")).strip()
assert status == "active", f"{unit.name} lldpd is not active"
status = juju.status()
# lldpd is a subordinate, so its units live under the principal's units in
# status; get_units() resolves them via subordinate_to.
units = status.get_units("lldpd")
assert (
len(units) == NUM_UNITS
), f"expected {NUM_UNITS} lldpd units, got {len(units)}"
for unit in units:
result = juju.ssh(unit, "systemctl is-active lldpd").strip()
assert result == "active", f"{unit} lldpd is not active"
3 changes: 1 addition & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,8 @@ commands =
[testenv:integration]
description = Run integration tests
deps =
juju<=3.6.0,>3.3.0
jubilant>=1.8,<2
pytest
pytest-operator
pytest-order
-r{toxinidir}/requirements.txt
commands =
Expand Down
Loading
Loading