Skip to content

Commit

Permalink
Add juju 3.1 support. (#66)
Browse files Browse the repository at this point in the history
* feat: Add juju3 support for func tests.

Add make target, tox environment to support juju3 along with juju2.

Some changes were done to run the same set of func tests for both
libjuju versions without having to check which version we're on:

* Replace `run_wait()` method with `run_command_on_unit()` in
conftest.py's `SyncHelper` class. This changes the functionality from
running a command on a unit with libjuju's `unit.run` (which could
potentially return different dictionaries for juju2 and juju3), to a
similar `juju exec --unit` command run using ops_test.juju.

* `juju exec` return code is different for failure between juju2 and
juju3. So the assert check for failure is changed to "> 0" instead of
checking strict equality with a non-zero integer.

* Update GitHub workflow files.

* Update GH workflow lxd nested containers to false

* Restructure requirements in tox file
  • Loading branch information
dashmage authored Sep 26, 2023
1 parent 53ccbd9 commit 4da52ef
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 66 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Check workflow running linter, unit and functional tests

on:
workflow_call:
pull_request:
types: [opened, synchronize, reopened]
branches: [master, main]
paths-ignore:
- "**.md"
- "**.rst"

jobs:
lint-unit:
uses: canonical/bootstack-actions/.github/workflows/lint-unit.yaml@v2
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.10"]
with:
python-version: ${{ matrix.python-version }}
tox-version: "<4"

func:
uses: canonical/bootstack-actions/.github/workflows/func.yaml@v2
needs: lint-unit
strategy:
fail-fast: false
matrix:
include:
- juju-channel: "2.9/stable"
command: "make functional"
- juju-channel: "3.1/stable"
command: "make functional31"
with:
command: ${{ matrix.command }}
juju-channel: ${{ matrix.juju-channel }}
nested-containers: false
provider: "lxd"
python-version: "3.10"
timeout-minutes: 120
tox-version: "<4"
24 changes: 0 additions & 24 deletions .github/workflows/pr.yaml

This file was deleted.

16 changes: 10 additions & 6 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ name: Release to Edge

on:
push:
branches: [ master, main ]
branches: [master, main]

concurrency:
group: release
cancel-in-progress: true

jobs:
check:
uses: ./.github/workflows/check.yaml

release:
uses: canonical/bootstack-actions/.github/workflows/charm-release.yaml@main
needs: check
uses: canonical/bootstack-actions/.github/workflows/charm-release.yaml@v2
secrets: inherit
with:
python-version-unit: "['3.8', '3.10']"
python-version-func: "3.10"
tox-version: "<4"
channel: "latest/edge"
upload-image: false
commands: "['FUNC_ARGS=\"--series jammy\" make functional']"
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ functional:
@echo "Executing functional tests using built charm at ${PROJECTPATH}"
@CHARM_LOCATION=${PROJECTPATH} tox -e func -- ${FUNC_ARGS}

functional31:
@echo "Executing functional tests using built charm at ${PROJECTPATH}"
@CHARM_LOCATION=${PROJECTPATH} tox -e func31 -- ${FUNC_ARGS}

test: lint unittests functional
@echo "Tests completed for charm ${CHARM_NAME}."

Expand Down
12 changes: 8 additions & 4 deletions tests/functional/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ class SyncHelper:
"""Helper class for running juju async function."""

@staticmethod
async def run_wait(unit, command, timeout=20):
action = await unit.run(command, timeout=timeout)
# await action.wait() # This is required in juju3
return action.results
async def run_command_on_unit(ops_test, unit_name, command):
complete_command = ["exec", "--unit", unit_name, "--", *command.split()]
return_code, stdout, _ = await ops_test.juju(*complete_command)
results = {
"return-code": return_code,
"stdout": stdout,
}
return results


