1
0
mirror of https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud.git synced 2025-12-14 19:02:45 +03:00

Add .editorconfig and replace spaces with tabs

This commit is contained in:
Natanael Copa 2023-05-19 04:13:16 +00:00 committed by Jake Buchholz Göktürk
parent 7d9a280a67
commit 000f41a48b
36 changed files with 968 additions and 942 deletions

18
.editorconfig Normal file
View File

@ -0,0 +1,18 @@
# https://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true
[Makefile]
indent_size = 8
indent_style = tab
[*.{md,yml,yaml}]
indent_style = space
indent_size = 2

192
bin/imds
View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# Tiny Cloud - Instance MetaData Service client # Tiny Cloud - Instance MetaData Service client
@ -9,43 +9,43 @@
. "$LIBDIR/tiny-cloud/common" . "$LIBDIR/tiny-cloud/common"
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
cat <<EOT cat <<-EOT
Usage: imds [-h] { -e | +e | +n | +s | +t | @<alias> | <imds-path> } ... Usage: imds [-h] { -e | +e | +n | +s | +t | @<alias> | <imds-path> } ...
-h : help -h : help
-e / +e : ignore / catch errors -e / +e : ignore / catch errors
+n / +s / +t : insert newline / space / tab +n / +s / +t : insert newline / space / tab
<alias> :- <alias> :-
hostname : instance hostname hostname : instance hostname
local-hostname : instance local hostname local-hostname : instance local hostname
ssh-keys : instance SSH keys ssh-keys : instance SSH keys
userdata : instance user data userdata : instance user data
nics : instance NICs nics : instance NICs
nic:<iface>[,<nic-key> ...] : specific NIC interface nic:<iface>[,<nic-key> ...] : specific NIC interface
<iface> : network interface (i.e. eth1) <iface> : network interface (i.e. eth1)
<nic-key> :- { -e | +e | +n | +s | +t | @<nic-alias> | <nic-path> } <nic-key> :- { -e | +e | +n | +s | +t | @<nic-alias> | <nic-path> }
<nic-alias> :- <nic-alias> :-
mac : mac address mac : mac address
ipv4 : ipv4 address(es) ipv4 : ipv4 address(es)
ipv6 : ipv6 address(es) ipv6 : ipv6 address(es)
ipv4-net : subnet ipv4 network(s) ipv4-net : subnet ipv4 network(s)
ipv6-net : subnet ipv6 network(s) ipv6-net : subnet ipv6 network(s)
ipv4-prefix : delegated ipv4 CIDR(s) ipv4-prefix : delegated ipv4 CIDR(s)
ipv6-prefix : delegated ipv6 CIDR(s) ipv6-prefix : delegated ipv6 CIDR(s)
EOT EOT
exit 0 exit 0
fi fi
### cloud-specific variables/functions ### cloud-specific variables/functions
unset \ unset \
IMDS_HEADER \ IMDS_HEADER \
IMDS_URI \ IMDS_URI \
IMDS_QUERY IMDS_QUERY
unset -f \ unset -f \
_imds_token \ _imds_token \
_imds_header \ _imds_header \
_imds_nic_index \ _imds_nic_index \
2>/dev/null || true 2>/dev/null || true
### default variables/functions ### default variables/functions
@ -67,19 +67,19 @@ IMDS_IPV4_PREFIX="ipv4-prefix"
IMDS_IPV6_PREFIX="ipv6-prefix" IMDS_IPV6_PREFIX="ipv6-prefix"
_imds() { _imds() {
wget --quiet --timeout 1 --output-document - \ wget --quiet --timeout 1 --output-document - \
--header "$(_imds_header)" \ --header "$(_imds_header)" \
"http://$IMDS_ENDPOINT/$IMDS_URI/$1$IMDS_QUERY" "http://$IMDS_ENDPOINT/$IMDS_URI/$1$IMDS_QUERY"
} }
_imds_userdata() { _imds "$IMDS_USERDATA"; } _imds_userdata() { _imds "$IMDS_USERDATA"; }
_imds_ssh_keys() { _imds_ssh_keys() {
local key local key
for key in $(_imds "$IMDS_SSH_KEYS"); do for key in $(_imds "$IMDS_SSH_KEYS"); do
_imds "$IMDS_SSH_KEYS/${key%=*}/openssh-key" _imds "$IMDS_SSH_KEYS/${key%=*}/openssh-key"
echo echo
done | sort -u done | sort -u
} }
_imds_nic_index() { cat "/sys/class/net/$1/address"; } _imds_nic_index() { cat "/sys/class/net/$1/address"; }
@ -87,71 +87,71 @@ _imds_nic_index() { cat "/sys/class/net/$1/address"; }
### load cloud-specific variables and functions ### load cloud-specific variables and functions
if [ ! -d "$LIBDIR/tiny-cloud/cloud/$CLOUD" ]; then if [ ! -d "$LIBDIR/tiny-cloud/cloud/$CLOUD" ]; then
echo "ERROR: Unknown Cloud '$CLOUD'" >&2 echo "ERROR: Unknown Cloud '$CLOUD'" >&2
fi fi
. "$LIBDIR/tiny-cloud/cloud/$CLOUD/imds" . "$LIBDIR/tiny-cloud/cloud/$CLOUD/imds"
### non-overrideable functions ### non-overrideable functions
imds() { imds() {
local cmd args key rv err=1 local cmd args key rv err=1
while [ -n "$1" ]; do while [ -n "$1" ]; do
cmd=_imds cmd=_imds
args= args=
key="$1"; shift key="$1"; shift
case $key in case $key in
# error handling # error handling
-e) err=0; continue ;; # ignore -e) err=0; continue ;; # ignore
+e) err=1; continue ;; # return +e) err=1; continue ;; # return
# TODO: retry/deadline # TODO: retry/deadline
# output control # output control
+n) printf "\n"; continue ;; # insert newline +n) printf "\n"; continue ;; # insert newline
+s) printf " "; continue ;; # insert space +s) printf " "; continue ;; # insert space
+t) printf "\t"; continue ;; # insert tab +t) printf "\t"; continue ;; # insert tab
# key aliasing # key aliasing
@hostname) args="$IMDS_HOSTNAME" ;; @hostname) args="$IMDS_HOSTNAME" ;;
@local-hostname) args="$IMDS_LOCAL_HOSTNAME" ;; @local-hostname) args="$IMDS_LOCAL_HOSTNAME" ;;
@ssh-keys) cmd=_imds_ssh_keys ;; @ssh-keys) cmd=_imds_ssh_keys ;;
@userdata) cmd=_imds_userdata ;; @userdata) cmd=_imds_userdata ;;
@nics) args="$IMDS_NICS" ;; @nics) args="$IMDS_NICS" ;;
@nic:*) @nic:*)
cmd=imds cmd=imds
args=$(_imds_nic_args $(echo "${key#@nic:}" | tr , ' ')) args=$(_imds_nic_args $(echo "${key#@nic:}" | tr , ' '))
;; ;;
# use key verbatim # use key verbatim
*) args="$key" ;; *) args="$key" ;;
esac esac
# TODO: retry/deadline # TODO: retry/deadline
"$cmd" $args "$cmd" $args
rv=$? rv=$?
[ $err -eq 0 ] && continue [ $err -eq 0 ] && continue
[ $rv = "0" ] || return $rv [ $rv = "0" ] || return $rv
done done
} }
_imds_nic_args() { _imds_nic_args() {
local key nic local key nic
nic=$(_imds_nic_index "$1") || return 1 nic=$(_imds_nic_index "$1") || return 1
if [ -z "$2" ]; then if [ -z "$2" ]; then
echo "$IMDS_NICS/$nic" echo "$IMDS_NICS/$nic"
return return
fi fi
while [ -n "$2" ]; do while [ -n "$2" ]; do
key="$2" key="$2"
shift shift
case "$key" in case "$key" in
@mac) key="$IMDS_MAC" ;; @mac) key="$IMDS_MAC" ;;
@ipv4) key="$IMDS_IPV4" ;; @ipv4) key="$IMDS_IPV4" ;;
@ipv6) key="$IMDS_IPV6" ;; @ipv6) key="$IMDS_IPV6" ;;
@ipv4-net) key="$IMDS_IPV4_NET" ;; @ipv4-net) key="$IMDS_IPV4_NET" ;;
@ipv6-net) key="$IMDS_IPV6_NET" ;; @ipv6-net) key="$IMDS_IPV6_NET" ;;
@ipv4-prefix) key="$IMDS_IPV4_PREFIX" ;; @ipv4-prefix) key="$IMDS_IPV4_PREFIX" ;;
@ipv6-prefix) key="$IMDS_IPV6_PREFIX" ;; @ipv6-prefix) key="$IMDS_IPV6_PREFIX" ;;
# error/output control passthrough # error/output control passthrough
-e|+[enst]) printf "$key\n"; continue ;; -e|+[enst]) printf "$key\n"; continue ;;
esac esac
printf "$IMDS_NICS/$nic/$key\n" printf "$IMDS_NICS/$nic/$key\n"
done done
} }
imds "$@" imds "$@"

View File

@ -1,5 +1,5 @@
#!/sbin/openrc-run #!/sbin/openrc-run
# vim:set ts=8 noet ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
description="Tiny Cloud Bootstrap - main phase" description="Tiny Cloud Bootstrap - main phase"

View File

@ -1,5 +1,5 @@
#!/sbin/openrc-run #!/sbin/openrc-run
# vim:set ts=8 noet ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
description="Tiny Cloud Bootstrap - early phase" description="Tiny Cloud Bootstrap - early phase"

View File

@ -1,5 +1,5 @@
#!/sbin/openrc-run #!/sbin/openrc-run
# vim:set ts=8 noet ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
description="Tiny Cloud Bootstrap - final phase" description="Tiny Cloud Bootstrap - final phase"

View File

@ -1,5 +1,5 @@
#!/sbin/openrc-run #!/sbin/openrc-run
# vim:set ts=8 noet ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
description="Tiny Cloud Bootstrap - net phase" description="Tiny Cloud Bootstrap - net phase"

View File

