forked from tinkerbell/playground
-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.sh
executable file
·515 lines (437 loc) · 13.3 KB
/
setup.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
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
#!/usr/bin/env bash
# stops the execution if a command or pipeline has an error
set -euxo pipefail
# Tinkerbell stack Linux setup script
#
# See https://tinkerbell.org/setup for the installation steps.
# file to hold all environment variables
ENV_FILE=.env
SCRATCH=$(mktemp -d -t tmp.XXXXXXXXXX)
readonly SCRATCH
function finish() (
rm -rf "$SCRATCH"
)
trap finish EXIT
DEPLOYDIR=$(pwd)/deploy
readonly DEPLOYDIR
readonly STATEDIR=$DEPLOYDIR/state
if command -v tput >/dev/null && tput setaf 1 >/dev/null 2>&1; then
# color codes
RED="$(tput setaf 1)"
GREEN="$(tput setaf 2)"
YELLOW="$(tput setaf 3)"
RESET="$(tput sgr0)"
fi
INFO="${GREEN:-}INFO:${RESET:-}"
ERR="${RED:-}ERROR:${RESET:-}"
WARN="${YELLOW:-}WARNING:${RESET:-}"
BLANK=" "
NEXT="${GREEN:-}NEXT:${RESET:-}"
get_distribution() (
local lsb_dist=""
# Every system that we officially support has /etc/os-release
if [[ -r /etc/os-release ]]; then
# shellcheck disable=SC1091
lsb_dist="$(. /etc/os-release && echo "$ID")"
fi
# Returning an empty string here should be alright since the
# case statements don't act unless you provide an actual value
echo "$lsb_dist" | tr '[:upper:]' '[:lower:]'
)
get_distro_version() (
local lsb_version="0"
# Every system that we officially support has /etc/os-release
if [[ -r /etc/os-release ]]; then
# shellcheck disable=SC1091
lsb_version="$(. /etc/os-release && echo "$VERSION_ID")"
fi
echo "$lsb_version"
)
is_network_configured() (
# Require the provisioner interface have the host IP
if ! ip addr show "$TINKERBELL_NETWORK_INTERFACE" |
grep -q "$TINKERBELL_HOST_IP"; then
return 1
fi
return 0
)
identify_network_strategy() (
local distro=$1
local version=$2
case "$distro" in
ubuntu)
if jq -n --exit-status '$distro_version >= 17.10' --argjson distro_version "$version" >/dev/null 2>&1; then
echo "setup_networking_netplan"
else
echo "setup_networking_ubuntu_legacy"
fi
;;
centos)
echo "setup_networking_centos"
;;
*)
echo "setup_networking_manually"
;;
esac
)
setup_networking() (
local distro=$1
local version=$2
setup_network_forwarding
if is_network_configured; then
echo "$INFO tinkerbell network interface is already configured"
return 0
fi
local strategy
strategy=$(identify_network_strategy "$distro" "$version")
"${strategy}" "$distro" "$version" # execute the strategy
if is_network_configured; then
echo "$INFO tinkerbell network interface configured successfully"
else
echo "$ERR tinkerbell network interface configuration failed"
fi
NAT_INTERFACE=""
if [[ -r .nat_interface ]]; then
NAT_INTERFACE=$(cat .nat_interface)
fi
if [[ -n $NAT_INTERFACE ]] && ip addr show "$NAT_INTERFACE" &>/dev/null; then
# TODO(nshalman) the terraform code would just run these commands as-is once
# but it would be nice to make these more persistent based on OS
iptables -A FORWARD -i "$TINKERBELL_NETWORK_INTERFACE" -o "$NAT_INTERFACE" -j ACCEPT
iptables -A FORWARD -i "$NAT_INTERFACE" -o "$TINKERBELL_NETWORK_INTERFACE" -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -o "$NAT_INTERFACE" -j MASQUERADE
fi
)
setup_networking_manually() (
local distro=$1
local version=$2
echo "$ERR this setup script cannot configure $distro ($version)"
echo "$BLANK please read this script's source and configure it manually."
exit 1
)
setup_network_forwarding() (
# enable IP forwarding for docker
if (($(sysctl -n net.ipv4.ip_forward) != 1)); then
if [[ -d /etc/sysctl.d ]]; then
echo "net.ipv4.ip_forward=1" >/etc/sysctl.d/99-tinkerbell.conf
elif [[ -f /etc/sysctl.conf ]]; then
echo "net.ipv4.ip_forward=1" >>/etc/sysctl.conf
fi
sysctl net.ipv4.ip_forward=1
fi
)
setup_networking_netplan() (
jq -n \
--arg interface "$TINKERBELL_NETWORK_INTERFACE" \
--arg cidr "$TINKERBELL_CIDR" \
--arg host_ip "$TINKERBELL_HOST_IP" \
'{
network: {
renderer: "networkd",
ethernets: {
($interface): {
addresses: [
"\($host_ip)/\($cidr)"
]
}
}
}
}' >"/etc/netplan/${TINKERBELL_NETWORK_INTERFACE}.yaml"
ip link set "$TINKERBELL_NETWORK_INTERFACE" nomaster
netplan apply
echo "$INFO waiting for the network configuration to be applied by systemd-networkd"
sleep 3
)
setup_networking_ubuntu_legacy() (
if ! [[ -f /etc/network/interfaces ]]; then
echo "$ERR file /etc/network/interfaces not found"
exit 1
fi
if grep -q "$TINKERBELL_NETWORK_INTERFACE" /etc/network/interfaces; then
echo "$ERR /etc/network/interfaces already has an entry for $TINKERBELL_NETWORK_INTERFACE."
echo "$BLANK To prevent breaking your network, please edit /etc/network/interfaces"
echo "$BLANK and configure $TINKERBELL_NETWORK_INTERFACE as follows:"
generate_iface_config
echo ""
echo "$BLANK Then run the following commands:"
echo "$BLANK ip link set $TINKERBELL_NETWORK_INTERFACE nomaster"
echo "$BLANK ifdown $TINKERBELL_NETWORK_INTERFACE"
echo "$BLANK ifup $TINKERBELL_NETWORK_INTERFACE"
exit 1
else
generate_iface_config >>/etc/network/interfaces
ip link set "$TINKERBELL_NETWORK_INTERFACE" nomaster
ifdown "$TINKERBELL_NETWORK_INTERFACE"
ifup "$TINKERBELL_NETWORK_INTERFACE"
fi
)
generate_iface_config() (
cat <<EOF
auto $TINKERBELL_NETWORK_INTERFACE
iface $TINKERBELL_NETWORK_INTERFACE inet static
address $TINKERBELL_HOST_IP/$TINKERBELL_CIDR
pre-up sleep 4
EOF
)
setup_networking_centos() (
local HWADDRESS
local content
HWADDRESS=$(ip addr show "$TINKERBELL_NETWORK_INTERFACE" | grep ether | awk -F 'ether' '{print $2}' | cut -d" " -f2)
content=$(
cat <<EOF
DEVICE=$TINKERBELL_NETWORK_INTERFACE
ONBOOT=yes
HWADDR=$HWADDRESS
BOOTPROTO=static
IPADDR=$TINKERBELL_HOST_IP
PREFIX=$TINKERBELL_CIDR
EOF
)
local cfgfile="/etc/sysconfig/network-scripts/ifcfg-$TINKERBELL_NETWORK_INTERFACE"
if [[ -f $cfgfile ]]; then
echo "$ERR network config already exists: $cfgfile"
echo "$BLANK Please update it to match this configuration:"
echo "$content"
echo ""
echo "$BLANK Then, run the following commands:"
echo "ip link set $TINKERBELL_NETWORK_INTERFACE nomaster"
echo "ifup $TINKERBELL_NETWORK_INTERFACE"
fi
echo "$content" >"$cfgfile"
ip link set "$TINKERBELL_NETWORK_INTERFACE" nomaster
ifup "$TINKERBELL_NETWORK_INTERFACE"
)
setup_osie() (
mkdir -p "$STATEDIR/webroot"
local osie_current=$STATEDIR/webroot/misc/osie/current
local tink_workflow=$STATEDIR/webroot/workflow/
if [[ ! -d $osie_current ]] || [[ ! -d $tink_workflow ]]; then
mkdir -p "$osie_current"
mkdir -p "$tink_workflow"
pushd "$SCRATCH"
if [[ -z ${TB_OSIE_TAR:-} ]]; then
curl "${OSIE_DOWNLOAD_LINK}" -o ./osie.tar.gz
tar -zxf osie.tar.gz
else
tar -zxf "$TB_OSIE_TAR"
fi
if pushd osie*/; then
if mv workflow-helper.sh workflow-helper-rc "$tink_workflow"; then
cp -r ./* "$osie_current"
else
echo "$ERR failed to move 'workflow-helper.sh' and 'workflow-helper-rc'"
exit 1
fi
popd
fi
else
echo "$INFO found existing osie files, skipping osie setup"
fi
)
check_container_status() (
local container_name="$1"
local container_id
container_id=$(docker-compose -f "$DEPLOYDIR/docker-compose.yml" ps -q "$container_name")
local start_moment
local current_status
start_moment=$(docker inspect "${container_id}" --format '{{ .State.StartedAt }}')
current_status=$(docker inspect "${container_id}" --format '{{ .State.Health.Status }}')
case "$current_status" in
starting)
: # move on to the events check
;;
healthy)
return 0
;;
unhealthy)
echo "$ERR $container_name is already running but not healthy. status: $current_status"
exit 1
;;
*)
echo "$ERR $container_name is already running but its state is a mystery. status: $current_status"
exit 1
;;
esac
local status
read -r status < <(docker events \
--since "$start_moment" \
--filter "container=$container_id" \
--filter "event=health_status" \
--format '{{.Status}}')
if [[ $status != "health_status: healthy" ]]; then
echo "$ERR $container_name is not healthy. status: $status"
exit 1
fi
)
generate_certificates() (
mkdir -p "$STATEDIR/certs"
if ! [[ -f "$STATEDIR/certs/ca.json" ]]; then
jq \
'.
| .names[0].L = $facility
' \
"$DEPLOYDIR/tls/ca.in.json" \
--arg ip "$TINKERBELL_HOST_IP" \
--arg facility "$FACILITY" \
>"$STATEDIR/certs/ca.json"
fi
if ! [[ -f "$STATEDIR/certs/server-csr.json" ]]; then
jq \
'.
| .hosts += [ $ip, "tinkerbell.\($facility).packet.net" ]
| .names[0].L = $facility
| .hosts = (.hosts | sort | unique)
' \
"$DEPLOYDIR/tls/server-csr.in.json" \
--arg ip "$TINKERBELL_HOST_IP" \
--arg facility "$FACILITY" \
>"$STATEDIR/certs/server-csr.json"
fi
docker build --tag "tinkerbell-certs" "$DEPLOYDIR/tls"
docker run --rm \
--volume "$STATEDIR/certs:/certs" \
--user "$UID:$(id -g)" \
tinkerbell-certs
local certs_dir="/etc/docker/certs.d/$TINKERBELL_HOST_IP"
# copy public key to NGINX for workers
if ! cmp --quiet "$STATEDIR/certs/ca.pem" "$STATEDIR/webroot/workflow/ca.pem"; then
cp "$STATEDIR/certs/ca.pem" "$STATEDIR/webroot/workflow/ca.pem"
fi
# update host to trust registry certificate
if ! cmp --quiet "$STATEDIR/certs/ca.pem" "$certs_dir/tinkerbell.crt"; then
if ! [[ -d "$certs_dir/" ]]; then
# The user will be told to create the directory
# in the next block, if copying the certs there
# fails.
mkdir -p "$certs_dir" || true >/dev/null 2>&1
fi
if ! cp "$STATEDIR/certs/ca.pem" "$certs_dir/tinkerbell.crt"; then
echo "$ERR please copy $STATEDIR/certs/ca.pem to $certs_dir/tinkerbell.crt"
echo "$BLANK and run $0 again:"
if ! [[ -d $certs_dir ]]; then
echo "sudo mkdir -p '$certs_dir'"
fi
echo "sudo cp '$STATEDIR/certs/ca.pem' '$certs_dir/tinkerbell.crt'"
exit 1
fi
fi
)
docker_login() (
echo -n "$TINKERBELL_REGISTRY_PASSWORD" | docker login -u="$TINKERBELL_REGISTRY_USERNAME" --password-stdin "$TINKERBELL_HOST_IP"
)
# This function takes an image specified as first parameter and it tags and
# push it using the second one. useful to proxy images from a repository to
# another.
docker_mirror_image() (
local from=$1
local to=$2
docker pull "$from"
docker tag "$from" "$to"
docker push "$to"
)
start_registry() (
docker-compose -f "$DEPLOYDIR/docker-compose.yml" up --build -d registry
check_container_status "registry"
)
# This function supposes that the registry is up and running.
# It configures with the required dependencies.
bootstrap_docker_registry() (
docker_login
# osie looks for tink-worker:latest, so we have to play with it a bit
# https://github.com/tinkerbell/osie/blob/master/apps/workflow-helper.sh#L66
docker_mirror_image "${TINKERBELL_TINK_WORKER_IMAGE}" "${TINKERBELL_HOST_IP}/tink-worker:latest"
)
setup_docker_registry() (
local registry_images="$STATEDIR/registry"
if ! [[ -d $registry_images ]]; then
mkdir -p "$registry_images"
fi
start_registry
bootstrap_docker_registry
)
start_components() (
local components=(db hegel tink-server boots tink-cli nginx)
for comp in "${components[@]}"; do
docker-compose -f "$DEPLOYDIR/docker-compose.yml" up --build -d "$comp"
sleep 3
check_container_status "$comp"
done
)
command_exists() (
command -v "$@" >/dev/null 2>&1
)
check_command() (
if ! command_exists "$1"; then
echo "$ERR Prerequisite executable command not found: $1"
return 1
fi
if ! [[ -s "$(which "$1")" ]]; then
echo "$ERR Prerequisite command is an empty file: $1"
fi
echo "$BLANK Found prerequisite: $1"
return 0
)
check_prerequisites() (
distro=$1
version=$2
echo "$INFO verifying prerequisites for $distro ($version)"
failed=0
check_command docker || failed=1
check_command docker-compose || failed=1
check_command ip || failed=1
check_command jq || failed=1
strategy=$(identify_network_strategy "$distro" "$version")
case "$strategy" in
"setup_networking_netplan")
check_command netplan || failed=1
;;
"setup_networking_ubuntu_legacy")
check_command ifdown || failed=1
check_command ifup || failed=1
;;
"setup_networking_centos")
check_command ifdown || failed=1
check_command ifup || failed=1
;;
"setup_networking_manually")
echo "$WARN this script cannot automatically configure your network."
;;
*)
echo "$ERR bug: unhandled network strategy: $strategy"
exit 1
;;
esac
if ((failed == 1)); then
echo "$ERR Prerequisites not met. Please install the missing commands and re-run $0."
exit 1
fi
)
whats_next() (
echo "$NEXT 1. Enter /deploy and run: source ../.env; docker-compose up -d"
echo "$BLANK 2. Try executing your first workflow."
echo "$BLANK Follow the steps described in https://tinkerbell.org/examples/hello-world/ to say 'Hello World!' with a workflow."
)
do_setup() (
# perform some very rudimentary platform detection
lsb_dist=$(get_distribution)
lsb_version=$(get_distro_version)
echo "$INFO starting tinkerbell stack setup"
check_prerequisites "$lsb_dist" "$lsb_version"
if ! [[ -f $ENV_FILE ]]; then
echo "$ERR Run './generate-env.sh network-interface > \"$ENV_FILE\"' before continuing."
exit 1
fi
# shellcheck disable=SC1090
source "$ENV_FILE"
if [[ -z $TINKERBELL_SKIP_NETWORKING ]]; then
setup_networking "$lsb_dist" "$lsb_version"
fi
setup_osie
generate_certificates
setup_docker_registry
echo "$INFO tinkerbell stack setup completed successfully on $lsb_dist server"
whats_next | tee /tmp/post-setup-message
)
# wrapped up in a function so that we have some protection against only getting
# half the file during "curl | sh"
do_setup