1
0
mirror of https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud.git synced 2026-02-04 04:22:43 +03:00

Add cloud provider "alpine" and user-data "alpine-conf"

This commit is contained in:
Natanael Copa 2023-05-09 14:15:31 +00:00 committed by Jake Buchholz Göktürk
parent 17d0b3d07b
commit b03030182f
11 changed files with 467 additions and 9 deletions

View File

@ -1,6 +1,6 @@
PREFIX?=/
SUBPACKAGES = core network openrc aws azure gcp oci nocloud
SUBPACKAGES = core network openrc aws azure gcp oci nocloud alpine
.PHONY: check install $(SUBPACKAGES)
@ -56,6 +56,11 @@ nocloud:
install -Dm644 -t $(PREFIX)/lib/tiny-cloud/cloud/nocloud \
lib/tiny-cloud/cloud/nocloud/*
alpine:
install -Dm644 -t $(PREFIX)/lib/tiny-cloud/cloud/alpine \
lib/tiny-cloud/cloud/alpine/init
ln -s ../nocloud/imds $(PREFIX)/lib/tiny-cloud/cloud/alpine/imds
check: tests/Kyuafile Kyuafile
kyua test || (kyua report --verbose && exit 1)

View File

@ -5,7 +5,7 @@ description="Tiny Cloud Bootstrap - main phase"
extra_commands="complete incomplete"
depend() {
need net
need net tiny-cloud-net
before sshd
}

14
dist/openrc/tiny-cloud-net vendored Executable file
View File

@ -0,0 +1,14 @@
#!/sbin/openrc-run
# vim:set ts=8 noet ft=sh:
description="Tiny Cloud Bootstrap - net phase"
depend() {
need net
before *
}
start() {
ebegin "Tiny Cloud - net"
tiny-cloud net
eend $?
}

View File

@ -0,0 +1 @@
../nocloud/imds

View File

@ -0,0 +1,26 @@
# Tiny Cloud - Init Functions
# vim:set ts=4 et ft=sh:
INIT_ACTIONS_EARLY="$(replace_word set_default_interfaces set_network_interfaces $INIT_ACTIONS_EARLY)"
set_resolv_conf() {
# 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"
done
}
init__set_network_interfaces() {
local interfaces="$(imds meta-data/network-interfaces)"
mkdir -p "$ROOT"/etc/network
if [ -n "$interfaces" ]; then
printf "%s\n" "$interfaces" > "$ROOT"/etc/network/interfaces
elif ! [ -f "$ROOT"/etc/network/interfaces ]; then
init__set_default_interfaces
fi
if ! grep -q dhcp "$ROOT"/etc/network/interfaces; then
set_resolv_conf
fi
}

View File

@ -29,3 +29,40 @@ log() {
crit|alert|emerg) exit 1 ;;
esac
}
# usage: replace_word <search> <replace> <list>...
replace_word() {
local search="$1" replace="$2"
shift 2
for word in "$@"; do
if [ "$word" = "$search" ]; then
echo "$replace"
else
echo "$word"
fi
done
}
# usage: insert_after <where> <what> <list>...
insert_after() {
local search="$1" addition="$2"
shift 2
for i in "$@"; do
echo "$i"
if [ "$i" = "$search" ]; then
echo "$addition"
fi
done
}
# usage: add_once <file> <line-to-add>...
add_once() {
local file="$1"
shift
for line; do
if ! grep -x -F "$line" "$file" 2>/dev/null; then
mkdir -p "${file%/*}"
printf "%s\n" "$line" >> "$file"
fi
done
}

View File

@ -17,14 +17,17 @@ DEFAULT_ACTIONS_EARLY="
create_default_user
enable_sshd
"
DEFAULT_ACTIONS_MAIN="
DEFAULT_ACTIONS_NET="
save_userdata
"
DEFAULT_ACTIONS_MAIN="
set_hostname
set_ssh_keys
"
DEFAULT_ACTIONS_FINAL=""
: "${INIT_ACTIONS_EARLY=$DEFAULT_ACTIONS_EARLY}"
: "${INIT_ACTIONS_NET=$DEFAULT_ACTIONS_NET}"
: "${INIT_ACTIONS_MAIN=$DEFAULT_ACTIONS_MAIN}"
: "${INIT_ACTIONS_FINAL=$DEFAULT_ACTIONS_FINAL}"
@ -242,6 +245,10 @@ init__set_ssh_keys() {
init__save_userdata() {
local userdata="$TINY_CLOUD_VAR/user-data"
if [ -f "$userdata" ]; then
log info "user-data already saved"
return
fi
local tmpfile=$(mktemp "$userdata.XXXXXX")
imds -e @userdata > "$tmpfile"

View File

@ -0,0 +1,116 @@
# Script UserData Functions
# vim:set ts=4 et ft=sh:
INIT_ACTIONS_MAIN="$(insert_after set_hostname \
"userdata_bootcmd userdata_ntp userdata_apk_cache userdata_apk_repositories userdata_packages" \
$INIT_ACTIONS_MAIN)"
INIT_ACTIONS_FINAL="$INIT_ACTIONS_FINAL userdata_runcmd"
init__userdata_bootcmd() {
# run bootcmd
local bootcmds="$(imds user-data/bootcmd)"
for i in $bootcmds; do
local cmd="$(imds user-data/bootcmd/"$i")"
sh -c "$cmd"
done
}
init__userdata_ntp() {
local ntp_enabled="$(imds user-data/ntp/enabled)"
if [ "$ntp_enabled" != "yes" ] && [ "$ntp_enabled" != "true" ]; then
return
fi
local ntp_client="$(imds user-data/ntp/ntp_client)"
local svc= pkg=
case "$ntp_client" in
busybox)
svc=ntpd
;;
chrony|"")
pkg=chrony
svc=chronyd
;;
openntpd)
pkg=openntpd
svc=openntpd
;;
esac
if [ -n "$pkg" ]; then
$MOCK apk add "$pkg"
fi
if [ -n "$svc" ]; then
$MOCK rc-update add "$svc" default
$MOCK rc-service "$svc" start
fi
}
init__userdata_apk_cache() {
local cache="$(imds user-data/apk/cache)"
if [ -z "$cache" ]; then
return
fi
mkdir -p "$ROOT/$cache"
# make link relative
case "$cache" in
/*) cache="../..$cache";;
esac
mkdir -p "$ROOT"/etc/apk
ln -sf "$cache" "$ROOT"/etc/apk/cache
}
init__userdata_apk_cache() {
local cache="$(imds user-data/apk/cache)"
if [ -z "$cache" ]; then
return
fi
mkdir -p "$ROOT/$cache"
# make link relative
case "$cache" in
/*) cache="../..$cache";;
esac
mkdir -p "$ROOT"/etc/apk
ln -sf "$cache" "$ROOT"/etc/apk/cache
}
init__userdata_apk_repositories() {
local repositories="$(imds user-data/apk/repositories)"
mkdir -p "$ROOT"/etc/apk
for r in $repositories; do
local baseurl="$(imds user-data/apk/repositories/$r/base_url)"
local repos="$(imds user-data/apk/repositories/$r/repos)"
local version="$(imds user-data/apk/repositories/$r/version)"
if [ -z "$version" ]; then
local version_id=$( . "$ROOT"/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%.*}";;
esac
fi
if [ -n "$version" ] && [ "$version" != "." ] && [ "$version" != "/" ]; then
baseurl="${baseurl%/}/$version"
fi
for repo in $repos; do
local uri="${baseurl%/}/$(imds user-data/apk/repositories/$r/repos/$repo)"
add_once "$ROOT"/etc/apk/repositories "$uri"
done
done
}
init__userdata_packages() {
local packages="$(imds user-data/packages)"
local pkgs=
for i in $packages; do
pkgs="$pkgs $(imds user-data/packages/$i)"
done
if [ -n "$pkgs" ]; then
$MOCK apk add $pkgs
fi
}
init__userdata_runcmd() {
local runcmds="$(imds user-data/runcmd)"
for i in $runcmds; do
local cmd="$(imds user-data/runcmd/$i)"
sh -c "$cmd"
done
}

View File

@ -10,7 +10,7 @@ set -e
usage() {
cat <<EOF
Usage: ${0##*/} [-h | --help] { early | main | final | --bootstrap {complete|incomplete} | --setup }
Usage: ${0##*/} [-h | --help] { early | net | main | final | --bootstrap {complete|incomplete} | --setup }
EOF
}
@ -60,7 +60,7 @@ phase="$1"
shift
case "$phase" in
early|main|final) ;;
early|net|main|final) ;;
*) usage >&2; exit 1;;
esac
@ -92,6 +92,7 @@ INIT_ACTIONS_FINAL="${INIT_ACTIONS_FINAL} bootstrap_complete"
case "$phase" in
early) INIT_ACTIONS="$INIT_ACTIONS_EARLY";;
net) INIT_ACTIONS="$INIT_ACTIONS_NET";;
main) INIT_ACTIONS="$INIT_ACTIONS_MAIN";;
final) INIT_ACTIONS="$INIT_ACTIONS_FINAL";;
*) usage >&2; exit 1