@ -1,8 +1,8 @@
#!/bin/sh #!/bin/sh
# vim:set ts=2 et: # vim:set ft=sh:
# NOTE: The mdev-conf APK handles this now, but only for xvd or sd links (not # NOTE: The mdev-conf APK handles this now, but only for xvd or sd links (not
# both) # both)
: "${LIBDIR:=$PREFIX/lib}" : "${LIBDIR:=$PREFIX/lib}"
. "$LIBDIR/tiny-cloud/common" . "$LIBDIR/tiny-cloud/common"
@ -11,39 +11,39 @@
[ -x /usr/sbin/nvme ] || log crit "nvme cli not installed" [ -x /usr/sbin/nvme ] || log crit "nvme cli not installed"
raw_ebs_alias() { raw_ebs_alias() {
/usr/sbin/nvme id-ctrl "/dev/$BASE" -b 2>/dev/null | /usr/sbin/nvme id-ctrl "/dev/$BASE" -b 2>/dev/null |
dd bs=32 skip=96 count=1 2>/dev/null dd bs=32 skip=96 count=1 2>/dev/null
} }
case $ACTION in case $ACTION in
add|"") add|"")
BASE=$(echo "$MDEV" | sed -re 's/^(nvme[0-9]+n[0-9]+).*/\1/') BASE=$(echo "$MDEV" | sed -re 's/^(nvme[0-9]+n[0-9]+).*/\1/')
PART=$(echo "$MDEV" | sed -re 's/nvme[0-9]+n[0-9]+p?//g') PART=$(echo "$MDEV" | sed -re 's/nvme[0-9]+n[0-9]+p?//g')
# TODO: deadline instead of max tries # TODO: deadline instead of max tries
MAXTRY=30 MAXTRY=30
TRY=0 TRY=0
until [ -n "$EBS" ]; do until [ -n "$EBS" ]; do
EBS=$(raw_ebs_alias | sed -nre '/^(\/dev\/)?(s|xv)d[a-z]{1,2} /p' | tr -d ' ') EBS=$(raw_ebs_alias | sed -nre '/^(\/dev\/)?(s|xv)d[a-z]{1,2} /p' | tr -d ' ')
[ -n "$EBS" ] && break [ -n "$EBS" ] && break
TRY=$((TRY + 1)) TRY=$((TRY + 1))
if [ $TRY -eq $MAXTRY ]; then if [ $TRY -eq $MAXTRY ]; then
log err "Failed to get EBS volume alias for $MDEV after $MAXTRY attempts ($(raw_ebs_alias))" log err "Failed to get EBS volume alias for $MDEV after $MAXTRY attempts ($(raw_ebs_alias))"
exit 1 exit 1
fi fi
sleep 0.1 sleep 0.1
done done
# remove any leading '/dev/', 'sd', or 'xvd', and append partition # remove any leading '/dev/', 'sd', or 'xvd', and append partition
EBS=${EBS#/dev/} EBS=${EBS#/dev/}
EBS=${EBS#sd} EBS=${EBS#sd}
EBS=${EBS#xvd}$PART EBS=${EBS#xvd}$PART
ln -sf "$MDEV" "sd$EBS" && log notice "Added sd$EBS symlink for $MDEV" ln -sf "$MDEV" "sd$EBS" && log notice "Added sd$EBS symlink for $MDEV"
ln -sf "$MDEV" "xvd$EBS" && log notice "Added xvd$EBS symlink for $MDEV" ln -sf "$MDEV" "xvd$EBS" && log notice "Added xvd$EBS symlink for $MDEV"
;; ;;
remove) remove)
for TARGET in sd* xvd* for TARGET in sd* xvd*
do do
[ "$(readlink "$TARGET" 2>/dev/null)" = "$MDEV" ] && rm -f "$TARGET" && \ [ "$(readlink "$TARGET" 2>/dev/null)" = "$MDEV" ] && rm -f "$TARGET" && \
log notice "Removed $TARGET symlink for $MDEV" log notice "Removed $TARGET symlink for $MDEV"
done done
;; ;;
esac esac

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# vim:set ts=4 et: # vim:set ft=sh:
set -e set -e
@ -7,52 +7,52 @@ set -e
. "$LIBDIR/tiny-cloud/common" . "$LIBDIR/tiny-cloud/common"
if [ -z "$MDEV" ] || [ -z "$ACTION" ]; then if [ -z "$MDEV" ] || [ -z "$ACTION" ]; then
log crit "MDEV or ACTION undefined, aborting" log crit "MDEV or ACTION undefined, aborting"
fi fi
IFACE_CFG=/etc/network/interfaces IFACE_CFG=/etc/network/interfaces
ip() { ip() {
local v=-4 lev=info local v=-4 lev=info
if [ "$1" = '-4' ] || [ "$1" = '-6' ]; then if [ "$1" = '-4' ] || [ "$1" = '-6' ]; then
v="$1" v="$1"
shift shift
fi fi
local op="$2" local op="$2"
[ "$op" = show ] && lev=debug [ "$op" = show ] && lev=debug
if /sbin/ip "$v" "$@" || [ -n "$FAIL_OK" ]; then if /sbin/ip "$v" "$@" || [ -n "$FAIL_OK" ]; then
log "$lev" "OK: ip $v $*" log "$lev" "OK: ip $v $*"
else else
log err "FAIL: ip $v $*" log err "FAIL: ip $v $*"
fi fi
} }
interface_up() { interface_up() {
log info "Bringing up $MDEV" log info "Bringing up $MDEV"
# umask so udhcpc PID file isn't non-owner writeable # umask so udhcpc PID file isn't non-owner writeable
(umask 0022 && ifup "$MDEV") (umask 0022 && ifup "$MDEV")
} }
cleanup_interface() { cleanup_interface() {
local v pref rtable="${MDEV#eth}" local v pref rtable="${MDEV#eth}"
let rtable+=10000 let rtable+=10000
log info "Cleaning up $MDEV" log info "Cleaning up $MDEV"
# kill related udhcpc, don't panic if it's not there # kill related udhcpc, don't panic if it's not there
kill "$(cat "/run/udhcpc.$MDEV.pid")" || true kill "$(cat "/run/udhcpc.$MDEV.pid")" || true
# tidy up /run/ifstate, if it exists # tidy up /run/ifstate, if it exists
[ -f /run/ifstate ] && sed -i -e "/^$MDEV=/d" /run/ifstate [ -f /run/ifstate ] && sed -i -e "/^$MDEV=/d" /run/ifstate
rm -f /run/ifstate."$MDEV".lock rm -f /run/ifstate."$MDEV".lock
# remove related rules # remove related rules
for v in 4 6; do for v in 4 6; do
for pref in $(ip -"$v" rule show table "$rtable" | cut -d: -f1); do for pref in $(ip -"$v" rule show table "$rtable" | cut -d: -f1); do
ip -"$v" rule del pref "$pref" ip -"$v" rule del pref "$pref"
done done
done done
} }
is_networking_started() { service networking status -q 2>/dev/null; } is_networking_started() { service networking status -q 2>/dev/null; }
@ -60,28 +60,28 @@ is_networking_started() { service networking status -q 2>/dev/null; }
log info "STARTING: $ACTION $MDEV" log info "STARTING: $ACTION $MDEV"
if exec 200>>"$IFACE_CFG"; then if exec 200>>"$IFACE_CFG"; then
if flock 200; then if flock 200; then
case $ACTION in case $ACTION in
add|"") add|"")
assemble-interfaces assemble-interfaces
is_networking_started && interface_up is_networking_started && interface_up
;; ;;
remove) remove)
assemble-interfaces assemble-interfaces
is_networking_started && cleanup_interface is_networking_started && cleanup_interface
;; ;;
*) *)
log err "Unknown action '$ACTION'" log err "Unknown action '$ACTION'"
exit 1 exit 1
;; ;;
esac esac
else else
log err "Unable to flock $IFACE_CFG" log err "Unable to flock $IFACE_CFG"
exit 1 exit 1
fi fi
else else
log err "Unable to assign fd 200 to flock $IFACE_CFG" log err "Unable to assign fd 200 to flock $IFACE_CFG"
exit 1 exit 1
fi fi
log info "FINISHED: $ACTION $MDEV" log info "FINISHED: $ACTION $MDEV"

View File

@ -1,27 +1,27 @@
# Tiny Cloud - Init Functions # Tiny Cloud - Init Functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
INIT_ACTIONS_EARLY="$(replace_word set_default_interfaces set_network_interfaces $INIT_ACTIONS_EARLY)" INIT_ACTIONS_EARLY="$(replace_word set_default_interfaces set_network_interfaces $INIT_ACTIONS_EARLY)"
set_resolv_conf() { set_resolv_conf() {
# resolv.conf # resolv.conf
local nameservers="$(imds meta-data/resolv_conf/nameservers)" local nameservers="$(imds meta-data/resolv_conf/nameservers)"
for i in $nameservers; do for i in $nameservers; do
local server="$(imds meta-data/resolv_conf/nameservers/$i)" local server="$(imds meta-data/resolv_conf/nameservers/$i)"
add_once "$ROOT"/etc/resolv.conf "nameserver $server" add_once "$ROOT"/etc/resolv.conf "nameserver $server"
done done
} }
init__set_network_interfaces() { init__set_network_interfaces() {
local interfaces="$(imds meta-data/network-interfaces)" local interfaces="$(imds meta-data/network-interfaces)"
mkdir -p "$ROOT"/etc/network mkdir -p "$ROOT"/etc/network
if [ -n "$interfaces" ]; then if [ -n "$interfaces" ]; then
printf "%s\n" "$interfaces" > "$ROOT"/etc/network/interfaces printf "%s\n" "$interfaces" > "$ROOT"/etc/network/interfaces
elif ! [ -f "$ROOT"/etc/network/interfaces ]; then elif ! [ -f "$ROOT"/etc/network/interfaces ]; then
init__set_default_interfaces init__set_default_interfaces
fi fi
if ! grep -q dhcp "$ROOT"/etc/network/interfaces; then if ! grep -q dhcp "$ROOT"/etc/network/interfaces; then
set_resolv_conf set_resolv_conf
fi fi
} }

View File

@ -1,5 +1,5 @@
# AWS Instance MetaData Service variables and functions # AWS Instance MetaData Service variables and functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
IMDS_HEADER="X-aws-ec2-metadata-token" IMDS_HEADER="X-aws-ec2-metadata-token"
@ -8,11 +8,11 @@ IMDS_TOKEN_TTL_HEADER="X-aws-ec2-metadata-token-ttl-seconds"
IMDS_URI="latest" IMDS_URI="latest"
_imds_token() { _imds_token() {
printf "PUT /latest/api/token HTTP/1.0\r\n%s: %s\r\n\r\n" \ printf "PUT /latest/api/token HTTP/1.0\r\n%s: %s\r\n\r\n" \
"$IMDS_TOKEN_TTL_HEADER" "$IMDS_TOKEN_TTL" \ "$IMDS_TOKEN_TTL_HEADER" "$IMDS_TOKEN_TTL" \
| nc -w 1 "$IMDS_ENDPOINT" 80 | tail -n 1 | nc -w 1 "$IMDS_ENDPOINT" 80 | tail -n 1
} }
_imds_header() { _imds_header() {
echo "$IMDS_HEADER: $(_imds_token)" echo "$IMDS_HEADER: $(_imds_token)"
} }

View File

@ -1,12 +1,12 @@
# AWS mdev Hotplug Modules # AWS mdev Hotplug Modules
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
# makes symlinks for NVMe devices that correlate to AWS EBS sd/xvd devices # makes symlinks for NVMe devices that correlate to AWS EBS sd/xvd devices
mod__nvme_ebs_links() { mod__nvme_ebs_links() {
# nvme-cli not installed? # nvme-cli not installed?
[ -x /usr/sbin/nvme ] || return 1 [ -x /usr/sbin/nvme ] || return 1
install_before '^nvme\.\*' \ install_before '^nvme\.\*' \
'nvme[0-9]+n.* root:disk 0660 */lib/mdev/nvme-ebs-links' 'nvme[0-9]+n.* root:disk 0660 */lib/mdev/nvme-ebs-links'
} }

