diff --git a/docs/tiny-cloud.8.scd b/docs/tiny-cloud.8.scd index 243d1d8..17c2f44 100644 --- a/docs/tiny-cloud.8.scd +++ b/docs/tiny-cloud.8.scd @@ -6,7 +6,7 @@ tiny-cloud - perform first-boot initialization for cloud instances # SYNOPSIS -*tiny-cloud* [*-h*|*--help*] { *boot* | *early* | *main* | *final* | *-b*|*--bootstrap* { *complete* | *incomplete* | *status* } | *-E*|*--enable* | *-D*|*--disable* } +*tiny-cloud* [*-h*|*--help*] { *boot* | *early* | *main* | *final* | *autodetect* | *-b*|*--bootstrap* { *complete* | *incomplete* | *status* } | *-E*|*--enable* | *-D*|*--disable* } # DESCRIPTION @@ -61,6 +61,11 @@ been marked complete, later phase invocations exit without doing further work. Run finalization actions. By default this marks bootstrap complete after any additional configured final actions have succeeded. +*autodetect* + Run provider autodetection probes and print the detected provider name. + This does not use the configured provider, kernel command line hints, or the + cached autodetection result. + # OPERATION When invoked with a phase argument, *tiny-cloud* first checks whether diff --git a/lib/tiny-cloud/common b/lib/tiny-cloud/common index a483ef3..1269a9a 100644 --- a/lib/tiny-cloud/common +++ b/lib/tiny-cloud/common @@ -47,31 +47,42 @@ line_kval() { cat "${2:--}" 2>/dev/null | xargs -n1 | grep "^$1=" | cut -d= -f2- | paste -sd' ' | tr "$3" ' ' } -if [ "$CLOUD" = "auto" ]; then +cloud_alias() { + case "$1" in + ec2) echo aws;; + gce) echo gcp;; + nocloud-net) echo nocloud;; + oracle) echo oci;; + *) echo "$1";; + esac +} + +cloud_hint() { + local F cloud + # 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" ] && { + cloud_alias "$cloud" + return + } + done +} + +cloud_autodetect() { + for i in "$LIBDIR"/tiny-cloud/cloud/*/autodetect; do + if [ -x "$i" ]; then + "$i" || : + fi + done | sort -n | cut -d' ' -f2 | head -n 1 +} + +if [ "$CLOUD" = "auto" ] && [ -z "$TINY_CLOUD_NO_RESOLVE" ]; 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 + CLOUD=$(cloud_hint) + [ -z "$CLOUD" ] && CLOUD=$(cloud_autodetect) if [ -z "$CLOUD" ] || [ ! -d "$LIBDIR/tiny-cloud/cloud/$CLOUD" ]; then log -t autodetect err "unable to determine cloud" CLOUD=unknown diff --git a/sbin/tiny-cloud b/sbin/tiny-cloud index 251f4b9..68be4bd 100755 --- a/sbin/tiny-cloud +++ b/sbin/tiny-cloud @@ -7,11 +7,12 @@ set -e : "${PREFIX:=/usr}" : "${LIBDIR:=$PREFIX/lib}" +[ "$1" = "autodetect" ] && TINY_CLOUD_NO_RESOLVE=1 . "$LIBDIR/tiny-cloud/common" usage() { cat <<-EOF - Usage: ${0##*/} [-h | --help] { boot | early | main | final | --bootstrap {complete|incomplete|status} | --enable | --disable } + Usage: ${0##*/} [-h | --help] { boot | early | main | final | autodetect | --bootstrap {complete|incomplete|status} | --enable | --disable } EOF } @@ -72,6 +73,14 @@ shift case "$phase" in boot|early|main|final) ;; + autodetect) + CLOUD=$(cloud_autodetect) + if [ -z "$CLOUD" ] || [ ! -d "$LIBDIR/tiny-cloud/cloud/$CLOUD" ]; then + echo unknown + exit 1 + fi + echo "$CLOUD" + exit 0;; *) usage >&2; exit 1;; esac diff --git a/tests/init.test b/tests/init.test index e999c0b..d60e82d 100755 --- a/tests/init.test +++ b/tests/init.test @@ -26,6 +26,8 @@ init_tests \ set_ssh_keys_gcp \ userdata_type \ run_userdata \ + autodetect_config_overrides_cmdline \ + autodetect_cache_overrides_cmdline \ autodetect_aws_cmdline \ autodetect_aws_nitro \ autodetect_aws_xen \ @@ -38,6 +40,7 @@ init_tests \ autodetect_nocloud_dmi \ autodetect_nocloud_volume \ autodetect_oci \ + autodetect_scaleway_cmdline \ autodetect_scaleway \ autodetect_unknown @@ -351,6 +354,23 @@ autodetect_unknown_body() { sh -c ". \"$lib\"; echo \$CLOUD" } +autodetect_config_overrides_cmdline_body() { + mkdir -p proc + echo "quiet ds=scaleway console=ttyS0" > proc/cmdline + CLOUD=aws atf_check \ + -o match:"aws" \ + sh -c ". \"$lib\"; echo \$CLOUD" +} + +autodetect_cache_overrides_cmdline_body() { + mkdir -p proc var/lib/cloud + echo aws > var/lib/cloud/.autodetect + echo "quiet ds=scaleway console=ttyS0" > proc/cmdline + atf_check \ + -o match:"aws" \ + sh -c ". \"$lib\"; echo \$CLOUD" +} + autodetect_aws_cmdline_body() { mkdir -p proc echo "quiet tinycloud=cloud=aws console=ttyS0" > proc/cmdline @@ -466,6 +486,14 @@ autodetect_oci_body() { sh -c ". \"$lib\"; echo \$CLOUD" } +autodetect_scaleway_cmdline_body() { + mkdir -p proc + echo "quiet ds=scaleway console=ttyS0" > proc/cmdline + atf_check \ + -o match:"scaleway" \ + sh -c ". \"$lib\"; echo \$CLOUD" +} + autodetect_scaleway_body() { mkdir -p sys/class/dmi/id cat > sys/class/dmi/id/modalias <<-EOT diff --git a/tests/tiny-cloud.test b/tests/tiny-cloud.test index 6ec74fc..7287e74 100755 --- a/tests/tiny-cloud.test +++ b/tests/tiny-cloud.test @@ -10,6 +10,8 @@ PROVIDERS="alpine aws azure digitalocean gcp incus hetzner nocloud oci scaleway" init_tests \ tiny_cloud_help \ + tiny_cloud_autodetect \ + tiny_cloud_autodetect_unknown \ tiny_cloud_disabled \ no_metadata_boot \ no_userdata_early \ @@ -28,6 +30,26 @@ tiny_cloud_help_body() { done } +tiny_cloud_autodetect_body() { + mkdir -p sys/class/dmi/id + cat > sys/class/dmi/id/modalias <<-EOT + dmi:bvnScaleway:bvrScaleway:bd10/22/2024:br1.0:svnScaleway:pnScaleway:pvr:rvnKVM:rnScaleway:rvr:cvnScaleway:ct1:cvr:sku: + EOT + atf_check -s exit:0 \ + -o match:"^scaleway$" \ + tiny-cloud autodetect +} + +tiny_cloud_autodetect_unknown_body() { + mkdir -p etc proc var/lib/cloud + echo "CLOUD=auto" > etc/tiny-cloud.conf + echo scaleway > var/lib/cloud/.autodetect + echo "quiet ds=scaleway console=ttyS0" > proc/cmdline + atf_check -s exit:1 \ + -o match:"^unknown$" \ + tiny-cloud autodetect +} + tiny_cloud_disabled_body() { mkdir -p etc touch etc/tiny-cloud.disabled