]>
Commit | Line | Data |
---|---|---|
58a46e06 SH |
1 | #!/bin/bash |
2 | ||
3 | # template script for generating ubuntu container for LXC | |
4 | # | |
5 | # This script consolidates and extends the existing lxc ubuntu scripts | |
6 | # | |
7 | ||
8cd80b50 | 8 | # Copyright © 2013 Canonical Ltd. |
58a46e06 SH |
9 | # Author: Scott Moser <scott.moser@canonical.com> |
10 | # | |
11 | # This program is free software; you can redistribute it and/or modify | |
12 | # it under the terms of the GNU General Public License version 2, as | |
13 | # published by the Free Software Foundation. | |
14 | ||
15 | # This program is distributed in the hope that it will be useful, | |
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | # GNU General Public License for more details. | |
19 | ||
20 | # You should have received a copy of the GNU General Public License along | |
21 | # with this program; if not, write to the Free Software Foundation, Inc., | |
22 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
8ec981fc SG |
23 | |
24 | # Detect use under userns (unsupported) | |
207bf0e4 SG |
25 | # Make sure the usual locations are in PATH |
26 | export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin | |
27 | ||
58a46e06 | 28 | VERBOSITY=0 |
58a46e06 | 29 | DOWNLOAD_URL="http://download.cirros-cloud.net/" |
58a46e06 SH |
30 | |
31 | UNAME_M=$(uname -m) | |
32 | ARCHES=( i386 x86_64 amd64 arm ) | |
33 | STREAMS=( released devel ) | |
80773206 | 34 | SOURCES=( nocloud none ) |
58a46e06 | 35 | BUILD="standard" |
6b410562 SH |
36 | LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" |
37 | ||
38 | LXC_MAPPED_GID= | |
39 | LXC_MAPPED_UID= | |
58a46e06 SH |
40 | |
41 | DEF_VERSION="released" | |
80773206 | 42 | DEF_SOURCE="nocloud" |
58a46e06 SH |
43 | case "${UNAME_M}" in |
44 | i?86) DEF_ARCH="i386";; | |
45 | x86_64) DEF_ARCH="x86_64";; | |
46 | arm*) DEF_ARCH="arm";; | |
47 | *) DEF_ARCH="i386";; | |
48 | esac | |
49 | ||
6b410562 SH |
50 | am_in_userns() { |
51 | [ -e /proc/self/uid_map ] || { echo no; return; } | |
52 | [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; } | |
53 | line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map) | |
54 | [ "$line" = "0 0 4294967295" ] && { echo no; return; } | |
55 | echo yes | |
56 | } | |
57 | ||
58 | in_userns=0 | |
59 | [ $(am_in_userns) = "yes" ] && in_userns=1 | |
60 | ||
6dc6f80b | 61 | # Allow the cache base to be set by environment variable |
6b410562 | 62 | if [ $(id -u) -eq 0 ]; then |
6dc6f80b | 63 | CACHE_D=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc/cirros"} |
6b410562 | 64 | else |
6dc6f80b | 65 | CACHE_D=${LXC_CACHE_PATH:-"$HOME/.cache/lxc/cirros"} |
6b410562 SH |
66 | fi |
67 | ||
58a46e06 | 68 | error() { echo "$@" 1>&2; } |
58a46e06 SH |
69 | inargs() { |
70 | local needle="$1" x="" | |
71 | shift | |
72 | for x in "$@"; do | |
73 | [ "$needle" = "$x" ] && return 0 | |
74 | done | |
75 | return 1 | |
76 | } | |
77 | ||
78 | Usage() { | |
79 | cat <<EOF | |
80 | ${0##*/} [options] | |
81 | ||
80773206 | 82 | -a | --arch A architecture to use [${ARCHES[*]}] |
58a46e06 SH |
83 | default: ${DEF_ARCH} |
84 | -h | --help this usage | |
85 | -v | --verbose increase verbosity | |
86 | -S | --auth-key K insert auth key 'K' | |
87 | -v | --version V version [${STREAMS[*]}] | |
88 | default: ${DEF_VERSION} | |
89 | -u | --userdata U user-data file | |
80773206 SH |
90 | --tarball T read from tarball 'T' rather than downloading |
91 | or using cache. | |
92 | --source S insert userdata/metadata via source S | |
93 | [${SOURCES[*]}] | |
58a46e06 SH |
94 | EOF |
95 | } | |
96 | ||
80773206 | 97 | bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; return 1; } |
58a46e06 SH |
98 | |
99 | debug() { | |
100 | local level=${1}; shift; | |
101 | [ "${level}" -gt "${VERBOSITY}" ] && return | |
102 | error "${@}" | |
103 | } | |
104 | jsondict() { | |
105 | local k="" v="" ret="{" | |
106 | for arg in "$@"; do | |
107 | k="${arg%%=*}" | |
108 | v="${arg#*=}" | |
109 | ret="${ret} \"${k}\": \"$v\"," | |
110 | done | |
111 | ret="${ret%,} }" | |
112 | echo "$ret" | |
113 | } | |
114 | ||
115 | copy_configuration() | |
116 | { | |
117 | local path=$1 rootfs=$2 name=$3 arch=$4 release=$5 | |
118 | cat >> "$path/config" <<EOF | |
119 | # Template used to create this container: cirros | |
120 | ||
121 | lxc.rootfs = $rootfs | |
58a46e06 SH |
122 | |
123 | lxc.tty = 4 | |
124 | lxc.pts = 1024 | |
125 | ||
b67771bc | 126 | lxc.uts.name = $name |
58a46e06 SH |
127 | lxc.arch = $arch |
128 | lxc.cap.drop = sys_module mac_admin mac_override sys_time | |
129 | ||
130 | # When using LXC with apparmor, uncomment the next line to run unconfined: | |
131 | #lxc.aa_profile = unconfined | |
f24a52d5 | 132 | lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed |
58a46e06 SH |
133 | |
134 | lxc.cgroup.devices.deny = a | |
135 | # Allow any mknod (but not using the node) | |
136 | lxc.cgroup.devices.allow = c *:* m | |
137 | lxc.cgroup.devices.allow = b *:* m | |
138 | # /dev/null and zero | |
139 | lxc.cgroup.devices.allow = c 1:3 rwm | |
140 | lxc.cgroup.devices.allow = c 1:5 rwm | |
141 | # consoles | |
142 | lxc.cgroup.devices.allow = c 5:1 rwm | |
143 | lxc.cgroup.devices.allow = c 5:0 rwm | |
144 | # /dev/{,u}random | |
145 | lxc.cgroup.devices.allow = c 1:9 rwm | |
146 | lxc.cgroup.devices.allow = c 1:8 rwm | |
147 | lxc.cgroup.devices.allow = c 136:* rwm | |
148 | lxc.cgroup.devices.allow = c 5:2 rwm | |
149 | # rtc | |
150 | lxc.cgroup.devices.allow = c 254:0 rwm | |
151 | # fuse | |
152 | lxc.cgroup.devices.allow = c 10:229 rwm | |
153 | # tun | |
154 | lxc.cgroup.devices.allow = c 10:200 rwm | |
155 | # full | |
156 | lxc.cgroup.devices.allow = c 1:7 rwm | |
157 | # hpet | |
158 | lxc.cgroup.devices.allow = c 10:228 rwm | |
159 | # kvm | |
160 | lxc.cgroup.devices.allow = c 10:232 rwm | |
161 | EOF | |
6b410562 SH |
162 | |
163 | if [ $in_userns -eq 1 ] && [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.userns.conf" ]; then | |
164 | echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu.userns.conf" >> $path/config | |
6b410562 SH |
165 | fi |
166 | ||
58a46e06 SH |
167 | } |
168 | ||
80773206 SH |
169 | insert_ds_nocloud() { |
170 | local root_d="$1" authkey="$2" udfile="$3" | |
58a46e06 SH |
171 | local sdir="$root_d/var/lib/cloud/seed/nocloud" |
172 | ||
173 | mkdir -p "$sdir" || | |
174 | { error "failed to make datasource dir $sdir"; return 1; } | |
175 | rm -f "$sdir/meta-data" "$sdir/user-data" || | |
176 | { error "failed to clean old data from $sdir"; return 1; } | |
177 | ||
178 | iid="iid-local01" | |
179 | jsondict "instance-id=$iid" \ | |
180 | ${authkeys:+"public-keys=${authkeys}"} > "$sdir/meta-data" || | |
181 | { error "failed to write metadata to $sdir/meta-data"; return 1; } | |
182 | ||
80773206 SH |
183 | if [ -n "$udfile" ]; then |
184 | cat "$udfile" > "$sdir/user-data" || | |
185 | { error "failed to write user-data to $sdir"; return 1; } | |
186 | else | |
187 | rm -f "$sdir/user-data" | |
188 | fi | |
189 | } | |
190 | ||
191 | insert_ds() { | |
192 | local dstype="$1" root_d="$2" authkey="$3" udfile="$4" | |
193 | case "$dstype" in | |
194 | nocloud) insert_ds_nocloud "$root_d" "$authkey" "$udfile" | |
195 | esac | |
58a46e06 SH |
196 | } |
197 | ||
198 | extract_rootfs() { | |
199 | local tarball="$1" rootfs_d="$2" | |
200 | mkdir -p "${rootfs_d}" || | |
201 | { error "failed to make rootfs dir ${rootfs_d}"; return 1; } | |
202 | ||
6b410562 SH |
203 | if [ $in_userns -eq 1 ]; then |
204 | tar -C "${rootfs_d}" --anchored --exclude="dev/*" -Sxzf "${tarball}" || | |
205 | { error "failed to populate ${rootfs_d}"; return 1; } | |
206 | else | |
207 | tar -C "${rootfs_d}" -Sxzf "${tarball}" || | |
208 | { error "failed to populate ${rootfs_d}"; return 1; } | |
209 | fi | |
58a46e06 SH |
210 | return 0 |
211 | } | |
212 | ||
213 | download_tarball() { | |
214 | local arch="$1" ver="$2" cached="$3" baseurl="$4" | |
215 | local out="" outd="" file="" dlpath="" | |
216 | file="cirros-$ver-$arch-lxc.tar.gz" | |
217 | dlpath="$ver/$file" | |
218 | outd="${cached}/${dlpath%/*}" | |
219 | if [ -f "$cached/$dlpath" ]; then | |
220 | _RET="$cached/$dlpath" | |
221 | return 0 | |
222 | fi | |
223 | ||
224 | mkdir -p "${outd}" || | |
225 | { error "failed to create ${outd}"; return 1; } | |
226 | ||
80773206 | 227 | debug 1 "downloading ${baseurl%/}/$dlpath" to "${cached}/$dlpath" |
58a46e06 SH |
228 | wget "${baseurl%/}/$dlpath" -O "$cached/${dlpath}.$$" && |
229 | mv "$cached/$dlpath.$$" "$cached/$dlpath" || { | |
230 | rm -f "$cached/$dlpath.$$"; | |
231 | error "failed to download $dlpath"; | |
232 | return 1; | |
233 | } | |
234 | _RET="$cached/$dlpath" | |
235 | } | |
236 | ||
80773206 SH |
237 | create_main() { |
238 | local short_opts="a:hn:p:S:uvV" | |
6b410562 | 239 | local long_opts="arch:,auth-key:,name:,path:,tarball:,userdata:,verbose,version:,rootfs:,mapped-uid:,mapped-gid:" |
80773206 SH |
240 | local getopt_out="" |
241 | getopt_out=$(getopt --name "${0##*/}" \ | |
242 | --options "${short_opts}" --long "${long_opts}" -- "$@") && | |
243 | eval set -- "${getopt_out}" || | |
244 | { bad_Usage; return; } | |
245 | ||
246 | local arch="${DEF_ARCH}" dsource="${DEF_SOURCE}" version="${DEF_VERSION}" | |
247 | local authkey_f="" authkeys="" userdata_f="" path="" tarball="" | |
248 | local cur="" next="" | |
4165b2c6 | 249 | local rootfs_d="" |
80773206 SH |
250 | |
251 | while [ $# -ne 0 ]; do | |
252 | cur=$1; next=$2; | |
253 | case "$cur" in | |
254 | -a|--arch) arch="$next"; shift;; | |
255 | -h|--help) Usage ; return 0;; | |
256 | -n|--name) name="$next"; shift;; | |
257 | -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; | |
258 | -S|--auth-key) authkey_f="$next"; shift;; | |
259 | -p|--path) path=$next; shift;; | |
260 | -v|--version) version=$next; shift;; | |
261 | -u|--userdata) userdata_f="$next"; shift;; | |
262 | --tarball) tarball="$next"; shift;; | |
263 | --source) dsource="$next"; shift;; | |
4165b2c6 | 264 | --rootfs) rootfs_d="$next"; shift;; |
6b410562 SH |
265 | --mapped-uid) LXC_MAPPED_UID=$next; shift;; |
266 | --mapped-gid) LXC_MAPPED_GID=$next; shift;; | |
80773206 SH |
267 | --) shift; break;; |
268 | esac | |
269 | shift; | |
270 | done | |
58a46e06 | 271 | |
4165b2c6 | 272 | [ -n "$rootfs_d" ] || rootfs_d="$path/rootfs" |
80773206 SH |
273 | [ $# -eq 0 ] || { bad_Usage "unexpected arguments: $*"; return; } |
274 | [ -n "$path" ] || { error "'path' parameter is required"; return 1; } | |
58a46e06 | 275 | |
80773206 SH |
276 | if [ "$(id -u)" != "0" ]; then |
277 | { error "must be run as root"; return 1; } | |
278 | fi | |
58a46e06 | 279 | |
80773206 SH |
280 | case "$arch" in |
281 | i?86) arch="i386";; | |
282 | amd64) arch="x86_64";; | |
283 | esac | |
284 | ||
285 | inargs "$arch" "${ARCHES[@]}" || | |
286 | { error "bad arch '$arch'. allowed: ${ARCHES[*]}"; return 1; } | |
287 | ||
288 | inargs "$dsource" "${SOURCES[@]}" || | |
289 | { error "bad source '$dsource'. allowed: ${SOURCES[*]}"; return 1; } | |
58a46e06 | 290 | |
80773206 SH |
291 | if [ "$dsource" = "none" ] && [ -n "$userdata_f" -o -n "$authkey_f" ]; then |
292 | error "userdata and authkey are incompatible with --source=none"; | |
293 | return 1; | |
294 | fi | |
58a46e06 | 295 | |
80773206 SH |
296 | if [ -n "$authkey_f" ]; then |
297 | if [ ! -f "$authkey_f" ]; then | |
298 | error "--auth-key=${authkey_f} must reference a file" | |
299 | return 1 | |
300 | fi | |
301 | authkeys=$(cat "$authkey_f") || | |
302 | { error "failed to read ${authkey_f}"; return 1; } | |
303 | fi | |
58a46e06 | 304 | |
80773206 SH |
305 | if [ -n "$userdata_f" -a ! -f "${userdata_f}" ]; then |
306 | error "${userdata_f}: --userdata arg not a file" | |
307 | return 1 | |
58a46e06 | 308 | fi |
58a46e06 | 309 | |
80773206 SH |
310 | if [ -z "$tarball" ]; then |
311 | if inargs "$version" "${STREAMS[@]}"; then | |
312 | out=$(wget -O - -q "${DOWNLOAD_URL%/}/version/$version") || | |
313 | { error "failed to convert 'version=$version'"; return 1; } | |
314 | version="$out" | |
315 | fi | |
316 | download_tarball "$arch" "$version" "${CACHE_D}" "${DOWNLOAD_URL}" || | |
317 | return | |
318 | tarball="$_RET" | |
319 | fi | |
58a46e06 | 320 | |
80773206 | 321 | extract_rootfs "${tarball}" "${rootfs_d}" || return |
58a46e06 | 322 | |
80773206 SH |
323 | if [ "$version" = "0.3.2~pre1" ]; then |
324 | debug 1 "fixing console for lxc and '$version'" | |
325 | sed -i 's,^\(#console.* 115200 \)# /dev/console,\1 console,g' \ | |
326 | "$rootfs_d/etc/inittab" || | |
327 | { error "failed to fix console entry for $version"; return 1; } | |
328 | fi | |
58a46e06 | 329 | |
80773206 SH |
330 | if [ "$dsource" != "none" ]; then |
331 | insert_ds "$dsource" "$path/rootfs" "$authkeys" "$userdata_f" || { | |
332 | error "failed to insert userdata to $path/rootfs" | |
333 | return 1 | |
334 | } | |
335 | fi | |
58a46e06 | 336 | |
80773206 SH |
337 | copy_configuration "$path" "$path/rootfs" "$name" "$arch" "$release" |
338 | return | |
339 | } | |
58a46e06 | 340 | |
80773206 | 341 | create_main "$@" |
58a46e06 SH |
342 | |
343 | # vi: ts=4 expandtab |