mirror_lxc/hooks/ubuntu-cloud-prep
Scott Moser d24d56d7ee ubuntu-cloud-prep: patch /sbin/start for overlayfs
upstart depends on inotify, and overlayfs does not support inotify.

That means that the following results in 'tgt' not running. tgt is simply
used here as an example of a service that installs an upstart job and
starts it on package install.
 lxc-clone -s -B overlayfs -o source-precise-amd64 -n test1
 lxc-start -n test1
 ..
 apt-get install tgt

The change here is to modify /sbin/start inside the container so that when
something explicitly tries 'start', it results in an explicit call to
'initctl reload-configuration' so that upstart is aware of the newly
placed job.

Should overlayfs ever gain inotify support, this should still not cause
any harm.

Signed-off-by: Scott Moser <smoser@ubuntu.com>
Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
2013-08-16 16:05:40 -05:00

223 lines
7.1 KiB
Bash
Executable File

#!/bin/bash
## If the container being cloned has one or more lxc.hook.clone
## specified, then the specified hooks will be called for the new
## container. The arguments passed to the clone hook are:
## 1. the container name
## 2. a section ('lxc')
## 3. hook type ('clone')
## 4. .. additional arguments to lxc-clone
## Environment variables:
## LXC_ROOTFS_MOUNT: path to the root filesystem
## LXC_CONFIG_FILE: path to config file
## LXC_SRC_NAME: old container name
## LXC_ROOTFS_PATH: path or device on which the root fs is located
set -f
VERBOSITY="0"
error() { echo "$@" 1>&2; }
debug() { [ "$VERBOSITY" -ge "$1" ] || return; shift; error "$@"; }
fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
prep_usage() {
cat <<EOF
Usage: ${0##*/} [options] root-dir
root-dir is the root directory to operate on
[ -C | --cloud ]: do not configure a datasource. incompatible with
options marked '[ds]'
[ -i | --instance-id]: instance-id for cloud-init, defaults to random [ds]
[ -L | --nolocales ]: Do not copy host's locales into container
[ -S | --auth-key ]: ssh public key file for datasource [ds]
[ -u | --userdata ]: user-data file for cloud-init [ds]
EOF
}
write_patched_start() {
cat > "$1" <<"EOF"
#!/bin/bash
## This is a wrapper around upstart's /sbin/start to ensure that
## calling 'start' on a job after writing it to /etc/init will function
## correctly despite broken/missing support for inotify in overlayfs.
##
real() { exec -a /sbin/start "/sbin/start.real" "$@"; }
# no args or not root
[ $# -ne 0 -a "$UID" = "0" ] || real "$@"
job=""
# find first argument that doesn't start with '-' as 'job'
for x in "$@"; do
[ "${x#-}" = "$x" ] && { job="$x"; break; }
done
# if job isn't there, no reason to check further
[ -n "$job" ] && [ -f "/etc/init/$job.conf" ] || real "$@"
# on Unknown, 'status' exits 1, and prints 'Unknown job' to stderr.
out=$(status "$@" 2>&1)
[ $? -eq 1 -a "${out#*nknown job}" != "$out" ] || real "$@"
initctl reload-configuration >/dev/null 2>&1
real "$@"
EOF
chmod 755 "$1"
}
patch_start() {
# patch /sbin/start inside root_d to deal with lack of inotify
local root_d="$1"
# already patched
[ -f "$root_d/sbin/start.real" ] &&
{ debug 1 "$root_d 'start' seems already patched"; return 1; }
debug 1 "patching /sbin/start in $root_d"
chroot "$root_d" dpkg-divert --local --rename \
--divert /sbin/start.real --add /sbin/start ||
{ error "failed to patch /sbin/start for overlayfs"; return 1; }
write_patched_start "$root_d/sbin/start"
}
prep() {
local short_opts="Chi:L:S:u:v"
local long_opts="auth-key:,cloud,help,hostid:,name:,nolocales:,patch-start,userdata:,verbose"
local getopt_out getopt_ret
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@" 2>/dev/null) ||
:
getopt_ret=$?
if [ $getopt_ret -eq 0 ]; then
eval set -- "${getopt_out}" ||
{ error "Unexpected error reading usage"; return 1; }
fi
local cur="" next=""
local userdata="" hostid="" authkey="" locales=1 cloud=0 name=""
local patch_start=0
while [ $# -ne 0 ]; do
cur="$1"; next="$2";
case "$cur" in
-C|--cloud) cloud=1;;
-h|--help) prep_usage; return 0;;
--name) name="$next";;
-i|--hostid) hostid="$next";;
-L|--nolocales) locales=0;;
--patch-start) patch_start=1;;
-S|--auth-key)
[ -f "$next" ] ||
{ error "--auth-key: '$next' not a file"; return 1; }
authkey="$next";;
-u|--userdata)
[ -f "$next" ] ||
{ error "--userdata: '$next' not a file"; return 1; }
userdata="$next";;
-v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
--) shift; break;;
esac
shift;
done
[ $# -eq 1 ] || {
prep_usage 1>&2;
error "expected 1 arguments, got ($_LXC_HOOK) $#: $*";
return 1;
}
local root_d="$1";
if [ $getopt_ret -ne 0 -a "$_LXC_HOOK" = "clone" ]; then
# getopt above failed, but we were called from lxc clone. there might
# be multiple clone hooks and the args provided here not for us. This
# seems like not the greatest interface, so all we'll do is mention it.
error "${0##*}: usage failed, continuing with defaults"
fi
[ "$patch_start" -eq 0 ] || patch_start "$root_d" ||
{ error "failed to patch start for overlayfs"; return 1; }
local seed_d=""
seed_d="$root_d/var/lib/cloud/seed/nocloud-net"
if [ $cloud -eq 1 ]; then
debug 1 "--cloud provided, not modifying seed in '$seed_d'"
else
if [ -z "$hostid" ]; then
hostid=$(uuidgen | cut -c -8) && [ -n "$hostid" ] ||
{ error "failed to get hostid"; return 1; }
fi
mkdir -p "$seed_d" ||
{ error "failed to create '$seed_d'"; return 1; }
echo "instance-id: lxc-$hostid" > "$seed_d/meta-data" ||
{ error "failed to write to $seed_d/meta-data"; return 1; }
if [ -n "$authkey" ]; then
{
echo "public-keys:" &&
sed -e '/^$/d' -e 's,^,- ,' "$authkey"
} >> "$seed_d/meta-data"
[ $? -eq 0 ] ||
{ error "failed to write public keys to metadata"; return 1; }
fi
local larch="usr/lib/locale/locale-archive"
if [ $locales -eq 1 ]; then
cp "/$larch" "$root_d/$larch" || {
error "failed to cp '/$larch' '$root_d/$larch'";
return 1;
}
fi
if [ -z "$MIRROR" ]; then
MIRROR="http://archive.ubuntu.com/ubuntu"
fi
if [ -n "$userdata" ]; then
cp "$userdata" "$seed_d/user-data"
else
{
local lc=$(locale | awk -F= '/LANG=/ {print $NF; }')
echo "#cloud-config"
echo "output: {all: '| tee -a /var/log/cloud-init-output.log'}"
echo "apt_mirror: $MIRROR"
echo "manage_etc_hosts: localhost"
[ -z "$LANG" ] || echo "locale: $LANG";
echo "password: ubuntu"
echo "chpasswd: { expire: false; }"
} > "$seed_d/user-data"
fi
[ $? -eq 0 ] || {
error "failed to write user-data write to '$seed_d/user-data'";
return 1;
}
fi
}
main() {
# main just joins 2 modes of being called. from user one from lxc clone
local _LXC_HOOK
if [ -n "$LXC_ROOTFS_MOUNT" -a "$3" = "clone" ]; then
_LXC_HOOK="clone"
local name="$1" pstart=""
shift 3
# if mountpoint is overlayfs then add '--patch-start'
[ "${LXC_ROOTFS_PATH#overlayfs}" != "${LXC_ROOTFS_PATH}" ] &&
pstart="--patch-start"
debug 1 prep "--name=$name" $pstart "$LXC_ROOTFS_MOUNT" "$@"
prep "--name=$name" $pstart "$LXC_ROOTFS_MOUNT" "$@"
else
_LXC_HOOK=""
prep "$@"
fi
return $?
}
main "$@"
# vi: ts=4 expandtab