summaryrefslogtreecommitdiff
path: root/contrib/mkimage-debootstrap.sh
blob: 33ba7b07cbc05baba45feae279036572c25282ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#!/usr/bin/env bash
set -e

variant='minbase'
include='iproute,iputils-ping'
arch='amd64' # intentionally undocumented for now
skipDetection=
strictDebootstrap=
justTar=

usage() {
	echo >&2
	
	echo >&2 "usage: $0 [options] repo suite [mirror]"
	
	echo >&2
	echo >&2 'options: (not recommended)'
	echo >&2 "  -p set an http_proxy for debootstrap"
	echo >&2 "  -v $variant # change default debootstrap variant"
	echo >&2 "  -i $include # change default package includes"
	echo >&2 "  -d # strict debootstrap (do not apply any docker-specific tweaks)"
	echo >&2 "  -s # skip version detection and tagging (ie, precise also tagged as 12.04)"
	echo >&2 "     # note that this will also skip adding universe and/or security/updates to sources.list"
	echo >&2 "  -t # just create a tarball, especially for dockerbrew (uses repo as tarball name)"
	
	echo >&2
	echo >&2 "   ie: $0 username/debian squeeze"
	echo >&2 "       $0 username/debian squeeze http://ftp.uk.debian.org/debian/"
	
	echo >&2
	echo >&2 "   ie: $0 username/ubuntu precise"
	echo >&2 "       $0 username/ubuntu precise http://mirrors.melbourne.co.uk/ubuntu/"
	
	echo >&2
	echo >&2 "   ie: $0 -t precise.tar.bz2 precise"
	echo >&2 "       $0 -t wheezy.tgz wheezy"
	echo >&2 "       $0 -t wheezy-uk.tar.xz wheezy http://ftp.uk.debian.org/debian/"
	
	echo >&2
}

# these should match the names found at http://www.debian.org/releases/
debianStable=wheezy
debianUnstable=sid
# this should match the name found at http://releases.ubuntu.com/
ubuntuLatestLTS=precise
# this should match the name found at http://releases.tanglu.org/
tangluLatest=aequorea

while getopts v:i:a:p:dst name; do
	case "$name" in
		p)
			http_proxy="$OPTARG"
			;;
		v)
			variant="$OPTARG"
			;;
		i)
			include="$OPTARG"
			;;
		a)
			arch="$OPTARG"
			;;
		d)
			strictDebootstrap=1
			;;
		s)
			skipDetection=1
			;;
		t)
			justTar=1
			;;
		?)
			usage
			exit 0
			;;
	esac
done
shift $(($OPTIND - 1))

repo="$1"
suite="$2"
mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided

if [ ! "$repo" ] || [ ! "$suite" ]; then
	usage
	exit 1
fi

# some rudimentary detection for whether we need to "sudo" our docker calls
docker=''
if docker version > /dev/null 2>&1; then
	docker='docker'
elif sudo docker version > /dev/null 2>&1; then
	docker='sudo docker'
elif command -v docker > /dev/null 2>&1; then
	docker='docker'
else
	echo >&2 "warning: either docker isn't installed, or your current user cannot run it;"
	echo >&2 "         this script is not likely to work as expected"
	sleep 3
	docker='docker' # give us a command-not-found later
fi

# make sure we have an absolute path to our final tarball so we can still reference it properly after we change directory
if [ "$justTar" ]; then
	if [ ! -d "$(dirname "$repo")" ]; then
		echo >&2 "error: $(dirname "$repo") does not exist"
		exit 1
	fi
	repo="$(cd "$(dirname "$repo")" && pwd -P)/$(basename "$repo")"
fi

# will be filled in later, if [ -z "$skipDetection" ]
lsbDist=''

target="/tmp/docker-rootfs-debootstrap-$suite-$$-$RANDOM"

cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
returnTo="$(pwd -P)"

if [ "$suite" = 'lucid' ]; then
	# lucid fails and doesn't include gpgv in minbase; "apt-get update" fails
	include+=',gpgv'
fi

set -x

# bootstrap
mkdir -p "$target"
sudo http_proxy=$http_proxy debootstrap --verbose --variant="$variant" --include="$include" --arch="$arch" "$suite" "$target" "$mirror"

cd "$target"

