lxc-cirros.in revision 58a46e06210a6321c530735f15f66eb648c4657d
#!/bin/bash
# template script for generating ubuntu container for LXC
#
# This script consolidates and extends the existing lxc ubuntu scripts
#
# Copyright � 2013 Canonical Ltd.
# Author: Scott Moser <scott.moser@canonical.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2, as
# published by the Free Software Foundation.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
VERBOSITY=0
TEMP_D=""
DOWNLOAD_URL="http://download.cirros-cloud.net/"
CACHE_D="/var/cache/lxc/cirros"
CACHE_D="@LOCALSTATEDIR@/cache/lxc/cirros"
TMP_FILE=""
UNAME_M=$(uname -m)
ARCHES=( i386 x86_64 amd64 arm )
STREAMS=( released devel )
BUILD="standard"
DEF_VERSION="released"
case "${UNAME_M}" in
i?86) DEF_ARCH="i386";;
x86_64) DEF_ARCH="x86_64";;
arm*) DEF_ARCH="arm";;
*) DEF_ARCH="i386";;
esac
error() { echo "$@" 1>&2; }
errorp() { printf "$@" 1>&2; }
fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
failp() { [ $# -eq 0 ] || errorp "$@"; exit 1; }
inargs() {
local needle="$1" x=""
shift
for x in "$@"; do
[ "$needle" = "$x" ] && return 0
done
return 1
}
Usage() {
cat <<EOF
${0##*/} [options]
-a | --arch A architecture to use [${ARCHES}]
default: ${DEF_ARCH}
-h | --help this usage
-v | --verbose increase verbosity
-S | --auth-key K insert auth key 'K'
-v | --version V version [${STREAMS[*]}]
default: ${DEF_VERSION}
-u | --userdata U user-data file
EOF
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; exit 1; }
cleanup() {
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
[ -z "${TEMP_FILE}" ] || rm -Rf "${TEMP_FILE}"
}
debug() {
local level=${1}; shift;
[ "${level}" -gt "${VERBOSITY}" ] && return
error "${@}"
}
jsondict() {
local k="" v="" ret="{"
for arg in "$@"; do
k="${arg%%=*}"
v="${arg#*=}"
ret="${ret} \"${k}\": \"$v\","
done
ret="${ret%,} }"
echo "$ret"
}
copy_configuration()
{
local path=$1 rootfs=$2 name=$3 arch=$4 release=$5
cat >> "$path/config" <<EOF
# Template used to create this container: cirros
lxc.rootfs = $rootfs
#lxc.mount = $path/fstab
lxc.pivotdir = lxc_putold
lxc.tty = 4
lxc.pts = 1024
lxc.utsname = $name
lxc.arch = $arch
lxc.cap.drop = sys_module mac_admin mac_override sys_time
# When using LXC with apparmor, uncomment the next line to run unconfined:
#lxc.aa_profile = unconfined
# To support container nesting on an Ubuntu host, uncomment next two lines:
#lxc.aa_profile = lxc-container-default-with-nesting
#lxc.hook.mount = /usr/share/lxc/hooks/mountcgroups
lxc.cgroup.devices.deny = a
# Allow any mknod (but not using the node)
lxc.cgroup.devices.allow = c *:* m
lxc.cgroup.devices.allow = b *:* m
# /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rwm
# fuse
lxc.cgroup.devices.allow = c 10:229 rwm
# tun
lxc.cgroup.devices.allow = c 10:200 rwm
# full
lxc.cgroup.devices.allow = c 1:7 rwm
# hpet
lxc.cgroup.devices.allow = c 10:228 rwm
# kvm
lxc.cgroup.devices.allow = c 10:232 rwm
EOF
}
insert_ds() {
local root_d="$1" dstype="$2" authkey="$3" userdata="$4"
local sdir="$root_d/var/lib/cloud/seed/nocloud"
mkdir -p "$sdir" ||
{ error "failed to make datasource dir $sdir"; return 1; }
rm -f "$sdir/meta-data" "$sdir/user-data" ||
{ error "failed to clean old data from $sdir"; return 1; }
iid="iid-local01"
jsondict "instance-id=$iid" \
${authkeys:+"public-keys=${authkeys}"} > "$sdir/meta-data" ||
{ error "failed to write metadata to $sdir/meta-data"; return 1; }
[ -z "$userdata" ] ||
echo "$userdata" > "$sdir/user-data" ||
{ error "failed to write user-data to $sdir"; return 1; }
}
extract_rootfs() {
local tarball="$1" rootfs_d="$2"
mkdir -p "${rootfs_d}" ||
{ error "failed to make rootfs dir ${rootfs_d}"; return 1; }
tar -C "${rootfs_d}" -Sxzf "${tarball}" ||
{ error "failed to populate ${rootfs_d}"; return 1; }
return 0
}
download_tarball() {
local arch="$1" ver="$2" cached="$3" baseurl="$4"
local out="" outd="" file="" dlpath=""
file="cirros-$ver-$arch-lxc.tar.gz"
dlpath="$ver/$file"
outd="${cached}/${dlpath%/*}"
if [ -f "$cached/$dlpath" ]; then
_RET="$cached/$dlpath"
return 0
fi
mkdir -p "${outd}" ||
{ error "failed to create ${outd}"; return 1; }
wget "${baseurl%/}/$dlpath" -O "$cached/${dlpath}.$$" &&
mv "$cached/$dlpath.$$" "$cached/$dlpath" || {
rm -f "$cached/$dlpath.$$";
error "failed to download $dlpath";
return 1;
}
_RET="$cached/$dlpath"
}
short_opts="a:n:p:S:huV"
long_opts="arch:,auth-key:,name:,path:,userdata:,verbose,version:"
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
bad_Usage
arch="${DEF_ARCH}"
version="${DEF_VERSION}"
authkey_f=""
authkeys=""
userdata=""
seed=true
path=""
while [ $# -ne 0 ]; do
cur=$1; next=$2;
case "$cur" in
-a|--arch) arch="$next"; shift;;
-h|--help) Usage ; exit 0;;
-n|--name) name="$next"; shift;;
-v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
-S|--auth-key) authkey_f="$next"; shift;;
-p|--path) path=$next; shift;;
-v|--version) version=$next; shift;;
-u|--userdata) userdata="$next"; shift;;
--) shift; break;;
esac
shift;
done
[ $# -eq 0 ] || bad_Usage "unexpected arguments: $*"
[ -n "$path" ] || fail "'path' parameter is required"
if [ "$(id -u)" != "0" ]; then
fail "must be run as root"
fi
case "$arch" in
i?86) arch="i386";;
amd64) arch="x86_65";;
esac
inargs "$arch" "${ARCHES[@]}" ||
fail "bad arch '$arch'. allowed: ${ARCHES[*]}"
if inargs "$version" "${STREAMS[@]}"; then
out=$(wget -O - -q "${DOWNLOAD_URL%/}/version/$version") ||
fail "failed to convert 'version=$version'"
version="$out"
fi
if [ -n "$authkey_f" ]; then
if [ ! -f "$authkey_f" ]; then
echo "--auth-key=${authkey_f} must reference a file"
exit 1
fi
authkeys=$(cat "$authkey")
fi
trap cleanup EXIT
download_tarball "$arch" "$version" "${CACHE_D}" "${DOWNLOAD_URL}" || fail
tarball="$_RET"
rootfs_d="$path/rootfs"
extract_rootfs "${tarball}" "${rootfs_d}" || fail
# oopsie - some cirros tarballs ship random as a blockdev, breaking
# dropbear.
if [ -b ${rootfs_d}/dev/random ]; then
rm -f ${rootfs_d}/dev/random
rm -f ${rootfs_d}/dev/urandom
mknod ${rootfs_d}/dev/random c 1 8
mknod ${rootfs_d}/dev/urandom c 1 9
chmod 777 ${rootfs_d}/dev/random ${rootfs_d}/dev/urandom
fi
if false; then
insert_ds "$path/rootfs" "nocloud" "$authkeys" "$userdata" ||
fail "failed to insert userdata to $path/rootfs"
else
# disable ec2, because its annoying
sed -i 's,ec2,,' "$path/rootfs/etc/cirros-init/config"
fi
copy_configuration "$path" "$path/rootfs" "$name" "$arch" "$release"
# vi: ts=4 expandtab