From: Scott Moser Date: Thu, 8 Aug 2013 18:16:59 +0000 (+0100) Subject: add a clone hook for ubuntu-cloud images X-Git-Tag: lxc-1.0.0.alpha1~1^2~101 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=65d8ae9c4a66f5ca85289c02dc06d63261c84619;p=thirdparty%2Flxc.git add a clone hook for ubuntu-cloud images This allows ability to now specify '--userdata' arguments to 'create' or to 'clone'. So now, the following means very fast start of instances with different user-data. $ sudo lxc-create -t ubuntu-cloud -n precise -- \ -r precise --arch amd64 $ sudo lxc-clone -B overlayfs -o precise -s -n ephem1 \ --userdata="my.userdata1" $ sudo lxc-clone -B overlayfs -o precise -s -n ephem2 \ --userdata="my.userdata2" Also present here is * an improvement to the static list of Ubuntu releases. It uses ubuntu-distro-info if available degrades back to a static list on failure. * moving of the replacement variables to the top of the create template This is just to make it more obvious what is being replaced and put them in a single location. Signed-off-by: Scott Moser --- diff --git a/hooks/ubuntu-cloud-prep b/hooks/ubuntu-cloud-prep new file mode 100755 index 000000000..d9a32c4cb --- /dev/null +++ b/hooks/ubuntu-cloud-prep @@ -0,0 +1,162 @@ +#!/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 first +## 3 arguments passed to the clone hook will be: +## 1. the container name +## 2. a section ('lxc') +## 3. hook type ('clone') +## 4. .. additional arguments to lxc-clone +## Environment variables: +## LXC_ROOTFS_MOUNT: path under which the container's root fs is mounted. +## LXC_CONFIG_FILE: The configuration file pathname +## LXC_SRC_NAME: old container name +## LXC_ROOTFS_PATH: path or device on which the root fs is located + +VERBOSITY="" + +error() { echo "$@" 1>&2; } +debug() { [ "$1" -ge "$VERBOSITY" ] || return; shift; error "$@"; } +fail() { [ $# -eq 0 ] || error "$@"; exit 1; } + +prep_usage() { +cat </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="" + 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;; + -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 + + 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 + + { + 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" + [ $? -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" + shift 3 + debug 1 prep "--name=$name" "$LXC_ROOTFS_MOUNT" "$@" + prep "--name=$name" "$LXC_ROOTFS_MOUNT" "$@" + else + _LXC_HOOK="" + prep "$@" + fi + return $? +} + +main "$@" + +# vi: ts=4 expandtab diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in index 25be52e0a..81720f5e1 100644 --- a/templates/lxc-ubuntu-cloud.in +++ b/templates/lxc-ubuntu-cloud.in @@ -1,7 +1,7 @@ #!/bin/bash -# template script for generating ubuntu container for LXC based on released cloud -# images +# template script for generating ubuntu container for LXC based on released +# cloud images. # # Copyright © 2012 Serge Hallyn # @@ -21,6 +21,10 @@ set -e +STATE_DIR="@LOCALSTATEDIR@" +HOOK_DIR="@LXCHOOKDIR@" +CLONE_HOOK_FN="$HOOK_DIR/ubuntu-cloud-prep" + if [ -r /etc/default/lxc ]; then . /etc/default/lxc fi @@ -75,6 +79,8 @@ lxc.cap.drop = sys_module mac_admin mac_override sys_time #lxc.aa_profile = lxc-container-default-with-nesting #lxc.hook.mount = /usr/share/lxc/hooks/mountcgroups +lxc.hook.clone = ${CLONE_HOOK_FN} + lxc.cgroup.devices.deny = a # Allow any mknod (but not using the node) lxc.cgroup.devices.allow = c *:* m @@ -143,17 +149,13 @@ Generic Options [ -r | --release ]: Release name of container, defaults to host [ --rootfs ]: Path in which rootfs will be placed [ -a | --arch ]: Arhcitecture of container, defaults to host arcitecture -[ -C | --cloud ]: Configure container for use with meta-data service, defaults to no [ -T | --tarball ]: Location of tarball [ -d | --debug ]: Run with 'set -x' to debug errors [ -s | --stream]: Use specified stream rather than 'released' -Options, mutually exclusive of "-C" and "--cloud": - [ -i | --hostid ]: HostID for cloud-init, defaults to random string - [ -u | --userdata ]: Cloud-init user-data file to configure container on start - [ -S | --auth-key ]: SSH Public key file to inject into container - [ -L | --nolocales ]: Do not copy host's locales into container - +Additionally, clone hooks can be passed through (ie, --userdata). For those, +see: + $CLONE_HOOK_FN --help EOF return 0 } @@ -165,14 +167,15 @@ if [ $? -ne 0 ]; then fi eval set -- "$options" -release=lucid +# default release is precise, or the systems release if recognized +release=precise if [ -f /etc/lsb-release ]; then . /etc/lsb-release - case "$DISTRIB_CODENAME" in - lucid|natty|oneiric|precise|quantal) - release=$DISTRIB_CODENAME - ;; - esac + rels=$(ubuntu-distro-info --supported 2>/dev/null) || + rels="lucid natty oneiric precise quantal raring saucy" + for r in $rels; do + [ "$DISTRIB_CODENAME" = "$r" ] && release="$r" + done fi # Code taken from debootstrap @@ -201,6 +204,7 @@ cloud=0 locales=1 flushcache=0 stream="released" +cloneargs=() while true do case "$1" in @@ -210,20 +214,22 @@ do -F|--flush-cache) flushcache=1; shift 1;; -r|--release) release=$2; shift 2;; -a|--arch) arch=$2; shift 2;; - -i|--hostid) host_id=$2; shift 2;; - -u|--userdata) userdata=$2; shift 2;; - -C|--cloud) cloud=1; shift 1;; - -S|--auth-key) auth_key=$2; shift 2;; - -L|--no_locales) locales=0; shift 1;; -T|--tarball) tarball=$2; shift 2;; -d|--debug) debug=1; shift 1;; -s|--stream) stream=$2; shift 2;; - --rootfs) rootfs=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; + -L|--no?locales) cloneargs[${#cloneargs[@]}]="--no-locales"; shift 1;; + -i|--hostid) cloneargs[${#cloneargs[@]}]="--hostid=$2"; shift 2;; + -u|--userdata) cloneargs[${#cloneargs[@]}]="--userdata=$2"; shift 2;; + -C|--cloud) cloneargs[${#cloneargs[@]}]="--cloud"; shift 1;; + -S|--auth-key) cloneargs[${#cloneargs[@]}]="--auth-key=$2"; shift 2;; --) shift 1; break ;; *) break ;; esac done +cloneargs=( "--name=$name" "${cloneargs[@]}" ) + if [ $debug -eq 1 ]; then set -x fi @@ -263,24 +269,6 @@ if [ "$stream" != "daily" -a "$stream" != "released" ]; then exit 1 fi -if [ -n "$userdata" ]; then - if [ ! -f "$userdata" ]; then - echo "Userdata ($userdata) does not exist" - exit 1 - else - userdata=`readlink -f $userdata` - fi -fi - -if [ -n "$auth_key" ]; then - if [ ! -f "$auth_key" ]; then - echo "--auth-key=${auth_key} must reference a file" - exit 1 - fi - auth_key=$(readlink -f "${auth_key}") || - { echo "failed to get full path for auth_key"; exit 1; } -fi - if [ -z "$path" ]; then echo "'path' parameter is required" exit 1 @@ -306,7 +294,7 @@ type wget # determine the url, tarball, and directory names # download if needed -cache="@LOCALSTATEDIR@/cache/lxc/cloud-$release" +cache="$STATE_DIR/cache/lxc/cloud-$release" mkdir -p $cache @@ -383,74 +371,22 @@ do_extract_rootfs() { mkdir -p $rootfs cd $rootfs tar -zxf $cache/$filename - - - if [ $cloud -eq 0 ]; then - echo "Configuring for running outside of a cloud environment" - echo "If you want to configure for a cloud evironment, please use '-- -C' to create the container" - - seed_d=$rootfs/var/lib/cloud/seed/nocloud-net - rhostid=$(uuidgen | cut -c -8) - host_id=${hostid:-$rhostid} - mkdir -p $seed_d - - cat > "$seed_d/meta-data" <> "$seed_d/meta-data" - [ $? -eq 0 ] || - { echo "failed to write public keys to metadata"; exit 1; } - fi - - rm $rootfs/etc/hostname - - if [ $locales -eq 1 ]; then - cp /usr/lib/locale/locale-archive $rootfs/usr/lib/locale/locale-archive - fi - - if [ -f "$userdata" ]; then - echo "Using custom user-data" - cp $userdata $seed_d/user-data - else - - if [ -z "$MIRROR" ]; then - MIRROR="http://archive.ubuntu.com/ubuntu" - fi - - cat > "$seed_d/user-data" <@LOCALSTATEDIR@/lock/subsys/lxc-ubuntu-cloud + ) 200>"$STATE_DIR/lock/subsys/lxc-ubuntu-cloud" fi copy_configuration $path $rootfs $name $arch $release +"$CLONE_HOOK_FN" "${cloneargs[@]}" "$rootfs" + echo "Container $name created." exit 0