if [ -z "$strictDebootstrap" ]; then
	# prevent init scripts from running during install/update
	#  policy-rc.d (for most scripts)
	echo $'#!/bin/sh\nexit 101' | sudo tee usr/sbin/policy-rc.d > /dev/null
	sudo chmod +x usr/sbin/policy-rc.d
	#  initctl (for some pesky upstart scripts)
	sudo chroot . dpkg-divert --local --rename --add /sbin/initctl
	sudo ln -sf /bin/true sbin/initctl
	# see https://github.com/dotcloud/docker/issues/446#issuecomment-16953173
	
	# shrink the image, since apt makes us fat (wheezy: ~157.5MB vs ~120MB)
	sudo chroot . apt-get clean
	
	if strings usr/bin/dpkg | grep -q unsafe-io; then
		# while we're at it, apt is unnecessarily slow inside containers
		#  this forces dpkg not to call sync() after package extraction and speeds up install
		#    the benefit is huge on spinning disks, and the penalty is nonexistent on SSD or decent server virtualization
		echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null
		# we have this wrapped up in an "if" because the "force-unsafe-io"
		# option was added in dpkg 1.15.8.6
		# (see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=584254#82),
		# and ubuntu lucid/10.04 only has 1.15.5.6
	fi
	
	# we want to effectively run "apt-get clean" after every install to keep images small (see output of "apt-get clean -s" for context)
	{
		aptGetClean='"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true";'
		echo "DPkg::Post-Invoke { ${aptGetClean} };"
		echo "APT::Update::Post-Invoke { ${aptGetClean} };"
		echo 'Dir::Cache::pkgcache ""; Dir::Cache::srcpkgcache "";'
	} | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null
	
	# and remove the translations, too
	echo 'Acquire::Languages "none";' | sudo tee etc/apt/apt.conf.d/no-languages > /dev/null
	
	# helpful undo lines for each the above tweaks (for lack of a better home to keep track of them):
	#  rm /usr/sbin/policy-rc.d
	#  rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl
	#  rm /etc/dpkg/dpkg.cfg.d/02apt-speedup
	#  rm /etc/apt/apt.conf.d/no-cache
	#  rm /etc/apt/apt.conf.d/no-languages
	
	if [ -z "$skipDetection" ]; then
		# see also rudimentary platform detection in hack/install.sh
		lsbDist=''
		if [ -r etc/lsb-release ]; then
			lsbDist="$(. etc/lsb-release && echo "$DISTRIB_ID")"
		fi
		if [ -z "$lsbDist" ] && [ -r etc/debian_version ]; then
			lsbDist='Debian'
		fi
		
		case "$lsbDist" in
			Debian)
				# add the updates and security repositories
				if [ "$suite" != "$debianUnstable" -a "$suite" != 'unstable' ]; then
					# ${suite}-updates only applies to non-unstable
					sudo sed -i "p; s/ $suite main$/ ${suite}-updates main/" etc/apt/sources.list
					
					# same for security updates
					echo "deb http://security.debian.org/ $suite/updates main" | sudo tee -a etc/apt/sources.list > /dev/null
				fi
				;;
			Ubuntu)
				# add the universe, updates, and security repositories
				sudo sed -i "
					s/ $suite main$/ $suite main universe/; p;
					s/ $suite main/ ${suite}-updates main/; p;
					s/ $suite-updates main/ ${suite}-security main/
				" etc/apt/sources.list
				;;
			Tanglu)
				# add the updates repository
				if [ "$suite" = "$tangluLatest" ]; then
					# ${suite}-updates only applies to stable Tanglu versions
					sudo sed -i "p; s/ $suite main$/ ${suite}-updates main/" etc/apt/sources.list
				fi
				;;
			SteamOS)
				# add contrib and non-free
				sudo sed -i "s/ $suite main$/ $suite main contrib non-free/" etc/apt/sources.list
				;;
		esac
	fi
	
	# make sure our packages lists are as up to date as we can get them
	sudo chroot . apt-get update
	sudo chroot . apt-get dist-upgrade -y
fi

if [ "$justTar" ]; then
	# create the tarball file so it has the right permissions (ie, not root)
	touch "$repo"
	
	# fill the tarball
	sudo tar --numeric-owner -caf "$repo" .
else
	# create the image (and tag $repo:$suite)
	sudo tar --numeric-owner -c . | $docker import - $repo:$suite
	
	# test the image
	$docker run -i -t $repo:$suite echo success
	
	if [ -z "$skipDetection" ]; then
		case "$lsbDist" in
			Debian)
				if [ "$suite" = "$debianStable" -o "$suite" = 'stable' ] && [ -r etc/debian_version ]; then
					# tag latest
					$docker tag $repo:$suite $repo:latest
					
					if [ -r etc/debian_version ]; then
						# tag the specific debian release version (which is only reasonable to tag on debian stable)
						ver=$(cat etc/debian_version)
						$docker tag $repo:$suite $repo:$ver
					fi
				fi
				;;
			Ubuntu)
				if [ "$suite" = "$ubuntuLatestLTS" ]; then
					# tag latest
					$docker tag $repo:$suite $repo:latest
				fi
				if [ -r etc/lsb-release ]; then
					lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")"
					if [ "$lsbRelease" ]; then
						# tag specific Ubuntu version number, if available (12.04, etc.)
						$docker tag $repo:$suite $repo:$lsbRelease
					fi
				fi
				;;
			Tanglu)
				if [ "$suite" = "$tangluLatest" ]; then
					# tag latest
					$docker tag $repo:$suite $repo:latest
				fi
				if [ -r etc/lsb-release ]; then
					lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")"
					if [ "$lsbRelease" ]; then
						# tag specific Tanglu version number, if available (1.0, 2.0, etc.)
						$docker tag $repo:$suite $repo:$lsbRelease
					fi
				fi
				;;
			SteamOS)
				if [ -r etc/lsb-release ]; then
					lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")"
					if [ "$lsbRelease" ]; then
						# tag specific SteamOS version number, if available (1.0, 2.0, etc.)
						$docker tag $repo:$suite $repo:$lsbRelease
					fi
				fi
				;;
		esac
	fi
fi

# cleanup
cd "$returnTo"
sudo rm -rf "$target"