forked from experimental-platform/platform-configure
/
prep.sh
executable file
·320 lines (269 loc) · 11.3 KB
/
prep.sh
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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#!/bin/bash
set -e
set -o pipefail
MOUNTROOT=${MOUNTROOT:="/mnt"}
CHANNEL=${CHANNEL:="development"}
PLATFORM_REMOVE_OLD_IMAGES=${PLATFORM_REMOVE_OLD_IMAGES:="true"}
MANIFEST_URL=${MANIFEST_URL:='https://raw.githubusercontent.com/protonet/builds/master/$CHANNEL.json'}
DOCKER="/docker"
NEWBUILDNUMBER=
declare -A IMGTAGLIST
function fetch_release_json() {
local CHANNEL JSON JQSCRIPT
CHANNEL="$1"
curl --fail --silent "$(eval echo "$MANIFEST_URL")"
}
function fetch_release_data() {
local JSONIMGLIST JSONDATA
JSONDATA="$(fetch_release_json "$CHANNEL")"
JQSCRIPT_IMAGES='max_by(.build) | .images | keys[] as $k | $k + ":" + .[$k]'
JQSCRIPT_BUILDNO='max_by(.build) | .build'
JQSCRIPT_RELEASENOTES='max_by(.build) | .url'
JSONIMGLIST="$(jq "$JQSCRIPT_IMAGES" --raw-output <<< "$JSONDATA")"
NEWBUILDNUMBER="$(jq "$JQSCRIPT_BUILDNO" --raw-output <<< "$JSONDATA")"
NEWRELEASENOTESURL="$(jq "$JQSCRIPT_RELEASENOTES" --raw-output <<< "$JSONDATA")"
for img in ${JSONIMGLIST}; do
# this splits a line in the format 'A:B' and assigns IMGTAGLIST[A]=B
IFS=':' read -ra I <<< "$img"
IMGTAGLIST[${I[0]}]=${I[1]}
done
}
function pull_all_images() {
for i in ${!IMGTAGLIST[@]}; do
download_and_verify_image "$i:${IMGTAGLIST[$i]}"
done
}
function set_status() {
mkdir -p ${MOUNTROOT}/etc/protonet/system
echo "$@" > ${MOUNTROOT}/etc/protonet/system/configure-script-status
}
function setup_paths() {
echo -n "Creating paths in ${MOUNTROOT}/etc/systemd in case they don't exist yet... "
mkdir -p ${MOUNTROOT}/etc/systemd/journald.conf.d
mkdir -p ${MOUNTROOT}/etc/systemd/system/
mkdir -p ${MOUNTROOT}/etc/systemd/system/docker.service.d
mkdir -p ${MOUNTROOT}/etc/systemd/system/scripts/
mkdir -p ${MOUNTROOT}/etc/udev/rules.d
mkdir -p ${MOUNTROOT}/opt/bin
echo "DONE."
}
function cleanup_systemd() {
echo -n "Cleaning up ${MOUNTROOT}/etc/systemd/system/... "
# First remove broken links, this should avoid confusing error messages
find -L ${MOUNTROOT}/etc/systemd/system/ -type l -exec rm -f {} +
( grep -Hlr '# ExperimentalPlatform' ${MOUNTROOT}/etc/systemd/system/ || true ) | xargs --no-run-if-empty rm -rf
# do it again to remove garbage
find -L ${MOUNTROOT}/etc/systemd/system/ -type l -exec rm -f {} +
( grep -Hlr '# ExperimentalPlatform' ${MOUNTROOT}/etc/systemd/network || true ) | xargs --no-run-if-empty rm -rf
echo "DONE."
}
function systemd_enable_units() {
if [ $# -eq 0 ]; then return; fi
busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager EnableUnitFiles asbb $# $@ false true
}
function systemd_daemon_reload() {
busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Reload
}
function setup_systemd() {
echo -n "Setting up systemd services... "
cp /services/* ${MOUNTROOT}/etc/systemd/system/
cp /config/50-log-warn.conf ${MOUNTROOT}/etc/systemd/system/docker.service.d/50-log-warn.conf
cp /config/journald_protonet.conf ${MOUNTROOT}/etc/systemd/journald.conf.d/journald_protonet.conf
cp /config/sysctl-klog.conf ${MOUNTROOT}/etc/sysctl.d/sysctl-klog.conf
# Network configuration
cp /config/*.network ${MOUNTROOT}/etc/systemd/network
echo "Reloading the config files."
systemd_daemon_reload
# Make sure we're actually waiting for the network if it's required.
systemd_enable_units systemd-networkd-wait-online.service
echo "ENABLing all config files."
systemd_enable_units $(find ${MOUNTROOT}/etc/systemd/system -maxdepth 1 ! -name "*.sh" -type f | \
xargs --no-run-if-empty basename -a )
echo "RESTARTing all .path files."
systemd_enable_units $(find ${MOUNTROOT}/etc/systemd/system -maxdepth 1 -name "*.path" -type f | \
xargs --no-run-if-empty basename -a )
echo "DONE."
}
function setup_channel_file() {
# update the channel file in case there's none or if we're changing channels.
# TODO: Bail if either CHANNEL or CHANNEL_FILE are not set
echo -n "Detecting channel... "
if [[ ! -f ${CHANNEL_FILE} ]] || [[ ! $(cat ${CHANNEL_FILE}) = "${CHANNEL}" ]]; then
echo -n "using NEW '${CHANNEL}'... "
busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager StopUnit 'ss' trigger-update-protonet.path replace
sleep 1
mkdir -p $(dirname ${CHANNEL_FILE})
echo ${CHANNEL} > ${CHANNEL_FILE}
busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager StartUnit 'ss' trigger-update-protonet.path replace
else
echo -n "using OLD '${CHANNEL}'... "
fi
echo "DONE."
}
function download_and_verify_image() {
# TODO: DUPLICATED CODE MARK
# TODO: Bail if IMAGE_STATE_DIR or REGISTRY is not set
local image
image=$1
echo -ne "\t Image ${image}..."
RETRIES=0
MAXRETRIES=10
while ( ! $DOCKER pull $image &>/dev/null ) && [ $RETRIES -ne $MAXRETRIES ]; do
sleep 1
echo " Pull failed, retrying."
RETRIES=$(($RETRIES+1))
done
if [ $RETRIES -eq $MAXRETRIES ]; then
echo " Failed to retrieve $image"
exit 1
fi
local driver=$(${DOCKER} info | grep '^Storage Driver: ' | sed -r 's/^Storage Driver: (.*)/\1/')
# if using OverlayFS then verify layers
if [ "${driver}" == "overlay" ]; then
# TODO: this basically works with ZFS too, it just has slightly different path names
for layer in $(${DOCKER} history --no-trunc ${image} | tail -n +2 | awk '{ print $1 }'); do
# This is the most stupid way to check if all layer were downloaded correctly.
# But it is the fastest one. The docker save command takes about 30 Minutes for all images,
# even with output piped to /dev/null.
if [[ ! -e ${MOUNTROOT}/var/lib/docker/overlay/${layer} || ! -e ${MOUNTROOT}/var/lib/docker/graph/${layer} ]]; then
echo "Image '${image}' arrived broken"
exit 1
fi
done
fi
# TODO: Might wanna add --type=image for good measure once Docker 1.8 hits the CoreOS stable.
local image_id=$(${DOCKER} inspect --format '{{.Id}}' ${image})
image=${image#$REGISTRY/} # remove Registry prefix
mkdir -p $(dirname ${IMAGE_STATE_DIR}/${image})
echo $image_id > ${IMAGE_STATE_DIR}/${image}
echo "DONE."
}
function finalize() {
# save the current release number to SKVS
mkdir -p ${MOUNTROOT}/etc/protonet/system
echo -n "$NEWBUILDNUMBER" > ${MOUNTROOT}/etc/protonet/system/release_number
echo -n "$NEWRELEASENOTESURL" > ${MOUNTROOT}/etc/protonet/system/release_notes_url
sync
# prefetch buildstep. so the first deployment doesn't have to fetch it.
download_and_verify_image experimentalplatform/buildstep:herokuish
set_status "finalizing"
if [ "$PLATFORM_INSTALL_RELOAD" = true ]; then
echo "Reloading SystemD after update."
systemd_daemon_reload
busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager RestartUnit 'ss' init-protonet.service replace
exit 0
fi
if [ "$PLATFORM_REMOVE_OLD_IMAGES" == "true" ]; then
remove_old_images
fi
echo "===================================================================="
echo "After the reboot your experimental platform will be reachable via:"
echo "http://$(cat $HOSTNAME_FILE).local"
echo "(don't worry, you can change this later)"
echo "===================================================================="
}
function remove_old_images() {
local ALL_PLATFORM_IMAGES SORTED_NEW_IMAGES
ALL_PLATFORM_IMAGES=$($DOCKER images | awk '{print $1 ":" $2 }' | grep -e '^quay.io/experimentalplatform/.*' -e '^quay.io/protonetinc/.*' | sort)
SORTED_NEW_IMAGES="$(
for i in ${!IMGTAGLIST[@]}; do
echo ""$i:${IMGTAGLIST[$i]}""
done | sort
)"
comm -23 <(echo "$ALL_PLATFORM_IMAGES") <(echo "$SORTED_NEW_IMAGES") | xargs --no-run-if-empty ${DOCKER} rmi || true
}
function setup_udev() {
echo -n "Setting up UDEV rules..."
cp /config/sound-permissions.rules ${MOUNTROOT}/etc/udev/rules.d/sound-permissions.rules
cp /config/video-permissions.rules ${MOUNTROOT}/etc/udev/rules.d/video-permissions.rules
cp /config/tty-permissions.rules ${MOUNTROOT}/etc/udev/rules.d/tty-permissions.rules
cp /config/80-protonet.rules ${MOUNTROOT}/etc/udev/rules.d/80-protonet.rules
udevadm control --reload-rules || true
echo "DONE."
}
function setup_utility_scripts () {
# Automates installation of utility scripts and services from scripts/* into
# $PATH on target systems.
echo "Installing scripts:"
ETC_PATH=${ETC_PATH:=${MOUNTROOT}/etc/}
BIN_PATH=${BIN_PATH:=${MOUNTROOT}/opt/bin/}
# must be '-not -name protonet_zpool.sh' or it will break the bootstick
find ${BIN_PATH} -mindepth 1 -not -name protonet_zpool.sh -delete
find ${ETC_PATH}systemd/system/scripts/ -mindepth 1 -delete
for f in scripts/*.sh; do
name=$(basename ${f} .sh)
dest=${ETC_PATH}systemd/system/scripts/${name}.sh
echo -ne "\t * '${name}' to ${dest}... "
cp /scripts/${name}.sh ${dest}
chmod +x ${dest}
if [ -d ${BIN_PATH} ]; then
# this needs to be the full path on host, not in container
ln -sf /etc/systemd/system/scripts/${name}.sh ${BIN_PATH}${name}
fi
echo "DONE."
done
cp /button ${BIN_PATH}
cp /tcpdump "${BIN_PATH}"
cp /speedtest "${BIN_PATH}"
cp /masterpassword "${BIN_PATH}"
cp /ipmitool "${BIN_PATH}"
cp /self_destruct "${MOUNTROOT}/opt/"
cp /binaries/* "${BIN_PATH}"
echo "ALL DONE"
}
function rescue_legacy_script () {
if [[ -d "/host-bin" ]]; then
echo "Legacy script detected... "
ETC_PATH="/data/"
BIN_PATH="/host-bin/"
setup_utility_scripts
set_status "Legacy script detected... RUN UPDATE AGAIN TO FIX THIS."
echo -e "\n\nRUN UPDATE AGAIN TO FIX THIS.\n\n"
exit 42
else
setup_utility_scripts
fi
}
parse_template() {
local SERVICE_FILE IMAGE TAG
SERVICE_FILE="$1"
IMAGE=$((grep -oe 'quay.io/[a-z]*/[a-z0-9\-]*' "$SERVICE_FILE" || true) | head -n1)
if [ -z "$IMAGE" ]; then return; fi
TAG="${IMGTAGLIST[$IMAGE]}"
echo -e "Building '${SERVICE_FILE}' with IMAGE '$IMAGE' and TAG '$TAG':"
pystache "$(<$SERVICE_FILE)" "{\"tag\":\"$TAG\"}" > ${SERVICE_FILE}.new
mv ${SERVICE_FILE}.new ${SERVICE_FILE}
}
parse_all_templates() {
for SERVICE_FILE in services/*
do
parse_template "$SERVICE_FILE"
done
}
trap "/button error >/dev/null 2>&1 || true" SIGINT SIGTERM EXIT
/button "rainbow" >/dev/null 2>&1 || true
setup_paths
# FIRST: Update the platform-configure.script itself!
rescue_legacy_script
# Now the stuff that may break...
fetch_release_data
if [ "${TEMPLATES_ONLY:-"false"}" == "true" ]; then
/button "hdd" >/dev/null 2>&1 || true
parse_all_templates
exit 0
fi
pull_all_images
parse_all_templates
if grep -qE '^#?DefaultTimeoutStopSec=.*' /mnt/etc/systemd/system.conf; then
sed -E 's/^#?DefaultTimeoutStopSec=.*/DefaultTimeoutStopSec=150s/' -i /mnt/etc/systemd/system.conf
else
echo 'DefaultTimeoutStopSec=150s' >> /mnt/etc/systemd/system.conf
fi
cleanup_systemd
setup_udev
/button "rainbow" >/dev/null 2>&1 || true
setup_systemd
setup_channel_file
finalize
trap - SIGINT SIGTERM EXIT
/button "shimmer" >/dev/null 2>&1 || true