From 3ea8b15de0d2aa960c821bb13ecf4470075c2e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jake=20Buchholz=20G=C3=B6kt=C3=BCrk?= Date: Thu, 14 Nov 2024 01:22:48 +0000 Subject: [PATCH] cloud via cmdline / move autodetect --- bin/imds | 2 +- lib/tiny-cloud/cloud/nocloud/autodetect | 5 +-- lib/tiny-cloud/cloud/nocloud/imds | 5 +-- lib/tiny-cloud/cloud/nocloud/init | 14 +++---- lib/tiny-cloud/cloud/oci/imds | 2 +- lib/tiny-cloud/common | 53 ++++++++++++++++++++++-- lib/tiny-cloud/init | 55 ++++++++----------------- lib/tiny-cloud/user-data/alpine-config | 16 +++---- lib/tiny-cloud/user-data/cloud-config | 14 +++---- sbin/tiny-cloud | 8 ++-- 10 files changed, 100 insertions(+), 74 deletions(-) diff --git a/bin/imds b/bin/imds index 4512cfc..cbec3ca 100755 --- a/bin/imds +++ b/bin/imds @@ -84,7 +84,7 @@ _imds_ssh_keys() { done | sort -u } -_imds_nic_index() { cat "/sys/class/net/$1/address"; } +_imds_nic_index() { cat "$SYS/class/net/$1/address"; } ### load cloud-specific variables and functions diff --git a/lib/tiny-cloud/cloud/nocloud/autodetect b/lib/tiny-cloud/cloud/nocloud/autodetect index 7f04819..8055524 100755 --- a/lib/tiny-cloud/cloud/nocloud/autodetect +++ b/lib/tiny-cloud/cloud/nocloud/autodetect @@ -4,8 +4,7 @@ # vim:set filetype=sh: # shellcheck shell=sh -grep -w 'ds=nocloud' "$ROOT"/proc/cmdline 2>/dev/null \ - || grep -w "^ds=nocloud" "$ROOT"/sys/class/dmi/id/product_serial 2>/dev/null \ - || findfs LABEL=cidata >/dev/null 2>&1 \ +# kernel cmdline & DMI product serial are checked in common +findfs LABEL=cidata >/dev/null 2>&1 \ || findfs LABEL=CIDATA >/dev/null 2>&1 \ && echo "10 nocloud" diff --git a/lib/tiny-cloud/cloud/nocloud/imds b/lib/tiny-cloud/cloud/nocloud/imds index a69a66c..5d12b5b 100644 --- a/lib/tiny-cloud/cloud/nocloud/imds +++ b/lib/tiny-cloud/cloud/nocloud/imds @@ -8,8 +8,8 @@ is_nocloud_loaded() { [ -f "$TINY_CLOUD_VAR/.nocloud_loaded" ]; } _load_nocloud_cmdline() { local kopt kv k v data - for kopt in $(cat "$ROOT/proc/cmdline" 2>/dev/null) \ - $(grep '^ds=nocloud' "$ROOT"/sys/class/dmi/id/product_serial 2>/dev/null) ; do + for kopt in $(cat "$PROC/cmdline" 2>/dev/null) \ + $(grep '^ds=nocloud' "$SYS"/class/dmi/id/product_serial 2>/dev/null) ; do echo "$kopt" | grep -qE '(^|=)ds=nocloud(-net)?;' || continue for kv in $(echo "${kopt#*;}" | tr \; ' '); do k=$(echo "$kv" | cut -d= -f1) @@ -88,7 +88,6 @@ load_nocloud() { } _imds() { - mkdir -p "$TINY_CLOUD_VAR" local file="$TINY_CLOUD_VAR/$(echo "$1" | cut -d/ -f1)" local keypath="$(echo "$1" | cut -d/ -f2- | tr / ' ')" diff --git a/lib/tiny-cloud/cloud/nocloud/init b/lib/tiny-cloud/cloud/nocloud/init index 48b8380..8795a4e 100644 --- a/lib/tiny-cloud/cloud/nocloud/init +++ b/lib/tiny-cloud/cloud/nocloud/init @@ -9,7 +9,7 @@ set_resolv_conf() { local nameservers="$(imds meta-data/resolv_conf/nameservers)" for i in $nameservers; do local server="$(imds meta-data/resolv_conf/nameservers/$i)" - add_once "$ROOT"/etc/resolv.conf "nameserver $server" + add_once "$ETC"/resolv.conf "nameserver $server" done } @@ -22,8 +22,8 @@ want_ephemeral_network() { if has_ipv4_address; then return 1 fi - for i in $(cat "$ROOT"/proc/cmdline 2>/dev/null) \ - $(cat "$ROOT"/sys/class/dmi/id/product_serial 2>/dev/null); do + for i in $(cat "$PROC"/cmdline 2>/dev/null) \ + $(cat "$SYS"/class/dmi/id/product_serial 2>/dev/null); do case "$i" in "ds=nocloud;"*) for kv in $(echo "${i#*;}" | tr \; ' '); do @@ -45,13 +45,13 @@ want_ephemeral_network() { init__set_network_interfaces() { local interfaces="$(imds meta-data/network-interfaces)" - mkdir -p "$ROOT"/etc/network + mkdir -p "$ETC"/network if [ -n "$interfaces" ]; then - printf "%s\n" "$interfaces" > "$ROOT"/etc/network/interfaces - elif ! [ -f "$ROOT"/etc/network/interfaces ]; then + printf "%s\n" "$interfaces" > "$ETC"/network/interfaces + elif ! [ -f "$ETC"/network/interfaces ]; then init__set_default_interfaces fi - if ! grep -q dhcp "$ROOT"/etc/network/interfaces; then + if ! grep -q dhcp "$ETC"/network/interfaces; then set_resolv_conf fi } diff --git a/lib/tiny-cloud/cloud/oci/imds b/lib/tiny-cloud/cloud/oci/imds index 0f444ad..4c458ef 100644 --- a/lib/tiny-cloud/cloud/oci/imds +++ b/lib/tiny-cloud/cloud/oci/imds @@ -29,7 +29,7 @@ _imds_ssh_keys() { _imds "$IMDS_SSH_KEYS"; } _imds_nic_index() { local m n=0 - local mac=$(cat "/sys/class/net/$1/mac") + local mac=$(cat "$SYS/class/net/$1/mac") while m=$(imds "$IMDS_NICS/$n/mac" | tr A-F a-f); do [ "$m" = "$mac" ] && echo $n; return 0 done diff --git a/lib/tiny-cloud/common b/lib/tiny-cloud/common index 960ae3a..305636b 100644 --- a/lib/tiny-cloud/common +++ b/lib/tiny-cloud/common @@ -3,11 +3,58 @@ # shellcheck shell=sh # set defaults -[ -f "$ROOT/etc/tiny-cloud.conf" ] && . "$ROOT/etc/tiny-cloud.conf" +: "${ETC:=$ROOT/etc}" +: "${PROC:=$ROOT/proc}" +: "${SYS:=$ROOT/sys}" +[ -f "$ETC/tiny-cloud.conf" ] && . "$ETC/tiny-cloud.conf" : "${CLOUD:=auto}" : "${CLOUD_USER:=alpine}" -: "${TINY_CLOUD_LOGS:=$ROOT/var/log}" -: "${TINY_CLOUD_VAR:=$ROOT/var/lib/cloud}" +mkdir -p "${TINY_CLOUD_LOGS:=$ROOT/var/log}" +mkdir -p "${TINY_CLOUD_VAR:=$ROOT/var/lib/cloud}" + +lower() { tr 'A-Z' 'a-z'; } +line_has_k() { + # [] - line(s) in stdin/ have defined? + cat "${2:--}" 2>/dev/null | xargs -n1 | cut -d= -f1 | grep -q "^$1$" +} +line_kval() { + # [] [] - value(s) from stdin/ line(s) + # replace output with spaces (suitable for "sub-kv" situations) + cat "${2:--}" 2>/dev/null | xargs -n1 | grep "^$1=" | cut -d= -f2- | paste -sd' ' | tr "$3" ' ' +} + +if [ "$CLOUD" = "auto" ]; then + # previously detected? + CLOUD=$(cat "$TINY_CLOUD_VAR"/.autodetect 2>/dev/null) || { + # try kernel cmdline & DMI product serial + for F in "$PROC/cmdline" "$SYS/class/dmi/id/product_serial"; do + CLOUD=$(line_kval tinycloud "$F" : | line_kval cloud) + [ -z "$CLOUD" ] && CLOUD=$(line_kval ds "$F" ';' | cut -d' ' -f1 | lower) + [ -n "$CLOUD" ] && break + done + if [ -n "$CLOUD" ]; then + # convert cloud-init cloud names + case "$CLOUD" in + ec2) CLOUD=aws;; + gce) CLOUD=gcp;; + nocloud-net) CLOUD=nocloud;; + oracle) CLOUD=oci;; + esac + else + # try all the autodetects, sorted by confidence... + CLOUD=$( + for i in "$LIBDIR"/tiny-cloud/cloud/*/autodetect; do + [ -x "$i" ] && "$i" + done | sort -n | cut -d' ' -f2 | head -n 1 + ) + fi + if [ -z "$CLOUD" ] || [ ! -d "$LIBDIR/tiny-cloud/cloud/$CLOUD" ]; then + CLOUD=unknown + else + printf "%s\n" "$CLOUD" > "$TINY_CLOUD_VAR"/.autodetect + fi + } +fi log() { local facility="local7" diff --git a/lib/tiny-cloud/init b/lib/tiny-cloud/init index a3e99f4..0590431 100644 --- a/lib/tiny-cloud/init +++ b/lib/tiny-cloud/init @@ -32,31 +32,12 @@ DEFAULT_ACTIONS_FINAL="" : "${INIT_ACTIONS_MAIN=$DEFAULT_ACTIONS_MAIN}" : "${INIT_ACTIONS_FINAL=$DEFAULT_ACTIONS_FINAL}" -# 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 - -# autodetect cloud -if [ "$CLOUD" = "auto" ]; then - CLOUD=$(cat "$TINY_CLOUD_VAR"/.autodetect 2>/dev/null) || { - CLOUD=$( - for i in "$LIBDIR"/tiny-cloud/cloud/*/autodetect; do - if [ -f "$i" ]; then - "$i" - fi - done | sort -n | cut -d' ' -f2 | head -n 1 - ) - : ${CLOUD:=unknown} - printf "%s\n" "$CLOUD" > "$TINY_CLOUD_VAR"/.autodetect - } -fi - ### standard boot phase functions... init__expand_root() { - local dev=$(awk '$2 == "/" {print $1}' "$ROOT"/proc/mounts 2>/dev/null) - local filesystem=$(awk '$2 == "/" {print $3}' "$ROOT"/proc/mounts 2>/dev/null) - local partition=$(cat "$ROOT/sys/class/block/${dev#/dev/}/partition" 2>/dev/null) + local dev=$(awk '$2 == "/" {print $1}' "$PROC"/mounts 2>/dev/null) + local filesystem=$(awk '$2 == "/" {print $3}' "$PROC"/mounts 2>/dev/null) + local partition=$(cat "$SYS/class/block/${dev#/dev/}/partition" 2>/dev/null) # only support ext2/ext3/ext4 for now case "$filesystem" in @@ -66,7 +47,7 @@ init__expand_root() { if [ -n "$partition" ]; then # it's a partition, resize it - local volume=$(readlink -f "$ROOT/sys/class/block/${dev#/dev/}/..") + local volume=$(readlink -f "$SYS/class/block/${dev#/dev/}/..") volume="/dev/${volume##*/}" echo ", +" | $MOCK sfdisk -q --no-reread -N "$partition" "$volume" $MOCK partx -u "$volume" @@ -79,7 +60,7 @@ init__expand_root() { # collect ethernet interfaces, sorted by index ethernets() { - for i in "$ROOT/sys/class/net/"*; do + for i in "$SYS/class/net/"*; do local iface="${i##*/}" case "$iface" in eth*) echo "$(cat "$i/ifindex") $iface";; @@ -93,7 +74,7 @@ find_first_interface_up() { [ $# -eq 0 ] && return while [ $n -le ${TINY_CLOUD_LINK_WAIT_MAX:-10} ]; do for i in "$@"; do - if [ "$(cat "$ROOT/sys/class/net/$i/operstate")" = "up" ]; then + if [ "$(cat "$SYS/class/net/$i/operstate")" = "up" ]; then echo "$i" return fi @@ -148,16 +129,16 @@ init__set_ephemeral_network() { } init__set_default_interfaces() { - if [ -f "$ROOT"/etc/network/interfaces ]; then + if [ -f "$ETC"/network/interfaces ]; then log -i -t "$phase" info "$ACTION: already set up" return fi - mkdir -p "$ROOT/etc/network" + mkdir -p "$ETC/network" printf "%s\n%s\n\n" \ "auto lo" \ "iface lo inet loopback" \ - > "$ROOT/etc/network/interfaces" + > "$ETC/network/interfaces" local iface="$(auto_detect_ethernet_interface)" if [ -z "$iface" ]; then @@ -167,7 +148,7 @@ init__set_default_interfaces() { printf "%s\n%s\n\t%s\n\n" \ "auto $iface" \ "iface $iface" \ - "use dhcp" >> "$ROOT/etc/network/interfaces" + "use dhcp" >> "$ETC/network/interfaces" } init__create_default_user() { @@ -188,12 +169,12 @@ init__create_default_user() { echo "$user:*" | $MOCK chpasswd -e # setup sudo and/or doas - if [ -d "$ROOT/etc/sudoers.d" ]; then - echo '%wheel ALL=(ALL) NOPASSWD: ALL' > "$ROOT/etc/sudoers.d/wheel" + if [ -d "$ETC/sudoers.d" ]; then + echo '%wheel ALL=(ALL) NOPASSWD: ALL' > "$ETC/sudoers.d/wheel" fi - if [ -d "$ROOT/etc/doas.d" ]; then + if [ -d "$ETC/doas.d" ]; then echo 'permit nopass :wheel' > "$TARGET/etc/doas.d/wheel.conf" - elif [ -f "$ROOT/etc/doas.conf" ]; then + elif [ -f "$ETC/doas.conf" ]; then add_once "$TARGET/etc/doas.conf" "permit nopass :wheel" fi } @@ -247,10 +228,10 @@ init__set_hostname() { return 1 fi - mkdir -p "$ROOT"/etc - echo "$host" > "$ROOT"/etc/hostname - $MOCK hostname -F "$ROOT"/etc/hostname - echo -e "127.0.1.1\t$fqdn $host" >> "$ROOT"/etc/hosts + mkdir -p "$ETC" + echo "$host" > "$ETC"/hostname + $MOCK hostname -F "$ETC"/hostname + echo -e "127.0.1.1\t$fqdn $host" >> "$ETC"/hosts } init__set_ssh_keys() { diff --git a/lib/tiny-cloud/user-data/alpine-config b/lib/tiny-cloud/user-data/alpine-config index 606b7e4..def1b1f 100644 --- a/lib/tiny-cloud/user-data/alpine-config +++ b/lib/tiny-cloud/user-data/alpine-config @@ -21,24 +21,24 @@ init__userdata_apk_cache() { case "$cache" in /*) cache="../..$cache";; esac - mkdir -p "$ROOT"/etc/apk - ln -sf "$cache" "$ROOT"/etc/apk/cache + mkdir -p "$ETC"/apk + ln -sf "$cache" "$ETC"/apk/cache } init__userdata_apk_repositories() { local apk="$(get_userdata apk)" - if [ -z "$apk" ] && ! [ -e "$ROOT"/etc/apk/repositories ]; then + if [ -z "$apk" ] && ! [ -e "$ETC"/apk/repositories ]; then $MOCK setup-apkrepos -1 -c return fi local repositories="$(get_userdata apk/repositories)" - mkdir -p "$ROOT"/etc/apk + mkdir -p "$ETC"/apk for r in $repositories; do local baseurl="$(get_userdata apk/repositories/$r/base_url)" local repos="$(get_userdata apk/repositories/$r/repos)" local version="$(get_userdata apk/repositories/$r/version)" if [ -z "$version" ]; then - local version_id=$( . "$ROOT"/etc/os-release 2>/dev/null && echo "$VERSION_ID") + local version_id=$( . "$ETC"/os-release 2>/dev/null && echo "$VERSION_ID") case "$version_id" in edge*|*_alpha*) version="edge";; [0-9]*.[0-9]*.[0-9]*) version="v${version_id%.*}";; @@ -49,14 +49,14 @@ init__userdata_apk_repositories() { fi for repo in $repos; do local uri="${baseurl%/}/$(get_userdata apk/repositories/$r/repos/$repo)" - add_once "$ROOT"/etc/apk/repositories "$uri" + add_once "$ETC"/apk/repositories "$uri" done done } find_biggest_empty_disk() { local d p - for d in "$ROOT"/sys/class/block/*/device; do + for d in "$SYS"/class/block/*/device; do p=${d%/device} if [ -e "$p"/size ] && [ -z "$(blkid /dev/${p##*/})" ]; then echo "$(cat $p/size) ${p##*/}" @@ -69,7 +69,7 @@ init__userdata_autoinstall() { if [ "$autoinstall" = "true" ]; then local disk="$(find_biggest_empty_disk)" if [ -n "$disk" ]; then - rm -f "$ROOT"/etc/runlevels/*/tiny-cloud* + rm -f "$ETC"/runlevels/*/tiny-cloud* $MOCK lbu include /root/.ssh /home $MOCK ERASE_DISKS=/dev/$disk setup-disk -m sys /dev/$disk # TODO: make reboot configurable diff --git a/lib/tiny-cloud/user-data/cloud-config b/lib/tiny-cloud/user-data/cloud-config index 57f7d6f..cdd2af7 100644 --- a/lib/tiny-cloud/user-data/cloud-config +++ b/lib/tiny-cloud/user-data/cloud-config @@ -291,17 +291,17 @@ init__userdata_users() { done fi if in_list doas $keys; then - if [ -d "$ROOT/etc/doas.d" ]; then - touch "$ROOT/etc/doas.d/$name.conf" - chmod 660 "$ROOT/etc/doas.d/$name.conf" + if [ -d "$ETC/doas.d" ]; then + touch "$ETC/doas.d/$name.conf" + chmod 660 "$ETC/doas.d/$name.conf" fi local j for j in $(get_userdata users/$i/doas); do local line="$(get_userdata users/$i/doas/$j)" - if [ -d "$ROOT/etc/doas.d" ]; then - echo "$line" >> "$ROOT/etc/doas.d/$name.conf" - elif [ -f "$ROOT/etc/doas.conf" ]; then - add_once "$ROOT/etc/doas.conf" "$line" + if [ -d "$ETC/doas.d" ]; then + echo "$line" >> "$ETC/doas.d/$name.conf" + elif [ -f "$ETC/doas.conf" ]; then + add_once "$ETC/doas.conf" "$line" fi done fi diff --git a/sbin/tiny-cloud b/sbin/tiny-cloud index 48c813e..3f98afd 100755 --- a/sbin/tiny-cloud +++ b/sbin/tiny-cloud @@ -49,15 +49,15 @@ while true; do -[ED]|--enable|--disable) # just openrc for now : "${ROOT:=}" # for mounted volumes # always start with a clean slate - rm -f "$ROOT"/etc/runlevels/*/tiny-cloud* + rm -f "$ETC"/runlevels/*/tiny-cloud* log -i info "- tiny-cloud* services removed from all runlevels" if [ "$1" = '-D' ] || [ "$1" = '--disable' ]; then exit 0 fi - ln -s /etc/init.d/tiny-cloud-boot "$ROOT"/etc/runlevels/boot + ln -s /etc/init.d/tiny-cloud-boot "$ETC"/runlevels/boot log -i info "+ tiny-cloud-boot service added to boot runlevel" for p in early main final; do - ln -s "/etc/init.d/tiny-cloud-$p" "$ROOT"/etc/runlevels/default + ln -s "/etc/init.d/tiny-cloud-$p" "$ETC"/runlevels/default log -i info "+ tiny-cloud-$p service added to default runlevel" done exit 0;; @@ -75,7 +75,7 @@ case "$phase" in *) usage >&2; exit 1;; esac -if [ -e "$ROOT"/etc/tiny-cloud.disabled ]; then +if [ -e "$ETC"/tiny-cloud.disabled ]; then log -i -t "$phase" info "tiny-cloud disabled" exit 0 fi