add --setup-hook, --essential-hook and rename --customize to --customize-hook

This commit is contained in:
Johannes 'josch' Schauer 2019-02-20 13:32:49 +01:00
parent d72a582a8b
commit 0b058c7db1
Signed by untrusted user: josch
GPG key ID: F2CBA5C78FBD83E1
2 changed files with 176 additions and 50 deletions

View file

@ -300,8 +300,8 @@ cat << END > shared/test.sh
set -eu
export LC_ALL=C.UTF-8
mount -t tmpfs -o nodev,nosuid,size=300M tmpfs /tmp
# use --customize to exercise the mounting/unmounting code of block devices in root mode
$CMD --mode=root --variant=apt --customize='mount | grep /dev/full' --customize='test "\$(echo foo | tee /dev/full 2>&1 1>/dev/null)" = "tee: /dev/full: No space left on device"' unstable /tmp/unstable-chroot.tar $mirror
# use --customize-hook to exercise the mounting/unmounting code of block devices in root mode
$CMD --mode=root --variant=apt --customize-hook='mount | grep /dev/full' --customize-hook='test "\$(echo foo | tee /dev/full 2>&1 1>/dev/null)" = "tee: /dev/full: No space left on device"' unstable /tmp/unstable-chroot.tar $mirror
tar -tf /tmp/unstable-chroot.tar | sort > tar2.txt
diff -u tar1.txt tar2.txt
rm /tmp/unstable-chroot.tar
@ -528,7 +528,60 @@ else
./run_null.sh SUDO
fi
print_header "mode=root,variant=apt: test --customize"
print_header "mode=root,variant=apt: test --setup-hook"
cat << END > shared/test.sh
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
cat << 'SCRIPT' > customize.sh
#!/bin/sh
for d in sbin lib; do ln -s usr/\$d "\$1/\$d"; mkdir -p "\$1/usr/\$d"; done
SCRIPT
chmod +x customize.sh
$CMD --mode=root --variant=apt --setup-hook='ln -s usr/bin "\$1/bin"; mkdir -p "\$1/usr/bin"' --setup-hook=./customize.sh unstable /tmp/debian-unstable $mirror
tar -C /tmp/debian-unstable --one-file-system -c . | tar -t | sort > tar2.txt
{ sed -e 's/^\.\/bin\//.\/usr\/bin\//;s/^\.\/lib\//.\/usr\/lib\//;s/^\.\/sbin\//.\/usr\/sbin\//;' tar1.txt; echo ./bin; echo ./lib; echo ./sbin; } | sort -u | diff -u - tar2.txt
rm customize.sh
rm -r /tmp/debian-unstable
END
if [ "$HAVE_QEMU" = "yes" ]; then
./run_qemu.sh
else
./run_null.sh SUDO
fi
print_header "mode=root,variant=apt: test --essential-hook"
cat << END > shared/test.sh
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
cat << 'SCRIPT' > customize.sh
#!/bin/sh
echo tzdata tzdata/Zones/Europe select Berlin | chroot "\$1" debconf-set-selections
SCRIPT
chmod +x customize.sh
$CMD --mode=root --variant=apt --include=tzdata --essential-hook='echo tzdata tzdata/Areas select Europe | chroot "\$1" debconf-set-selections' --essential-hook=./customize.sh unstable /tmp/debian-unstable $mirror
echo Europe/Berlin | cmp /tmp/debian-unstable/etc/timezone
tar -C /tmp/debian-unstable --one-file-system -c . | tar -t | sort \
| grep -v '^./etc/localtime' \
| grep -v '^./etc/timezone' \
| grep -v '^./usr/sbin/tzconfig' \
| grep -v '^./usr/share/doc/tzdata' \
| grep -v '^./usr/share/zoneinfo' \
| grep -v '^./var/lib/dpkg/info/tzdata.' \
| grep -v '^./var/log/apt/eipp.log.xz$' \
> tar2.txt
diff -u tar1.txt tar2.txt
rm customize.sh
rm -r /tmp/debian-unstable
END
if [ "$HAVE_QEMU" = "yes" ]; then
./run_qemu.sh
else
./run_null.sh SUDO
fi
print_header "mode=root,variant=apt: test --customize-hook"
cat << END > shared/test.sh
#!/bin/sh
set -eu
@ -539,7 +592,7 @@ chroot "\$1" whoami > "\$1/output2"
chroot "\$1" pwd >> "\$1/output2"
SCRIPT
chmod +x customize.sh
$CMD --mode=root --variant=apt --customize='chroot "\$1" sh -c "whoami; pwd" > "\$1/output1"' --customize=./customize.sh unstable /tmp/debian-unstable $mirror
$CMD --mode=root --variant=apt --customize-hook='chroot "\$1" sh -c "whoami; pwd" > "\$1/output1"' --customize-hook=./customize.sh unstable /tmp/debian-unstable $mirror
printf "root\n/\n" | cmp /tmp/debian-unstable/output1
printf "root\n/\n" | cmp /tmp/debian-unstable/output2
rm /tmp/debian-unstable/output1

View file