View File

@ -1,5 +1,5 @@
# Azure Instance MetaData Service variables and functions # Azure Instance MetaData Service variables and functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
IMDS_HEADER="Metadata" IMDS_HEADER="Metadata"
@ -14,28 +14,28 @@ IMDS_NICS="network/interface"
# TODO: flesh out networking # TODO: flesh out networking
unset \ unset \
IMDS_MAC \ IMDS_MAC \
IMDS_IPV4 \ IMDS_IPV4 \
IMDS_IPV6 \ IMDS_IPV6 \
IMDS_IPV4_NET \ IMDS_IPV4_NET \
IMDS_IPV6_NET \ IMDS_IPV6_NET \
IMDS_IPV4_PREFIX \ IMDS_IPV4_PREFIX \
IMDS_IPV6_PREFIX IMDS_IPV6_PREFIX
_imds_header() { _imds_header() {
echo "$IMDS_HEADER: true" echo "$IMDS_HEADER: true"
} }
# dig deeper than default # dig deeper than default
_imds_ssh_keys() { _imds_ssh_keys() {
local key local key
for key in $(imds "$IMDS_SSH_KEYS"); do for key in $(imds "$IMDS_SSH_KEYS"); do
imds "$IMDS_SSH_KEYS/${key}/keyData" imds "$IMDS_SSH_KEYS/${key}/keyData"
done | sort -u done | sort -u
} }
# decode userdata value # decode userdata value
_imds_userdata() { _imds_userdata() {
imds "$IMDS_USERDATA" | base64 -d imds "$IMDS_USERDATA" | base64 -d
} }

View File

@ -1,5 +1,5 @@
# Google Cloud Instance MetaData Service variables and functions # Google Cloud Instance MetaData Service variables and functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
IMDS_HEADER="Metadata-Flavor" IMDS_HEADER="Metadata-Flavor"
@ -8,21 +8,21 @@ IMDS_URI="computeMetadata/v1"
IMDS_HOSTNAME="instance/hostname" IMDS_HOSTNAME="instance/hostname"
IMDS_LOCAL_HOSTNAME="$IMDS_HOSTNAME" IMDS_LOCAL_HOSTNAME="$IMDS_HOSTNAME"
IMDS_SSH_KEYS=" IMDS_SSH_KEYS="
project/attributes/ssh-keys project/attributes/ssh-keys
instance/attributes/ssh-keys instance/attributes/ssh-keys
" "
IMDS_USERDATA="instance/attributes/user-data" IMDS_USERDATA="instance/attributes/user-data"
_imds_header() { _imds_header() {
echo "$IMDS_HEADER: Google" echo "$IMDS_HEADER: Google"
} }
# merge project and instance keys # merge project and instance keys
_imds_ssh_keys() { _imds_ssh_keys() {
local ssh_keys local ssh_keys
for ssh_keys in $IMDS_SSH_KEYS; do for ssh_keys in $IMDS_SSH_KEYS; do
# ignore errors and strip leading '<login>:' # ignore errors and strip leading '<login>:'
imds -e "$ssh_keys" | cut -d: -f2- imds -e "$ssh_keys" | cut -d: -f2-
done | sort -u done | sort -u
} }

View File

