From d471f97c86f7096f10873a10acf2a46309bc9825 Mon Sep 17 00:00:00 2001 From: Robert Gildein Date: Thu, 11 Apr 2024 13:53:57 +0200 Subject: [PATCH 1/6] Set controller channel to 3.4 in CI The functional tests requiered some changes, they were done strangely and I'm not sure why they worked before. I think it's maybe due [1], but I did not have time to investigate more. --- [1]: https://github.com/pytest-dev/pytest-asyncio/issues/706 --- .github/workflows/check.yaml | 2 +- Makefile | 10 +- tests/functional/conftest.py | 11 +- tests/functional/test_charm.py | 722 ++++++++++++++++----------------- 4 files changed, 370 insertions(+), 375 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 6560d6f..52ceecd 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -32,7 +32,7 @@ jobs: - command: "FUNC_ARGS='--series jammy' make functional" with: command: ${{ matrix.command }} - juju-channel: "3.1/stable" + juju-channel: "3.4/stable" nested-containers: false provider: "lxd" python-version: "3.10" diff --git a/Makefile b/Makefile index cffac3c..c8d53bf 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,3 @@ -# This is a template `Makefile` file for ops charms -# This file is managed by bootstack-charms-spec and should not be modified -# within individual charm repos. https://launchpad.net/bootstack-charms-spec - PYTHON := /usr/bin/python3 PROJECTPATH=$(dir $(realpath $(MAKEFILE_LIST))) @@ -47,8 +43,6 @@ submodules-update: @git submodule update --init --recursive --remote --merge clean: - @echo "Cleaning files" - @git clean -ffXd -e '!.idea' -e '!.vscode' @echo "Cleaning existing build" @rm -rf ${PROJECTPATH}/${CHARM_NAME}*.charm @echo "Cleaning charmcraft" @@ -77,8 +71,8 @@ unittests: @tox -e unit -- ${UNIT_ARGS} functional: - @echo "Executing functional tests using built charm at ${PROJECTPATH}" - @CHARM_LOCATION=${PROJECTPATH} tox -e func -- ${FUNC_ARGS} + @echo "Executing functional tests with args: ${FUNC_ARGS}" + @tox -e func -- ${FUNC_ARGS} test: lint unittests functional @echo "Tests completed for charm ${CHARM_NAME}." diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py index 77fd0d8..5f1a27e 100644 --- a/tests/functional/conftest.py +++ b/tests/functional/conftest.py @@ -36,17 +36,17 @@ def series(request): return request.config.getoption("--series") -@pytest.fixture(scope="class") +@pytest.fixture def apt_mirror_app(ops_test): return ops_test.model.applications["apt-mirror"] -@pytest.fixture(scope="class") +@pytest.fixture def apt_mirror_unit(apt_mirror_app): return apt_mirror_app.units[0] -@pytest.fixture(scope="class") +@pytest.fixture def configs(apt_mirror_app): async def get_config_synced(): return await apt_mirror_app.get_config() @@ -59,3 +59,8 @@ async def get_config_synced(): @pytest.fixture(scope="class") def helper(): return Helper + + +@pytest.fixture +def base_path(configs): + return configs.get("base-path").get("value") diff --git a/tests/functional/test_charm.py b/tests/functional/test_charm.py index c26ab7d..9a6780d 100644 --- a/tests/functional/test_charm.py +++ b/tests/functional/test_charm.py @@ -21,6 +21,7 @@ async def test_build_and_deploy(ops_test, series): series=series, ) ) + await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="blocked") await ops_test.model.wait_for_idle(apps=["nginx"], status="active") @@ -31,436 +32,431 @@ async def test_build_and_deploy(ops_test, series): ), "apt-mirror did not show correct block message." -class TestCharmActions: - """Perform very basic checking of the actions.""" +async def test_publish_snapshot_action(ops_test, apt_mirror_unit, base_path, helper): + """Test publish_snapshot action.""" + name = "snapshot-publishme" + create_cmd = "mkdir -p {}/{}".format(base_path, name) + check_cmd = "readlink {}/publish".format(base_path) + cleanup_cmd = "rm -rf {}/{} {}/publish".format(base_path, name, base_path) - @pytest.fixture - def base_path(self, configs): - return configs.get("base-path").get("value") + results = await helper.run_wait(apt_mirror_unit, create_cmd) + assert results.get("return-code") == 0 - async def test_publish_snapshot_action(self, ops_test, apt_mirror_unit, base_path, helper): - """Test publish_snapshot action.""" - name = "snapshot-publishme" - create_cmd = "mkdir -p {}/{}".format(base_path, name) - check_cmd = "readlink {}/publish".format(base_path) - cleanup_cmd = "rm -rf {}/{} {}/publish".format(base_path, name, base_path) + results = await helper.run_action_wait(apt_mirror_unit, "publish-snapshot", name=name) + await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="active") + assert results.get("return-code") == 0 - results = await helper.run_wait(apt_mirror_unit, create_cmd) - assert results.get("return-code") == 0 + results = await helper.run_wait(apt_mirror_unit, check_cmd) + assert results.get("return-code") == 0 + assert results.get("stdout").strip() == "{}/{}".format(base_path, name) - results = await helper.run_action_wait(apt_mirror_unit, "publish-snapshot", name=name) - await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="active") - assert results.get("return-code") == 0 + results = await helper.run_wait(apt_mirror_unit, cleanup_cmd) + assert results.get("return-code") == 0 + + +async def test_create_snapshot_action(ops_test, apt_mirror_unit, base_path, helper): + """Test create_snapshot action.""" + count_cmd = "ls {} | grep ^snapshot | wc -l".format(base_path) + cleanup_cmd = "rm -rf {}/snapshot*".format(base_path) + + results = await helper.run_wait(apt_mirror_unit, count_cmd) + original_count = int(results.get("stdout").strip()) + + results = await helper.run_action_wait(apt_mirror_unit, "create-snapshot") + await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="blocked") + assert results.get("return-code") == 0 + results = await helper.run_wait(apt_mirror_unit, count_cmd) + expected_count = int(results.get("stdout").strip()) + assert original_count + 1 == expected_count + + results = await helper.run_wait(apt_mirror_unit, cleanup_cmd) + assert results.get("return-code") == 0 + + +@pytest.mark.parametrize( + "name,expected_status", + [("snapshot-deleteme", "completed"), ("random-name", "failed")], +) +async def test_delete_snapshot_action(name, expected_status, apt_mirror_unit, base_path, helper): + """Test delete_snapshot action.""" + create_cmd = "mkdir {}/{}".format(base_path, name) + check_cmd = "find {}/{}".format(base_path, name) + + results = await helper.run_wait(apt_mirror_unit, create_cmd) + assert results.get("return-code") == 0 + + action = await apt_mirror_unit.run_action("delete-snapshot", name=name) + await action.wait() + assert action.status == expected_status + + if expected_status == "failed": results = await helper.run_wait(apt_mirror_unit, check_cmd) assert results.get("return-code") == 0 - assert results.get("stdout").strip() == "{}/{}".format(base_path, name) + else: + results = await helper.run_wait(apt_mirror_unit, check_cmd) + assert results.get("return-code") != 0 - results = await helper.run_wait(apt_mirror_unit, cleanup_cmd) - assert results.get("return-code") == 0 - async def test_create_snapshot_action(self, ops_test, apt_mirror_unit, base_path, helper): - """Test create_snapshot action.""" - count_cmd = "ls {} | grep ^snapshot | wc -l".format(base_path) - cleanup_cmd = "rm -rf {}/snapshot*".format(base_path) +async def test_list_snapshots_action(apt_mirror_unit, helper): + """Test list snapshots action.""" + results = await helper.run_action_wait(apt_mirror_unit, "list-snapshots") + assert results.get("return-code") == 0 - results = await helper.run_wait(apt_mirror_unit, count_cmd) - original_count = int(results.get("stdout").strip()) - results = await helper.run_action_wait(apt_mirror_unit, "create-snapshot") - await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="blocked") - assert results.get("return-code") == 0 +async def test_synchronize_action(apt_mirror_unit, helper): + """Test synchronize action.""" + results = await helper.run_action_wait(apt_mirror_unit, "synchronize") + assert results.get("return-code") == 0 - results = await helper.run_wait(apt_mirror_unit, count_cmd) - expected_count = int(results.get("stdout").strip()) - assert original_count + 1 == expected_count - results = await helper.run_wait(apt_mirror_unit, cleanup_cmd) - assert results.get("return-code") == 0 +async def test_check_packages_action(apt_mirror_unit, helper): + """Test check packages action.""" + results = await helper.run_action_wait(apt_mirror_unit, "check-packages") + assert results.get("return-code") == 0 - @pytest.mark.parametrize( - "name,expected_status", - [("snapshot-deleteme", "completed"), ("random-name", "failed")], - ) - async def test_delete_snapshot_action( - self, name, expected_status, apt_mirror_unit, base_path, helper - ): - """Test delete_snapshot action.""" - create_cmd = "mkdir {}/{}".format(base_path, name) - check_cmd = "find {}/{}".format(base_path, name) - - results = await helper.run_wait(apt_mirror_unit, create_cmd) - assert results.get("return-code") == 0 - action = await apt_mirror_unit.run_action("delete-snapshot", name=name) - await action.wait() - assert action.status == expected_status +async def test_clean_up_packages_action(apt_mirror_unit, helper): + """Test clean up packages action.""" + results = await helper.run_action_wait(apt_mirror_unit, "clean-up-packages", confirm=False) + assert results.get("return-code") == 0 + assert "Aborted!" in results.get("message") - if expected_status == "failed": - results = await helper.run_wait(apt_mirror_unit, check_cmd) - assert results.get("return-code") == 0 - else: - results = await helper.run_wait(apt_mirror_unit, check_cmd) - assert results.get("return-code") != 0 + results = await helper.run_action_wait(apt_mirror_unit, "clean-up-packages", confirm=True) + assert results.get("return-code") == 0 + assert "Freed up" in results.get("message") - async def test_list_snapshots_action(self, apt_mirror_unit, helper): - """Test list snapshots action.""" - results = await helper.run_action_wait(apt_mirror_unit, "list-snapshots") - assert results.get("return-code") == 0 - async def test_synchronize_action(self, apt_mirror_unit, helper): - """Test synchronize action.""" - results = await helper.run_action_wait(apt_mirror_unit, "synchronize") - assert results.get("return-code") == 0 +async def test_setup_cron_schedule(ops_test, apt_mirror_app, apt_mirror_unit, helper): + """Test setup cron schedule config option. - async def test_check_packages_action(self, apt_mirror_unit, helper): - """Test check packages action.""" - results = await helper.run_action_wait(apt_mirror_unit, "check-packages") - assert results.get("return-code") == 0 + Test cron job for automatic synchronization is added. + """ + cron_schedule = "0 5 * * 1" + await apt_mirror_app.set_config({"cron-schedule": cron_schedule}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - async def test_clean_up_packages_action(self, apt_mirror_unit, helper): - """Test clean up packages action.""" - results = await helper.run_action_wait(apt_mirror_unit, "clean-up-packages", confirm=False) - assert results.get("return-code") == 0 - assert "Aborted!" in results.get("message") + results = await helper.run_wait( + apt_mirror_unit, "cat /etc/cron.d/{}".format(apt_mirror_app.name) + ) + assert results.get("stdout").strip() == "{} root apt-mirror".format(cron_schedule) - results = await helper.run_action_wait(apt_mirror_unit, "clean-up-packages", confirm=True) - assert results.get("return-code") == 0 - assert "Freed up" in results.get("message") + # restore configs + await apt_mirror_app.reset_config(["cron-schedule"]) -class TestCharm: - """Perform various functional testing of the charm and its actions.""" +async def test_remove_cron_schedule(ops_test, apt_mirror_app, apt_mirror_unit, helper): + """Test remove cron schedule config option. - @pytest.fixture - def base_path(self, configs): - return configs.get("base-path").get("value") + Test cron job for automatic synchronization is removed. + """ + cron_schedule = "" + await apt_mirror_app.set_config({"cron-schedule": cron_schedule}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - async def test_setup_cron_schedule(self, ops_test, apt_mirror_app, apt_mirror_unit, helper): - """Test setup cron schedule config option. + results = await helper.run_wait( + apt_mirror_unit, "ls /etc/cron.d/{}".format(apt_mirror_app.name) + ) + assert results.get("return-code") != 0 - Test cron job for automatic synchronization is added. - """ - cron_schedule = "0 5 * * 1" - await apt_mirror_app.set_config({"cron-schedule": cron_schedule}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + # restore configs + await apt_mirror_app.reset_config(["cron-schedule"]) - results = await helper.run_wait( - apt_mirror_unit, "cat /etc/cron.d/{}".format(apt_mirror_app.name) - ) - assert results.get("stdout").strip() == "{} root apt-mirror".format(cron_schedule) - # restore configs - await apt_mirror_app.reset_config(["cron-schedule"]) +async def test_bad_mirror_list_options( + ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper +): + """Test bad mirror-list config option. - async def test_remove_cron_schedule(self, ops_test, apt_mirror_app, apt_mirror_unit, helper): - """Test remove cron schedule config option. + Test if the input mirror-list is not a valid string containing: . Note that it does + not verify each part, such as , is valid or not. + """ + # Clean up + await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) - Test cron job for automatic synchronization is removed. - """ - cron_schedule = "" - await apt_mirror_app.set_config({"cron-schedule": cron_schedule}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + # Create bad mirror-list option - 1 + mirror_list = "deb" + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="blocked") + app = ops_test.model.applications["apt-mirror"] + status_msg = app.units[0].workload_status_message + assert bool( + re.search("^An error .* option.$", status_msg) + ), "apt-mirror did not show correct block message." - results = await helper.run_wait( - apt_mirror_unit, "ls /etc/cron.d/{}".format(apt_mirror_app.name) - ) - assert results.get("return-code") != 0 + # Create bad mirror-list option - 2 + mirror_list = "deb fake-uri" + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="blocked") + app = ops_test.model.applications["apt-mirror"] + status_msg = app.units[0].workload_status_message + assert bool( + re.search("^An error .* option.$", status_msg) + ), "apt-mirror did not show correct block message." - # restore configs - await apt_mirror_app.reset_config(["cron-schedule"]) - - async def test_bad_mirror_list_options( - self, ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper - ): - """Test bad mirror-list config option. - - Test if the input mirror-list is not a valid string containing: . Note that it does - not verify each part, such as , is valid or not. - """ - # Clean up - await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) - - # Create bad mirror-list option - 1 - mirror_list = "deb" - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="blocked") - app = ops_test.model.applications["apt-mirror"] - status_msg = app.units[0].workload_status_message - assert bool( - re.search("^An error .* option.$", status_msg) - ), "apt-mirror did not show correct block message." - - # Create bad mirror-list option - 2 - mirror_list = "deb fake-uri" - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="blocked") - app = ops_test.model.applications["apt-mirror"] - status_msg = app.units[0].workload_status_message - assert bool( - re.search("^An error .* option.$", status_msg) - ), "apt-mirror did not show correct block message." - - # Fix the mirror-list option - url = "ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu" - mirror_list = """\ + # Fix the mirror-list option + url = "ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu" + mirror_list = """\ deb https://{0} focal main deb https://{0} bionic main\ """.format( - url - ) - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="blocked") - app = ops_test.model.applications["apt-mirror"] - status_msg = app.units[0].workload_status_message - assert bool( - re.search("^Last sync: .* not published$", status_msg) - ), "apt-mirror did not show correct block message." - - async def test_client_access( - self, ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper - ): - """Test client access. - - Test remote apt server can be accessed by client. - """ - # Clean up - await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) - - # Let's use bootstack public ppa for testing; it's very small compared - # to ubuntu or other os's repos. - url = "ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu" - mirror_list = """\ + url + ) + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="blocked") + app = ops_test.model.applications["apt-mirror"] + status_msg = app.units[0].workload_status_message + assert bool( + re.search("^Last sync: .* not published$", status_msg) + ), "apt-mirror did not show correct block message." + + +async def test_client_access(ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper): + """Test client access. + + Test remote apt server can be accessed by client. + """ + # Clean up + await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) + + # Let's use bootstack public ppa for testing; it's very small compared + # to ubuntu or other os's repos. + url = "ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu" + mirror_list = """\ deb https://{0} focal main deb https://{0} bionic main\ """.format( - url - ) - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + url + ) + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - await apt_mirror_unit.run_action("synchronize") - await apt_mirror_unit.run_action("create-snapshot") - results = await helper.run_action_wait(apt_mirror_unit, "list-snapshots") - list_outputs = results.get("snapshots").strip() - snapshot_name = re.findall(r"snapshot-\d+", list_outputs)[0] + await apt_mirror_unit.run_action("synchronize") + await apt_mirror_unit.run_action("create-snapshot") + results = await helper.run_action_wait(apt_mirror_unit, "list-snapshots") + list_outputs = results.get("snapshots").strip() + snapshot_name = re.findall(r"snapshot-\d+", list_outputs)[0] - await apt_mirror_unit.run_action("publish-snapshot", name=snapshot_name) - await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="active") - assert bool(re.match("^Publishes.*", apt_mirror_unit.workload_status_message)) + await apt_mirror_unit.run_action("publish-snapshot", name=snapshot_name) + await ops_test.model.wait_for_idle(apps=["apt-mirror"], status="active") + assert bool(re.match("^Publishes.*", apt_mirror_unit.workload_status_message)) - nginx_unit = ops_test.model.applications["nginx"].units[0] - nginx_public_ip = await nginx_unit.get_public_address() + nginx_unit = ops_test.model.applications["nginx"].units[0] + nginx_public_ip = await nginx_unit.get_public_address() - client_unit = ops_test.model.applications["client"].units[0] - # Note these can be changed if you changed the test url and mirror_list - test_apts = """\ + client_unit = ops_test.model.applications["client"].units[0] + # Note these can be changed if you changed the test url and mirror_list + test_apts = """\ deb http://{0}/apt-mirror/{1} focal main deb http://{0}/apt-mirror/{1} bionic main\ """.format( - nginx_public_ip, url - ) - await client_unit.run("echo '{}' > /etc/apt/sources.list".format(test_apts)) - # Add public key; this is only required for this particular mirror-list - # option. The magic number: "4b9a81747a207542" is coming from - # https://launchpad.net/~canonical-bootstack/+archive/ubuntu/public - await client_unit.run( - "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4b9a81747a207542" - ) - results = await helper.run_wait(client_unit, "apt-get update") - assert results.get("return-code") == 0 + nginx_public_ip, url + ) + await client_unit.run("echo '{}' > /etc/apt/sources.list".format(test_apts)) + # Add public key; this is only required for this particular mirror-list + # option. The magic number: "4b9a81747a207542" is coming from + # https://launchpad.net/~canonical-bootstack/+archive/ubuntu/public + await client_unit.run( + "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4b9a81747a207542" + ) + results = await helper.run_wait(client_unit, "apt-get update") + assert results.get("return-code") == 0 + + +async def test_unreferenced_packages_config_changed( + ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper +): + """Test removing unreferenced packages. + + Test unreferenced packages can be removed when mirror-list is + changed and there's no snapshot requiring them. + """ + # Clean up + await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) - async def test_unreferenced_packages_config_changed( - self, ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper - ): - """Test removing unreferenced packages. - - Test unreferenced packages can be removed when mirror-list is - changed and there's no snapshot requiring them. - """ - # Clean up - await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) - - # Start with 2 mirror lists. - mirror_list = """\ + # Start with 2 mirror lists. + mirror_list = """\ deb https://ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu jammy main deb http://ppa.launchpad.net/landscape/self-hosted-23.10/ubuntu jammy main\ """ - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - await apt_mirror_unit.run_action("synchronize") + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + await apt_mirror_unit.run_action("synchronize") - # End up with 1 mirror lists. - mirror_list = """\ + # End up with 1 mirror lists. + mirror_list = """\ deb https://ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu jammy main """ - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - await apt_mirror_unit.run_action("synchronize") - - # Since landscape mirror is removed, we expect there are no extra - # unreferenced packages because they will be removed during - # synchronization. - results = await helper.run_action_wait(apt_mirror_unit, "check-packages") - count = int(results.get("count")) - assert count == 0 - - async def test_unreferenced_packages_config_changed_snapshoted( - self, ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper - ): - """Test not removing snapshoted packages. - - Test packages will not be removed when mirror-list is changed but a - snapshot is still requiring them. - """ - # Clean up - await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) - - # Start with 2 mirror lists. - mirror_list = """\ + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + await apt_mirror_unit.run_action("synchronize") + + # Since landscape mirror is removed, we expect there are no extra + # unreferenced packages because they will be removed during + # synchronization. + results = await helper.run_action_wait(apt_mirror_unit, "check-packages") + count = int(results.get("count")) + assert count == 0 + + +async def test_unreferenced_packages_config_changed_snapshoted( + ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper +): + """Test not removing snapshoted packages. + + Test packages will not be removed when mirror-list is changed but a + snapshot is still requiring them. + """ + # Clean up + await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) + + # Start with 2 mirror lists. + mirror_list = """\ deb https://ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu focal main deb http://ppa.launchpad.net/landscape/self-hosted-23.10/ubuntu jammy main\ """ - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - await apt_mirror_unit.run_action("synchronize") + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + await apt_mirror_unit.run_action("synchronize") - # Create a snapshot at this point. - await apt_mirror_unit.run_action("create-snapshot") + # Create a snapshot at this point. + await apt_mirror_unit.run_action("create-snapshot") - # End up with 1 mirror lists. - mirror_list = """\ + # End up with 1 mirror lists. + mirror_list = """\ deb https://ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu focal main """ - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - await apt_mirror_unit.run_action("synchronize") - - # Even though we end up with only 1 mirror list, but since we created - # a snapshot before changing mirror list, we should still have - # references to the packages in the snapshot, thus there should be no - # packages to be removed. - results = await helper.run_action_wait(apt_mirror_unit, "check-packages") - count = int(results.get("count")) - assert count == 0 - - # Let's try to delete the snapshot and check if there are - # still some unreferenced packages remain. - await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) - results = await helper.run_action_wait(apt_mirror_unit, "check-packages") - count = int(results.get("count")) - assert count > 0 - - async def test_outdated_packages_version_changed( - self, ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper - ): - """Test removing outdated packages. - - Test outdated packages can be removed when current index is not - requiring them and is pointing to newer versions. - """ - # Clean up - await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) - - # Start with a test mirror - mirror_list = """\ + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + await apt_mirror_unit.run_action("synchronize") + + # Even though we end up with only 1 mirror list, but since we created + # a snapshot before changing mirror list, we should still have + # references to the packages in the snapshot, thus there should be no + # packages to be removed. + results = await helper.run_action_wait(apt_mirror_unit, "check-packages") + count = int(results.get("count")) + assert count == 0 + + # Let's try to delete the snapshot and check if there are + # still some unreferenced packages remain. + await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) + results = await helper.run_action_wait(apt_mirror_unit, "check-packages") + count = int(results.get("count")) + assert count > 0 + + +async def test_outdated_packages_version_changed( + ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper +): + """Test removing outdated packages. + + Test outdated packages can be removed when current index is not + requiring them and is pointing to newer versions. + """ + # Clean up + await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) + + # Start with a test mirror + mirror_list = """\ deb https://ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu focal main """ - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - await apt_mirror_unit.run_action("synchronize") - - # Simulate there are outdated packages in the pool; for example adding - # copies of current packages to the pool, but appending _outdated to - # the filename. - cmd_1 = r"for f in $(find {} -name *.deb)".format(Path(base_path, "mirror")) - cmd_2 = r"; do cp $f $(echo $f | sed -e s/\.deb/_outdated\.deb/); done;" - cmd = cmd_1 + cmd_2 - await apt_mirror_unit.run(cmd) - - # Make sure we find outdated packages. - results = await helper.run_action_wait(apt_mirror_unit, "check-packages") - count = int(results.get("count")) - assert count > 0 - - # Let's try to delete the outdated packages and check if there are - # still some outdated packages remain. - await helper.run_action_wait(apt_mirror_unit, "clean-up-packages", confirm=True) - results = await helper.run_action_wait(apt_mirror_unit, "check-packages") - count = int(results.get("count")) - assert count == 0 - - async def test_outdated_packages_distro_changed( - self, ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper - ): - """Test removing outdated packages. - - Test outdated packages are removed when a distro is upgraded, when no - indices are not requiring them. Also, test the outdated packages are - not removed when the index of the old distro is kept in the snapshot. - """ - # Clean up - await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) - await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) - - # Start with a test mirror - mirror_list = """\ + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + await apt_mirror_unit.run_action("synchronize") + + # Simulate there are outdated packages in the pool; for example adding + # copies of current packages to the pool, but appending _outdated to + # the filename. + cmd_1 = r"for f in $(find {} -name *.deb)".format(Path(base_path, "mirror")) + cmd_2 = r"; do cp $f $(echo $f | sed -e s/\.deb/_outdated\.deb/); done;" + cmd = cmd_1 + cmd_2 + await apt_mirror_unit.run(cmd) + + # Make sure we find outdated packages. + results = await helper.run_action_wait(apt_mirror_unit, "check-packages") + count = int(results.get("count")) + assert count > 0 + + # Let's try to delete the outdated packages and check if there are + # still some outdated packages remain. + await helper.run_action_wait(apt_mirror_unit, "clean-up-packages", confirm=True) + results = await helper.run_action_wait(apt_mirror_unit, "check-packages") + count = int(results.get("count")) + assert count == 0 + + +async def test_outdated_packages_distro_changed( + ops_test, apt_mirror_app, apt_mirror_unit, base_path, helper +): + """Test removing outdated packages. + + Test outdated packages are removed when a distro is upgraded, when no + indices are not requiring them. Also, test the outdated packages are + not removed when the index of the old distro is kept in the snapshot. + """ + # Clean up + await apt_mirror_unit.run("rm -rf {}/mirror/*".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/publish".format(base_path)) + await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) + + # Start with a test mirror + mirror_list = """\ deb https://ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu focal main """ - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - await apt_mirror_unit.run_action("synchronize") + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + await apt_mirror_unit.run_action("synchronize") - # Upgrade the distro to Jammy - mirror_list = """\ + # Upgrade the distro to Jammy + mirror_list = """\ deb https://ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu jammy main """ - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - await apt_mirror_unit.run_action("synchronize") - - # Make sure we don't find outdated packages because they should be - # removed during synchronization. - results = await helper.run_action_wait(apt_mirror_unit, "check-packages") - count = int(results.get("count")) - assert count == 0 - - # Let's switch back to Focal and create a snapshot before switching to - # Jammy. - mirror_list = """\ + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + await apt_mirror_unit.run_action("synchronize") + + # Make sure we don't find outdated packages because they should be + # removed during synchronization. + results = await helper.run_action_wait(apt_mirror_unit, "check-packages") + count = int(results.get("count")) + assert count == 0 + + # Let's switch back to Focal and create a snapshot before switching to + # Jammy. + mirror_list = """\ deb https://ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu focal main """ - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - await apt_mirror_unit.run_action("synchronize") - await helper.run_action_wait(apt_mirror_unit, "create-snapshot") - mirror_list = """\ + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + await apt_mirror_unit.run_action("synchronize") + await helper.run_action_wait(apt_mirror_unit, "create-snapshot") + mirror_list = """\ deb https://ppa.launchpadcontent.net/canonical-bootstack/public/ubuntu jammy main """ - await apt_mirror_app.set_config({"mirror-list": mirror_list}) - await ops_test.model.wait_for_idle(apps=["apt-mirror"]) - await apt_mirror_unit.run_action("synchronize") - - # This time we should not find any "outdated" packages because they are - # still required in the snapshot. - results = await helper.run_action_wait(apt_mirror_unit, "check-packages") - count = int(results.get("count")) - assert count == 0 - - # Remove the snapshot, and we should find the "outdated" packages - await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) - results = await helper.run_action_wait(apt_mirror_unit, "check-packages") - count = int(results.get("count")) - assert count > 0 + await apt_mirror_app.set_config({"mirror-list": mirror_list}) + await ops_test.model.wait_for_idle(apps=["apt-mirror"]) + await apt_mirror_unit.run_action("synchronize") + + # This time we should not find any "outdated" packages because they are + # still required in the snapshot. + results = await helper.run_action_wait(apt_mirror_unit, "check-packages") + count = int(results.get("count")) + assert count == 0 + + # Remove the snapshot, and we should find the "outdated" packages + await apt_mirror_unit.run("rm -rf {}/snapshot-*".format(base_path)) + results = await helper.run_action_wait(apt_mirror_unit, "check-packages") + count = int(results.get("count")) + assert count > 0 From 8a57e7e4c999d606b2fb873f5a98890ae6fa551b Mon Sep 17 00:00:00 2001 From: Robert Gildein Date: Thu, 11 Apr 2024 14:32:02 +0200 Subject: [PATCH 2/6] tmp fix until [1] is not resulved --- [1]: https://github.com/canonical/charmcraft/issues/1640 --- .github/workflows/check.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 52ceecd..6bcbc6d 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -22,7 +22,7 @@ jobs: tox-version: "<4" func: - uses: canonical/bootstack-actions/.github/workflows/func.yaml@v2 + uses: canonical/bootstack-actions/.github/workflows/func.yaml@v3 needs: lint-unit strategy: fail-fast: false @@ -33,6 +33,7 @@ jobs: with: command: ${{ matrix.command }} juju-channel: "3.4/stable" + lxd-channel: "5.20/stable" # tmp fix until https://github.com/canonical/charmcraft/issues/1640 nested-containers: false provider: "lxd" python-version: "3.10" From 62c2f9bb3f47d49d83cd3c1e99899121c80b7a82 Mon Sep 17 00:00:00 2001 From: Robert Gildein Date: Thu, 11 Apr 2024 14:41:09 +0200 Subject: [PATCH 3/6] drop bootstack-actions for func tests --- .github/workflows/check.yaml | 37 +++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 6bcbc6d..0ad66ff 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -21,21 +21,32 @@ jobs: python-version: ${{ matrix.python-version }} tox-version: "<4" + func: - uses: canonical/bootstack-actions/.github/workflows/func.yaml@v3 + name: Functional tests + runs-on: ubuntu-latest + timeout-minutes: 120 needs: lint-unit strategy: fail-fast: false matrix: - include: - - command: "FUNC_ARGS='--series focal' make functional" - - command: "FUNC_ARGS='--series jammy' make functional" - with: - command: ${{ matrix.command }} - juju-channel: "3.4/stable" - lxd-channel: "5.20/stable" # tmp fix until https://github.com/canonical/charmcraft/issues/1640 - nested-containers: false - provider: "lxd" - python-version: "3.10" - timeout-minutes: 120 - tox-version: "<4" + series: ['focal', 'jammy'] + juju-channel: ['3.4/stable'] + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: Setup Juju ${{ matrix.juju-channel }} LXD environment + uses: charmed-kubernetes/actions-operator@main + with: + provider: lxd + juju-channel: ${{ matrix.juju-channel }} + lxd-channel: "5.20/stable" # tmp fix until https://github.com/canonical/charmcraft/issues/1640 + - name: Show juju information + run: | + juju version + juju controllers | grep Version -A 1 | awk '{print $9}' + - name: Run functional test + run: FUNC_ARGS='--series ${{ matrix.series }}' make functional From c0a91051541d2a358adf52293e12928c7d4cb4bd Mon Sep 17 00:00:00 2001 From: Robert Gildein Date: Thu, 11 Apr 2024 16:45:29 +0200 Subject: [PATCH 4/6] define concurrency in CI --- .github/workflows/check.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 0ad66ff..0ab4017 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -10,6 +10,10 @@ on: - '**.md' - '**.rst' +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + jobs: lint-unit: uses: canonical/bootstack-actions/.github/workflows/lint-unit.yaml@v2 @@ -21,7 +25,6 @@ jobs: python-version: ${{ matrix.python-version }} tox-version: "<4" - func: name: Functional tests runs-on: ubuntu-latest From ad347b099980f8fbef13b352f1eb5e3bc67fae79 Mon Sep 17 00:00:00 2001 From: Robert Gildein Date: Fri, 12 Apr 2024 15:03:49 +0200 Subject: [PATCH 5/6] dropping tmp usage of 5.20/stable for LXD --- .github/workflows/check.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 0ab4017..936a461 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -46,7 +46,6 @@ jobs: with: provider: lxd juju-channel: ${{ matrix.juju-channel }} - lxd-channel: "5.20/stable" # tmp fix until https://github.com/canonical/charmcraft/issues/1640 - name: Show juju information run: | juju version From a6bb7d388bd15d1bd6e51a403eebe8cb1ba3bbd8 Mon Sep 17 00:00:00 2001 From: Robert Gildein Date: Tue, 16 Apr 2024 10:10:25 +0200 Subject: [PATCH 6/6] Switch back to bootstack-actions --- .github/workflows/check.yaml | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 936a461..f72d293 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -23,32 +23,20 @@ jobs: python-version: ["3.8", "3.10"] with: python-version: ${{ matrix.python-version }} - tox-version: "<4" func: - name: Functional tests - runs-on: ubuntu-latest - timeout-minutes: 120 + uses: canonical/bootstack-actions/.github/workflows/func.yaml@v2 needs: lint-unit strategy: fail-fast: false matrix: - series: ['focal', 'jammy'] - juju-channel: ['3.4/stable'] - steps: - - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - name: Setup Juju ${{ matrix.juju-channel }} LXD environment - uses: charmed-kubernetes/actions-operator@main - with: - provider: lxd - juju-channel: ${{ matrix.juju-channel }} - - name: Show juju information - run: | - juju version - juju controllers | grep Version -A 1 | awk '{print $9}' - - name: Run functional test - run: FUNC_ARGS='--series ${{ matrix.series }}' make functional + include: + - command: "FUNC_ARGS='--series focal' make functional" + - command: "FUNC_ARGS='--series jammy' make functional" + with: + command: ${{ matrix.command }} + juju-channel: "3.4/stable" + nested-containers: false + provider: "lxd" + python-version: "3.10" + timeout-minutes: 120