@ -875,6 +875,40 @@ sub run_chroot(&$) {
}
}
sub run_hooks($$) {
my $name = shift;
my $options = shift;
if (scalar @{$options->{"${name}_hook"}} == 0) {
return;
}
my $runner = sub {
foreach my $script (@{$options->{"${name}_hook"}}) {
if ( -x $script || $script !~ m/[^\w@\%+=:,.\/-]/a) {
info "running --$name-hook directly: $script $options->{root}";
# execute it directly if it's an executable file
# or if it there are no shell metacharacters
# (the /a regex modifier makes \w match only ASCII)
0 == system($script, $options->{root}) or error "command failed: $script";
} else {
info "running --$name-hook in shell: sh -c '$script' exec $options->{root}";
# otherwise, wrap everything in sh -c
0 == system('sh', '-c', $script, 'exec', $options->{root}) or error "command failed: $script";
}
}
};
if ($name eq 'setup') {
# execute directly without mounting anything (the mount points do not
# exist yet)
&{$runner}();
} else {
run_chroot \&$runner, $options;
}
}
sub setup {
my $options = shift;
@ -1068,6 +1102,16 @@ sub setup {
# into account.
$ENV{"APT_CONFIG"} = "$tmpfile";
# setting PATH for chroot, ldconfig, start-stop-daemon...
if (defined $ENV{PATH} && $ENV{PATH} ne "") {
$ENV{PATH} = "$ENV{PATH}:/usr/sbin:/usr/bin:/sbin:/bin";
} else {
$ENV{PATH} = "/usr/sbin:/usr/bin:/sbin:/bin";
}
# run setup hooks
run_hooks('setup', $options);
info "running apt-get update...";
run_apt_progress({ ARGV => ['apt-get', 'update'] });
@ -1085,13 +1129,6 @@ sub setup {
}
}
# setting PATH for chroot, ldconfig, start-stop-daemon...
if (defined $ENV{PATH} && $ENV{PATH} ne "") {
$ENV{PATH} = "$ENV{PATH}:/usr/sbin:/usr/bin:/sbin:/bin";
} else {
$ENV{PATH} = "/usr/sbin:/usr/bin:/sbin:/bin";
}
my %pkgs_to_install;
if (defined $options->{include}) {
for my $pkg (split /,/, $options->{include}) {
@ -1307,6 +1344,9 @@ sub setup {
if (any { $_ eq $options->{variant} } ('extract', 'custom')) {
# nothing to do
} elsif (any { $_ eq $options->{variant} } ('essential', 'apt', 'standard', 'important', 'required', 'buildd', 'minbase')) {
# run essential hooks
run_hooks('essential', $options);
if (%pkgs_to_install) {
run_apt_progress({
ARGV => ['apt-get', '--yes',
@ -1495,6 +1535,9 @@ sub setup {
unlink "$options->{root}/$deb" or error "cannot unlink $deb";
}
# run essential hooks
run_hooks('essential', $options);
if (%pkgs_to_install) {
# some packages have to be installed from the outside before anything
# can be installed from the inside.
@ -1592,23 +1635,7 @@ sub setup {
error "unknown mode: $options->{mode}";
}
if (scalar @{$options->{customize}} > 0) {
run_chroot {
foreach my $script (@{$options->{customize}}) {
if ( -x $script || $script !~ m/[^\w@\%+=:,.\/-]/a) {
info "running customize script directly: $script $options->{root}";
# execute it directly if it's an executable file
# or if it there are no shell metacharacters
# (the /a regex modifier makes \w match only ASCII)
0 == system($script, $options->{root}) or error "customization script failed: $script";
} else {
info "running customize script in shell: sh -c '$script' exec $options->{root}";
# otherwise, wrap everything in sh -c
0 == system('sh', '-c', $script, 'exec', $options->{root}) or error "customization script failed: $script";
}
}
} $options;
}
run_hooks('customize', $options);
# clean up temporary configuration file
unlink "$options->{root}/etc/apt/apt.conf.d/00mmdebstrap" or error "failed to unlink /etc/apt/apt.conf.d/00mmdebstrap: $!";
@ -1663,7 +1690,9 @@ sub main() {
dpkgopts => [],
aptopts => [],
noop => [],
customize => [],
setup_hook => [],
essential_hook => [],
customize_hook => [],
};
chomp ($options->{architectures} = `dpkg --print-architecture`);
Getopt::Long::Configure ("bundling");
@ -1685,8 +1714,10 @@ sub main() {
'resolve-deps' => sub { push @{$options->{noop}}, 'resolve-deps'; },
'merged-usr' => sub { push @{$options->{noop}}, 'merged-usr'; },
'no-merged-usr' => sub { push @{$options->{noop}}, 'no-merged-usr'; },
# option is hidden until I'm happy with it
'customize=s@' => \$options->{customize},
# hook options are hidden until I'm happy with them
'setup-hook=s@' => \$options->{setup_hook},
'essential-hook=s@' => \$options->{essential_hook},
'customize-hook=s@' => \$options->{customize_hook},
) or pod2usage(-exitval => 2, -verbose => 1);
foreach my $arg (@{$options->{noop}}) {
@ -2465,23 +2496,65 @@ running mmdebstrap.
=begin comment
=item B<--customize>=I<command>
=item B<--setup-hook>=I<command>
Execute arbitrary I<command>s after the chroot is set up and before it is
cleaned up. Can be specified multiple times. The commands are executed in the
order in which they are given on the command line. If I<command> is an
existing executable file or if I<command> does not contain any shell
metacharacters, then I<command> is directly exec-ed with the path to the
chroot directory passed as the first argument. Otherwise, I<command> is
executed under I<sh> and the chroot directory can be accessed via I<$1>.
Execute arbitrary I<command>s right after initial setup (directory creation,
configuration of apt and dpkg, ...) but before any packages are downloaded or
installed. At that point, the chroot directory does not contain any
executables and thus cannot be chroot-ed into. The option can be specified
multiple times and the commands are executed in the order in which they are
given on the command line. If I<command> is an existing executable file or if
I<command> does not contain any shell metacharacters, then I<command> is
directly exec-ed with the path to the chroot directory passed as the first
argument. Otherwise, I<command> is executed under I<sh> and the chroot
directory can be accessed via I<$1>. All environment variables used by
B<mmdebstrap> (like C<APT_CONFIG>, C<DEBIAN_FRONTEND>, C<LC_ALL> and C<PATH>)
are preserved.
Examples:
--customize='chroot "$1" passwd --delete root'
--customize='chroot "$1" useradd --home-dir /home/user --create-home user'
--customize='chroot "$1" passwd --delete user'
--customize='echo host > "$1/etc/hostname"'
--customize=/usr/share/autopkgtest/setup-commands/setup-testbed
--setup-hook='for d in bin sbin lib; do ln -s usr/$d "$1/$d"; mkdir -p "$1/usr/$d"; done'
=item B<--essential-hook>=I<command>
Execute arbitrary I<command>s after the Essential:yes packages have been
installed but before installing the remaining packages. The hook is not
executed for the B<extract> and B<custom> variants. The option can be
specified multiple times and the commands are executed in the order in which
they are given on the command line. If I<command> is an existing executable
file or if I<command> does not contain any shell metacharacters, then
I<command> is directly exec-ed with the path to the chroot directory passed as
the first argument. Otherwise, I<command> is executed under I<sh> and the
chroot directory can be accessed via I<$1>. All environment variables used by
B<mmdebstrap> (like C<APT_CONFIG>, C<DEBIAN_FRONTEND>, C<LC_ALL> and C<PATH>)
are preserved.
Examples:
--essential-hook='echo unattended-upgrades unattended-upgrades/enable_auto_updates boolean true | chroot "$1" debconf-set-selections'
--essential-hook='echo tzdata tzdata/Areas select Europe | chroot "$1" debconf-set-selections'
--essential-hook='echo tzdata tzdata/Zones/Europe select Berlin | chroot "$1" debconf-set-selections'
=item B<--customize-hook>=I<command>
Execute arbitrary I<command>s after the chroot is set up and all packages got
installed but before final cleanup actions are carried out. The option can be
specified multiple times and the commands are executed in the order in which
they are given on the command line. If I<command> is an existing executable
file or if I<command> does not contain any shell metacharacters, then
I<command> is directly exec-ed with the path to the chroot directory passed as
the first argument. Otherwise, I<command> is executed under I<sh> and the
chroot directory can be accessed via I<$1>. All environment variables used by
B<mmdebstrap> (like C<APT_CONFIG>, C<DEBIAN_FRONTEND>, C<LC_ALL> and C<PATH>)
are preserved.
Examples:
--customize-hook='chroot "$1" passwd --delete root'
--customize-hook='chroot "$1" useradd --home-dir /home/user --create-home user'
--customize-hook='chroot "$1" passwd --delete user'
--customize-hook='echo host > "$1/etc/hostname"'
--customize-hook=/usr/share/autopkgtest/setup-commands/setup-testbed
=end comment
@ -2643,12 +2716,12 @@ Use as debootstrap replacement in sbuild-createchroot:
Use as replacement for autopkgtest-build-qemu and vmdb2:
$ mmdebstrap --variant=important --include=linux-image-amd64 \
--customize='chroot "$1" passwd --delete root' \
--customize='chroot "$1" useradd --home-dir /home/user --create-home user' \
--customize='chroot "$1" passwd --delete user' \
--customize='echo host > "$1/etc/hostname"' \
--customize='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' \
--customize=/usr/share/autopkgtest/setup-commands/setup-testbed \
--customize-hook='chroot "$1" passwd --delete root' \
--customize-hook='chroot "$1" useradd --home-dir /home/user --create-home user' \
--customize-hook='chroot "$1" passwd --delete user' \
--customize-hook='echo host > "$1/etc/hostname"' \
--customize-hook='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' \
--customize-hook=/usr/share/autopkgtest/setup-commands/setup-testbed \
unstable debian-unstable.tar
$ cat << END > extlinux.conf
> default linux