def pytest_addoption(parser):
Expand Down
4 changes: 2 additions & 2 deletions tests/functional/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Make OpsTest works with controller version < 3
juju < 3
pytest
pytest-operator
46 changes: 18 additions & 28 deletions tests/functional/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,6 @@ async def test_build_and_deploy(ops_test: OpsTest, series, sync_helper):
Assert on the unit status before any relations/configurations take place.
"""
# Fail the test if the version of juju is not 2.9
# This maybe remove when we support version 3
try:
from juju.client.connector import SUPPORTED_JUJU_API_PREFIX

assert "2.9" in SUPPORTED_JUJU_API_PREFIX
except ImportError:
logger.error("The juju version is not supported")
raise

# Build and deploy charm from local source folder
charm = await ops_test.build_charm(".")
assert charm, "Charm was not built successfully."
Expand Down Expand Up @@ -95,12 +85,13 @@ async def test_build_and_deploy(ops_test: OpsTest, series, sync_helper):
for msg in messages:
assert msg in unit.workload_status_message

check_active_cmd = "systemctl is-active hardware-exporter"

# Test without cos-agent relation
for unit in ops_test.model.applications[APP_NAME].units:
check_active_cmd = "systemctl is-active hardware-exporter"
results = await sync_helper.run_wait(unit, check_active_cmd)
assert results.get("Code") == "3"
assert results.get("Stdout").strip() == "inactive"
results = await sync_helper.run_command_on_unit(ops_test, unit.name, check_active_cmd)
assert results.get("return-code") > 0
assert results.get("stdout").strip() == "inactive"

# Add cos-agent relation
await asyncio.gather(
Expand All @@ -116,10 +107,9 @@ async def test_build_and_deploy(ops_test: OpsTest, series, sync_helper):

# Test with cos-agent relation
for unit in ops_test.model.applications[APP_NAME].units:
check_active_cmd = "systemctl is-active hardware-exporter"
results = await sync_helper.run_wait(unit, check_active_cmd)
assert results.get("Code") == "0"
assert results.get("Stdout").strip() == "active"
results = await sync_helper.run_command_on_unit(ops_test, unit.name, check_active_cmd)
assert results.get("return-code") == 0
assert results.get("stdout").strip() == "active"
assert unit.workload_status_message == AppStatus.READY


Expand All @@ -135,9 +125,9 @@ async def test_00_config_changed_port(self, app, unit, sync_helper, ops_test):
)

cmd = "cat /etc/hardware-exporter-config.yaml"
results = await sync_helper.run_wait(unit, cmd)
assert results.get("Code") == "0"
config = yaml.safe_load(results.get("Stdout").strip())
results = await sync_helper.run_command_on_unit(ops_test, unit.name, cmd)
assert results.get("return-code") == 0
config = yaml.safe_load(results.get("stdout").strip())
assert config["port"] == int(new_port)

await app.reset_config(["exporter-port"])
Expand All @@ -151,9 +141,9 @@ async def test_01_config_changed_log_level(self, app, unit, sync_helper, ops_tes
)

cmd = "cat /etc/hardware-exporter-config.yaml"
results = await sync_helper.run_wait(unit, cmd)
assert results.get("Code") == "0"
config = yaml.safe_load(results.get("Stdout").strip())
results = await sync_helper.run_command_on_unit(ops_test, unit.name, cmd)
assert results.get("return-code") == 0
config = yaml.safe_load(results.get("stdout").strip())
assert config["level"] == new_log_level

await app.reset_config(["exporter-log-level"])
Expand Down Expand Up @@ -206,12 +196,12 @@ async def test_20_on_remove_event(self, app, sync_helper, ops_test):
principal_unit = ops_test.model.applications[PRINCIPAL_APP_NAME].units[0]

cmd = "ls /etc/hardware-exporter-config.yaml"
results = await sync_helper.run_wait(principal_unit, cmd)
assert results.get("Code") == "2"
results = await sync_helper.run_command_on_unit(ops_test, principal_unit.name, cmd)
assert results.get("return-code") > 0

cmd = "ls /etc/systemd/system/hardware-exporter.service"
results = await sync_helper.run_wait(principal_unit, cmd)
assert results.get("Code") == "2"
results = await sync_helper.run_command_on_unit(ops_test, principal_unit.name, cmd)
assert results.get("return-code") > 0

await asyncio.gather(
ops_test.model.add_relation(
Expand Down
8 changes: 6 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ deps =
[testenv:func]
commands = pytest {toxinidir}/tests/functional {posargs:-v}
deps =
pytest
pytest-operator
-r {toxinidir}/tests/functional/requirements.txt
juju < 3

[testenv:func31]
commands = pytest {toxinidir}/tests/functional {posargs:-v}
deps =
-r {toxinidir}/tests/functional/requirements.txt

0 comments on commit 4da52ef

Please sign in to comment.