@ -1,5 +1,5 @@
# NoCloud Instance Metadata # NoCloud Instance Metadata
# vim: ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
NOCLOUD_FILES="meta-data user-data vendor-data network-config" NOCLOUD_FILES="meta-data user-data vendor-data network-config"
@ -7,100 +7,100 @@ NOCLOUD_FILES="meta-data user-data vendor-data network-config"
is_nocloud_loaded() { [ -f "$TINY_CLOUD_VAR/.nocloud_loaded" ]; } is_nocloud_loaded() { [ -f "$TINY_CLOUD_VAR/.nocloud_loaded" ]; }
_load_nocloud_cmdline() { _load_nocloud_cmdline() {
local kopt kv k v data local kopt kv k v data
for kopt in $(cat "$ROOT/proc/cmdline" 2>/dev/null); do for kopt in $(cat "$ROOT/proc/cmdline" 2>/dev/null); do
echo "$kopt" | grep -qE '(^|=)ds=nocloud(-net)?;' || continue echo "$kopt" | grep -qE '(^|=)ds=nocloud(-net)?;' || continue
for kv in $(echo "${kopt#*;}" | tr \; ' '); do for kv in $(echo "${kopt#*;}" | tr \; ' '); do
k=$(echo "$kv" | cut -d= -f1) k=$(echo "$kv" | cut -d= -f1)
v=$(echo "$kv" | cut -d= -f2-) v=$(echo "$kv" | cut -d= -f2-)
case "$k" in case "$k" in
h|local-hostname) h|local-hostname)
printf "\nlocal-hostname: %s" "$v" >> "$TINY_CLOUD_VAR/meta-data" printf "\nlocal-hostname: %s" "$v" >> "$TINY_CLOUD_VAR/meta-data"
;; ;;
i|instance-id) i|instance-id)
printf "\ninstance-id: %s" "$v" >> "$TINY_CLOUD_VAR/meta-data" printf "\ninstance-id: %s" "$v" >> "$TINY_CLOUD_VAR/meta-data"
;; ;;
s|seedfrom) s|seedfrom)
for data in $NOCLOUD_FILES; do for data in $NOCLOUD_FILES; do
case "${v#file:/}" in case "${v#file:/}" in
/*) /*)
cat "$v/$data" >> "$TINY_CLOUD_VAR/$data" || continue cat "$v/$data" >> "$TINY_CLOUD_VAR/$data" || continue
echo >> "$TINY_CLOUD_VAR/$data" echo >> "$TINY_CLOUD_VAR/$data"
;; ;;
http://*|https://*) http://*|https://*)
wget -qO - "$v/$data" >> "$TINY_CLOUD_VAR/$data" || continue wget -qO - "$v/$data" >> "$TINY_CLOUD_VAR/$data" || continue
echo >> "$TINY_CLOUD_VAR/$data" echo >> "$TINY_CLOUD_VAR/$data"
;; ;;
*) log -s warning "Unknown NoCloud seedfrom value '$v'" *) log -s warning "Unknown NoCloud seedfrom value '$v'"
;; ;;
esac esac
done done
;; ;;
*) log -s warning "Unknown NoCloud kernel cmdline key '$k'" *) log -s warning "Unknown NoCloud kernel cmdline key '$k'"
;; ;;
esac esac
done done
return return
done done
return 1 return 1
} }
_load_nocloud_volume() { _load_nocloud_volume() {
mkdir -p "$ROOT"/run/tiny-cloud mkdir -p "$ROOT"/run/tiny-cloud
local mntdir=$(mktemp -d "$ROOT/run/tiny-cloud/cidata-XXXXXX") local mntdir=$(mktemp -d "$ROOT/run/tiny-cloud/cidata-XXXXXX")
local data mounted local data mounted
mkdir -p "$mntdir" mkdir -p "$mntdir"
for fstype in vfat iso9660; do for fstype in vfat iso9660; do
[ "$mounted" ] && break [ "$mounted" ] && break
for label in cidata CIDATA; do for label in cidata CIDATA; do
[ -n "$mounted" ] && break [ -n "$mounted" ] && break
mount -o ro -t "$fstype" LABEL="$label" "$mntdir" && mounted=1 mount -o ro -t "$fstype" LABEL="$label" "$mntdir" && mounted=1
done done
done done
if [ -n "$mounted" ]; then if [ -n "$mounted" ]; then
for data in $NOCLOUD_FILES; do for data in $NOCLOUD_FILES; do
# lack of source results in empty target # lack of source results in empty target
cat "$mntdir/$data" > "$TINY_CLOUD_VAR/$data" 2>/dev/null cat "$mntdir/$data" > "$TINY_CLOUD_VAR/$data" 2>/dev/null
done done
umount "$mntdir" umount "$mntdir"
else else
return 1 return 1
fi fi
rmdir "$mntdir" rmdir "$mntdir"
} }
load_nocloud() { load_nocloud() {
# start with a clean slate # start with a clean slate
(cd "$TINY_CLOUD_VAR" && rm -f $NOCLOUD_FILES) (cd "$TINY_CLOUD_VAR" && rm -f $NOCLOUD_FILES)
if _load_nocloud_cmdline || _load_nocloud_volume; then if _load_nocloud_cmdline || _load_nocloud_volume; then
touch "$TINY_CLOUD_VAR/.nocloud_loaded" touch "$TINY_CLOUD_VAR/.nocloud_loaded"
else else
log -s err "Unable to load NoCloud datasource" log -s err "Unable to load NoCloud datasource"
return 1 return 1
fi fi
# minimally, we expect some content in meta-data # minimally, we expect some content in meta-data
[ -s "$TINY_CLOUD_VAR/meta-data" ] || [ -s "$TINY_CLOUD_VAR/meta-data" ] ||
log -s warning "NoCloud 'meta-data' is empty" log -s warning "NoCloud 'meta-data' is empty"
} }
_imds() { _imds() {
mkdir -p "$TINY_CLOUD_VAR" mkdir -p "$TINY_CLOUD_VAR"
local file="$TINY_CLOUD_VAR/$(echo "$1" | cut -d/ -f1)" local file="$TINY_CLOUD_VAR/$(echo "$1" | cut -d/ -f1)"
local keypath="$(echo "$1" | cut -d/ -f2- | tr / ' ')" local keypath="$(echo "$1" | cut -d/ -f2- | tr / ' ')"
is_nocloud_loaded || load_nocloud is_nocloud_loaded || load_nocloud
# does file exist? # does file exist?
[ -f "$file" ] || return 1 [ -f "$file" ] || return 1
# use 'file/' to get top-level keys # use 'file/' to get top-level keys
if [ $(basename "$file") = "$keypath" ]; then if [ $(basename "$file") = "$keypath" ]; then
cat "$file" cat "$file"
else else
yx -f "$file" $keypath yx -f "$file" $keypath
fi fi
} }

View File

@ -1,5 +1,5 @@
# OCI Instance MetaData Service variables and functions # OCI Instance MetaData Service variables and functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
IMDS_HEADER="Authorization" IMDS_HEADER="Authorization"
@ -13,25 +13,25 @@ IMDS_NICS="nics"
# TODO: flesh out networking # TODO: flesh out networking
unset \ unset \
IMDS_MAC \ IMDS_MAC \
IMDS_IPV4 \ IMDS_IPV4 \
IMDS_IPV6 \ IMDS_IPV6 \
IMDS_IPV4_NET \ IMDS_IPV4_NET \
IMDS_IPV6_NET \ IMDS_IPV6_NET \
IMDS_IPV4_PREFIX \ IMDS_IPV4_PREFIX \
IMDS_IPV6_PREFIX IMDS_IPV6_PREFIX
_imds_header() { _imds_header() {
echo "$IMDS_HEADER: Bearer Oracle" echo "$IMDS_HEADER: Bearer Oracle"
} }
_imds_ssh_keys() { _imds "$IMDS_SSH_KEYS"; } _imds_ssh_keys() { _imds "$IMDS_SSH_KEYS"; }
_imds_nic_index() { _imds_nic_index() {
local m n=0 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 while m=$(imds "$IMDS_NICS/$n/mac" | tr A-F a-f); do
[ "$m" = "$mac" ] && echo $n; return 0 [ "$m" = "$mac" ] && echo $n; return 0
done done
return 1 return 1
} }

View File

@ -1,5 +1,5 @@
# Tiny Cloud - common script functions # Tiny Cloud - common script functions
# vim: ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
# set defaults # set defaults
@ -10,62 +10,62 @@
: "${TINY_CLOUD_VAR:=$ROOT/var/lib/cloud}" : "${TINY_CLOUD_VAR:=$ROOT/var/lib/cloud}"
log() { log() {
local facility="local7" local facility="local7"
local stderr init local stderr init
local tag=$(basename "$0") local tag=$(basename "$0")
while [ "${1#-}" != "$1" ]; do while [ "${1#-}" != "$1" ]; do
case "$1" in case "$1" in
-i) init=1 ;; # TODO: value = indent? -i) init=1 ;; # TODO: value = indent?
-f) facility="$2"; shift ;; -f) facility="$2"; shift ;;
-s) stderr=-s ;; -s) stderr=-s ;;
-t) tag="$tag/$2"; shift ;; -t) tag="$tag/$2"; shift ;;
esac esac
shift shift
done done
local level="$1" local level="$1"
[ -z "$DEBUG" ] && [ "$level" = debug ] && return [ -z "$DEBUG" ] && [ "$level" = debug ] && return
shift shift
[ -n "$init" ] && echo "$@" >&2 [ -n "$init" ] && echo "$@" >&2
logger $stderr -p "$facility.$level" -t "${tag}[$$]" "$@" logger $stderr -p "$facility.$level" -t "${tag}[$$]" "$@"
case "$level" in case "$level" in
crit|alert|emerg) exit 1 ;; crit|alert|emerg) exit 1 ;;
esac esac
} }
# usage: replace_word <search> <replace> <list>... # usage: replace_word <search> <replace> <list>...
replace_word() { replace_word() {
local search="$1" replace="$2" local search="$1" replace="$2"
shift 2 shift 2
for word in "$@"; do for word in "$@"; do
if [ "$word" = "$search" ]; then if [ "$word" = "$search" ]; then
echo "$replace" echo "$replace"
else else
echo "$word" echo "$word"
fi fi
done done
} }
# usage: insert_after <where> <what> <list>... # usage: insert_after <where> <what> <list>...
insert_after() { insert_after() {
local search="$1" addition="$2" local search="$1" addition="$2"
shift 2 shift 2
for i in "$@"; do for i in "$@"; do
echo "$i" echo "$i"
if [ "$i" = "$search" ]; then if [ "$i" = "$search" ]; then
echo "$addition" echo "$addition"
fi fi
done done
} }
# usage: add_once <file> <line-to-add>... # usage: add_once <file> <line-to-add>...
add_once() { add_once() {
local file="$1" local file="$1"
shift shift
for line; do for line; do
if ! grep -x -F "$line" "$file" 2>/dev/null; then if ! grep -x -F "$line" "$file" 2>/dev/null; then
mkdir -p "${file%/*}" mkdir -p "${file%/*}"
printf "%s\n" "$line" >> "$file" printf "%s\n" "$line" >> "$file"
fi fi
done done
} }

View File

@ -1,5 +1,5 @@
# Tiny Cloud - Init Functions # Tiny Cloud - Init Functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
# set defaults # set defaults
@ -12,18 +12,18 @@
### default phase actions (without leading 'init__') ### default phase actions (without leading 'init__')
DEFAULT_ACTIONS_EARLY=" DEFAULT_ACTIONS_EARLY="
expand_root expand_root
install_hotplugs install_hotplugs
set_default_interfaces set_default_interfaces
create_default_user create_default_user
enable_sshd enable_sshd
" "
DEFAULT_ACTIONS_NET=" DEFAULT_ACTIONS_NET="
save_userdata save_userdata
" "
DEFAULT_ACTIONS_MAIN=" DEFAULT_ACTIONS_MAIN="
set_hostname set_hostname
set_ssh_keys set_ssh_keys
" "
DEFAULT_ACTIONS_FINAL="" DEFAULT_ACTIONS_FINAL=""
@ -40,236 +40,236 @@ DEFAULT_ACTIONS_FINAL=""
### standard init-early functions... ### standard init-early functions...
init__expand_root() { init__expand_root() {
local dev=$(awk '$2 == "/" {print $1}' "$ROOT"/proc/mounts 2>/dev/null) 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 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 partition=$(cat "$ROOT/sys/class/block/${dev#/dev/}/partition" 2>/dev/null)
# only support ext2/ext3/ext4 for now # only support ext2/ext3/ext4 for now
case "$filesystem" in case "$filesystem" in
ext*) ;; ext*) ;;
*) return;; *) return;;
esac esac
if [ -n "$partition" ]; then if [ -n "$partition" ]; then
# it's a partition, resize it # it's a partition, resize it
local volume=$(readlink -f "$ROOT/sys/class/block/${dev#/dev/}/..") local volume=$(readlink -f "$ROOT/sys/class/block/${dev#/dev/}/..")
volume="/dev/${volume##*/}" volume="/dev/${volume##*/}"
echo ", +" | $MOCK sfdisk -q --no-reread -N "$partition" "$volume" echo ", +" | $MOCK sfdisk -q --no-reread -N "$partition" "$volume"
$MOCK partx -u "$volume" $MOCK partx -u "$volume"
fi fi
# resize filesystem # resize filesystem
$MOCK resize2fs "$dev" $MOCK resize2fs "$dev"
} }
init__install_hotplugs() { init__install_hotplugs() {
local level result rc=0 local level result rc=0
[ ! -n "$HOTPLUG_MODULES" ] && return [ ! -n "$HOTPLUG_MODULES" ] && return
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
for module in $HOTPLUG_MODULES; do for module in $HOTPLUG_MODULES; do
result='unknown' result='unknown'
level='err' level='err'
printf " >> " >&2 printf " >> " >&2
log -i -t "$phase/$ACTION" info "$module: installing" log -i -t "$phase/$ACTION" info "$module: installing"
if type "mod__$module" | grep -q -w "function"; then if type "mod__$module" | grep -q -w "function"; then
if "mod__$module"; then if "mod__$module"; then
result='installed' result='installed'
level='info' level='info'
else else
result='failed' result='failed'
rc=1 rc=1
fi fi
fi fi
printf " >> " >&2 printf " >> " >&2
log -i -t "$phase/$ACTION" info "$module: $result" log -i -t "$phase/$ACTION" info "$module: $result"
done done
return $rc return $rc
} }
# collect ethernet interfaces, sorted by index # collect ethernet interfaces, sorted by index
ethernets() { ethernets() {
for i in "$ROOT/sys/class/net/"*; do for i in "$ROOT/sys/class/net/"*; do
local iface="${i##*/}" local iface="${i##*/}"
case "$iface" in case "$iface" in
eth*) echo "$(cat "$i/ifindex") $iface";; eth*) echo "$(cat "$i/ifindex") $iface";;
esac esac
done | sort -n | awk '{print $2}' done | sort -n | awk '{print $2}'
} }
# find the interface that is has operstate up # find the interface that is has operstate up
find_first_interface_up() { find_first_interface_up() {
local n=0 local n=0
[ $# -eq 0 ] && return [ $# -eq 0 ] && return
while [ $n -le ${TINY_CLOUD_LINK_WAIT_MAX:-10} ]; do while [ $n -le ${TINY_CLOUD_LINK_WAIT_MAX:-10} ]; do
for i in "$@"; do for i in "$@"; do
if [ "$(cat "$ROOT/sys/class/net/$i/operstate")" = "up" ]; then if [ "$(cat "$ROOT/sys/class/net/$i/operstate")" = "up" ]; then
echo "$i" echo "$i"
return return
fi fi
done done
sleep 0.1 sleep 0.1
n=$((n+1)) n=$((n+1))
done done
} }
# auto detect which network interface to auto configure # auto detect which network interface to auto configure
# check which is connected or fallback to first # check which is connected or fallback to first
# This will set link to down to all eth* except the found # This will set link to down to all eth* except the found
auto_detect_ethernet_interface() { auto_detect_ethernet_interface() {
local ifaces="$(ethernets)" local ifaces="$(ethernets)"
[ -z "$ifaces" ] && return [ -z "$ifaces" ] && return
# find first connected interface # find first connected interface
for i in $ifaces; do for i in $ifaces; do
$MOCK ip link set dev $i up >/dev/null $MOCK ip link set dev $i up >/dev/null
done done
local iface="$(find_first_interface_up $ifaces)" local iface="$(find_first_interface_up $ifaces)"
# use first if all are disconnected # use first if all are disconnected
if [ -z "$iface" ]; then if [ -z "$iface" ]; then
set -- $ifaces set -- $ifaces
iface="$1" iface="$1"
fi fi
# we will use the found interface later so lets keep it up # we will use the found interface later so lets keep it up
for i in $ifaces; do for i in $ifaces; do
if [ "$i" != "$iface" ]; then if [ "$i" != "$iface" ]; then
$MOCK ip link set dev $i down >/dev/null $MOCK ip link set dev $i down >/dev/null
fi fi
done done
echo "$iface" echo "$iface"
} }
init__set_default_interfaces() { init__set_default_interfaces() {
if [ -f "$ROOT"/etc/network/interfaces ]; then if [ -f "$ROOT"/etc/network/interfaces ]; then
log -i -t "$phase" info "$ACTION: already set up" log -i -t "$phase" info "$ACTION: already set up"
return return
fi fi
mkdir -p "$ROOT/etc/network" mkdir -p "$ROOT/etc/network"
printf "%s\n%s\n\n" \ printf "%s\n%s\n\n" \
"auto lo" \ "auto lo" \
"iface lo inet loopback" \ "iface lo inet loopback" \
> "$ROOT/etc/network/interfaces" > "$ROOT/etc/network/interfaces"
local iface="$(auto_detect_ethernet_interface)" local iface="$(auto_detect_ethernet_interface)"
if [ -z "$iface" ]; then if [ -z "$iface" ]; then
# TODO: message/log? # TODO: message/log?
return return
fi fi
printf "%s\n%s\n\t%s\n\n" \ printf "%s\n%s\n\t%s\n\n" \
"auto $iface" \ "auto $iface" \
"iface $iface" \ "iface $iface" \
"use dhcp" >> "$ROOT/etc/network/interfaces" "use dhcp" >> "$ROOT/etc/network/interfaces"
} }
init__create_default_user() { init__create_default_user() {
local user="$CLOUD_USER" local user="$CLOUD_USER"
# don't do anything if it already exists # don't do anything if it already exists
if getent passwd "$user" >/dev/null; then if getent passwd "$user" >/dev/null; then
log -i -t "$phase" info "$ACTION: already exists" log -i -t "$phase" info "$ACTION: already exists"
return return
fi fi
$MOCK addgroup "$user" $MOCK addgroup "$user"
$MOCK adduser -h "/home/$user" -s /bin/sh -G "$user" -D "$user" $MOCK adduser -h "/home/$user" -s /bin/sh -G "$user" -D "$user"
$MOCK addgroup "$user" wheel $MOCK addgroup "$user" wheel
echo "$user:*" | $MOCK chpasswd -e echo "$user:*" | $MOCK chpasswd -e
# setup sudo and/or doas # setup sudo and/or doas
if [ -d "$ROOT/etc/sudoers.d" ]; then if [ -d "$ROOT/etc/sudoers.d" ]; then
echo '%wheel ALL=(ALL) NOPASSWD: ALL' > "$ROOT/etc/sudoers.d/wheel" echo '%wheel ALL=(ALL) NOPASSWD: ALL' > "$ROOT/etc/sudoers.d/wheel"
fi fi
if [ -d "$ROOT/etc/doas.d" ]; then if [ -d "$ROOT/etc/doas.d" ]; then
echo 'permit nopass :wheel' > "$TARGET/etc/doas.d/wheel.conf" echo 'permit nopass :wheel' > "$TARGET/etc/doas.d/wheel.conf"
fi fi
} }
init__enable_sshd() { init__enable_sshd() {
$MOCK rc-update add sshd default $MOCK rc-update add sshd default
# in case something else has enabled/disabled dservices # in case something else has enabled/disabled dservices
$MOCK rc-update --update $MOCK rc-update --update
} }
### standard init-main functions ### standard init-main functions
init__set_hostname() { init__set_hostname() {
local fqdn=$(imds @hostname) local fqdn=$(imds @hostname)
if [ -z "$fqdn" ]; then if [ -z "$fqdn" ]; then
log -i -t "$phase" info "$ACTION: no hostname set" log -i -t "$phase" info "$ACTION: no hostname set"
return return
fi fi
local host="${fqdn%%\.*}" local host="${fqdn%%\.*}"
if [ -z "$host" ]; then if [ -z "$host" ]; then
log -i -t "$phase" warn "$ACTION: invalid hostname '$fqdn'" log -i -t "$phase" warn "$ACTION: invalid hostname '$fqdn'"
return 1 return 1
fi fi
mkdir -p "$ROOT"/etc mkdir -p "$ROOT"/etc
echo "$host" > "$ROOT"/etc/hostname echo "$host" > "$ROOT"/etc/hostname
$MOCK hostname -F "$ROOT"/etc/hostname $MOCK hostname -F "$ROOT"/etc/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
} }
init__set_ssh_keys() { init__set_ssh_keys() {
local sshkeys="$(imds @ssh-keys)" local sshkeys="$(imds @ssh-keys)"
if [ -z "$sshkeys" ]; then if [ -z "$sshkeys" ]; then
log -i -t "$phase" info "$ACTION: no ssh key found" log -i -t "$phase" info "$ACTION: no ssh key found"
return return
fi fi
local user="$CLOUD_USER" local user="$CLOUD_USER"
local pwent="$(getent passwd "$user")" local pwent="$(getent passwd "$user")"
if [ -z "$pwent" ]; then if [ -z "$pwent" ]; then
log -i -t "$phase" err "$ACTION: failed to find user $user" log -i -t "$phase" err "$ACTION: failed to find user $user"
return 1 return 1
fi fi
local group=$(echo "$pwent" | cut -d: -f4) local group=$(echo "$pwent" | cut -d: -f4)
local ssh_dir="${ROOT}$(echo "$pwent" | cut -d: -f6)/.ssh" local ssh_dir="${ROOT}$(echo "$pwent" | cut -d: -f6)/.ssh"
local keys_file="$ssh_dir/authorized_keys" local keys_file="$ssh_dir/authorized_keys"
if [ ! -d "$ssh_dir" ]; then if [ ! -d "$ssh_dir" ]; then
mkdir -p "$ssh_dir" mkdir -p "$ssh_dir"
chmod 700 "$ssh_dir" chmod 700 "$ssh_dir"
fi fi
touch "$keys_file" touch "$keys_file"
chmod 600 "$keys_file" chmod 600 "$keys_file"
$MOCK chown -R "$user:$group" "$ssh_dir" $MOCK chown -R "$user:$group" "$ssh_dir"
echo "$sshkeys" > "$keys_file" echo "$sshkeys" > "$keys_file"
} }
init__save_userdata() { init__save_userdata() {
local userdata="$TINY_CLOUD_VAR/user-data" local userdata="$TINY_CLOUD_VAR/user-data"
if [ -f "$userdata" ]; then if [ -f "$userdata" ]; then
log -i -t "$phase" info "$ACTION: user-data already saved" log -i -t "$phase" info "$ACTION: user-data already saved"
return return
fi fi
local tmpfile=$(mktemp "$userdata.XXXXXX") local tmpfile=$(mktemp "$userdata.XXXXXX")
imds -e @userdata > "$tmpfile" imds -e @userdata > "$tmpfile"
if printf '\037\213\010' | cmp -s -n 3 "$tmpfile"; then if printf '\037\213\010' | cmp -s -n 3 "$tmpfile"; then
gzip -dc "$tmpfile" > "$userdata" gzip -dc "$tmpfile" > "$userdata"
elif printf 'BZh' | cmp -s -n 3 "$tmpfile"; then elif printf 'BZh' | cmp -s -n 3 "$tmpfile"; then
bzip2 -dc "$tmpfile" > "$userdata" bzip2 -dc "$tmpfile" > "$userdata"
elif printf '\375\067\172\130\132\000' | cmp -s -n 6 "$tmpfile"; then elif printf '\375\067\172\130\132\000' | cmp -s -n 6 "$tmpfile"; then
unxz -c "$tmpfile" > "$userdata" unxz -c "$tmpfile" > "$userdata"
elif printf '\135\000\000' | cmp -s -n 3 "$tmpfile"; then elif printf '\135\000\000' | cmp -s -n 3 "$tmpfile"; then
lzma -dc "$tmpfile" > "$userdata" lzma -dc "$tmpfile" > "$userdata"
elif printf '\211\114\132' | cmp -s -n 3 "$tmpfile"; then elif printf '\211\114\132' | cmp -s -n 3 "$tmpfile"; then
lzop -dc "$tmpfile" > "$userdata" lzop -dc "$tmpfile" > "$userdata"
elif printf '\004\042\115\030' | cmp -s -n 4 "$tmpfile"; then elif printf '\004\042\115\030' | cmp -s -n 4 "$tmpfile"; then
lz4 -dc "$tmpfile" > "$userdata" lz4 -dc "$tmpfile" > "$userdata"
elif printf '(\265/\375' | cmp -s -n 4 "$tmpfile"; then elif printf '(\265/\375' | cmp -s -n 4 "$tmpfile"; then
zstd -dc "$tmpfile" > "$userdata" zstd -dc "$tmpfile" > "$userdata"
else else
cp "$tmpfile" "$userdata" cp "$tmpfile" "$userdata"
fi fi
rm "$tmpfile" rm "$tmpfile"
} }
@ -279,26 +279,26 @@ init__save_userdata() {
### load cloud-specific init functions / vars (potentially overriding) ### load cloud-specific init functions / vars (potentially overriding)
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
### load user-data type-specific init functions / vars (potentially overriding) ### load user-data type-specific init functions / vars (potentially overriding)
userdata_type() { userdata_type() {
if [ ! -f "$TINY_CLOUD_VAR/user-data" ]; then if [ ! -f "$TINY_CLOUD_VAR/user-data" ]; then
echo missing echo missing
return return
fi fi
header=$(head -n1 "$TINY_CLOUD_VAR/user-data" | sed -e 's/[[:space:]].*//g') header=$(head -n1 "$TINY_CLOUD_VAR/user-data" | sed -e 's/[[:space:]].*//g')
case "$header" in case "$header" in
'#!'*) echo script;; '#!'*) echo script;;
'#'*) echo ${header#\#};; '#'*) echo ${header#\#};;
*) echo unknown;; *) echo unknown;;
esac esac
} }
USERDATA_TYPE="$(userdata_type)" USERDATA_TYPE="$(userdata_type)"
if [ -f "$LIBDIR/tiny-cloud/user-data/$USERDATA_TYPE" ]; then if [ -f "$LIBDIR/tiny-cloud/user-data/$USERDATA_TYPE" ]; then
. "$LIBDIR/tiny-cloud/user-data/$USERDATA_TYPE" . "$LIBDIR/tiny-cloud/user-data/$USERDATA_TYPE"
fi fi

