From 3ff0b18a08a03fafca0ceed976420c56221cafb3 Mon Sep 17 00:00:00 2001 From: Oliver Smith Date: Fri, 10 Apr 2020 13:14:31 +0200 Subject: [PATCH] pmb.chroot.init: verify chroot channel (MR 1912) Refuse to work with a chroot that was created for another release channel. --- pmb/chroot/init.py | 3 +- pmb/config/workdir.py | 46 +++++++++++++++++++++++------ test/test_config_workdir.py | 58 +++++++++++++++++++++++++++++++++---- test/test_newapkbuild.py | 10 +++++-- 4 files changed, 100 insertions(+), 17 deletions(-) diff --git a/pmb/chroot/init.py b/pmb/chroot/init.py index 245ea576..b6954f8e 100644 --- a/pmb/chroot/init.py +++ b/pmb/chroot/init.py @@ -53,6 +53,7 @@ def init(args, suffix="native"): pmb.chroot.mount(args, suffix) setup_qemu_emulation(args, suffix) if os.path.islink(chroot + "/bin/sh"): + pmb.config.workdir.chroot_check_channel(args, suffix) copy_resolv_conf(args, suffix) pmb.chroot.apk.update_repository_list(args, suffix) return @@ -74,7 +75,7 @@ def init(args, suffix="native"): copy_resolv_conf(args, suffix) pmb.chroot.apk.update_repository_list(args, suffix) - pmb.config.workdir.chroot_save_date(args, suffix) + pmb.config.workdir.chroot_save_init(args, suffix) # Install alpine-base pmb.helpers.repo.update(args, arch) diff --git a/pmb/config/workdir.py b/pmb/config/workdir.py index 282341c8..a9d45f88 100644 --- a/pmb/config/workdir.py +++ b/pmb/config/workdir.py @@ -9,21 +9,26 @@ import os import time import pmb.config +import pmb.config.pmaports -def chroot_save_date(args, suffix): - """ Save the chroot initialization date in $WORK/workdir.cfg. """ +def chroot_save_init(args, suffix): + """ Save the chroot initialization data in $WORK/workdir.cfg. """ # Read existing cfg cfg = configparser.ConfigParser() path = args.work + "/workdir.cfg" if os.path.isfile(path): cfg.read(path) - # Set current date for chroot suffix - key = "chroot-init-dates" - if key not in cfg: - cfg[key] = {} - cfg[key][suffix] = str(int(time.time())) + # Create sections + for key in ["chroot-init-dates", "chroot-channels"]: + if key not in cfg: + cfg[key] = {} + + # Update sections + channel = pmb.config.pmaports.read_config(args)["channel"] + cfg["chroot-channels"][suffix] = channel + cfg["chroot-init-dates"][suffix] = str(int(time.time())) # Write back with open(path, "w") as handle: @@ -54,6 +59,28 @@ def chroots_outdated(args): return False +def chroot_check_channel(args, suffix): + path = args.work + "/workdir.cfg" + msg_again = "Run 'pmbootstrap zap' to delete your chroots and try again." + msg_unknown = ("Could not figure out on which release channel the" + f" '{suffix}' chroot is.") + if not os.path.exists(path): + raise RuntimeError(f"{msg_unknown} {msg_again}") + + cfg = configparser.ConfigParser() + cfg.read(path) + key = "chroot-channels" + if key not in cfg or suffix not in cfg[key]: + raise RuntimeError(f"{msg_unknown} {msg_again}") + + channel = pmb.config.pmaports.read_config(args)["channel"] + channel_cfg = cfg[key][suffix] + if channel != channel_cfg: + raise RuntimeError(f"Chroot '{suffix}' was created for the" + f" '{channel_cfg}' channel, but you are on the" + f" '{channel}' channel now. {msg_again}") + + def clean(args): """ Remove obsolete data data from workdir.cfg. :returns: None if workdir does not exist, @@ -69,9 +96,10 @@ def clean(args): cfg.read(path) # Remove entries for deleted chroots - key = "chroot-init-dates" changed = False - if key in cfg: + for key in ["chroot-init-dates", "chroot-channels"]: + if key not in cfg: + continue for suffix in cfg[key]: path_suffix = args.work + "/chroot_" + suffix if os.path.exists(path_suffix): diff --git a/test/test_config_workdir.py b/test/test_config_workdir.py index 3543412f..55dba93c 100644 --- a/test/test_config_workdir.py +++ b/test/test_config_workdir.py @@ -8,6 +8,7 @@ import time import pmb_test # noqa import pmb.config +import pmb.config.pmaports import pmb.config.workdir @@ -22,24 +23,36 @@ def args(request): return args -def test_chroot_save_date(args, tmpdir, monkeypatch): +def test_chroot_save_init(args, tmpdir, monkeypatch): # Override time.time() def fake_time(): return 1234567890.1234 monkeypatch.setattr(time, "time", fake_time) + # Pretend channel=stable in pmaports.cfg + def read_config(args): + return {"channel": "stable"} + monkeypatch.setattr(pmb.config.pmaports, "read_config", read_config) + args.work = str(tmpdir) - func = pmb.config.workdir.chroot_save_date + func = pmb.config.workdir.chroot_save_init func(args, "native") - expected = "[chroot-init-dates]\nnative = 1234567890\n\n" + expected = ("[chroot-init-dates]\n" + "native = 1234567890\n\n" + "[chroot-channels]\n" + "native = stable\n\n") with open(args.work + "/workdir.cfg", "r") as handle: assert handle.read() == expected # Write again (different code path) func(args, "buildroot_armhf") - expected = ("[chroot-init-dates]\nnative = 1234567890\n" - "buildroot_armhf = 1234567890\n\n") + expected = ("[chroot-init-dates]\n" + "native = 1234567890\n" + "buildroot_armhf = 1234567890\n\n" + "[chroot-channels]\n" + "native = stable\n" + "buildroot_armhf = stable\n\n") with open(args.work + "/workdir.cfg", "r") as handle: assert handle.read() == expected @@ -74,6 +87,41 @@ def test_chroots_outdated(args, tmpdir, monkeypatch): assert func(args) is False +def test_chroot_check_channel(args, tmpdir, monkeypatch): + func = pmb.config.workdir.chroot_check_channel + args.work = str(tmpdir) + channel = "edge" + + # Pretend to have a certain channel in pmaports.cfg + def read_config(args): + return {"channel": channel} + monkeypatch.setattr(pmb.config.pmaports, "read_config", read_config) + + # workdir.cfg does not exist + with pytest.raises(RuntimeError) as e: + func(args, "native") + assert "Could not figure out on which release channel" in str(e.value) + + # Write workdir.cfg + with open(f"{args.work}/workdir.cfg", "w") as handle: + handle.write("[chroot-channels]\nnative = stable\n\n") + + # workdir.cfg: no entry for buildroot_armhf chroot + with pytest.raises(RuntimeError) as e: + func(args, "buildroot_armhf") + assert "Could not figure out on which release channel" in str(e.value) + + # Chroot was created for wrong channel + with pytest.raises(RuntimeError) as e: + func(args, "native") + exp = "created for the 'stable' channel, but you are on the 'edge'" + assert exp in str(e.value) + + # Check runs through without raising an exception + channel = "stable" + func(args, "native") + + def test_clean(args, tmpdir): args.work = str(tmpdir) diff --git a/test/test_newapkbuild.py b/test/test_newapkbuild.py index dd1776d1..84d554bd 100644 --- a/test/test_newapkbuild.py +++ b/test/test_newapkbuild.py @@ -3,9 +3,11 @@ import glob import os import pytest +import shutil import sys import pmb_test # noqa +import pmb_test.const import pmb.build.newapkbuild import pmb.config import pmb.config.init @@ -15,7 +17,8 @@ import pmb.helpers.logging @pytest.fixture def args(tmpdir, request): import pmb.parse - sys.argv = ["pmbootstrap.py", "init"] + cfg = f"{pmb_test.const.testdata}/channels.cfg" + sys.argv = ["pmbootstrap.py", "--config-channels", cfg, "init"] args = pmb.parse.arguments() args.log = args.work + "/log_testsuite.txt" pmb.helpers.logging.init(args) @@ -24,6 +27,8 @@ def args(tmpdir, request): def test_newapkbuild(args, monkeypatch, tmpdir): + testdata = pmb_test.const.testdata + # Fake functions def confirm_true(*nargs): return True @@ -35,11 +40,12 @@ def test_newapkbuild(args, monkeypatch, tmpdir): monkeypatch.setattr(pmb.helpers.cli, "confirm", confirm_false) pmb.build.init(args) args.aports = tmpdir = str(tmpdir) + shutil.copy(f"{testdata}/pmaports.cfg", args.aports) func = pmb.build.newapkbuild # Show the help func(args, "main", ["-h"]) - assert glob.glob(tmpdir + "/*") == [] + assert glob.glob(f"{tmpdir}/*") == [f"{tmpdir}/pmaports.cfg"] # Test package pkgname = "testpackage"