diff --git a/lib/tiny-cloud/cloud/scaleway/imds b/lib/tiny-cloud/cloud/scaleway/imds new file mode 100644 index 0000000..21792a8 --- /dev/null +++ b/lib/tiny-cloud/cloud/scaleway/imds @@ -0,0 +1,40 @@ +# Scaleway Instance MetaData Service variables and functions +# vim:set filetype=sh: +# shellcheck shell=sh + +IMDS_ENDPOINT="169.254.42.42" +IMDS_URI="conf" + +IMDS_HOSTNAME="HOSTNAME" +IMDS_LOCAL_HOSTNAME="$IMDS_HOSTNAME" +IMDS_SSH_KEYS="SSH_PUBLIC_KEYS_[0-9]+_KEY" + +_imds() { + wget --quiet --timeout 1 --output-document - "http://$IMDS_ENDPOINT/$IMDS_URI$IMDS_QUERY" | egrep "^${1}=" | cut -d'=' -f2 +} + +_imds_ssh_keys() { + imds "$IMDS_SSH_KEYS" | tr -d "'" | sort -u +} + +_imds_userdata() { + mkdir -p "$ROOT/run/tiny-cloud" + TMPFILE=$(mktemp "$ROOT/run/tiny-cloud/userdata.XXXXXX") + + trap cleanup EXIT + + cleanup() { + rm $TMPFILE + } + + # Scaleway requires IMDS to use low-numbered ports to fetch + # userdata, which wget can't do + printf "GET /user_data/cloud-init HTTP/1.0\r\n\r\n" \ + | nc -w 1 -p 1000 "$IMDS_ENDPOINT" 80 > $TMPFILE + + STATUS=$(head -n 1 $TMPFILE | cut -d ' ' -f 2) + + if [ $STATUS -eq 200 ]; then + awk 'NR==1,/^\r$/ {next} {printf "%s%s",$0,RT}' $TMPFILE + fi +} diff --git a/tests/bin/nc b/tests/bin/nc index e705ce9..d74e918 100755 --- a/tests/bin/nc +++ b/tests/bin/nc @@ -1,6 +1,27 @@ #!/bin/sh input="$(cat)" +host="" +port="" +local_port=1024 case "$input" in -*) echo "nc: bad input: \$input" >&2; exit 1;; esac -echo "token-foo" +while [ ! -z $1 ]; do + case $1 in + -w) shift 2;; + -p) local_port=$2; shift 2;; + *) if [ -z $host ]; then host=$1; elif [ -z $port ]; then port=$1; fi; shift 1;; + esac +done + +# Scaleway +if [ "$CLOUD" = "scaleway" ]; then + if [ $local_port -gt 1023 ]; then + NC_CONTENT="invalid local port" + else + NC_CONTENT="$(cat ${NC_FILE:-$host.txt})" + fi + printf "HTTP/1.1 200 OK\r\n\r\n%s" "$NC_CONTENT" +else + echo "token-foo" +fi diff --git a/tests/bin/wget b/tests/bin/wget index 9961d0a..b600f7d 100755 --- a/tests/bin/wget +++ b/tests/bin/wget @@ -84,12 +84,18 @@ path="${url#http*://$host}" path="${path#${WGET_STRIP_PREFIX:-/}}" path="${path%\?*}" + if [ -z "$WGETCONTENT" ]; then - ( - IFS=/ - set -- ${path#/} - yx -f "${WGET_YAML:-$host.yaml}" "$@" 2>/dev/null - ) + # Scaleway has all the data on a single TXT endpoint + if [ "$CLOUD" = "scaleway" ]; then + cat "${WGET_YAML:-$host.txt}" + else + ( + IFS=/ + set -- ${path#/} + yx -f "${WGET_YAML:-$host.yaml}" "$@" 2>/dev/null + ) + fi fi : "${outfile:=index.html}" diff --git a/tests/imds.test b/tests/imds.test index ec98b6d..aa8b140 100755 --- a/tests/imds.test +++ b/tests/imds.test @@ -5,7 +5,7 @@ . $(atf_get_srcdir)/test_env.sh export PREFIX="$srcdir" -PROVIDERS="alpine aws azure gcp nocloud" +PROVIDERS="alpine aws azure gcp nocloud scaleway" init_tests \ imds_help \ @@ -17,6 +17,7 @@ init_tests \ imds_hostname_gcp \ imds_hostname_nocloud \ imds_hostname_oci \ + imds_hostname_scaleway \ \ imds_local_hostname_alpine \ imds_local_hostname_aws \ @@ -24,6 +25,7 @@ init_tests \ imds_local_hostname_gcp \ imds_local_hostname_nocloud \ imds_local_hostname_oci \ + imds_local_hostname_scaleway \ \ imds_ssh_keys_alpine \ imds_ssh_keys_aws \ @@ -31,9 +33,12 @@ init_tests \ imds_ssh_keys_gcp \ imds_ssh_keys_nocloud \ imds_ssh_keys_oci \ + imds_ssh_keys_scaleway \ \ imds_nocloud_cmdline_local_hostname \ - imds_nocloud_smbios_local_hostname + imds_nocloud_smbios_local_hostname \ + \ + imds_userdata_scaleway imds_help_body() { atf_check -o match:"Usage: imds" imds -h @@ -66,6 +71,12 @@ imds_hostname_azure_body() { check_hostname azure; } imds_hostname_gcp_body() { check_hostname gcp; } imds_hostname_nocloud_body() { check_hostname nocloud; } imds_hostname_oci_body() { check_hostname oci; } +imds_hostname_scaleway_body() { + fake_metadata scaleway <<-EOF +HOSTNAME=myhostname +EOF + CLOUD="scaleway" atf_check -o match:"myhostname" imds @hostname +} check_local_hostname() { fake_metadata "$1" <<-EOF @@ -86,6 +97,12 @@ imds_local_hostname_azure_body() { check_local_hostname azure; } imds_local_hostname_gcp_body() { check_local_hostname gcp; } imds_local_hostname_nocloud_body() { check_local_hostname nocloud; } imds_local_hostname_oci_body() { check_local_hostname oci; } +imds_local_hostname_scaleway_body() { + fake_metadata scaleway <<-EOF +HOSTNAME=myhostname +EOF + CLOUD="scaleway" atf_check -o match:"myhostname" imds @local-hostname +} check_ssh_keys() { local key="ssh-ed25519 keydata" @@ -106,6 +123,7 @@ check_ssh_keys() { # oci metadata: ssh_authorized_keys: $key + EOF CLOUD="$1" atf_check -o match:"$key" imds @ssh-keys } @@ -115,6 +133,21 @@ imds_ssh_keys_azure_body() { check_ssh_keys azure; } imds_ssh_keys_gcp_body() { check_ssh_keys gcp; } imds_ssh_keys_nocloud_body() { check_ssh_keys nocloud; } imds_ssh_keys_oci_body() { check_ssh_keys oci; } +imds_ssh_keys_scaleway_body() { + local key="ssh-ed25519 keydata" + fake_metadata scaleway <<-EOF +SSH_PUBLIC_KEYS=1 +SSH_PUBLIC_KEYS_0='ID KEY FINGERPRINT CREATION_DATE MODIFICATION_DATE DESCRIPTION IP' +SSH_PUBLIC_KEYS_0_ID=00000000-0000-0000-0000-000000000000 +SSH_PUBLIC_KEYS_0_KEY='$key' +SSH_PUBLIC_KEYS_0_FINGERPRINT='256 MD5:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 ssh_test_key (ssh-ed25519)' +SSH_PUBLIC_KEYS_0_CREATION_DATE=2024-01-25T19:51:28.263483+00:00 +SSH_PUBLIC_KEYS_0_MODIFICATION_DATE=2024-01-25T19:51:28.263483+00:00 +SSH_PUBLIC_KEYS_0_DESCRIPTION=ssh_test_key +SSH_PUBLIC_KEYS_0_IP= +EOF + CLOUD="scaleway" atf_check -o match:"$key" imds @ssh-keys +} imds_nocloud_cmdline_local_hostname_body() { atf_require_prog yx @@ -137,3 +170,11 @@ imds_nocloud_smbios_local_hostname_body() { imds @local-hostname done } + +imds_userdata_scaleway_body() { + local cmd="sh ./cmd" + fake_userdata_scaleway <<-EOF +$cmd +EOF + CLOUD="scaleway" atf_check -o match:"$cmd" imds @userdata +} diff --git a/tests/init.test b/tests/init.test index 307338d..84a43eb 100755 --- a/tests/init.test +++ b/tests/init.test @@ -25,7 +25,7 @@ init_tests \ userdata_type \ run_userdata -PROVIDERS="alpine aws azure gcp nocloud oci" +PROVIDERS="alpine aws azure gcp nocloud oci scaleway" expand_root_body() { mkdir proc diff --git a/tests/test_env.sh b/tests/test_env.sh index e274bbb..9e0de37 100644 --- a/tests/test_env.sh +++ b/tests/test_env.sh @@ -7,6 +7,7 @@ PATH="$atf_srcdir/bin:$srcdir/bin:$srcdir/sbin:$PATH" export TINY_CLOUD_BASEDIR="$srcdir" export ROOT="$PWD" + init_tests() { TESTS= for t; do @@ -83,6 +84,15 @@ fake_metadata_oci() { export WGET_STRIP_PREFIX="/opc/v2" } +fake_metadata_scaleway() { + cat > "169.254.42.42.txt" + export WGET_STRIP_PREFIX="/conf" +} + +fake_userdata_scaleway() { + cat > "169.254.42.42.txt" +} + fake_metadata() { case "${1:-$CLOUD}" in alpine|nocloud) fake_metadata_nocloud;; @@ -90,6 +100,7 @@ fake_metadata() { azure) fake_metadata_azure;; gcp) fake_metadata_gcp;; oci) fake_metadata_oci;; + scaleway) fake_metadata_scaleway;; *) echo "TODO: fake_metadata_$CLOUD" >&2;; esac } diff --git a/tests/tiny-cloud.test b/tests/tiny-cloud.test index b7e2288..425effc 100755 --- a/tests/tiny-cloud.test +++ b/tests/tiny-cloud.test @@ -6,7 +6,7 @@ export PREFIX="$srcdir" export MOCK=echo -PROVIDERS="alpine aws azure gcp nocloud oci" +PROVIDERS="alpine aws azure gcp nocloud oci scaleway" init_tests \ tiny_cloud_help \