View File

@ -1,36 +1,36 @@
# Tiny Cloud - mdev hotplug functions # Tiny Cloud - mdev hotplug functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
# generic helper function to install mdev rules # generic helper function to install mdev rules
install_before() { install_before() {
local before="$1" local before="$1"
shift shift
local line="$*" local line="$*"
# already installed # already installed
fgrep -q "$line" /etc/mdev.conf && return 0 fgrep -q "$line" /etc/mdev.conf && return 0
if grep -q "$before" /etc/mdev.conf; then if grep -q "$before" /etc/mdev.conf; then
# install before existing rule # install before existing rule
line="-$line" line="-$line"
else else
# no rule exists, put it before the catch-all fallback # no rule exists, put it before the catch-all fallback
before="^# fallback" before="^# fallback"
line="$line\n" line="$line\n"
fi fi
sed -i -Ee "s|($before.*)|$line\n\1|" /etc/mdev.conf sed -i -Ee "s|($before.*)|$line\n\1|" /etc/mdev.conf
} }
# hotpluggable VNICs (multi-cloud) # hotpluggable VNICs (multi-cloud)
mod__vnic_eth_hotplug() { mod__vnic_eth_hotplug() {
[ -f /lib/mdev/vnic-eth-hotplug ] || return 1 [ -f /lib/mdev/vnic-eth-hotplug ] || return 1
install_before "^eth" \ install_before "^eth" \
"eth[0-9] root:root 0644 */lib/mdev/vnic-eth-hotplug" "eth[0-9] root:root 0644 */lib/mdev/vnic-eth-hotplug"
# NICs attached at launch don't get added with mdev -s # NICs attached at launch don't get added with mdev -s
assemble-interfaces assemble-interfaces
} }
# load cloud-specific functions # load cloud-specific functions

View File

