mirror of
https://github.com/lxc/lxc-templates.git
synced 2024-12-22 22:40:16 +00:00
344 lines
10 KiB
Plaintext
344 lines
10 KiB
Plaintext
|
#!/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.
|
||
|
|
||
|
# Detect use under userns (unsupported)
|
||
|
# Make sure the usual locations are in PATH
|
||
|
export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
|
||
|
|
||
|
VERBOSITY=0
|
||
|
DOWNLOAD_URL="http://download.cirros-cloud.net/"
|
||
|
|
||
|
UNAME_M=$(uname -m)
|
||
|
ARCHES=( i386 x86_64 amd64 arm )
|
||
|
STREAMS=( released devel )
|
||
|
SOURCES=( nocloud none )
|
||
|
BUILD="standard"
|
||
|
LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@"
|
||
|
|
||
|
LXC_MAPPED_GID=
|
||
|
LXC_MAPPED_UID=
|
||
|
|
||
|
DEF_VERSION="released"
|
||
|
DEF_SOURCE="nocloud"
|
||
|
case "${UNAME_M}" in
|
||
|
i?86) DEF_ARCH="i386";;
|
||
|
x86_64) DEF_ARCH="x86_64";;
|
||
|
arm*) DEF_ARCH="arm";;
|
||
|
*) DEF_ARCH="i386";;
|
||
|
esac
|
||
|
|
||
|
am_in_userns() {
|
||
|
[ -e /proc/self/uid_map ] || { echo no; return; }
|
||
|
[ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; }
|
||
|
line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map)
|
||
|
[ "$line" = "0 0 4294967295" ] && { echo no; return; }
|
||
|
echo yes
|
||
|
}
|
||
|
|
||
|
in_userns=0
|
||
|
[ $(am_in_userns) = "yes" ] && in_userns=1
|
||
|
|
||
|
# Allow the cache base to be set by environment variable
|
||
|
if [ $(id -u) -eq 0 ]; then
|
||
|
CACHE_D=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc/cirros"}
|
||
|
else
|
||
|
CACHE_D=${LXC_CACHE_PATH:-"$HOME/.cache/lxc/cirros"}
|
||
|
fi
|
||
|
|
||
|
error() { echo "$@" 1>&2; }
|
||
|
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
|
||
|
--tarball T read from tarball 'T' rather than downloading
|
||
|
or using cache.
|
||
|
--source S insert userdata/metadata via source S
|
||
|
[${SOURCES[*]}]
|
||
|
EOF
|
||
|
}
|
||
|
|
||
|
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; return 1; }
|
||
|
|
||
|
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.path = $rootfs
|
||
|
|
||
|
lxc.tty.max = 4
|
||
|
lxc.pty.max = 1024
|
||
|
|
||
|
lxc.uts.name = $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.apparmor.profile = unconfined
|
||
|
lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed
|
||
|
|
||
|
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
|
||
|
|
||
|
if [ $in_userns -eq 1 ] && [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.userns.conf" ]; then
|
||
|
echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu.userns.conf" >> $path/config
|
||
|
fi
|
||
|
|
||
|
}
|
||
|
|
||
|
insert_ds_nocloud() {
|
||
|
local root_d="$1" authkey="$2" udfile="$3"
|
||
|
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; }
|
||
|
|
||
|
if [ -n "$udfile" ]; then
|
||
|
cat "$udfile" > "$sdir/user-data" ||
|
||
|
{ error "failed to write user-data to $sdir"; return 1; }
|
||
|
else
|
||
|
rm -f "$sdir/user-data"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
insert_ds() {
|
||
|
local dstype="$1" root_d="$2" authkey="$3" udfile="$4"
|
||
|
case "$dstype" in
|
||
|
nocloud) insert_ds_nocloud "$root_d" "$authkey" "$udfile"
|
||
|
esac
|
||
|
}
|
||
|
|
||
|
extract_rootfs() {
|
||
|
local tarball="$1" rootfs_d="$2"
|
||
|
mkdir -p "${rootfs_d}" ||
|
||
|
{ error "failed to make rootfs dir ${rootfs_d}"; return 1; }
|
||
|
|
||
|
if [ $in_userns -eq 1 ]; then
|
||
|
tar -C "${rootfs_d}" --anchored --exclude="dev/*" -Sxzf "${tarball}" ||
|
||
|
{ error "failed to populate ${rootfs_d}"; return 1; }
|
||
|
else
|
||
|
tar -C "${rootfs_d}" -Sxzf "${tarball}" ||
|
||
|
{ error "failed to populate ${rootfs_d}"; return 1; }
|
||
|
fi
|
||
|
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; }
|
||
|
|
||
|
debug 1 "downloading ${baseurl%/}/$dlpath" to "${cached}/$dlpath"
|
||
|
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"
|
||
|
}
|
||
|
|
||
|
create_main() {
|
||
|
local short_opts="a:hn:p:S:uvV"
|
||
|
local long_opts="arch:,auth-key:,name:,path:,tarball:,userdata:,verbose,version:,rootfs:,mapped-uid:,mapped-gid:"
|
||
|
local getopt_out=""
|
||
|
getopt_out=$(getopt --name "${0##*/}" \
|
||
|
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
|
||
|
eval set -- "${getopt_out}" ||
|
||
|
{ bad_Usage; return; }
|
||
|
|
||
|
local arch="${DEF_ARCH}" dsource="${DEF_SOURCE}" version="${DEF_VERSION}"
|
||
|
local authkey_f="" authkeys="" userdata_f="" path="" tarball=""
|
||
|
local cur="" next=""
|
||
|
local rootfs_d=""
|
||
|
|
||
|
while [ $# -ne 0 ]; do
|
||
|
cur=$1; next=$2;
|
||
|
case "$cur" in
|
||
|
-a|--arch) arch="$next"; shift;;
|
||
|
-h|--help) Usage ; return 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_f="$next"; shift;;
|
||
|
--tarball) tarball="$next"; shift;;
|
||
|
--source) dsource="$next"; shift;;
|
||
|
--rootfs) rootfs_d="$next"; shift;;
|
||
|
--mapped-uid) LXC_MAPPED_UID=$next; shift;;
|
||
|
--mapped-gid) LXC_MAPPED_GID=$next; shift;;
|
||
|
--) shift; break;;
|
||
|
esac
|
||
|
shift;
|
||
|
done
|
||
|
|
||
|
[ -n "$rootfs_d" ] || rootfs_d="$path/rootfs"
|
||
|
[ $# -eq 0 ] || { bad_Usage "unexpected arguments: $*"; return; }
|
||
|
[ -n "$path" ] || { error "'path' parameter is required"; return 1; }
|
||
|
|
||
|
if [ "$(id -u)" != "0" ]; then
|
||
|
{ error "must be run as root"; return 1; }
|
||
|
fi
|
||
|
|
||
|
case "$arch" in
|
||
|
i?86) arch="i386";;
|
||
|
amd64) arch="x86_64";;
|
||
|
esac
|
||
|
|
||
|
inargs "$arch" "${ARCHES[@]}" ||
|
||
|
{ error "bad arch '$arch'. allowed: ${ARCHES[*]}"; return 1; }
|
||
|
|
||
|
inargs "$dsource" "${SOURCES[@]}" ||
|
||
|
{ error "bad source '$dsource'. allowed: ${SOURCES[*]}"; return 1; }
|
||
|
|
||
|
if [ "$dsource" = "none" ] && [ -n "$userdata_f" -o -n "$authkey_f" ]; then
|
||
|
error "userdata and authkey are incompatible with --source=none";
|
||
|
return 1;
|
||
|
fi
|
||
|
|
||
|
if [ -n "$authkey_f" ]; then
|
||
|
if [ ! -f "$authkey_f" ]; then
|
||
|
error "--auth-key=${authkey_f} must reference a file"
|
||
|
return 1
|
||
|
fi
|
||
|
authkeys=$(cat "$authkey_f") ||
|
||
|
{ error "failed to read ${authkey_f}"; return 1; }
|
||
|
fi
|
||
|
|
||
|
if [ -n "$userdata_f" -a ! -f "${userdata_f}" ]; then
|
||
|
error "${userdata_f}: --userdata arg not a file"
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
if [ -z "$tarball" ]; then
|
||
|
if inargs "$version" "${STREAMS[@]}"; then
|
||
|
out=$(wget -O - -q "${DOWNLOAD_URL%/}/version/$version") ||
|
||
|
{ error "failed to convert 'version=$version'"; return 1; }
|
||
|
version="$out"
|
||
|
fi
|
||
|
download_tarball "$arch" "$version" "${CACHE_D}" "${DOWNLOAD_URL}" ||
|
||
|
return
|
||
|
tarball="$_RET"
|
||
|
fi
|
||
|
|
||
|
extract_rootfs "${tarball}" "${rootfs_d}" || return
|
||
|
|
||
|
if [ "$version" = "0.3.2~pre1" ]; then
|
||
|
debug 1 "fixing console for lxc and '$version'"
|
||
|
sed -i 's,^\(#console.* 115200 \)# /dev/console,\1 console,g' \
|
||
|
"$rootfs_d/etc/inittab" ||
|
||
|
{ error "failed to fix console entry for $version"; return 1; }
|
||
|
fi
|
||
|
|
||
|
if [ "$dsource" != "none" ]; then
|
||
|
insert_ds "$dsource" "$path/rootfs" "$authkeys" "$userdata_f" || {
|
||
|
error "failed to insert userdata to $path/rootfs"
|
||
|
return 1
|
||
|
}
|
||
|
fi
|
||
|
|
||
|
copy_configuration "$path" "$path/rootfs" "$name" "$arch" "$release"
|
||
|
return
|
||
|
}
|
||
|
|
||
|
create_main "$@"
|
||
|
|
||
|
# vi: ts=4 expandtab
|