pmb.chroot.init: verify chroot channel (MR 1912)

Refuse to work with a chroot that was created for another release
channel.
This commit is contained in:
Oliver Smith 2020-04-10 13:14:31 +02:00
parent ce433ae012
commit 3ff0b18a08
No known key found for this signature in database
GPG Key ID: 5AE7F5513E0885CB
4 changed files with 100 additions and 17 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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"