1
0
mirror of https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud.git synced 2025-12-16 11:52: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}" : "${TINY_CLOUD_VAR:=$ROOT/var/lib/cloud}"
log() { log() {
local facility="kern" local facility="local7"
local stderr local stderr
local tag=$(basename "$0") local tag=$(basename "$0")
while [ "${1#-}" != "$1" ]; do while [ "${1#-}" != "$1" ]; do

View File

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

View File

@ -10,22 +10,19 @@ set -e
usage() { usage() {
cat <<EOF cat <<EOF
Usage: ${0##*/} [-h | --help] { early | main | final | --bootstrap {complete|incomplete} } Usage: ${0##*/} [-h | --help] { early | main | final | --bootstrap {complete|incomplete} | --setup }
EOF EOF
} }
bootstrap_complete() { init__bootstrap_complete() {
echo "Marking Instance Bootstrap Complete"
touch "$TINY_CLOUD_VAR/.bootstrap-complete" touch "$TINY_CLOUD_VAR/.bootstrap-complete"
} }
bootstrap_incomplete() { bootstrap_incomplete() {
echo "Marking Instance Bootstrap Incomplete"
rm -f "$TINY_CLOUD_VAR/.bootstrap-complete" rm -f "$TINY_CLOUD_VAR/.bootstrap-complete"
} }
args=$(getopt -o hb: --long help,bootstrap: -n ${0##*/} -- "$@") args=$(getopt -o hsb: --long help,setup,bootstrap: -n ${0##*/} -- "$@")
if [ $? -ne 0 ]; then if [ $? -ne 0 ] || [ $# -eq 0 ]; then
usage >&2 usage >&2
exit 1 exit 1
fi fi
@ -36,11 +33,22 @@ while true; do
-b|--bootstrap) shift -b|--bootstrap) shift
case "$1" in case "$1" in
complete) # indicate bootstrap is done complete) # indicate bootstrap is done
bootstrap_complete;; init__bootstrap_complete
log warn 'bootstrap marked complete';;
incomplete) # indicate bootstrap isn't done incomplete) # indicate bootstrap isn't done
bootstrap_incomplete;; bootstrap_incomplete
log warn 'bootstrap marked incomplete';;
*) usage >&2; exit 1;; *) usage >&2; exit 1;;
esac 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;; exit 0;;
--) shift; break;; --) shift; break;;
*) usage >&2; exit 1;; *) usage >&2; exit 1;;
@ -58,32 +66,56 @@ esac
# is initial bootstrap already done? # is initial bootstrap already done?
if [ -f "$TINY_CLOUD_VAR/.bootstrap-complete" ]; then if [ -f "$TINY_CLOUD_VAR/.bootstrap-complete" ]; then
log -s "Already bootstrapped" printf ' already bootstrapped\n' >&2
log info "$phase - already bootstrapped"
exit 0; exit 0;
fi 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 # load init functions
. "$LIBDIR/tiny-cloud/init" . "$LIBDIR/tiny-cloud/init"
# TODO? for loop over list of funcs? -- better for ebegin/eend-ish output ### non-overrideable stuff
echo $phase "$@"
# 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 \ init_tests \
expand_root \ expand_root \
expand_root_partition \ expand_root_partition \
expand_root_skip \
install_hotplugs_skip \
install_hotplugs_fail install_hotplugs_fail
PROVIDERS="aws azure gcp nocloud oci" PROVIDERS="aws azure gcp nocloud oci"
expand_root_body() { expand_root_body() {
fake_bin test-expand-root <<-EOF
#!/bin/sh
. "$lib"
expand_root
EOF
mkdir proc mkdir proc
echo "/dev/xvda / ext4 rw,noatime 0 0" > proc/mounts echo "/dev/xvda / ext4 rw,noatime 0 0" > proc/mounts
for provider in $PROVIDERS; do for provider in $PROVIDERS; do
CLOUD="$provider" atf_check \ CLOUD="$provider" atf_check \
-o match:"resize2fs /dev/xvda" \ -o match:"resize2fs /dev/xvda" \
test-expand-root sh -c ". $lib; init__expand_root"
done done
} }
expand_root_partition_body() { expand_root_partition_body() {
fake_bin test-expand-root <<-EOF
#!/bin/sh
. "$lib"
expand_root
EOF
mkdir -p proc sys/class/block \ 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/nvme0n1p2 \
sys/devices/pci0000:00/0000:00:1b.0/0000:01:00.0/nvme/nvme0/nvme0n1/device 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:"sfdisk .*/dev/nvme0n1" \
-o match:"partx .*/dev/nvme0n1" \ -o match:"partx .*/dev/nvme0n1" \
-o match:"resize2fs /dev/nvme0n1p2" \ -o match:"resize2fs /dev/nvme0n1p2" \
test-expand-root sh -c ". $lib; init__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
done done
} }
install_hotplugs_fail_body() { 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 \ CLOUD=aws atf_check -s not-exit:0 \
-o match:"^ vnic_eth_hotplug\(!\)$" \ -e match:"vnic_eth_hotplug\(!\)" \
test-install-hotplugs 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" echo "hello from user-data"
EOF EOF
CLOUD="nocloud" atf_check \ CLOUD="nocloud" atf_check \
sh -c ". \"$lib\"; save_userdata" sh -c ". \"$lib\"; init__save_userdata"
CLOUD="nocloud" atf_check \ CLOUD="nocloud" atf_check \
-o match:"hello from user-data" \ -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 "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" 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 \ CLOUD="nocloud" atf_check \
-o match:"hostname.*-F $PWD/etc/hostname" \ -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 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() { set_ssh_keys_body() {
@ -66,7 +58,7 @@ set_ssh_keys_body() {
EOF EOF
CLOUD="nocloud" atf_check \ CLOUD="nocloud" atf_check \
-o match:"chown.*/\.ssh" \ -o match:"chown.*/\.ssh" \
sh -c ". \"$lib\"; set_ssh_keys" sh -c ". \"$lib\"; init__set_ssh_keys"
atf_check -o match:"^ssh-ed25519 keydata" \ atf_check -o match:"^ssh-ed25519 keydata" \
-o match:"^ssh-rsa foobar" \ -o match:"^ssh-rsa foobar" \
cat home/alpine/.ssh/authorized_keys cat home/alpine/.ssh/authorized_keys
@ -77,7 +69,7 @@ save_userdata_plain_body() {
#userdata #userdata
EOF EOF
CLOUD="nocloud" atf_check \ 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 atf_check -o match:"^#userdata" cat var/lib/cloud/user-data
} }
@ -88,7 +80,7 @@ save_userdata_compressed_body() {
fake_userdata_nocloud < tmpfile fake_userdata_nocloud < tmpfile
CLOUD="nocloud" atf_check \ CLOUD="nocloud" atf_check \
sh -c ". \"$lib\"; save_userdata" sh -c ". \"$lib\"; init__save_userdata"
if ! grep "^#userdata" var/lib/cloud/user-data; then if ! grep "^#userdata" var/lib/cloud/user-data; then
atf_fail "$comp failed" atf_fail "$comp failed"