241
tests/tiny-cloud-alpine.test Executable file
View File

@ -0,0 +1,241 @@
#!/usr/bin/env atf-sh
. $(atf_get_srcdir)/test_env.sh
export PREFIX="$srcdir"
export MOCK=echo
export CLOUD=alpine
lib="$srcdir"/lib/tiny-cloud/cloud/alpine/init
init_tests \
set_network_config_network_interfaces \
set_network_config_auto \
userdata_bootcmd \
userdata_ntp \
userdata_ntp_busybox \
userdata_ntp_openntpd \
userdata_apk_cache \
userdata_apk_repositories \
userdata_apk_repositories_version \
userdata_apk_repositories_version_auto_edge \
userdata_packages \
userdata_runcmd
set_network_config_network_interfaces_body() {
fake_metadata_nocloud <<-EOF
network-interfaces: |
auto eth1
iface eth1
address 192.168.100.1
netmask 255.255.255.0
resolv_conf:
nameservers:
- 8.8.8.8
- 8.8.4.4
EOF
atf_check \
-o match:"rc-update" \
-e match:"set_network_interfaces .*DONE" \
tiny-cloud early
atf_check \
-o match:"auto eth1" \
-o match:"iface eth1" \
-o match:"address 192.168.100.1" \
cat etc/network/interfaces
atf_check \
-o match:"^nameserver 8.8.8.8$" \
-o match:"^nameserver 8.8.4.4$" \
cat etc/resolv.conf
}
set_network_config_auto_body() {
fake_metadata_nocloud <<-EOF
resolv_conf:
nameservers:
- 8.8.8.8
- 8.8.4.4
EOF
fake_interfaces eth0 eth1 eth2
echo up > sys/class/net/eth1/operstate
atf_check \
-o match:"rc-update" \
-e match:"set_network_interfaces .*DONE" \
tiny-cloud early
atf_check \
-o match:"auto eth1" \
-o match:"iface eth1" \
-o match:"use dhcp" \
cat etc/network/interfaces
# resolv.conf should be ignored with dhcp
if [ -e etc/resolv.conf ]; then
atf_fail "etc/resolv.conf should not been created with DHCP"
fi
}
userdata_bootcmd_body() {
fake_userdata_nocloud <<-EOF
#alpine-config
bootcmd:
- echo foo
- echo bar
EOF
atf_check -e ignore -o ignore tiny-cloud net
atf_check \
-e match:"userdata_bootcmd .*DONE" \
-o match:"^foo$" -o match:"^bar$" \
tiny-cloud main
}
userdata_ntp_body() {
fake_userdata_nocloud <<-EOF
#alpine-config
ntp:
enabled: true
EOF
atf_check -e ignore -o ignore tiny-cloud net
atf_check \
-e match:"userdata_ntp .*DONE" \
-o match:"apk add.*chrony" \
-o match:"rc-update .*chronyd" \
-o match:"rc-service .*chronyd" \
tiny-cloud main
}
userdata_ntp_busybox_body() {
fake_userdata_nocloud <<-EOF
#alpine-config
ntp:
enabled: true
ntp_client: busybox
EOF
atf_check -e ignore -o ignore tiny-cloud net
atf_check \
-e match:"userdata_ntp .*DONE" \
-o not-match:"apk add" \
-o match:"rc-update .*ntpd" \
-o match:"rc-service .*ntpd" \
tiny-cloud main
}
userdata_ntp_openntpd_body() {
fake_userdata_nocloud <<-EOF
#alpine-config
ntp:
enabled: true
ntp_client: openntpd
EOF
atf_check -e ignore -o ignore tiny-cloud net
atf_check \
-e match:"userdata_ntp .*DONE" \
-o match:"apk add.*openntpd" \
-o match:"rc-update .*openntpd" \
-o match:"rc-service .*openntpd" \
tiny-cloud main
}
userdata_apk_cache_body() {
fake_userdata_nocloud <<-EOF
#alpine-config
apk:
cache: /var/cache/apk
EOF
atf_check -e ignore -o ignore tiny-cloud net
atf_check \
-e match:"userdata_apk_cache .*DONE" \
-o ignore \
tiny-cloud main
atf_check -o match:"$PWD/var/cache/apk" readlink -f etc/apk/cache
}
userdata_apk_repositories_body() {
fake_userdata_nocloud <<-EOF
#alpine-config
apk:
repositories:
- base_url: /srv/packages
repos: [ "main", "community" ]
EOF
atf_check -e ignore -o ignore tiny-cloud net
atf_check \
-e match:"userdata_apk_repositories .*DONE" \
-o ignore \
tiny-cloud main
atf_check -o match:"^/srv/packages/main$" \
-o match:"^/srv/packages/community$" \
cat etc/apk/repositories
}
userdata_apk_repositories_version_body() {
fake_userdata_nocloud <<-EOF
#alpine-config
apk:
repositories:
- base_url: https://cdn.alpinelinux.org/
version: edge
repos: [ "main", "community" ]
EOF
atf_check -e ignore -o ignore tiny-cloud net
atf_check \
-e match:"userdata_apk_repositories .*DONE" \
-o ignore \
tiny-cloud main
atf_check -o match:"^https://cdn.alpinelinux.org/edge/main$" \
-o match:"^https://cdn.alpinelinux.org/edge/community$" \
cat etc/apk/repositories
}
userdata_apk_repositories_version_auto_edge_body() {
fake_userdata_nocloud <<-EOF
#alpine-config
apk:
repositories:
- base_url: https://cdn.alpinelinux.org/
repos: [ "main", "community" ]
EOF
mkdir -p etc
echo "VERSION_ID=3.18_alpha20230329" > etc/os-release
atf_check -e ignore -o ignore tiny-cloud net
atf_check \
-e match:"userdata_apk_repositories .*DONE" \
-o ignore \
tiny-cloud main
atf_check -o match:"^https://cdn.alpinelinux.org/edge/main$" \
-o match:"^https://cdn.alpinelinux.org/edge/community$" \
cat etc/apk/repositories
}
userdata_packages_body() {
fake_userdata_nocloud <<-EOF
#alpine-config
packages:
- tmux
- vim
EOF
atf_check -e ignore -o ignore tiny-cloud net
atf_check \
-e match:"userdata_packages .*DONE" \
-o match:"apk add .*tmux" \
-o match:"apk add .*vim" \
tiny-cloud main
}
userdata_runcmd_body() {
fake_userdata_nocloud <<-EOF
#alpine-config
runcmd:
- echo foo
- echo bar
EOF
# run net phase to extract the user data
atf_check -e ignore -o ignore tiny-cloud net
atf_check \
-e match:"userdata_runcmd .*DONE" \
-o match:"^foo$" -o match:"^bar$" \
tiny-cloud final
}

