1
0
mirror of https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud.git synced 2025-12-15 11:22:43 +03:00

Fixes for tiny-cloud Wrapper

* switch from phase functions to variables containing list of actions
* init actions prefixed with 'init__'
* refine output during init
* add syslog entries
* add --setup for putting init scripts in the right runlevel
This commit is contained in:
Jake Buchholz Göktürk 2023-05-03 02:11:01 +00:00
parent 3894cd9f8d
commit 58ac2108cd
6 changed files with 145 additions and 182 deletions

View File

@ -9,7 +9,7 @@
: "${TINY_CLOUD_VAR:=$ROOT/var/lib/cloud}"
log() {
local facility="kern"
local facility="local7"
local stderr
local tag=$(basename "$0")
while [ "${1#-}" != "$1" ]; do

View File

@ -8,19 +8,28 @@
: "${SKIP_INIT_ACTIONS:=}"
: "${HOTPLUG_TYPE:=mdev}"
# TODO: default phase actions
### default phase actions (without leading 'init__')
# ensure existence of output directories
[ ! -d "$TINY_CLOUD_LOGS" ] && mkdir -p "$TINY_CLOUD_LOGS"
[ ! -d "$TINY_CLOUD_VAR" ] && mkdir -p "$TINY_CLOUD_VAR"
INIT_ACTIONS_EARLY="
expand_root
install_hotplugs
"
INIT_ACTIONS_MAIN="
save_userdata
set_hostname
set_ssh_keys
"
INIT_ACTIONS_FINAL="
run_userdata
"
### NOTE: init-early functions...
# try to ensure existence of output directories, but otherwise don't panic
[ ! -d "$TINY_CLOUD_LOGS" ] && mkdir -p "$TINY_CLOUD_LOGS" || true
[ ! -d "$TINY_CLOUD_VAR" ] && mkdir -p "$TINY_CLOUD_VAR" || true
expand_root() {
skip_action expand_root && return
echo "Expanding Root Volume/ Partition"
### init-early functions...
init__expand_root() {
local dev=$(awk '$2 == "/" {print $1}' "$ROOT"/proc/mounts)
local partition=$(cat "$ROOT/sys/class/block/${dev#/dev/}/partition" 2>/dev/null)
@ -35,36 +44,38 @@ expand_root() {
$MOCK resize2fs "$dev"
}
install_hotplugs() {
skip_action install_hotplugs && return
init__install_hotplugs() {
local level result rc=0
[ ! -n "$HOTPLUG_MODULES" ] && return
echo "Installing Cloud Hotplugs"
local result rc=0
if [ -f "$LIBDIR/tiny-cloud/$HOTPLUG_TYPE" ]; then
. "$LIBDIR/tiny-cloud/$HOTPLUG_TYPE"
. "$LIBDIR/tiny-cloud/$HOTPLUG_TYPE"
fi
printf ' :' >&2
for module in $HOTPLUG_MODULES; do
result='-'
printf " $module"
result='?'
level='err'
printf "$module" >&2
log info "$phase $ACTION $module"
if type "mod__$module" | grep -q -w "function"; then
"mod__$module" && result='+' || { result='!'; rc=1; }
if "mod__$module"; then
result='+'
level='info'
else
result='!'
rc=1
fi
fi
printf "($result)"
printf '(%s) ' $result >&2
log "$level" "$phase $ACTION $module ($result)"
done
return $rc
}
### NOTE: init-main functions
set_hostname() {
skip_action set_hostname && return
echo "Setting Instance Hostname"
### init-main functions
init__set_hostname() {
local fqdn=$(imds @hostname)
local host="${fqdn%%\.*}"
@ -74,11 +85,7 @@ set_hostname() {
echo -e "127.0.1.1\t$fqdn $host" >> "$ROOT"/etc/hosts
}
set_ssh_keys() {
skip_action set_ssh_keys && return
echo "Installing SSH Keys for $CLOUD_USER User"
init__set_ssh_keys() {
local user="$CLOUD_USER"
local pwent="$(getent passwd "$user")"
local group=$(echo "$pwent" | cut -d: -f4)
@ -96,87 +103,62 @@ set_ssh_keys() {
imds @ssh-keys > "$keys_file"
}
save_userdata() {
skip_action save_userdata && return
# TODO: this trips save_userdata_* and run_userdata tests: "stdout not empty"
#echo "Saving Instance UserData"
init__save_userdata() {
local userdata="$TINY_CLOUD_VAR/user-data"
local tmpfile=$(mktemp "$userdata.XXXXXX")
local cmd
imds -e @userdata > "$tmpfile"
cmd="cat"
if ! skip_action decompress_userdata; then
if printf '\037\213\010' | cmp -s -n 3 "$tmpfile"; then
gzip -dc "$tmpfile" > "$userdata"
elif printf 'BZh' | cmp -s -n 3 "$tmpfile"; then
bzip2 -dc "$tmpfile" > "$userdata"
elif printf '\375\067\172\130\132\000' | cmp -s -n 6 "$tmpfile"; then
unxz -c "$tmpfile" > "$userdata"
elif printf '\135\000\000' | cmp -s -n 3 "$tmpfile"; then
lzma -dc "$tmpfile" > "$userdata"
elif printf '\211\114\132' | cmp -s -n 3 "$tmpfile"; then
lzop -dc "$tmpfile" > "$userdata"
elif printf '\004\042\115\030' | cmp -s -n 4 "$tmpfile"; then
lz4 -dc "$tmpfile" > "$userdata"
elif printf '(\265/\375' | cmp -s -n 4 "$tmpfile"; then
zstd -dc "$tmpfile" > "$userdata"
else
cp "$tmpfile" "$userdata"
fi
if printf '\037\213\010' | cmp -s -n 3 "$tmpfile"; then
gzip -dc "$tmpfile" > "$userdata"
elif printf 'BZh' | cmp -s -n 3 "$tmpfile"; then
bzip2 -dc "$tmpfile" > "$userdata"
elif printf '\375\067\172\130\132\000' | cmp -s -n 6 "$tmpfile"; then
unxz -c "$tmpfile" > "$userdata"
elif printf '\135\000\000' | cmp -s -n 3 "$tmpfile"; then
lzma -dc "$tmpfile" > "$userdata"
elif printf '\211\114\132' | cmp -s -n 3 "$tmpfile"; then
lzop -dc "$tmpfile" > "$userdata"
elif printf '\004\042\115\030' | cmp -s -n 4 "$tmpfile"; then
lz4 -dc "$tmpfile" > "$userdata"
elif printf '(\265/\375' | cmp -s -n 4 "$tmpfile"; then
zstd -dc "$tmpfile" > "$userdata"
else
cp "$tmpfile" "$userdata"
fi
rm "$tmpfile"
}
### TODO: init-final functions
run_userdata() {
skip_action run_userdata && return
if [ $(userdata_type) != "script" ]; then
echo "UserData is not executable"
return
fi
echo "Executing UserData Script"
### init-final functions
init__run_userdata() {
local log="$TINY_CLOUD_LOGS/user-data.log"
local exit="$TINY_CLOUD_LOGS/user-data.exit"
local userdata="$TINY_CLOUD_VAR/user-data"
if [ $(userdata_type) != "script" ]; then
printf '(Not Executable) ' >&2
log info "$phase $ACTION - not exectutable"
return
fi
chmod +x "$userdata"
{ "$userdata" 2>& 1; echo $? > "$exit"; } | tee "$log"
return $(cat "$exit")
}
### potentially override the above, per cloud
# load cloud-specific init functions / vars
if [ -f "$LIBDIR/tiny-cloud/cloud/$CLOUD/init" ]; then
. "$LIBDIR/tiny-cloud/cloud/$CLOUD/init"
. "$LIBDIR/tiny-cloud/cloud/$CLOUD/init"
fi
# TODO: load user-data type-specific init functions / vars
### non-overrideable functions
# should we skip this action?
skip_action() {
local action="$1"
for i in $SKIP_INIT_ACTIONS; do
if [ "$i" = "$action" ]; then
printf " SKIPPING"
return 0
fi
done
return 1
}
# this should be non-overrideable, but need this before we...
userdata_type() {
if [ -f "$TINY_CLOUD_VAR/user-data" ]; then
header=$(head -n1 "$TINY_CLOUD_VAR/user-data")
header=$(head -n1 "$TINY_CLOUD_VAR/user-data" | sed -e 's/[[:space:]].*//g')
case "$header" in
'#cloud-config') echo cloud-config;;
'#!'*) echo script;;
@ -186,3 +168,6 @@ userdata_type() {
echo missing
fi
}
# ...load user-data type-specific init functions / vars
# TODO

View File

@ -10,22 +10,19 @@ set -e
usage() {
cat <<EOF
Usage: ${0##*/} [-h | --help] { early | main | final | --bootstrap {complete|incomplete} }
Usage: ${0##*/} [-h | --help] { early | main | final | --bootstrap {complete|incomplete} | --setup }
EOF
}
bootstrap_complete() {
echo "Marking Instance Bootstrap Complete"
init__bootstrap_complete() {
touch "$TINY_CLOUD_VAR/.bootstrap-complete"
}
bootstrap_incomplete() {
echo "Marking Instance Bootstrap Incomplete"
rm -f "$TINY_CLOUD_VAR/.bootstrap-complete"
}
args=$(getopt -o hb: --long help,bootstrap: -n ${0##*/} -- "$@")
if [ $? -ne 0 ]; then
args=$(getopt -o hsb: --long help,setup,bootstrap: -n ${0##*/} -- "$@")
if [ $? -ne 0 ] || [ $# -eq 0 ]; then
usage >&2
exit 1
fi
@ -36,11 +33,22 @@ while true; do
-b|--bootstrap) shift
case "$1" in
complete) # indicate bootstrap is done
bootstrap_complete;;
init__bootstrap_complete
log warn 'bootstrap marked complete';;
incomplete) # indicate bootstrap isn't done
bootstrap_incomplete;;
bootstrap_incomplete
log warn 'bootstrap marked incomplete';;
*) usage >&2; exit 1;;
esac
printf ' bootstrap marked "%s"\n' "$1" >&2
exit 0;;
-s|--setup) # just openrc for now
for phase in -early '' -final; do
rc-update -a del "tiny-cloud$phase" || true
done
rc-update add tiny-cloud-early boot
rc-update add tiny-cloud default
rc-update add tiny-cloud-final default
exit 0;;
--) shift; break;;
*) usage >&2; exit 1;;
@ -58,32 +66,56 @@ esac
# is initial bootstrap already done?
if [ -f "$TINY_CLOUD_VAR/.bootstrap-complete" ]; then
log -s "Already bootstrapped"
printf ' already bootstrapped\n' >&2
log info "$phase - already bootstrapped"
exit 0;
fi
### default phase actions
# TODO? represent as vars containing lists of funcs?
early() {
expand_root
install_hotplugs
}
main() {
save_userdata
set_hostname
set_ssh_keys
}
final() {
run_userdata
bootstrap_complete
}
# load init functions
. "$LIBDIR/tiny-cloud/init"
# TODO? for loop over list of funcs? -- better for ebegin/eend-ish output
echo $phase "$@"
### non-overrideable stuff
# should we skip this action?
skip_action() {
local action="$1"
for i in $SKIP_INIT_ACTIONS; do
[ "$i" = "$action" ] && return 0
done
return 1
}
# mandatory final action...
INIT_ACTIONS_FINAL="${INIT_ACTIONS_FINAL} bootstrap_complete"
### let's do stuff!
case "$phase" in
early) INIT_ACTIONS="$INIT_ACTIONS_EARLY";;
main) INIT_ACTIONS="$INIT_ACTIONS_MAIN";;
final) INIT_ACTIONS="$INIT_ACTIONS_FINAL";;
*) usage >&2; exit 1
esac
for ACTION in $INIT_ACTIONS; do
if skip_action "$ACTION"; then
printf '\n -- %s : [SKIP]' $ACTION >&2
log warn "$phase - $ACTION - SKIPPED"
continue
fi
printf '\n ++ %s ' $ACTION >&2
log info "$phase - $ACTION - START"
RESULT="UNKNOWN"
LEVEL="err"
if type "init__$ACTION" | grep -q -w "function"; then
if "init__$ACTION" "$@"; then
RESULT="DONE"
LEVEL="info"
else
RESULT="FAIL"
fi
fi
printf ': [%s]' $RESULT >&2
log "$LEVEL" "$phase - $ACTION - $RESULT"
done
echo >&2

View File

@ -9,33 +9,21 @@ lib="$srcdir"/lib/tiny-cloud/init
init_tests \
expand_root \
expand_root_partition \
expand_root_skip \
install_hotplugs_skip \
install_hotplugs_fail
PROVIDERS="aws azure gcp nocloud oci"
expand_root_body() {
fake_bin test-expand-root <<-EOF
#!/bin/sh
. "$lib"
expand_root
EOF
mkdir proc
echo "/dev/xvda / ext4 rw,noatime 0 0" > proc/mounts
for provider in $PROVIDERS; do
CLOUD="$provider" atf_check \
-o match:"resize2fs /dev/xvda" \
test-expand-root
sh -c ". $lib; init__expand_root"
done
}
expand_root_partition_body() {
fake_bin test-expand-root <<-EOF
#!/bin/sh
. "$lib"
expand_root
EOF
mkdir -p proc sys/class/block \
sys/devices/pci0000:00/0000:00:1b.0/0000:01:00.0/nvme/nvme0/nvme0n1/nvme0n1p2 \
sys/devices/pci0000:00/0000:00:1b.0/0000:01:00.0/nvme/nvme0/nvme0n1/device
@ -49,46 +37,12 @@ expand_root_partition_body() {
-o match:"sfdisk .*/dev/nvme0n1" \
-o match:"partx .*/dev/nvme0n1" \
-o match:"resize2fs /dev/nvme0n1p2" \
test-expand-root
done
}
expand_root_skip_body() {
fake_bin test-expand-root <<-EOF
#!/bin/sh
SKIP_INIT_ACTIONS=expand_root
. "$lib"
expand_root
EOF
for provider in $PROVIDERS; do
CLOUD="$provider" atf_check \
-o match:'^ SKIPPING$' \
test-expand-root
done
}
install_hotplugs_skip_body() {
fake_bin test-install-hotplugs <<-EOF
#!/bin/sh
SKIP_INIT_ACTIONS=install_hotplugs
. "$lib"
install_hotplugs
EOF
for provider in $PROVIDERS; do
CLOUD="$provider" atf_check \
-o match:'^ SKIPPING$' \
test-install-hotplugs
sh -c ". $lib; init__expand_root"
done
}
install_hotplugs_fail_body() {
fake_bin test-install-hotplugs <<-EOF
#!/bin/sh
HOTPLUG_MODULES="vnic_eth_hotplug"
. "$lib"
install_hotplugs
EOF
CLOUD=aws atf_check -s not-exit:0 \
-o match:"^ vnic_eth_hotplug\(!\)$" \
test-install-hotplugs
-e match:"vnic_eth_hotplug\(!\)" \
sh -c ". $lib; HOTPLUG_MODULES='vnic_eth_hotplug'; init__install_hotplugs"
}

View File

@ -38,10 +38,10 @@ run_userdata_body() {
echo "hello from user-data"
EOF
CLOUD="nocloud" atf_check \
sh -c ". \"$lib\"; save_userdata"
sh -c ". \"$lib\"; init__save_userdata"
CLOUD="nocloud" atf_check \
-o match:"hello from user-data" \
sh -c ". \"$lib\"; run_userdata"
sh -c ". \"$lib\"; init__run_userdata"
grep "hello from user-data" var/log/user-data.log || atf_fail "user-data.log failed"
grep -w "0" var/log/user-data.exit || atf_fail "user-data.exit failed"
}

View File

@ -42,16 +42,8 @@ set_hostname_body() {
CLOUD="nocloud" atf_check \
-o match:"hostname.*-F $PWD/etc/hostname" \
sh -c ". \"$lib\"; set_hostname"
sh -c ". \"$lib\"; init__set_hostname"
atf_check -o match:"^myhostname$" cat etc/hostname
rm etc/hostname
CLOUD="nocloud" atf_check \
-o not-match:"hostname.*-F $PWD/etc/hostname" \
sh -c "SKIP_INIT_ACTIONS=set_hostname ; . \"$lib\"; set_hostname"
if test -f etc/hostname; then
atf_fail "etc/hostname should not exist"
fi
}
set_ssh_keys_body() {
@ -66,7 +58,7 @@ set_ssh_keys_body() {
EOF
CLOUD="nocloud" atf_check \
-o match:"chown.*/\.ssh" \
sh -c ". \"$lib\"; set_ssh_keys"
sh -c ". \"$lib\"; init__set_ssh_keys"
atf_check -o match:"^ssh-ed25519 keydata" \
-o match:"^ssh-rsa foobar" \
cat home/alpine/.ssh/authorized_keys
@ -77,7 +69,7 @@ save_userdata_plain_body() {
#userdata
EOF
CLOUD="nocloud" atf_check \
sh -c ". \"$lib\"; save_userdata"
sh -c ". \"$lib\"; init__save_userdata"
atf_check -o match:"^#userdata" cat var/lib/cloud/user-data
}
@ -88,7 +80,7 @@ save_userdata_compressed_body() {
fake_userdata_nocloud < tmpfile
CLOUD="nocloud" atf_check \
sh -c ". \"$lib\"; save_userdata"
sh -c ". \"$lib\"; init__save_userdata"
if ! grep "^#userdata" var/lib/cloud/user-data; then
atf_fail "$comp failed"