1
0
mirror of https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud.git synced 2025-12-15 11:22:43 +03:00

move logic from init scripts to /lib/tiny-cloud/*

This commit is contained in:
Jake Buchholz Göktürk 2022-01-02 15:09:21 -08:00
parent 07f0e646e8
commit b5af450a8e
14 changed files with 201 additions and 125 deletions

View File

@ -22,8 +22,8 @@ instance:
* set the instance's hostname from instance metadata
* install SSH keys from instance metadata to the cloud user account's
`authorized_keys` file (the user must already exist)
* save the instance user-data to a file and if it's a script, execute it at the
end of the **default** runlevel
* save the instance user-data to a file, and if it's a script, execute it at
the end of the **default** runlevel
Optional features, which may not be universally necessary:
* manage symlinks from NVMe block devices to `/dev/xvd` and `/dev/sd` devices
@ -68,26 +68,26 @@ rc-update add tiny-cloud-final default
## Configuration
Tiny Cloud looks expects configuration to be found at
[`/etc/conf.d/tiny-cloud`](etc/conf.d/tiny-cloud), which documents all
tuneable parameters (and their defaults).
By default, Tiny Cloud expects configuration at `/etc/conf.d/tiny-cloud`,
although the location can be overridden by setting the `TINY_CLOUD_CONF`
environment variable. The stock [`etc/conf.d/tiny-cloud`](
etc/conf.d/tiny-cloud) file contains details of all tuneable settings.
However, because Tiny Cloud does not do auto-detection, you ***must*** set a
value for `CLOUD` indicating which cloud provider which will be used when
instantiating the image. Current valid values are `aws`, `azure`, `gcp`, and
`oci`.
*Because Tiny Cloud does not currently do auto-detection, you **MUST** set a
configuration value for `CLOUD` indicating which cloud provider will be used.
Current valid values are `aws`, `azure`, `gcp`, and `oci`.*
## Operation
The first time an instance boots -- either freshly instantiated from an image,
or after installation on an existing instance -- Tiny Cloud sets up the
or after installation on a pre-existing instance -- Tiny Cloud sets up the
instance in three phases...
### Early Phase
The `tiny-cloud-early` init script does not depend on the cloud provider's
Instance MetaData Service (IMDS), and does therefore does not have a dependency
on networking. During this "early" phase, the root filesystem is expanded, and
Instance MetaData Service (IMDS), and therefore does not have a dependency on
networking. During this "early" phase, the root filesystem is expanded, and
any necessary `mdev` rules for device hotplug are set up.
### Main Phase
@ -99,16 +99,18 @@ data, and sets up instance's hostname and the cloud user's SSH keys before
### Final Phase
`tiny-cloud-final` should be the very last init script to run in the
**default** runlevel, and saves the instance's user data to
`/var/lib/cloud/user-data`.
**default** runlevel. By default, it saves the instance's user data to
`/var/lib/cloud/user-data`, which is overrideable via the `TINY_CLOUD_VAR`
andr `CLOUD_USERDATA` config settings.
If the user data is a script that starts with `#!` (aka "[shebang](
https://en.wikipedia.org/wiki/Shebang_(Unix))"), it will be executed; its
output (combined STDOUT and STDERR) is saved to `/var/log/cloud/user-data.log`
and the exit code can be found in `/var/log/cloud/user-data.exit`.
If the user data is a script starting with `#!/`, it will be executed; its
output (combined STDOUT and STDERR) and exit code are saved to
`/var/log/user-data.log` and `/var/log/user-data.exit`, respectively -- unless
overriden with `TINY_CLOUD_LOGS` and `CLOUD_USERDATA` config settings.
If all went well, the very last thing `tiny-cloud-final` does is touch
`/var/lib/cloud/.bootstrap-complete` into existence.
a `.bootstrap-complete` file into existence in `/var/lib/cloud` or another
directory specified by the `TINY_CLOUD_VAR` config setting.
### Further Reboots

View File

@ -1,4 +1,4 @@
# tiny-cloud configuration
# Tiny Cloud configuration
# REQUIRED: The instance's cloud provider (valid: aws, azure, gcp, oci)
CLOUD=
@ -6,5 +6,17 @@ CLOUD=
# User account where instance SSH keys are installed
#CLOUD_USER=alpine
# Number of seconds an AWS IMDS token is valid
#IMDS_TOKEN_TTL=5
# Filename of userdata file (in TINY_CLOUD_VAR directory)
#CLOUD_USERDATA=user-data
# IMDS token validity, in seconds (AWS only)
#IMDS_TOKEN_TTL=5
# Location of libraries
#TINY_CLOUD_LIBS=/lib/tiny-cloud
# Location of var directory
#TINY_CLOUD_VAR=/var/lib/cloud
# Location of log directory
#TINY_CLOUD_LOGS=/var/log

View File

@ -8,44 +8,18 @@ depend() {
before sshd
}
_update_hostname() {
local fqdn=$(imds_hostname)
local host="${fqdn%%\.*}"
echo "$host" > /etc/hostname
hostname -F /etc/hostname
echo -e "127.0.1.1\t$fqdn $host" >> /etc/hosts
}
_set_ssh_keys() {
local user="$1"
local pwent=$(getent passwd "$user")
local group=$(echo "$pwent" | cut -d: -f4)
local ssh_dir="$(echo "$pwent" | cut -d: -f6)/.ssh"
local keys_file="$ssh_dir/authorized_keys"
if [ ! -d "$ssh_dir" ]; then
mkdir -p "$ssh_dir"
chmod 700 "$ssh_dir"
fi
touch "$keys_file"
chmod 600 "$keys_file"
chown -R "$user:$group" "$ssh_dir"
imds_ssh_keys > "$keys_file"
}
start() {
[ -f "/var/lib/cloud/.bootstrap-complete" ] && return 0
[ -d "/var/lib/cloud" ] || mkdir -p /var/lib/cloud
source /lib/tiny-cloud/init-main
source /var/lib/tiny-cloud/imds
CLOUD_USER="${CLOUD_USER:-alpine}"
# TODO: background tiny-cloud network watcher?
ebegin "Setting Instance Hostname"; _update_hostname; eend $?
is_bootstrapped && return 0
ebegin "Installing SSH Keys for $CLOUD_USER User"
_set_ssh_keys "$CLOUD_USER"
ebegin "Setting Instance Hostname"
set_hostname
eend $?
# TODO: background tiny-cloud-network watcher?
ebegin "Installing SSH Keys for $CLOUD_USER User"
set_ssh_keys "$CLOUD_USER"
eend $?
}

View File

@ -7,30 +7,14 @@ depend() {
before mdev
}
_expand_root_partition() {
local mountpoint=$(busybox mountpoint -n / | cut -d' ' -f1)
local volume=$(echo "$mountpoint" |
sed -Ee "s/(nvme\d+n\d|(xv|s)d[a-z])p?\d?$/\1/"
)
if [ "$mountpoint" != "$volume" ]; then
partition=$(echo "$volume" | sed -Ee "s/.*(\d+)$/\2/")
einfo "Expanding root partition to volume size..."
echo ", +" | sfdisk -q --no-reread -N "$partition" "$volume"
einfo "Updating kernel with new partition table..."
partx -u "$volume"
fi
einfo "Resizing root filesystem..."
resize2fs "$mountpoint"
}
start() {
[ -f "/var/lib/cloud/.bootstrap-complete" ] && return 0
[ -d "/var/lib/cloud" ] || mkdir -p /var/lib/cloud
source /lib/tiny-cloud/init-early
source /etc/conf.d/tiny-cloud # or use auto-sourced tiny-cloud-early?
is_bootstrapped && return 0
ebegin "Expanding Root Partition"; _expand_root_partition; eend $?
ebegin "Expanding Root Volume/Partition"
expand_root
eend $?
# TODO: _setup mdev things, if applicable
}

View File

@ -8,30 +8,18 @@ depend() {
provide cloud-final
}
_save_userdata() {
imds_userdata > "$USERDATA"
}
_run_userdata() {
local log="/var/log/user-data.log"
local exit="/var/log/user-data.exit"
chmod +x "$USERDATA"
{ "$USERDATA" 2>& 1; echo $? > "$exit"; } | tee "$log"
return $(cat "$exit")
}
start() {
[ -f "/var/lib/cloud/.bootstrap-complete" ] && return 0
[ -d "/var/lib/cloud" ] || mkdir -p /var/lib/cloud
source /lib/tiny-cloud/init-final
source /lib/tiny-cloud/imds
is_bootstrapped && return 0
USERDATA="/var/lib/cloud/user-data"
ebegin "Saving Instance UserData"
save_userdata
eend $?
ebegin "Saving Instance UserData"; _save_userdata; eend $?
if head -n1 "$USERDATA" | grep -q '^#!/'; then
ebegin "Executing UserData Script"; _run_userdata; eend $?
if is_userdata_script; then
ebegin "Executing UserData Script"
run_userdata
eend $?
fi
}

View File

@ -1,4 +1,4 @@
# AWS Instance MetaData Service related variables and functions
# AWS Instance MetaData Service variables and functions
# vim:set ft=sh noet ts=4:
IMDS_HEADER="X-aws-ec2-metadata-token"

View File

@ -1,4 +1,4 @@
# Azure metadata-related variables and functions
# Azure Instance MetaData Service variables and functions
# vim:set ft=sh noet ts=4:
IMDS_HEADER="Metadata"

View File

@ -1,4 +1,4 @@
# Google Cloud metadata-related variables and functions
# Google Cloud Instance MetaData Service variables and functions
# vim:set ft=sh noet ts=4:
IMDS_HEADER="Metadata-Flavor"

View File

@ -1,32 +1,33 @@
# tiny-cloud Instance MetaData Service related functions and variables
# Tiny Cloud - Instance MetaData Service functions and variables
# vim:set ft=sh ts=4 noet:
CONF_DIR="/etc/conf.d"
LIB_DIR="/lib/tiny-cloud"
### configuration
### load configuration
[ -f "$CONF_DIR"/tiny-cloud ] && source "$CONF_DIR"/tiny-cloud
### configuration defaults
CLOUD="${CLOUD:-unknown}"
CLOUD_LOGIN="${CLOUD_LOGIN:-alpine}"
if [ ! -d "$LIB_DIR/$CLOUD" ]; then
echo "ERROR: Unknown Cloud '$CLOUD'" >&2
exit 1
if [ -z "$TINY_CLOUD_LIBS" ]; then
TINY_CLOUD_CONF=${TINY_CLOUD_CONF:-/etc/conf.d/tiny-cloud}
[ -f "$TINY_CLOUD_CONF" ] && source "$TINY_CLOUD_CONF"
TINY_CLOUD_LIBS=${TINY_CLOUD_LIBS:-/lib/tiny-cloud}
fi
IMDS_HEADER=
IMDS_QUERY=
IMDS_URL=
# defaults
CLOUD="${CLOUD:-unknown}"
IMDS_HOSTNAME=
IMDS_SSH_KEYS=
IMDS_USERDATA=
# these are always cloud-specific
### default/common functions
unset \
IMDS_HEADER \
IMDS_QUERY \
IMDS_URL \
IMDS_HOSTNAME \
IMDS_SSH_KEYS \
IMDS_USERDATA
unset -f \
_imds_token \
_imds_header \
2>/dev/null || true
# without error reporting or ending newline enforcement
_imds() {
wget --quiet --header "$(_imds_header)" --output-document - \
"$IMDS_URL/$1/$IMDS_QUERY"
@ -55,6 +56,11 @@ imds_ssh_keys() {
imds "$IMDS_SSH_KEYS"
}
# cloud-specific things (potentially overriding the above)
### load cloud-specific things
[ -f "$LIB_DIR/$CLOUD/metadata" ] && source "$LIB_DIR/$CLOUD"/imds
if [ ! -d "$TINY_CLOUD_LIBS/$CLOUD" ]; then
echo "ERROR: Unknown Cloud '$CLOUD'" >&2
exit 1
fi
source "$TINY_CLOUD_LIBS/$CLOUD/imds"

View File

@ -0,0 +1,15 @@
# Tiny Cloud - Common Initialization
# vim:set ft=sh ts=4 noet:
# set defaults
CLOUD_USER=${CLOUD_USER:-alpine}
CLOUD_USERDATA=${CLOUD_USERDATA:-user-data}
TINY_CLOUD_LOGS=${TINY_CLOUD_LOGS:-/var/log}
TINY_CLOUD_VAR=${TINY_CLOUD_VAR:-/var/lib/cloud}
# ensure existence of output directories
[ ! -d "$TINY_CLOUD_VAR" ] && mkdir -p "$TINY_CLOUD_VAR"
[ ! -d "$TINY_CLOUD_LOGS" ] && mkdir -p "$TINY_CLOUD_LOGS"
# is initial bootstrap already done?
is_bootstrapped() { [ -f "$TINY_CLOUD_VAR"/.bootstrap-complete ]; }

28
lib/tiny-cloud/init-early Normal file
View File

@ -0,0 +1,28 @@
# Tiny Cloud - Early Phase Functions
# vim:set ft=sh ts=4 noet:
if [ -z "$TINY_CLOUD_LIBS" ]; then
TINY_CLOUD_CONF=${TINY_CLOUD_CONF:-/etc/conf.d/tiny-cloud}
[ -f "$TINY_CLOUD_CONF" ] && source "$TINY_CLOUD_CONF"
TINY_CLOUD_LIBS=${TINY_CLOUD_LIBS:-/lib/tiny-cloud}
fi
source "$TINY_CLOUD_LIBS"/init-common
expand_root() {
# explicitly use busybox, in case util-linux is also installed
local mountpoint=$(busybox mountpoint -n / | cut -d' ' -f1)
local volume=$(echo "$mountpoint" |
sed -Ee "s/(nvme\d+n\d|(xv|s)d[a-z])p?\d?$/\1/"
)
if [ "$mountpoint" != "$volume" ]; then
partition=$(echo "$volume" | sed -Ee "s/.*(\d+)$/\2/")
einfo "Expanding root partition to volume size..."
echo ", +" | sfdisk -q --no-reread -N "$partition" "$volume"
einfo "Updating kernel with new partition table..."
partx -u "$volume"
fi
einfo "Resizing root filesystem..."
resize2fs "$mountpoint"
}

30
lib/tiny-cloud/init-final Normal file
View File

@ -0,0 +1,30 @@
# Tiny Cloud - Final Phase Functions
# vim:set ft=sh ts=4 noet:
if [ -z "$TINY_CLOUD_LIBS" ]; then
TINY_CLOUD_CONF=${TINY_CLOUD_CONF:-/etc/conf.d/tiny-cloud}
[ -f "$TINY_CLOUD_CONF" ] && source "$TINY_CLOUD_CONF"
TINY_CLOUD_LIBS=${TINY_CLOUD_LIBS:-/lib/tiny-cloud}
fi
source "$TINY_CLOUD_LIBS"/init-common
source "$TINY_CLOUD_LIBS"/imds
save_userdata() {
imds_userdata > "$USERDATA"
}
is_userdata_script() {
head -n1 "$TINY_CLOUD_VAR/$CLOUD_USERSDATA" | grep -q '%#!/'
}
run_userdata() {
local log="$TINY_CLOUD_LOGS/$CLOUD_USERDATA.log"
local exit="$TINY_CLOUD_LOGS/$CLOUD_USERDATA.exit"
local userdata="$TINY_CLOUD_VAR/$CLOUD_USERDATA"
chmod +x "$userdata"
{ "$userdata" 2>& 1; echo $? > "$exit"; } | tee "$log"
return $(cat "$exit")
}

37
lib/tiny-cloud/init-main Normal file
View File

@ -0,0 +1,37 @@
# Tiny Cloud - Main Phase Functions
# vim:set ft=sh ts=4 noet:
if [ -z "$TINY_CLOUD_LIBS" ]; then
TINY_CLOUD_CONF=${TINY_CLOUD_CONF:-/etc/conf.d/tiny-cloud}
[ -f "$TINY_CLOUD_CONF" ] && source "$TINY_CLOUD_CONF"
TINY_CLOUD_LIBS=${TINY_CLOUD_LIBS:-/lib/tiny-cloud}
fi
source "$TINY_CLOUD_LIBS"/init-common
source "$TINY_CLOUD_LIBS"/imds
set_hostname() {
local fqdn=$(imds_hostname)
local host="${fqdn%%\.*}"
echo "$host" > /etc/hostname
hostname -F /etc/hostname
echo -e "127.0.1.1\t$fqdn $host" >> /etc/hosts
}
set_ssh_keys() {
local user="$CLOUD_USER"
local pwent=$(getent passwd "$user")
local group=$(echo "$pwent" | cut -d: -f4)
local ssh_dir="$(echo "$pwent" | cut -d: -f6)/.ssh"
local keys_file="$ssh_dir/authorized_keys"
if [ ! -d "$ssh_dir" ]; then
mkdir -p "$ssh_dir"
chmod 700 "$ssh_dir"
fi
touch "$keys_file"
chmod 600 "$keys_file"
chown -R "$user:$group" "$ssh_dir"
imds_ssh_keys > "$keys_file"
}

View File

@ -1,4 +1,4 @@
# OCI metadata-related variables and functions
# OCI Instance MetaData Service variables and functions
# vim:set ft=sh noet ts=4:
IMDS_HEADER="Authorization"