View File

@ -9,6 +9,7 @@ PROVIDERS="alpine aws azure gcp nocloud oci"
init_tests \
tiny_cloud_help \
no_metadata_early \
no_userdata_net \
no_userdata_main \
no_userdata_final
@ -27,8 +28,8 @@ tiny_cloud_help_body() {
no_metadata_early_body() {
fake_netcat
for provider in $PROVIDERS; do
# TODO: -e not-match:"UNKNOWN" \
CLOUD="$provider" atf_check \
-e not-match:"UNKNOWN" \
-e not-match:"not found" \
-e not-match:"o such file" \
-o match:"rc-update add.*sshd" \
@ -36,14 +37,23 @@ no_metadata_early_body() {
done
}
no_userdata_net_body() {
fake_netcat
for provider in $PROVIDERS; do
CLOUD="$provider" atf_check \
-e not-match:"UNKNOWN" \
-e match:"save_userdata.*DONE" \
tiny-cloud net
done
}
no_userdata_main_body() {
fake_netcat
for provider in $PROVIDERS; do
# we should not set empty hostname
# we should not create .ssh dir for non-existing user
# TODO: -e not-match:"UNKNOWN" \
CLOUD="$provider" atf_check \
-e ignore \
-e not-match:"UNKNOWN" \
-o not-match:"hostname.*-F" \
-o not-match:"chown.*/\.ssh" \
tiny-cloud main
@ -58,8 +68,8 @@ no_userdata_main_body() {
no_userdata_final_body() {
fake_netcat
for provider in $PROVIDERS; do
# TODO: -e not-match:"UNKNOWN" \
CLOUD="$provider" atf_check \
-e not-match:"UNKNOWN" \
-e match:"bootstrap_complete .*" \
tiny-cloud final
CLOUD="$provider" atf_check \