mirror of
https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud.git
synced 2025-12-15 11:22:43 +03:00
split alpine-config out of cloud-config
This commit is contained in:
parent
e75ec5b4f6
commit
b8218bd1f0
@ -11,13 +11,13 @@ works with multiple cloud providers. Currently, the following are supported:
|
|||||||
* [AWS](https://aws.amazon.com) - Amazon Web Services
|
* [AWS](https://aws.amazon.com) - Amazon Web Services
|
||||||
* [Azure](https://azure.microsoft.com) - Microsoft Azure
|
* [Azure](https://azure.microsoft.com) - Microsoft Azure
|
||||||
* [GCP](https://cloud.google.com) - Google Cloud Platform
|
* [GCP](https://cloud.google.com) - Google Cloud Platform
|
||||||
* [Hetzner](https://www.hetzner.com)
|
* [Hetzner](https://www.hetzner.com) - Hetzner Cloud
|
||||||
* [Incus](https://linuxcontainers.org/incus)
|
* [Incus](https://linuxcontainers.org/incus) - Incus Containers and Virtual Machines
|
||||||
* [NoCloud](
|
* [NoCloud](
|
||||||
https://cloudinit.readthedocs.io/en/latest/reference/datasources/nocloud.html
|
https://cloudinit.readthedocs.io/en/latest/reference/datasources/nocloud.html
|
||||||
) - cloud-init's NoCloud AWS-compatible user provided data source
|
) - cloud-init's NoCloud AWS-compatible user provided data source
|
||||||
* [OCI](https://cloud.oracle.com) - Oracle Cloud Infrastructure
|
* [OCI](https://cloud.oracle.com) - Oracle Cloud Infrastructure
|
||||||
[Scaleway](https://www.scaleway.com)
|
[Scaleway](https://www.scaleway.com) - Scaleway Cloud
|
||||||
|
|
||||||
Tiny Cloud is also used for Alpine Linux's experimental "auto-install" feature.
|
Tiny Cloud is also used for Alpine Linux's experimental "auto-install" feature.
|
||||||
|
|
||||||
@ -52,9 +52,6 @@ As Tiny Cloud is meant to be tiny, it has few dependencies:
|
|||||||
* `sfdisk`
|
* `sfdisk`
|
||||||
* [`yx`](https://gitlab.com/tomalok/yx) (for extracting data from YAML files)
|
* [`yx`](https://gitlab.com/tomalok/yx) (for extracting data from YAML files)
|
||||||
|
|
||||||
Optional dependencies:
|
|
||||||
* `nvme-cli` (for AWS nitro NVMe symlinks)
|
|
||||||
|
|
||||||
_Tiny Cloud has been developed specifically for use with the
|
_Tiny Cloud has been developed specifically for use with the
|
||||||
[Alpine Cloud Images](
|
[Alpine Cloud Images](
|
||||||
https://gitlab.alpinelinux.org/alpine/cloud/alpine-cloud-images)
|
https://gitlab.alpinelinux.org/alpine/cloud/alpine-cloud-images)
|
||||||
|
|||||||
4
TODO.md
4
TODO.md
@ -2,10 +2,6 @@
|
|||||||
|
|
||||||
## SOON-ish
|
## SOON-ish
|
||||||
|
|
||||||
* Move the bulk of `#alpine-config` handler that is compatible with
|
|
||||||
`#cloud-config` to that handler (which is currently just a stub), and only
|
|
||||||
`#alpine-config` extensions remain there.
|
|
||||||
|
|
||||||
* Support cloud auto-detection, where it's possible to do so.
|
* Support cloud auto-detection, where it's possible to do so.
|
||||||
|
|
||||||
## FUTURE
|
## FUTURE
|
||||||
|
|||||||
@ -1,176 +1,14 @@
|
|||||||
# Script UserData Functions
|
# #alpine-config UserData Functions
|
||||||
# vim:set filetype=sh:
|
# vim:set filetype=sh:
|
||||||
# shellcheck shell=sh
|
# shellcheck shell=sh
|
||||||
|
|
||||||
INIT_ACTIONS_MAIN="$(insert_before create_default_user userdata_user $INIT_ACTIONS_MAIN)"
|
# NOTE: alpine-config extends cloud-config
|
||||||
INIT_ACTIONS_MAIN="$(insert_after set_hostname \
|
|
||||||
"userdata_bootcmd userdata_groups userdata_users userdata_write_files userdata_ntp userdata_apk_cache userdata_apk_repositories userdata_package_update userdata_package_upgrade userdata_packages" \
|
|
||||||
$INIT_ACTIONS_MAIN)"
|
|
||||||
INIT_ACTIONS_MAIN="$(insert_after set_ssh_keys ssh_authorized_keys $INIT_ACTIONS_MAIN)"
|
|
||||||
INIT_ACTIONS_FINAL="$INIT_ACTIONS_FINAL userdata_runcmd userdata_autoinstall"
|
|
||||||
|
|
||||||
get_userdata() {
|
: "${LIBDIR:=$PREFIX/lib}"
|
||||||
IFS="/"
|
. "${LIBDIR}/tiny-cloud/user-data/cloud-config"
|
||||||
yx -f "$TINY_CLOUD_VAR/user-data" $1 2>/dev/null
|
|
||||||
unset IFS
|
|
||||||
}
|
|
||||||
|
|
||||||
init__userdata_user() {
|
INIT_ACTIONS_MAIN="$(insert_after userdata_ntp "userdata_apk_cache userdata_apk_repositories" $INIT_ACTIONS_MAIN)"
|
||||||
local name="$(get_userdata user/name)"
|
INIT_ACTIONS_FINAL="$INIT_ACTIONS_FINAL userdata_autoinstall"
|
||||||
if [ -z "$name" ]; then
|
|
||||||
name="$(get_userdata user)"
|
|
||||||
if [ -n "$(get_userdata user/$name)" ]; then
|
|
||||||
log -s err "user/name is required"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if get_userdata | grep -q -x users; then
|
|
||||||
local default_user="$(get_userdata users/1)"
|
|
||||||
if [ "$default_user" != "default" ]; then
|
|
||||||
CLOUD_USER="$(get_userdata users/1/name)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
CLOUD_USER="${name:-$CLOUD_USER}"
|
|
||||||
}
|
|
||||||
|
|
||||||
set_ssh_authorized_keys_for() {
|
|
||||||
local user="$1"
|
|
||||||
local userdata_path="$2"
|
|
||||||
local sshkeys="$(get_userdata $userdata_path)"
|
|
||||||
if [ -z "$sshkeys" ]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local pwent="$(getent passwd "$user")"
|
|
||||||
if [ -z "$pwent" ]; then
|
|
||||||
log -i -t "$phase" err "$ACTION: failed to find user $user"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
local group=$(echo "$pwent" | cut -d: -f4)
|
|
||||||
local ssh_dir="${ROOT}$(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"
|
|
||||||
$MOCK chown -R "$user:$group" "$ssh_dir"
|
|
||||||
for i in $sshkeys; do
|
|
||||||
local key="$(get_userdata $userdata_path/$i)"
|
|
||||||
if [ -n "$key" ]; then
|
|
||||||
echo "$key" >> "$keys_file"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
init__ssh_authorized_keys() {
|
|
||||||
if [ -z "$CLOUD_USER" ]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
set_ssh_authorized_keys_for "$CLOUD_USER" ssh_authorized_keys
|
|
||||||
}
|
|
||||||
|
|
||||||
init__userdata_bootcmd() {
|
|
||||||
# run bootcmd
|
|
||||||
local bootcmds="$(get_userdata bootcmd)"
|
|
||||||
for i in $bootcmds; do
|
|
||||||
local cmd="$(get_userdata bootcmd/"$i")"
|
|
||||||
sh -c "$cmd"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# write_file <path> <mode> <owner> <encoding> <append>
|
|
||||||
write_file() {
|
|
||||||
# Defaults used are the same as for full cloud-init "spec":
|
|
||||||
# https://cloudinit.readthedocs.io/en/latest/reference/modules.html#write-files
|
|
||||||
local path="$1"
|
|
||||||
local mode="${2:-0644}"
|
|
||||||
local owner="${3:-root:root}"
|
|
||||||
local encoding="${4:-text/plain}"
|
|
||||||
local append="${5:-false}"
|
|
||||||
|
|
||||||
if [ "$append" != "true" ] && [ "$append" != "false" ]; then
|
|
||||||
log err "append must be true or false"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local tmpfile="$(mktemp $TINY_CLOUD_VAR/user-data.write_files.XXXXXX)"
|
|
||||||
|
|
||||||
case "$encoding" in
|
|
||||||
gzip|gz|gz+base64|gzip+base64|gz+b64|gzip+b64)
|
|
||||||
base64 -d | gzip -d > "$tmpfile"
|
|
||||||
;;
|
|
||||||
base64|b64)
|
|
||||||
base64 -d > "$tmpfile"
|
|
||||||
;;
|
|
||||||
text/plain)
|
|
||||||
cat > "$tmpfile"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ "$append" = "true" ]; then
|
|
||||||
cat "$tmpfile" >> "$path"
|
|
||||||
else
|
|
||||||
cat "$tmpfile" > "$path"
|
|
||||||
fi
|
|
||||||
rm -f "$tmpfile"
|
|
||||||
|
|
||||||
chmod "$mode" "$path"
|
|
||||||
# mocked as we do not know which users we could use in testing
|
|
||||||
# this way we can check the proper invocation at least
|
|
||||||
$MOCK chown "$owner" "$path"
|
|
||||||
}
|
|
||||||
|
|
||||||
init__userdata_write_files() {
|
|
||||||
local files="$(get_userdata write_files)"
|
|
||||||
|
|
||||||
for i in $files; do
|
|
||||||
local path="$(get_userdata write_files/$i/path)"
|
|
||||||
if [ -z "$path" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$(dirname "$ROOT/$path")"
|
|
||||||
get_userdata write_files/$i/content | write_file "$ROOT/$path" \
|
|
||||||
"$(get_userdata write_files/$i/permissions)" \
|
|
||||||
"$(get_userdata write_files/$i/owner)" \
|
|
||||||
"$(get_userdata write_files/$i/encoding)" \
|
|
||||||
"$(get_userdata write_files/$i/append)"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
init__userdata_ntp() {
|
|
||||||
local ntp_enabled="$(get_userdata ntp/enabled)"
|
|
||||||
if [ "$ntp_enabled" != "yes" ] && [ "$ntp_enabled" != "true" ]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
local ntp_client="$(get_userdata 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() {
|
init__userdata_apk_cache() {
|
||||||
local cache="$(get_userdata apk/cache)"
|
local cache="$(get_userdata apk/cache)"
|
||||||
@ -215,142 +53,8 @@ init__userdata_apk_repositories() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
init__userdata_package_update() {
|
|
||||||
local update="$(get_userdata package_update)"
|
|
||||||
if [ "$update" = "true" ]; then
|
|
||||||
$MOCK apk update
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
init__userdata_package_upgrade() {
|
|
||||||
local upgrade="$(get_userdata package_upgrade)"
|
|
||||||
if [ "$upgrade" = "true" ]; then
|
|
||||||
$MOCK apk upgrade
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
init__userdata_packages() {
|
|
||||||
local packages="$(get_userdata packages)"
|
|
||||||
local pkgs=
|
|
||||||
for i in $packages; do
|
|
||||||
pkgs="$pkgs $(get_userdata packages/$i)"
|
|
||||||
done
|
|
||||||
if [ -n "$pkgs" ]; then
|
|
||||||
$MOCK apk add $pkgs
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
init__userdata_runcmd() {
|
|
||||||
local runcmds="$(get_userdata runcmd)"
|
|
||||||
for i in $runcmds; do
|
|
||||||
local cmd="$(get_userdata runcmd/$i)"
|
|
||||||
sh -c "$cmd"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
init__userdata_groups() {
|
|
||||||
local groups="$(get_userdata groups)"
|
|
||||||
for i in $groups; do
|
|
||||||
local group="$(get_userdata groups/$i)"
|
|
||||||
$MOCK addgroup $group
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
in_list() {
|
|
||||||
local i needle="$1"
|
|
||||||
shift
|
|
||||||
for i in "$@"; do
|
|
||||||
if [ "$i" = "$needle" ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
init__userdata_users() {
|
|
||||||
local users="$(get_userdata users)"
|
|
||||||
for i in $users; do
|
|
||||||
local name gecos homedir shell primary_group groups
|
|
||||||
local system=false no_create_home=false lock_passwd=true
|
|
||||||
local keys="$(get_userdata users/$i)"
|
|
||||||
if [ "$i" = 1 ] && [ "$keys" = "default" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
if in_list name $keys; then
|
|
||||||
name="$(get_userdata users/$i/name)"
|
|
||||||
else
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
if in_list gecos $keys; then
|
|
||||||
gecos="$(get_userdata users/$i/gecos)"
|
|
||||||
fi
|
|
||||||
if in_list homedir $keys; then
|
|
||||||
homedir="$(get_userdata users/$i/homedir)"
|
|
||||||
fi
|
|
||||||
if in_list shell $keys; then
|
|
||||||
shell="$(get_userdata users/$i/shell)"
|
|
||||||
fi
|
|
||||||
if in_list primary_group $keys; then
|
|
||||||
primary_group="$(get_userdata users/$i/primary_group)"
|
|
||||||
fi
|
|
||||||
if in_list system $keys; then
|
|
||||||
system="$(get_userdata users/$i/system)"
|
|
||||||
fi
|
|
||||||
if in_list no_create_home $keys; then
|
|
||||||
no_create_home="$(get_userdata users/$i/no_create_home)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if getent passwd "$user" >/dev/null; then
|
|
||||||
log -i -t "$phase" info "$ACTION: user $user already exists"
|
|
||||||
else
|
|
||||||
if [ "$system" != "true" ]; then
|
|
||||||
unset system
|
|
||||||
fi
|
|
||||||
if [ "$no_create_home" != "true" ]; then
|
|
||||||
unset no_create_home
|
|
||||||
fi
|
|
||||||
$MOCK adduser -D ${gecos:+-g "$gecos"} ${homedir:+-h "$homedir"} ${shell:+-s "$shell"} ${primary_group:+-G "$primary_group"} ${system:+-S} ${no_create_home:+-H} "$name"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if in_list lock_passwd $keys; then
|
|
||||||
lock_passwd="$(get_userdata users/$i/lock_passwd)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$lock_passwd" != "false" ]; then
|
|
||||||
echo "$name:*" | $MOCK chpasswd -e
|
|
||||||
fi
|
|
||||||
|
|
||||||
if in_list ssh_authorized_keys $keys; then
|
|
||||||
set_ssh_authorized_keys_for "$name" users/$i/ssh_authorized_keys
|
|
||||||
fi
|
|
||||||
|
|
||||||
if in_list groups $keys; then
|
|
||||||
groups="$(get_userdata users/$i/groups | tr ',' ' ')"
|
|
||||||
local group
|
|
||||||
for group in $groups; do
|
|
||||||
$MOCK addgroup "$name" "$group"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
if in_list doas $keys; then
|
|
||||||
if [ -d "$ROOT/etc/doas.d" ]; then
|
|
||||||
touch "$ROOT/etc/doas.d/$name.conf"
|
|
||||||
chmod 660 "$ROOT/etc/doas.d/$name.conf"
|
|
||||||
fi
|
|
||||||
local j
|
|
||||||
for j in $(get_userdata users/$i/doas); do
|
|
||||||
local line="$(get_userdata users/$i/doas/$j)"
|
|
||||||
if [ -d "$ROOT/etc/doas.d" ]; then
|
|
||||||
echo "$line" >> "$ROOT/etc/doas.d/$name.conf"
|
|
||||||
elif [ -f "$ROOT/etc/doas.conf" ]; then
|
|
||||||
add_once "$ROOT/etc/doas.conf" "$line"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
find_biggest_empty_disk() {
|
find_biggest_empty_disk() {
|
||||||
local d
|
local d p
|
||||||
for d in "$ROOT"/sys/class/block/*/device; do
|
for d in "$ROOT"/sys/class/block/*/device; do
|
||||||
p=${d%/device}
|
p=${d%/device}
|
||||||
if [ -e "$p"/size ] && [ -z "$(blkid /dev/${p##*/})" ]; then
|
if [ -e "$p"/size ] && [ -z "$(blkid /dev/${p##*/})" ]; then
|
||||||
|
|||||||
@ -1,5 +1,309 @@
|
|||||||
# CloudConfig UserData Functions
|
# #cloud-config UserData Functions
|
||||||
# vim:set filetype=sh:
|
# vim:set filetype=sh:
|
||||||
# shellcheck shell=sh
|
# shellcheck shell=sh
|
||||||
|
|
||||||
# TODO
|
# NOTE: This is only a subset of what cloud-init supports!
|
||||||
|
|
||||||
|
INIT_ACTIONS_MAIN="$(insert_before create_default_user userdata_user $INIT_ACTIONS_MAIN)"
|
||||||
|
INIT_ACTIONS_MAIN="$(insert_after set_hostname \
|
||||||
|
"userdata_bootcmd userdata_groups userdata_users userdata_write_files userdata_ntp userdata_package_update userdata_package_upgrade userdata_packages" \
|
||||||
|
$INIT_ACTIONS_MAIN)"
|
||||||
|
INIT_ACTIONS_MAIN="$(insert_after set_ssh_keys ssh_authorized_keys $INIT_ACTIONS_MAIN)"
|
||||||
|
INIT_ACTIONS_FINAL="$INIT_ACTIONS_FINAL userdata_runcmd"
|
||||||
|
|
||||||
|
get_userdata() {
|
||||||
|
IFS="/"
|
||||||
|
yx -f "$TINY_CLOUD_VAR/user-data" $1 2>/dev/null
|
||||||
|
unset IFS
|
||||||
|
}
|
||||||
|
|
||||||
|
init__userdata_user() {
|
||||||
|
local name="$(get_userdata user/name)"
|
||||||
|
if [ -z "$name" ]; then
|
||||||
|
name="$(get_userdata user)"
|
||||||
|
if [ -n "$(get_userdata user/$name)" ]; then
|
||||||
|
log -s err "user/name is required"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if get_userdata | grep -q -x users; then
|
||||||
|
local default_user="$(get_userdata users/1)"
|
||||||
|
if [ "$default_user" != "default" ]; then
|
||||||
|
CLOUD_USER="$(get_userdata users/1/name)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
CLOUD_USER="${name:-$CLOUD_USER}"
|
||||||
|
}
|
||||||
|
|
||||||
|
set_ssh_authorized_keys_for() {
|
||||||
|
local user="$1"
|
||||||
|
local userdata_path="$2"
|
||||||
|
local sshkeys="$(get_userdata $userdata_path)"
|
||||||
|
if [ -z "$sshkeys" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pwent="$(getent passwd "$user")"
|
||||||
|
if [ -z "$pwent" ]; then
|
||||||
|
log -i -t "$phase" err "$ACTION: failed to find user $user"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local group=$(echo "$pwent" | cut -d: -f4)
|
||||||
|
local ssh_dir="${ROOT}$(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"
|
||||||
|
$MOCK chown -R "$user:$group" "$ssh_dir"
|
||||||
|
for i in $sshkeys; do
|
||||||
|
local key="$(get_userdata $userdata_path/$i)"
|
||||||
|
if [ -n "$key" ]; then
|
||||||
|
echo "$key" >> "$keys_file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
init__ssh_authorized_keys() {
|
||||||
|
if [ -z "$CLOUD_USER" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
set_ssh_authorized_keys_for "$CLOUD_USER" ssh_authorized_keys
|
||||||
|
}
|
||||||
|
|
||||||
|
init__userdata_bootcmd() {
|
||||||
|
# run bootcmd
|
||||||
|
local bootcmds="$(get_userdata bootcmd)"
|
||||||
|
for i in $bootcmds; do
|
||||||
|
local cmd="$(get_userdata bootcmd/"$i")"
|
||||||
|
sh -c "$cmd"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# write_file <path> <mode> <owner> <encoding> <append>
|
||||||
|
write_file() {
|
||||||
|
# Defaults used are the same as for full cloud-init "spec":
|
||||||
|
# https://cloudinit.readthedocs.io/en/latest/reference/modules.html#write-files
|
||||||
|
local path="$1"
|
||||||
|
local mode="${2:-0644}"
|
||||||
|
local owner="${3:-root:root}"
|
||||||
|
local encoding="${4:-text/plain}"
|
||||||
|
local append="${5:-false}"
|
||||||
|
|
||||||
|
if [ "$append" != "true" ] && [ "$append" != "false" ]; then
|
||||||
|
log err "append must be true or false"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local tmpfile="$(mktemp $TINY_CLOUD_VAR/user-data.write_files.XXXXXX)"
|
||||||
|
|
||||||
|
case "$encoding" in
|
||||||
|
gzip|gz|gz+base64|gzip+base64|gz+b64|gzip+b64)
|
||||||
|
base64 -d | gzip -d > "$tmpfile"
|
||||||
|
;;
|
||||||
|
base64|b64)
|
||||||
|
base64 -d > "$tmpfile"
|
||||||
|
;;
|
||||||
|
text/plain)
|
||||||
|
cat > "$tmpfile"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$append" = "true" ]; then
|
||||||
|
cat "$tmpfile" >> "$path"
|
||||||
|
else
|
||||||
|
cat "$tmpfile" > "$path"
|
||||||
|
fi
|
||||||
|
rm -f "$tmpfile"
|
||||||
|
|
||||||
|
chmod "$mode" "$path"
|
||||||
|
# mocked as we do not know which users we could use in testing
|
||||||
|
# this way we can check the proper invocation at least
|
||||||
|
$MOCK chown "$owner" "$path"
|
||||||
|
}
|
||||||
|
|
||||||
|
init__userdata_write_files() {
|
||||||
|
local files="$(get_userdata write_files)"
|
||||||
|
|
||||||
|
for i in $files; do
|
||||||
|
local path="$(get_userdata write_files/$i/path)"
|
||||||
|
if [ -z "$path" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$ROOT/$path")"
|
||||||
|
get_userdata write_files/$i/content | write_file "$ROOT/$path" \
|
||||||
|
"$(get_userdata write_files/$i/permissions)" \
|
||||||
|
"$(get_userdata write_files/$i/owner)" \
|
||||||
|
"$(get_userdata write_files/$i/encoding)" \
|
||||||
|
"$(get_userdata write_files/$i/append)"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
init__userdata_ntp() {
|
||||||
|
local ntp_enabled="$(get_userdata ntp/enabled)"
|
||||||
|
if [ "$ntp_enabled" != "yes" ] && [ "$ntp_enabled" != "true" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
local ntp_client="$(get_userdata 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_package_update() {
|
||||||
|
local update="$(get_userdata package_update)"
|
||||||
|
if [ "$update" = "true" ]; then
|
||||||
|
$MOCK apk update
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
init__userdata_package_upgrade() {
|
||||||
|
local upgrade="$(get_userdata package_upgrade)"
|
||||||
|
if [ "$upgrade" = "true" ]; then
|
||||||
|
$MOCK apk upgrade
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
init__userdata_packages() {
|
||||||
|
local packages="$(get_userdata packages)"
|
||||||
|
local pkgs=
|
||||||
|
for i in $packages; do
|
||||||
|
pkgs="$pkgs $(get_userdata packages/$i)"
|
||||||
|
done
|
||||||
|
if [ -n "$pkgs" ]; then
|
||||||
|
$MOCK apk add $pkgs
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
init__userdata_runcmd() {
|
||||||
|
local runcmds="$(get_userdata runcmd)"
|
||||||
|
for i in $runcmds; do
|
||||||
|
local cmd="$(get_userdata runcmd/$i)"
|
||||||
|
sh -c "$cmd"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
init__userdata_groups() {
|
||||||
|
local groups="$(get_userdata groups)"
|
||||||
|
for i in $groups; do
|
||||||
|
local group="$(get_userdata groups/$i)"
|
||||||
|
$MOCK addgroup $group
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
in_list() {
|
||||||
|
local i needle="$1"
|
||||||
|
shift
|
||||||
|
for i in "$@"; do
|
||||||
|
if [ "$i" = "$needle" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
init__userdata_users() {
|
||||||
|
local users="$(get_userdata users)"
|
||||||
|
for i in $users; do
|
||||||
|
local name gecos homedir shell primary_group groups
|
||||||
|
local system=false no_create_home=false lock_passwd=true
|
||||||
|
local keys="$(get_userdata users/$i)"
|
||||||
|
if [ "$i" = 1 ] && [ "$keys" = "default" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if in_list name $keys; then
|
||||||
|
name="$(get_userdata users/$i/name)"
|
||||||
|
else
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if in_list gecos $keys; then
|
||||||
|
gecos="$(get_userdata users/$i/gecos)"
|
||||||
|
fi
|
||||||
|
if in_list homedir $keys; then
|
||||||
|
homedir="$(get_userdata users/$i/homedir)"
|
||||||
|
fi
|
||||||
|
if in_list shell $keys; then
|
||||||
|
shell="$(get_userdata users/$i/shell)"
|
||||||
|
fi
|
||||||
|
if in_list primary_group $keys; then
|
||||||
|
primary_group="$(get_userdata users/$i/primary_group)"
|
||||||
|
fi
|
||||||
|
if in_list system $keys; then
|
||||||
|
system="$(get_userdata users/$i/system)"
|
||||||
|
fi
|
||||||
|
if in_list no_create_home $keys; then
|
||||||
|
no_create_home="$(get_userdata users/$i/no_create_home)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if getent passwd "$user" >/dev/null; then
|
||||||
|
log -i -t "$phase" info "$ACTION: user $user already exists"
|
||||||
|
else
|
||||||
|
if [ "$system" != "true" ]; then
|
||||||
|
unset system
|
||||||
|
fi
|
||||||
|
if [ "$no_create_home" != "true" ]; then
|
||||||
|
unset no_create_home
|
||||||
|
fi
|
||||||
|
$MOCK adduser -D ${gecos:+-g "$gecos"} ${homedir:+-h "$homedir"} ${shell:+-s "$shell"} ${primary_group:+-G "$primary_group"} ${system:+-S} ${no_create_home:+-H} "$name"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if in_list lock_passwd $keys; then
|
||||||
|
lock_passwd="$(get_userdata users/$i/lock_passwd)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$lock_passwd" != "false" ]; then
|
||||||
|
echo "$name:*" | $MOCK chpasswd -e
|
||||||
|
fi
|
||||||
|
|
||||||
|
if in_list ssh_authorized_keys $keys; then
|
||||||
|
set_ssh_authorized_keys_for "$name" users/$i/ssh_authorized_keys
|
||||||
|
fi
|
||||||
|
|
||||||
|
if in_list groups $keys; then
|
||||||
|
groups="$(get_userdata users/$i/groups | tr ',' ' ')"
|
||||||
|
local group
|
||||||
|
for group in $groups; do
|
||||||
|
$MOCK addgroup "$name" "$group"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if in_list doas $keys; then
|
||||||
|
if [ -d "$ROOT/etc/doas.d" ]; then
|
||||||
|
touch "$ROOT/etc/doas.d/$name.conf"
|
||||||
|
chmod 660 "$ROOT/etc/doas.d/$name.conf"
|
||||||
|
fi
|
||||||
|
local j
|
||||||
|
for j in $(get_userdata users/$i/doas); do
|
||||||
|
local line="$(get_userdata users/$i/doas/$j)"
|
||||||
|
if [ -d "$ROOT/etc/doas.d" ]; then
|
||||||
|
echo "$line" >> "$ROOT/etc/doas.d/$name.conf"
|
||||||
|
elif [ -f "$ROOT/etc/doas.conf" ]; then
|
||||||
|
add_once "$ROOT/etc/doas.conf" "$line"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user