@ -1,117 +1,117 @@
# Script UserData Functions # Script UserData Functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
INIT_ACTIONS_MAIN="$(insert_after set_hostname \ INIT_ACTIONS_MAIN="$(insert_after set_hostname \
"userdata_bootcmd userdata_write_files userdata_ntp userdata_apk_cache userdata_apk_repositories userdata_packages" \ "userdata_bootcmd userdata_write_files userdata_ntp userdata_apk_cache userdata_apk_repositories userdata_packages" \
$INIT_ACTIONS_MAIN)" $INIT_ACTIONS_MAIN)"
INIT_ACTIONS_FINAL="$INIT_ACTIONS_FINAL userdata_runcmd" INIT_ACTIONS_FINAL="$INIT_ACTIONS_FINAL userdata_runcmd"
get_userdata() { get_userdata() {
IFS="/" IFS="/"
yx -f "$TINY_CLOUD_VAR/user-data" $1 2>/dev/null yx -f "$TINY_CLOUD_VAR/user-data" $1 2>/dev/null
unset IFS unset IFS
} }
init__userdata_bootcmd() { init__userdata_bootcmd() {
# run bootcmd # run bootcmd
local bootcmds="$(get_userdata bootcmd)" local bootcmds="$(get_userdata bootcmd)"
for i in $bootcmds; do for i in $bootcmds; do
local cmd="$(get_userdata bootcmd/"$i")" local cmd="$(get_userdata bootcmd/"$i")"
sh -c "$cmd" sh -c "$cmd"
done done
} }
init__userdata_ntp() { init__userdata_ntp() {
local ntp_enabled="$(get_userdata ntp/enabled)" local ntp_enabled="$(get_userdata ntp/enabled)"
if [ "$ntp_enabled" != "yes" ] && [ "$ntp_enabled" != "true" ]; then if [ "$ntp_enabled" != "yes" ] && [ "$ntp_enabled" != "true" ]; then
return return
fi fi
local ntp_client="$(get_userdata ntp/ntp_client)" local ntp_client="$(get_userdata ntp/ntp_client)"
local svc= pkg= local svc= pkg=
case "$ntp_client" in case "$ntp_client" in
busybox) busybox)
svc=ntpd svc=ntpd
;; ;;
chrony|"") chrony|"")
pkg=chrony pkg=chrony
svc=chronyd svc=chronyd
;; ;;
openntpd) openntpd)
pkg=openntpd pkg=openntpd
svc=openntpd svc=openntpd
;; ;;
esac esac
if [ -n "$pkg" ]; then if [ -n "$pkg" ]; then
$MOCK apk add "$pkg" $MOCK apk add "$pkg"
fi fi
if [ -n "$svc" ]; then if [ -n "$svc" ]; then
$MOCK rc-update add "$svc" default $MOCK rc-update add "$svc" default
$MOCK rc-service "$svc" start $MOCK rc-service "$svc" start
fi fi
} }
init__userdata_apk_cache() { init__userdata_apk_cache() {
local cache="$(get_userdata apk/cache)" local cache="$(get_userdata apk/cache)"
if [ -z "$cache" ]; then if [ -z "$cache" ]; then
return return
fi fi
mkdir -p "$ROOT/$cache" mkdir -p "$ROOT/$cache"
# make link relative # make link relative
case "$cache" in case "$cache" in
/*) cache="../..$cache";; /*) cache="../..$cache";;
esac esac
mkdir -p "$ROOT"/etc/apk mkdir -p "$ROOT"/etc/apk
ln -sf "$cache" "$ROOT"/etc/apk/cache ln -sf "$cache" "$ROOT"/etc/apk/cache
} }
init__userdata_apk_cache() { init__userdata_apk_cache() {
local cache="$(get_userdata apk/cache)" local cache="$(get_userdata apk/cache)"
if [ -z "$cache" ]; then if [ -z "$cache" ]; then
return return
fi fi
mkdir -p "$ROOT/$cache" mkdir -p "$ROOT/$cache"
# make link relative # make link relative
case "$cache" in case "$cache" in
/*) cache="../..$cache";; /*) cache="../..$cache";;
esac esac
mkdir -p "$ROOT"/etc/apk mkdir -p "$ROOT"/etc/apk
ln -sf "$cache" "$ROOT"/etc/apk/cache ln -sf "$cache" "$ROOT"/etc/apk/cache
} }
init__userdata_apk_repositories() { init__userdata_apk_repositories() {
local repositories="$(get_userdata apk/repositories)" local repositories="$(get_userdata apk/repositories)"
mkdir -p "$ROOT"/etc/apk mkdir -p "$ROOT"/etc/apk
for r in $repositories; do for r in $repositories; do
local baseurl="$(get_userdata apk/repositories/$r/base_url)" local baseurl="$(get_userdata apk/repositories/$r/base_url)"
local repos="$(get_userdata apk/repositories/$r/repos)" local repos="$(get_userdata apk/repositories/$r/repos)"
local version="$(get_userdata apk/repositories/$r/version)" local version="$(get_userdata apk/repositories/$r/version)"
if [ -z "$version" ]; then if [ -z "$version" ]; then
local version_id=$( . "$ROOT"/etc/os-release 2>/dev/null && echo "$VERSION_ID") local version_id=$( . "$ROOT"/etc/os-release 2>/dev/null && echo "$VERSION_ID")
case "$version_id" in case "$version_id" in
edge*|*_alpha*) version="edge";; edge*|*_alpha*) version="edge";;
[0-9]*.[0-9]*.[0-9]*) version="v${version_id%.*}";; [0-9]*.[0-9]*.[0-9]*) version="v${version_id%.*}";;
esac esac
fi fi
if [ -n "$version" ] && [ "$version" != "." ] && [ "$version" != "/" ]; then if [ -n "$version" ] && [ "$version" != "." ] && [ "$version" != "/" ]; then
baseurl="${baseurl%/}/$version" baseurl="${baseurl%/}/$version"
fi fi
for repo in $repos; do for repo in $repos; do
local uri="${baseurl%/}/$(get_userdata apk/repositories/$r/repos/$repo)" local uri="${baseurl%/}/$(get_userdata apk/repositories/$r/repos/$repo)"
add_once "$ROOT"/etc/apk/repositories "$uri" add_once "$ROOT"/etc/apk/repositories "$uri"
done done
done done
} }
init__userdata_packages() { init__userdata_packages() {
local packages="$(get_userdata packages)" local packages="$(get_userdata packages)"
local pkgs= local pkgs=
for i in $packages; do for i in $packages; do
pkgs="$pkgs $(get_userdata packages/$i)" pkgs="$pkgs $(get_userdata packages/$i)"
done done
if [ -n "$pkgs" ]; then if [ -n "$pkgs" ]; then
$MOCK apk add $pkgs $MOCK apk add $pkgs
fi fi
} }
init__userdata_runcmd() { init__userdata_runcmd() {
@ -124,60 +124,60 @@ init__userdata_runcmd() {
# write_file <path> <mode> <owner> <encoding> <append> # write_file <path> <mode> <owner> <encoding> <append>
write_file() { write_file() {
# Defaults used are the same as for full cloud-init "spec": # Defaults used are the same as for full cloud-init "spec":
# https://cloudinit.readthedocs.io/en/latest/reference/modules.html#write-files # https://cloudinit.readthedocs.io/en/latest/reference/modules.html#write-files
local path="$1" local path="$1"
local mode="${2:-0644}" local mode="${2:-0644}"
local owner="${3:-root:root}" local owner="${3:-root:root}"
local encoding="${4:-text/plain}" local encoding="${4:-text/plain}"
local append="${5:-false}" local append="${5:-false}"
if [ "$append" != "true" ] && [ "$append" != "false" ]; then if [ "$append" != "true" ] && [ "$append" != "false" ]; then
log err "append must be true or false" log err "append must be true or false"
return return
fi fi
local tmpfile="$(mktemp $TINY_CLOUD_VAR/user-data.write_files.XXXXXX)" local tmpfile="$(mktemp $TINY_CLOUD_VAR/user-data.write_files.XXXXXX)"
case "$encoding" in case "$encoding" in
gzip|gz|gz+base64|gzip+base64|gz+b64|gzip+b64) gzip|gz|gz+base64|gzip+base64|gz+b64|gzip+b64)
base64 -d | gzip -d > "$tmpfile" base64 -d | gzip -d > "$tmpfile"
;; ;;
base64|b64) base64|b64)
base64 -d > "$tmpfile" base64 -d > "$tmpfile"
;; ;;
text/plain) text/plain)
cat > "$tmpfile" cat > "$tmpfile"
;; ;;
esac esac
if [ "$append" = "true" ]; then if [ "$append" = "true" ]; then
cat "$tmpfile" >> "$path" cat "$tmpfile" >> "$path"
else else
cat "$tmpfile" > "$path" cat "$tmpfile" > "$path"
fi fi
rm -f "$tmpfile" rm -f "$tmpfile"
chmod "$mode" "$path" chmod "$mode" "$path"
# mocked as we do not know which users we could use in testing # mocked as we do not know which users we could use in testing
# this way we can check the proper invocation at least # this way we can check the proper invocation at least
$MOCK chown "$owner" "$path" $MOCK chown "$owner" "$path"
} }
init__userdata_write_files() { init__userdata_write_files() {
local files="$(get_userdata write_files)" local files="$(get_userdata write_files)"
for i in $files; do for i in $files; do
local path="$(get_userdata write_files/$i/path)" local path="$(get_userdata write_files/$i/path)"
if [ -z "$path" ]; then if [ -z "$path" ]; then
continue continue
fi fi
mkdir -p "$(dirname "$ROOT/$path")" mkdir -p "$(dirname "$ROOT/$path")"
get_userdata write_files/$i/content | write_file "$ROOT/$path" \ get_userdata write_files/$i/content | write_file "$ROOT/$path" \
"$(get_userdata write_files/$i/permissions)" \ "$(get_userdata write_files/$i/permissions)" \
"$(get_userdata write_files/$i/owner)" \ "$(get_userdata write_files/$i/owner)" \
"$(get_userdata write_files/$i/encoding)" \ "$(get_userdata write_files/$i/encoding)" \
"$(get_userdata write_files/$i/append)" "$(get_userdata write_files/$i/append)"
done done
} }

View File

@ -1,5 +1,5 @@
# CloudConfig UserData Functions # CloudConfig UserData Functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
# TODO # TODO

View File

@ -1,9 +1,9 @@
# Missing UserData Functions # Missing UserData Functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
init__missing_userdata() { init__missing_userdata() {
log -i -t "$phase" notice "$ACTION: no user-data found" log -i -t "$phase" notice "$ACTION: no user-data found"
} }
INIT_ACTIONS_MAIN="missing_userdata ${INIT_ACTIONS_MAIN}" INIT_ACTIONS_MAIN="missing_userdata ${INIT_ACTIONS_MAIN}"

View File

@ -1,16 +1,16 @@
# Script UserData Functions # Script UserData Functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
init__run_userdata() { 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"
chmod u+x "$userdata" chmod u+x "$userdata"
{ "$userdata" 2>& 1; echo $? > "$exit"; } | tee "$log" { "$userdata" 2>& 1; echo $? > "$exit"; } | tee "$log"
return $(cat "$exit") return $(cat "$exit")
} }
# add init actions # add init actions

View File

@ -1,10 +1,10 @@
# Unknown UserData Functions # Unknown UserData Functions
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# shellcheck shell=sh # shellcheck shell=sh
init__unknown_userdata() { init__unknown_userdata() {
local type="$(userdata_type)" local type="$(userdata_type)"
log -i -t "$phase" warn "$ACTION: unable to process '$type' user-data" log -i -t "$phase" warn "$ACTION: unable to process '$type' user-data"
} }
INIT_ACTIONS_MAIN="unknown_userdata ${INIT_ACTIONS_MAIN}" INIT_ACTIONS_MAIN="unknown_userdata ${INIT_ACTIONS_MAIN}"

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# vim:set ts=4 et: # vim:set ft=sh:
set -e set -e
@ -10,33 +10,33 @@ cd "$IFACE_DIR"
cat > "$IFACE_CFG.new" <<EOT cat > "$IFACE_CFG.new" <<EOT
# NOTE: $0 rewrites this file. Edit files in # NOTE: $0 rewrites this file. Edit files in
# /etc/network/interfaces.d/ to persist any customizations. # /etc/network/interfaces.d/ to persist any customizations.
EOT EOT
# existing loopback and eths # existing loopback and eths
for i in $ROOT/sys/class/net/*; do for i in $ROOT/sys/class/net/*; do
IFACE="$(basename "$i")" IFACE="$(basename "$i")"
case $IFACE in case $IFACE in
lo|eth*) lo|eth*)
[ ! -f "$IFACE" ] && sed -e "s/%%/$IFACE/g" DEFAULT > "$IFACE" [ ! -f "$IFACE" ] && sed -e "s/%%/$IFACE/g" DEFAULT > "$IFACE"
printf "%s\n\n" "$(cat "$IFACE")" >> "$IFACE_CFG.new" printf "%s\n\n" "$(cat "$IFACE")" >> "$IFACE_CFG.new"
;; ;;
*) continue ;; *) continue ;;
esac esac
done done
# all the rest # all the rest
for i in "$IFACE_DIR"/*; do for i in "$IFACE_DIR"/*; do
IFACE="$(basename "$i")" IFACE="$(basename "$i")"
case $IFACE in case $IFACE in
DEFAULT|lo|eth*) DEFAULT|lo|eth*)
continue continue
;; ;;
*) *)
printf "%s\n\n" "$(cat "$IFACE")" >> "$IFACE_CFG.new" printf "%s\n\n" "$(cat "$IFACE")" >> "$IFACE_CFG.new"
;; ;;
esac esac
done done
# install new interfaces config # install new interfaces config

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# vim: ts=4 et ft=sh: # vim:set ft=sh:
# Sync interface's network configuration with IMDS # Sync interface's network configuration with IMDS
@ -21,129 +21,129 @@ let RTABLE+=10000
# ip [+F] [-4|-6] <object> <command> [<parameters>] # ip [+F] [-4|-6] <object> <command> [<parameters>]
ip() { ip() {
local fail_ok v=-4 cmd level local fail_ok v=-4 cmd level
[ "$1" = '+F' ] && fail_ok=1 && shift [ "$1" = '+F' ] && fail_ok=1 && shift
if [ "$1" = '-4' ] || [ "$1" = '-6' ]; then if [ "$1" = '-4' ] || [ "$1" = '-6' ]; then
v="$1" v="$1"
shift shift
fi fi
cmd="$2" cmd="$2"
[ "$cmd" = show ] && level=debug || level=info [ "$cmd" = show ] && level=debug || level=info
if /sbin/ip "$v" "$@" || [ -n "$fail_ok" ]; then if /sbin/ip "$v" "$@" || [ -n "$fail_ok" ]; then
log -s "$level" "OK: ip $v $*" log -s "$level" "OK: ip $v $*"
else else
log -s err "FAIL: ip $v $*" log -s err "FAIL: ip $v $*"
fi fi
} }
# get secondary IPv4s currently on the interface # get secondary IPv4s currently on the interface
iface_ip4s() { iface_ip4s() {
ip -4 addr show "$IFACE" secondary | ip -4 addr show "$IFACE" secondary |
sed -E -e '/inet /!d' -e 's/.*inet ([0-9.]+).*/\1/' sed -E -e '/inet /!d' -e 's/.*inet ([0-9.]+).*/\1/'
} }
# get IPv6s currently on the interface # get IPv6s currently on the interface
iface_ip6s() { iface_ip6s() {
ip -6 addr show "$IFACE" scope global | ip -6 addr show "$IFACE" scope global |
sed -E -e '/inet6/!d' -e 's/.*inet6 ([0-9a-f:]+).*/\1/' sed -E -e '/inet6/!d' -e 's/.*inet6 ([0-9a-f:]+).*/\1/'
} }
imds_ip4s() { imds_ip4s() {
local ip4=$(imds "@nic:$IFACE,@ipv4") local ip4=$(imds "@nic:$IFACE,@ipv4")
local ip4s=$(echo "$ip4" | tail +2) # secondary IPv4s local ip4s=$(echo "$ip4" | tail +2) # secondary IPv4s
local ip4p ip4_cidr ip4_gw local ip4p ip4_cidr ip4_gw
# non-eth0 interfaces need custom route tables # non-eth0 interfaces need custom route tables
# #
if [ "$IFACE" != eth0 ] && [ -n "$ip4s" ] && if [ "$IFACE" != eth0 ] && [ -n "$ip4s" ] &&
[ -z $(ip +F -4 route show table "$RTABLE" 2>/dev/null) ]; then [ -z $(ip +F -4 route show table "$RTABLE" 2>/dev/null) ]; then
ip4p=$(echo "$ip4" | head -1) # primary IPv4 ip4p=$(echo "$ip4" | head -1) # primary IPv4
ip4_cidr=$(imds "@nic:$IFACE,@ipv4-net") # TODO: get from iface instead? ip4_cidr=$(imds "@nic:$IFACE,@ipv4-net") # TODO: get from iface instead?
# TODO: this may not hold true for non-AWS clouds # TODO: this may not hold true for non-AWS clouds
ip4_gw=$(echo "$ip4_cidr" | cut -d/ -f1 | ip4_gw=$(echo "$ip4_cidr" | cut -d/ -f1 |
awk -F. '{ print $1"."$2"."$3"."$4+1 }') awk -F. '{ print $1"."$2"."$3"."$4+1 }')
ip -4 route add default via "$ip4_gw" dev "$IFACE" table "$RTABLE" ip -4 route add default via "$ip4_gw" dev "$IFACE" table "$RTABLE"
ip -4 route add "$ip4_cidr" dev "$IFACE" proto kernel scope link \ ip -4 route add "$ip4_cidr" dev "$IFACE" proto kernel scope link \
src "$ip4p" table "$RTABLE" src "$ip4p" table "$RTABLE"
fi fi
echo "$ip4s" echo "$ip4s"
} }
# TODO: 3.18+ when we use dhcpcd for ipv4 & ipv6, we only need to do secondary IPv6s # TODO: 3.18+ when we use dhcpcd for ipv4 & ipv6, we only need to do secondary IPv6s
# circle back and see how amazon-ec2-net-utils is handling everything these days # circle back and see how amazon-ec2-net-utils is handling everything these days
imds_ip6s() { imds_ip6s() {
local ip6s gw tries=20 local ip6s gw tries=20
ip6s=$(imds "@nic:$IFACE,@ipv6") ip6s=$(imds "@nic:$IFACE,@ipv6")
# non-eth0 interfaces need custom route tables # non-eth0 interfaces need custom route tables
# #
# NOTE: busybox iproute2 doesn't do 'route show table' properly for IPv6, # NOTE: busybox iproute2 doesn't do 'route show table' properly for IPv6,
# so iproute2-minimal package is required! # so iproute2-minimal package is required!
# #
if [ "$IFACE" != eth0 ] && [ -n "$ip6s" ] && if [ "$IFACE" != eth0 ] && [ -n "$ip6s" ] &&
[ -z $(ip +F -6 route show table "$RTABLE" 2>/dev/null) ]; then [ -z $(ip +F -6 route show table "$RTABLE" 2>/dev/null) ]; then
while true; do while true; do
gw=$(ip -6 route show dev "$IFACE" default | awk '{ print $3 }') gw=$(ip -6 route show dev "$IFACE" default | awk '{ print $3 }')
[ -n "$gw" ] && break [ -n "$gw" ] && break
let tries-- let tries--
if [ "$tries" -eq 0 ]; then if [ "$tries" -eq 0 ]; then
log -s warn "Unable to get IPv6 gateway RA after 10s" log -s warn "Unable to get IPv6 gateway RA after 10s"
break break
fi fi
sleep 0.5 sleep 0.5
done done
ip -6 route add default via "$gw" dev "$IFACE" table "$RTABLE" ip -6 route add default via "$gw" dev "$IFACE" table "$RTABLE"
# TODO? match imds_ip4s() with ip -6 route add "ip6_cidr" dev "$IFACE" ... # TODO? match imds_ip4s() with ip -6 route add "ip6_cidr" dev "$IFACE" ...
fi fi
echo "$ip6s" echo "$ip6s"
} }
in_list() { in_list() {
echo "$2" | grep -q "^$1$" echo "$2" | grep -q "^$1$"
} }
# ip_addr {4|6} {add|del} <ip> # ip_addr {4|6} {add|del} <ip>
ip_addr() { ip_addr() {
local mask=32 # IPv4 always /32 local mask=32 # IPv4 always /32
[ "$1" -eq 6 ] && mask=128 # IPv6 always /128 [ "$1" -eq 6 ] && mask=128 # IPv6 always /128
ip -"$1" addr "$2" "$3/$mask" dev "$IFACE" ip -"$1" addr "$2" "$3/$mask" dev "$IFACE"
# TODO? delegated ipv[46] prefixes? # TODO? delegated ipv[46] prefixes?
# non-eth0 interfaces get rules associating IPs with route tables # non-eth0 interfaces get rules associating IPs with route tables
[ "$IFACE" = eth0 ] && return [ "$IFACE" = eth0 ] && return
ip -"$1" rule "$2" from "$3" lookup "$RTABLE" ip -"$1" rule "$2" from "$3" lookup "$RTABLE"
ip -"$1" rule "$2" to "$3" lookup "$RTABLE" ip -"$1" rule "$2" to "$3" lookup "$RTABLE"
} }
# sync_ips {4|6} "<imds-ips>" "<iface-ips>" # sync_ips {4|6} "<imds-ips>" "<iface-ips>"
sync_ips() { sync_ips() {
local i local i
# remove extra IPs # remove extra IPs
for i in $3; do for i in $3; do
in_list "$i" "$2" || ip_addr "$1" del "$i" in_list "$i" "$2" || ip_addr "$1" del "$i"
done done
# add missing IPs # add missing IPs
# NOTE: this adds an extra <IPv4>/32 for the primary IP # NOTE: this adds an extra <IPv4>/32 for the primary IP
for i in $2; do for i in $2; do
in_list "$i" "$3" || ip_addr "$1" add "$i" in_list "$i" "$3" || ip_addr "$1" add "$i"
done done
} }
imds_iface_sync() { imds_iface_sync() {
log -s info "SYNCING: $IFACE" log -s info "SYNCING: $IFACE"
sync_ips 4 "$(imds_ip4s)" "$(iface_ip4s)" sync_ips 4 "$(imds_ip4s)" "$(iface_ip4s)"
sync_ips 6 "$(imds_ip6s)" "$(iface_ip6s)" sync_ips 6 "$(imds_ip6s)" "$(iface_ip6s)"
log -s info "FINISHED: $IFACE" log -s info "FINISHED: $IFACE"
} }
case "$PHASE" in case "$PHASE" in
post-up) post-up)
# TODO: daemonize this # TODO: daemonize this
imds_iface_sync imds_iface_sync
;; ;;
pre-down) pre-down)
# TODO: kill daemon, maybe some cleanup # TODO: kill daemon, maybe some cleanup
;; ;;
*) *)
esac esac

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# vim:set ts=4 et ft=sh: # vim:set ft=sh:
# Tiny Cloud # Tiny Cloud
@ -9,69 +9,69 @@ set -e
. "$LIBDIR/tiny-cloud/common" . "$LIBDIR/tiny-cloud/common"
usage() { usage() {
cat <<EOF cat <<-EOF
Usage: ${0##*/} [-h | --help] { early | net | main | final | --bootstrap [complete|incomplete|status] | --setup } Usage: ${0##*/} [-h | --help] { early | net | main | final | --bootstrap [complete|incomplete|status] | --setup }
EOF EOF
} }
bootstrap_complete() { bootstrap_complete() {
touch "$TINY_CLOUD_VAR/.bootstrap-complete" touch "$TINY_CLOUD_VAR/.bootstrap-complete"
} }
bootstrap_incomplete() { bootstrap_incomplete() {
rm -f "$TINY_CLOUD_VAR/.bootstrap-complete" rm -f "$TINY_CLOUD_VAR/.bootstrap-complete"
} }
is_bootstrap_complete() { is_bootstrap_complete() {
[ -f "$TINY_CLOUD_VAR/.bootstrap-complete" ] [ -f "$TINY_CLOUD_VAR/.bootstrap-complete" ]
} }
args=$(getopt -o hsb: --long help,setup,bootstrap: -n ${0##*/} -- "$@") || { usage >&2; exit 1; } args=$(getopt -o hsb: --long help,setup,bootstrap: -n ${0##*/} -- "$@") || { usage >&2; exit 1; }
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
usage >&2 usage >&2
exit 1 exit 1
fi fi
eval set -- "$args" eval set -- "$args"
while true; do while true; do
case "$1" in case "$1" in
-h|--help) usage; exit 0;; -h|--help) usage; exit 0;;
-b|--bootstrap) shift -b|--bootstrap) shift
case "$1" in case "$1" in
complete) # indicate bootstrap is done complete) # indicate bootstrap is done
bootstrap_complete bootstrap_complete
log -i notice 'bootstrap marked complete';; log -i notice 'bootstrap marked complete';;
incomplete) # indicate bootstrap isn't done incomplete) # indicate bootstrap isn't done
bootstrap_incomplete bootstrap_incomplete
log -i warn 'bootstrap marked incomplete';; log -i warn 'bootstrap marked incomplete';;
status) is_bootstrap_complete && echo 'complete' || echo 'incomplete' ;; status) is_bootstrap_complete && echo 'complete' || echo 'incomplete' ;;
*) usage >&2; exit 1;; *) usage >&2; exit 1;;
esac esac
exit 0;; exit 0;;
-s|--setup) # just openrc for now -s|--setup) # just openrc for now
for phase in -early -net '' -final; do for phase in -early -net '' -final; do
rc-update -a del "tiny-cloud$phase" || true rc-update -a del "tiny-cloud$phase" || true
done done
rc-update add tiny-cloud-early boot rc-update add tiny-cloud-early boot
rc-update add tiny-cloud-net default rc-update add tiny-cloud-net default
rc-update add tiny-cloud default rc-update add tiny-cloud default
rc-update add tiny-cloud-final default rc-update add tiny-cloud-final default
exit 0;; exit 0;;
--) shift; break;; --) shift; break;;
*) usage >&2; exit 1;; *) usage >&2; exit 1;;
esac esac
shift shift
done done
phase="$1" phase="$1"
shift shift
case "$phase" in case "$phase" in
early|net|main|final) ;; early|net|main|final) ;;
*) usage >&2; exit 1;; *) usage >&2; exit 1;;
esac esac
# is initial bootstrap already done? # is initial bootstrap already done?
if is_bootstrap_complete; then if is_bootstrap_complete; then
log -i -t "$phase" info "already bootstrapped" log -i -t "$phase" info "already bootstrapped"
exit 0; exit 0;
fi fi
# load init functions # load init functions
@ -81,48 +81,48 @@ fi
# should we skip this action? # should we skip this action?
skip_action() { skip_action() {
local action="$1" local action="$1"
for i in $SKIP_INIT_ACTIONS; do for i in $SKIP_INIT_ACTIONS; do
[ "$i" = "$action" ] && return 0 [ "$i" = "$action" ] && return 0
done done
return 1 return 1
} }
# mandatory final action... # mandatory final action...
init__bootstrap_complete() { init__bootstrap_complete() {
bootstrap_complete bootstrap_complete
} }
INIT_ACTIONS_FINAL="${INIT_ACTIONS_FINAL} bootstrap_complete" INIT_ACTIONS_FINAL="${INIT_ACTIONS_FINAL} bootstrap_complete"
### let's do stuff! ### let's do stuff!
case "$phase" in case "$phase" in
early) INIT_ACTIONS="$INIT_ACTIONS_EARLY";; early) INIT_ACTIONS="$INIT_ACTIONS_EARLY";;
net) INIT_ACTIONS="$INIT_ACTIONS_NET";; net) INIT_ACTIONS="$INIT_ACTIONS_NET";;
main) INIT_ACTIONS="$INIT_ACTIONS_MAIN";; main) INIT_ACTIONS="$INIT_ACTIONS_MAIN";;
final) INIT_ACTIONS="$INIT_ACTIONS_FINAL";; final) INIT_ACTIONS="$INIT_ACTIONS_FINAL";;
*) usage >&2; exit 1 *) usage >&2; exit 1
esac esac
printf '\n' >&2 printf '\n' >&2
for ACTION in $INIT_ACTIONS; do for ACTION in $INIT_ACTIONS; do
if skip_action "$ACTION"; then if skip_action "$ACTION"; then
printf ' -- ' >&2 printf ' -- ' >&2
log -i -t "$phase" notice "$ACTION: skipped" log -i -t "$phase" notice "$ACTION: skipped"
continue continue
fi fi
printf ' ++ ' >&2 printf ' ++ ' >&2
log -i -t "$phase" info "$ACTION: starting" log -i -t "$phase" info "$ACTION: starting"
RESULT="unknown" RESULT="unknown"
LEVEL="err" LEVEL="err"
if type "init__$ACTION" | grep -q -w "function"; then if type "init__$ACTION" | grep -q -w "function"; then
if "init__$ACTION" "$@"; then if "init__$ACTION" "$@"; then
RESULT="done" RESULT="done"
LEVEL="info" LEVEL="info"
else else
RESULT="failed" RESULT="failed"
fi fi
fi fi
printf ' ++ ' >&2 printf ' ++ ' >&2
log -i -t "$phase" "$LEVEL" "$ACTION: $RESULT" log -i -t "$phase" "$LEVEL" "$ACTION: $RESULT"
done done

