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 set -eu
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8
mount -t tmpfs -o nodev,nosuid,size=300M tmpfs /tmp mount -t tmpfs -o nodev,nosuid,size=300M tmpfs /tmp
# use --customize to exercise the mounting/unmounting code of block devices in root mode # use --customize-hook 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 $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 tar -tf /tmp/unstable-chroot.tar | sort > tar2.txt
diff -u tar1.txt tar2.txt diff -u tar1.txt tar2.txt
rm /tmp/unstable-chroot.tar rm /tmp/unstable-chroot.tar
@ -528,7 +528,60 @@ else
./run_null.sh SUDO ./run_null.sh SUDO
fi 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 cat << END > shared/test.sh
#!/bin/sh #!/bin/sh
set -eu set -eu
@ -539,7 +592,7 @@ chroot "\$1" whoami > "\$1/output2"
chroot "\$1" pwd >> "\$1/output2" chroot "\$1" pwd >> "\$1/output2"
SCRIPT SCRIPT
chmod +x customize.sh 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/output1
printf "root\n/\n" | cmp /tmp/debian-unstable/output2 printf "root\n/\n" | cmp /tmp/debian-unstable/output2
rm /tmp/debian-unstable/output1 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 { sub setup {
my $options = shift; my $options = shift;
@ -1068,6 +1102,16 @@ sub setup {
# into account. # into account.
$ENV{"APT_CONFIG"} = "$tmpfile"; $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..."; info "running apt-get update...";
run_apt_progress({ ARGV => ['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; my %pkgs_to_install;
if (defined $options->{include}) { if (defined $options->{include}) {
for my $pkg (split /,/, $options->{include}) { for my $pkg (split /,/, $options->{include}) {
@ -1307,6 +1344,9 @@ sub setup {
if (any { $_ eq $options->{variant} } ('extract', 'custom')) { if (any { $_ eq $options->{variant} } ('extract', 'custom')) {
# nothing to do # nothing to do
} elsif (any { $_ eq $options->{variant} } ('essential', 'apt', 'standard', 'important', 'required', 'buildd', 'minbase')) { } elsif (any { $_ eq $options->{variant} } ('essential', 'apt', 'standard', 'important', 'required', 'buildd', 'minbase')) {
# run essential hooks
run_hooks('essential', $options);
if (%pkgs_to_install) { if (%pkgs_to_install) {
run_apt_progress({ run_apt_progress({
ARGV => ['apt-get', '--yes', ARGV => ['apt-get', '--yes',
@ -1495,6 +1535,9 @@ sub setup {
unlink "$options->{root}/$deb" or error "cannot unlink $deb"; unlink "$options->{root}/$deb" or error "cannot unlink $deb";
} }
# run essential hooks
run_hooks('essential', $options);
if (%pkgs_to_install) { if (%pkgs_to_install) {
# some packages have to be installed from the outside before anything # some packages have to be installed from the outside before anything
# can be installed from the inside. # can be installed from the inside.
@ -1592,23 +1635,7 @@ sub setup {
error "unknown mode: $options->{mode}"; error "unknown mode: $options->{mode}";
} }
if (scalar @{$options->{customize}} > 0) { run_hooks('customize', $options);
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;
}
# clean up temporary configuration file # 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: $!"; 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 => [], dpkgopts => [],
aptopts => [], aptopts => [],
noop => [], noop => [],
customize => [], setup_hook => [],
essential_hook => [],
customize_hook => [],
}; };
chomp ($options->{architectures} = `dpkg --print-architecture`); chomp ($options->{architectures} = `dpkg --print-architecture`);
Getopt::Long::Configure ("bundling"); Getopt::Long::Configure ("bundling");
@ -1685,8 +1714,10 @@ sub main() {
'resolve-deps' => sub { push @{$options->{noop}}, 'resolve-deps'; }, 'resolve-deps' => sub { push @{$options->{noop}}, 'resolve-deps'; },
'merged-usr' => sub { push @{$options->{noop}}, 'merged-usr'; }, 'merged-usr' => sub { push @{$options->{noop}}, 'merged-usr'; },
'no-merged-usr' => sub { push @{$options->{noop}}, 'no-merged-usr'; }, 'no-merged-usr' => sub { push @{$options->{noop}}, 'no-merged-usr'; },
# option is hidden until I'm happy with it # hook options are hidden until I'm happy with them
'customize=s@' => \$options->{customize}, 'setup-hook=s@' => \$options->{setup_hook},
'essential-hook=s@' => \$options->{essential_hook},
'customize-hook=s@' => \$options->{customize_hook},
) or pod2usage(-exitval => 2, -verbose => 1); ) or pod2usage(-exitval => 2, -verbose => 1);
foreach my $arg (@{$options->{noop}}) { foreach my $arg (@{$options->{noop}}) {
@ -2465,23 +2496,65 @@ running mmdebstrap.
=begin comment =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 Execute arbitrary I<command>s right after initial setup (directory creation,
cleaned up. Can be specified multiple times. The commands are executed in the configuration of apt and dpkg, ...) but before any packages are downloaded or
order in which they are given on the command line. If I<command> is an installed. At that point, the chroot directory does not contain any
existing executable file or if I<command> does not contain any shell executables and thus cannot be chroot-ed into. The option can be specified
metacharacters, then I<command> is directly exec-ed with the path to the multiple times and the commands are executed in the order in which they are
chroot directory passed as the first argument. Otherwise, I<command> is given on the command line. If I<command> is an existing executable file or if
executed under I<sh> and the chroot directory can be accessed via I<$1>. 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: Examples:
--customize='chroot "$1" passwd --delete root' --setup-hook='for d in bin sbin lib; do ln -s usr/$d "$1/$d"; mkdir -p "$1/usr/$d"; done'
--customize='chroot "$1" useradd --home-dir /home/user --create-home user'
--customize='chroot "$1" passwd --delete user' =item B<--essential-hook>=I<command>
--customize='echo host > "$1/etc/hostname"'
--customize=/usr/share/autopkgtest/setup-commands/setup-testbed 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 =end comment
@ -2643,12 +2716,12 @@ Use as debootstrap replacement in sbuild-createchroot:
Use as replacement for autopkgtest-build-qemu and vmdb2: Use as replacement for autopkgtest-build-qemu and vmdb2:
$ mmdebstrap --variant=important --include=linux-image-amd64 \ $ mmdebstrap --variant=important --include=linux-image-amd64 \
--customize='chroot "$1" passwd --delete root' \ --customize-hook='chroot "$1" passwd --delete root' \
--customize='chroot "$1" useradd --home-dir /home/user --create-home user' \ --customize-hook='chroot "$1" useradd --home-dir /home/user --create-home user' \
--customize='chroot "$1" passwd --delete user' \ --customize-hook='chroot "$1" passwd --delete user' \
--customize='echo host > "$1/etc/hostname"' \ --customize-hook='echo host > "$1/etc/hostname"' \
--customize='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' \ --customize-hook='echo "127.0.0.1 localhost host" > "$1/etc/hosts"' \
--customize=/usr/share/autopkgtest/setup-commands/setup-testbed \ --customize-hook=/usr/share/autopkgtest/setup-commands/setup-testbed \
unstable debian-unstable.tar unstable debian-unstable.tar
$ cat << END > extlinux.conf $ cat << END > extlinux.conf
> default linux > default linux