diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 6560d6f..f72d293 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 @@ -19,7 +23,6 @@ jobs: 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 @@ -32,9 +35,8 @@ 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" timeout-minutes: 120 - tox-version: "<4" 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