View File

@ -1,4 +1,5 @@
#!/usr/bin/env atf-sh #!/usr/bin/env atf-sh
# vim:set ft=sh
# shellcheck shell=sh # shellcheck shell=sh
. $(atf_get_srcdir)/test_env.sh . $(atf_get_srcdir)/test_env.sh

View File

@ -2,27 +2,27 @@
prog=${0##*/} prog=${0##*/}
usage() { usage() {
cat <<EOF cat <<-EOF
usage: wget [-cqS] [--spider] [-O FILE] [-o LOGFILE] [--header STR] usage: wget [-cqS] [--spider] [-O FILE] [-o LOGFILE] [--header STR]
[--post-data STR | --post-file FILE] [-Y on/off] [--post-data STR | --post-file FILE] [-Y on/off]
[-P DIR] [-U AGENT] [-T SEC] URL... [-P DIR] [-U AGENT] [-T SEC] URL...
Retrieve files via HTTP or FTP Retrieve files via HTTP or FTP
--spider Only check URL existence: \$? is 0 if exists --spider Only check URL existence: \$? is 0 if exists
--header STR Add STR (of form 'header: value') to headers --header STR Add STR (of form 'header: value') to headers
--post-data STR Send STR using POST method --post-data STR Send STR using POST method
--post-file FILE Send FILE using POST method --post-file FILE Send FILE using POST method
-c Continue retrieval of aborted transfer -c Continue retrieval of aborted transfer
-q Quiet -q Quiet
-P DIR Save to DIR (default .) -P DIR Save to DIR (default .)
-S Show server response -S Show server response
-T SEC Network read timeout is SEC seconds -T SEC Network read timeout is SEC seconds
-O FILE Save to FILE ('-' for stdout) -O FILE Save to FILE ('-' for stdout)
-o LOGFILE Log messages to FILE -o LOGFILE Log messages to FILE
-U STR Use STR for User-Agent header -U STR Use STR for User-Agent header
-Y on/off Use proxy -Y on/off Use proxy
EOF EOF
exit $1 exit $1
} }

View File

@ -1,4 +1,5 @@
#!/usr/bin/env atf-sh #!/usr/bin/env atf-sh
# vim:set ft=sh
# shellcheck shell=sh # shellcheck shell=sh
. $(atf_get_srcdir)/test_env.sh . $(atf_get_srcdir)/test_env.sh

View File

@ -1,4 +1,5 @@
#!/usr/bin/env atf-sh #!/usr/bin/env atf-sh
# vim:set ft=sh
# shellcheck shell=sh # shellcheck shell=sh
. $(atf_get_srcdir)/test_env.sh . $(atf_get_srcdir)/test_env.sh

View File

@ -1,4 +1,5 @@
#!/usr/bin/env atf-sh #!/usr/bin/env atf-sh
# vim:set ft=sh
# shellcheck shell=sh # shellcheck shell=sh
. $(atf_get_srcdir)/test_env.sh . $(atf_get_srcdir)/test_env.sh

View File

@ -1,4 +1,5 @@
#!/usr/bin/env atf-sh #!/usr/bin/env atf-sh
# vim:set ft=sh
# shellcheck shell=sh # shellcheck shell=sh
. $(atf_get_srcdir)/test_env.sh . $(atf_get_srcdir)/test_env.sh

View File

@ -1,4 +1,5 @@
#!/usr/bin/env atf-sh #!/usr/bin/env atf-sh
# vim:set ft=sh
# shellcheck shell=sh # shellcheck shell=sh
. $(atf_get_srcdir)/test_env.sh . $(atf_get_srcdir)/test_env.sh

View File

@ -1,4 +1,5 @@
#!/usr/bin/env atf-sh #!/usr/bin/env atf-sh
# vim:set ft=sh
# shellcheck shell=sh # shellcheck shell=sh
. $(atf_get_srcdir)/test_env.sh . $(atf_get_srcdir)/test_env.sh

View File

@ -1,4 +1,5 @@
#!/usr/bin/env atf-sh #!/usr/bin/env atf-sh
# vim:set ft=sh
# shellcheck shell=sh # shellcheck shell=sh
. $(atf_get_srcdir)/test_env.sh . $(atf_get_srcdir)/test_env.sh

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# vim: set ts=8 noet: # vim:set ft=sh:
# Tiny Cloud IMDS ifupdown-ng executor # Tiny Cloud IMDS ifupdown-ng executor