arkdep/arkdep
2024-02-21 02:06:45 +01:00

686 lines
25 KiB
Bash
Executable file

#!/usr/bin/env bash
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
set -o pipefail
# Print manual if no parameters provided or invalid amount of parameters is provided
if [[ ! -n $1 ]]; then
cat <<- END
Usage: arkdep <action> [variant] [version]
Actions:
deploy Deploy a new or update an existing deployment
init Initialize arkdep on a new system
teardown Remove all arkdep-deploy related files and folders
healthcheck Check for and report any config issues or untracked deployments
get-available List available packages in repo
remove Remove a specified deployment
Examples:
arkdep deploy
Check for updates, if available download and deploy
arkdep deploy test-arkanelinux-kde
Download and deploy the latest release of the defined image variant
arkdep deploy - stable_v1
Download the defined image release from the default variant
arkdep deploy test-arkanelinux-kde stable_v1
Download the defined image release from the specified variant
arkdep deploy cache stable_v1
Deploy an image from the cache directly skipping database download and checksum check
arkdep remove stable_v1
Remove the defined deployment
END
exit 0
fi
## Set common variables
#
declare -r arkdep_dir='/arkdep/'
if [[ ! -d $arkdep_dir ]] && [[ ! $1 == 'init' ]]; then
printf "\e[1;31m<#>\e[0m\e[1m Arkep does not seem to be managing this system for $arkdep_dir was not found\e[0m\n"
exit 1
fi
## Load config file
#
source $(readlink -m $arkdep_dir/config)
# Set default variables if config variables are undefined
[[ -z ${enable_overlay+x} ]] && enable_overlay=1 && printf '\e[1;33m<!>\e[0m\e[1m enable_overlay not defined in config, using default\e[0m\n'
[[ -z ${repo_url+x} ]] && repo_url='https://arkanelinux.org/arkdep' && printf '\e[1;33m<!>\e[0m\e[1m repo_url not defined in config, using default\e[0m\n'
[[ -z ${repo_default_image+x} ]] && repo_default_image='arkanelinux' && printf '\e[1;33m<!>\e[0m\e[1m repo_default_image not defined in config, using default\e[0m\n'
[[ -z ${deploy_keep+x} ]] && deploy_keep=3 && printf '\e[1;33m<!>\e[0m\e[1m deploy_config not defined in config, using default\e[0m\n'
[[ -z ${clean_cache_on_remove+x} ]] && clean_cache_on_remove=1 && printf '\e[1;33m<!>\e[0m\e[1m clean_cache_on_remove not defined in config, using default\e[0m\n'
[[ -z ${always_healthcheck+x} ]] && always_healthcheck=1 && printf '\e[1;33m<!>\e[0m\e[1m always_healthcheck not defined in config, using default\e[0m\n'
[[ -z ${gpg_signature_check+x} ]] && gpg_signature_check=1 && printf '\e[1;33m<!>\e[0m\e[1m gpg_signature_check not defined in config, using default\e[0m\n'
[[ -z ${minimum_available_boot_storage+x} ]] && minimum_available_boot_storage=153600 && printf '\e[1;33m<!>\e[0m\e[1m minimum_available_boot_storage not defined in config, using default\e[0m\n'
[[ -z ${minimum_available_root_storage+x} ]] && minimum_available_root_storage=12582912 && printf '\e[1;33m<!>\e[0m\e[1m minimum_available_root_storage not defined in config, using default\e[0m\n'
## Common functions
#
# Cleanup and quit if error
cleanup_and_quit () {
# If any paramters are passed we will assume it to be an error message
[[ -n $1 ]] && printf "\e[1;31m<#>\e[0m $*\e[0m\n" >&2
# Ensure we do not try to remove our current deployment
if grep "$(readlink -m $arkdep_dir/deployments/$deployment)" /proc/cmdline > /dev/null; then
printf '\e[1;33m<!>\e[0m\e[1m Cleanup target is current active deployment, skipping\e[0m\n'
exit 1
fi
# Remove the subvolume we were working on
# TODO: Make this a generic function and share with the removal of old images?
if [[ -n ${data[0]} ]]; then
btrfs property set -f -ts $(readlink -m $arkdep_dir/deployments/${data[0]}/rootfs) ro false
rm -rf $arkdep_dir/deployments/${data[0]}
rm $arkdep_dir/cache/${data[0]}-*.img
rm -rf /boot/arkdep/${data[0]}
rm /boot/loader/entries/${data[0]}.conf
fi
exit 1
}
## Healthcheck
#
# Check for and report on any issues such as untracked deployments or hanging files in cache
healthcheck () {
# Gather tracked deployments
declare -r tracker=($(cat $arkdep_dir/tracker))
declare -r deployed=($(ls $arkdep_dir/deployments/))
declare untracked=${deployed[@]}
# Check for hanging cache files
declare -r cached=($(ls $arkdep_dir/cache/))
declare hanging_cache=()
# Generate grep regex for cache check
declare cache_regex=$(printf "|%s" "${tracker[@]}")
cache_regex=${cache_regex:1}
# Compare items in tracker to actual deployed
for tracked in ${tracker[@]}; do
untracked=("${untracked[@]/$tracked}")
done
for cached_item in ${cached[@]}; do
hanging_cache+=($(echo $cached_item | grep -v -E "$cache_regex"))
done
# Clean whitespaces
untracked=$(echo $untracked | xargs)
if [[ ! -z $untracked ]]; then
printf '\e[1;33m<!>\e[0m\e[1m The following deployments were found but are untracked\n\e[0m'
for t in ${untracked[@]}; do
printf "$t\n"
done
fi
if [[ ! -z $hanging_cache ]]; then
printf '\e[1;33m<!>\e[0m\e[1m The following hanging images were found in cache\n\e[0m'
for t in ${hanging_cache[@]}; do
printf "$t\n"
done
fi
# Warn if gpg check is enabled but no keys are installed
if [[ ! $gpg_signature_check -eq 0 ]] && [[ ! -s $arkdep_dir/keys/trusted-keys ]]; then
printf "\e[1;33m<!>\e[0m\e[1m gpg_signature_check is enabled but $(readlink -m $arkdep_dir/keys/trusted-keys) does not exist or is empty\n\e[0m"
fi
# If $1 is healthcheck it was manually called by the user
[[ $1 == 'healthcheck' ]] && exit 0
}
# Always healthcheck on run if requested in config, unless the user explicitely called it
[[ $always_healthcheck -eq 1 ]] && [[ ! $1 == 'healthcheck' ]] && healthcheck
## Error checking
#
# Quit if not root, only run if required
if [[ ! $1 =~ ^(get-available|healthcheck) ]]; then
if [[ ! $EUID -eq 0 ]]; then
printf '\e[1;31m<#>\e[0m\e[1m This program has to be run as root\n\e[0m' &&
exit 1
fi
fi
# Check if all dependencies are installed, quit if not
for prog in btrfs wget dracut bootctl curl gpg gpgv; do
if ! command -v $prog > /dev/null; then
printf "\e[1;31m<#>\e[0m\e[1m Failed to locate $prog, ensure it is installed\e[0m\n"
# Do not immediately exit to log all missing programs
err=1
fi
[[ $err ]] && exit 1
done
# Ensure minimum required storage is available, only run if new deployment will be made
if [[ $1 == 'deploy' ]]; then
declare boot_storage_available=($(df --output=avail /boot))
boot_storage_available=${boot_storage_available[1]}
declare root_storage_available=($(df --output=avail /))
root_storage_available=${root_storage_available[1]}
# Check amount of available boot storage, do not run if set to 0
if [[ $boot_storage_available -lt $minimum_available_boot_storage ]] && [[ $minimum_available_boot_storage -ne 0 ]]; then
printf "\e[1;31m<#>\e[0m\e[1m Less than ${minimum_available_boot_storage}Kib available on boot partition\e[0m\n"
exit 1
fi
# Check amount of available root storage, do not run if set to 0
if [[ $root_storage_available -lt $minimum_available_root_storage ]] && [[ $minimum_available_root_storage -ne 0 ]] ; then
printf "\e[1;31m<#>\e[0m\e[1m Less than ${minimum_available_root_storage}Kib available on root partition\e[0m\n"
exit 1
fi
fi
## Core functions
#
# Initialize the system for arkdep
init () {
# Ensure systemd-boot is installed before continuing, for it is the only thing we support
bootctl is-installed || cleanup_and_quit 'systemd-boot seems to not be installed'
printf '\e[1;34m-->\e[0m\e[1m Initializing arkdep\e[0m\n'
[[ -d $arkdep_dir ]] && cleanup_and_quit "$arkdep_dir already exists"
# Create the /arkdep subvolume
printf "\e[1;34m-->\e[0m\e[1m Creating $(readlink -m $arkdep_dir) subvolume\e[0m\n"
btrfs subvolume create $arkdep_dir || cleanup_and_quit "Failed to create btrfs subvolume"
# Create directory structure
printf '\e[1;34m-->\e[0m\e[1m Creating directory structure\e[0m\n'
mkdir -pv $(readlink -m $arkdep_dir/deployments) \
$(readlink -m $arkdep_dir/deployments) \
$(readlink -m $arkdep_dir/cache) \
$(readlink -m $arkdep_dir/templates) \
$(readlink -m $arkdep_dir/overlay) \
$(readlink -m $arkdep_dir/keys) \
$(readlink -m $arkdep_dir/shared) ||
cleanup_and_quit "Failed to create $arkdep_dir and related directories"
# Create empty database files
touch $(readlink -m $arkdep_dir/tracker)
touch $(readlink -m $arkdep_dir/keys/trusted-keys)
# Add home shared subvolume and make writable
btrfs subvolume create $(readlink -m $arkdep_dir/shared/home) || cleanup_and_quit "Failed to create home subvolume"
btrfs property set -f -ts $(readlink -m $arkdep_dir/shared/home) ro false
# Write default config file
printf '\e[1;34m-->\e[0m\e[1m Adding default config file\e[0m\n'
cat <<- END > $arkdep_dir/config
# Write /arkdep/overlay overlay to new deployments
enable_overlay=1
# URL to image repository, do not add trailing slash
repo_url='https://repo.arkanelinux.org/arkdep'
# Default image pulled from repo if nothing defined
repo_default_image='arkanelinux'
# Keep the latest n-1 deployments, remove anything older
deploy_keep=4
# Remove images from the cache when their deployments are removed
clean_cache_on_remove=1
# Check for untracked deployments and other issues on run
always_healthcheck=1
# Perform a GPG signature check on remote sources
# 1 = enabled but optional, 2 = required
gpg_signature_check=1
# Minimum amount of storage which needs to be available on /boot in Kib
minimum_available_boot_storage=153600
# Minimum amount of storage which needs to be available on / in Kib
minimum_available_root_storage=12582912
END
# Add default bootloader config file
cat <<- END > $arkdep_dir/templates/systemd-boot
title Arkane GNU/Linux - Arkdep
linux /arkdep/%target%/vmlinuz
initrd /amd-ucode.img
initrd /intel-ucode.img
initrd /arkdep/%target%/initramfs-linux.img
options root="LABEL=arkane_root" rootflags=subvol=/arkdep/deployments/%target%/rootfs rw
END
exit 0
}
teardown () {
cat <<- END
WARNING: Removing arkdep may leave your system in an unbootable state and you
may have to manually reconfigure your bootloader etc.. Only proceed if you know
what you are doing!
The following changes will be made to your system;
- All subvolumes under $arkdep_dir will be deleted
- All systemd-boot bootloader entries containing the word "arkdep" will be removed
- Kernel and initramfs storage location /boot/arkdep will be removed
END
# Ensure user knows what they are doing
read -p 'Type "I KNOW WHAT I AM DOING" in uppercase to confirm that you know what you are doing: ' input_confirm
if [[ $input_confirm == 'I KNOW WHAT I AM DOING' ]]; then
printf '\e[1;34m-->\e[0m\e[1m Tearing down arkdep\e[0m\n'
# Quit with error if $arkdep_dir does not exist
if [[ ! -d $arkdep_dir ]]; then
printf "\e[1;31m<#>\e[0m $(readlink -m $arkdep_dir) does not exist, there is nothing to tear down"
exit 1
fi
# Remove all bootloader entries
rm -v $(grep -ril arkdep /boot/loader/entries)
# Remove kernels and initramfs deployed by Arkdep
rm -rfv /boot/arkdep
# Ensure all nested volumes in arkdep are writable and remove
for volume in $(btrfs subvolume list / | grep -oE '[^ ]+$' | grep "^$arkdep_dir" | tac); do
btrfs property set -f -ts $(readlink -m /$volume) ro false
btrfs subvolume delete $volume
done
else
printf '\e[1;33m<!>\e[0m\e[1m Teardown canceled, no changes made to system\e[0m\n'
fi
exit 0
}
remove_deployment () {
# Ensure required vars are set
[[ -z $1 ]] && \
printf 'No deployment defined\n' && exit 1
# Ensure deployment is not currently active
if grep "$(readlink -m $arkdep_dir/deployments/$1)" /proc/cmdline > /dev/null; then
printf 'Target is currently active deployment\n'
exit 1
fi
# Ensure user only provided a single target
[[ ! -z $2 ]] && \
printf 'Multiple targets provided, remove only accepts a single target at a time\n' && exit 1
# Ensure requested deployment is tracked
declare -r hits=($(grep $1 $arkdep_dir/tracker))
if [[ ${#hits[@]} -gt 1 ]]; then
printf 'Multiple deployments match target, be more specific\n'
exit 1
elif [[ ${#hits[@]} -lt 1 ]]; then
printf 'No deployments match target\n'
exit 1
fi
declare -r target="${hits[0]}"
# Remove bootloader entry
rm -rfv /boot/loader/entries/$target.conf
rm -rfv /boot/arkdep/$target
# Ensure the deployment and all sub-volumes are writable
for volume in $(btrfs subvolume list / | grep -oE '[^ ]+$' | grep $target); do
btrfs property set -f -ts $(readlink -m /$volume) ro false || printf "failed to make subvol $volume writable\n"
done
# Remove the deployment
rm -rf $(readlink -m $arkdep_dir/deployments/$target)
# Remove from tracker
grep -v $1 $arkdep_dir/tracker > $arkdep_dir/tracker_tmp || cleanup_and_quit 'Failed to update tracker file'
mv $arkdep_dir/tracker_tmp $arkdep_dir/tracker || cleanup_and_quit 'Failed to move tracker_tmp file to tracker'
# Remove images from cache if requested
if [[ $clean_cache_on_remove -eq 1 ]]; then
# Only attempt remove if file exists
if ls $arkdep_dir/cache/ | grep $1; then
rm -v $(readlink -m $arkdep_dir/cache/$1.tar.*)
fi
fi
exit 0
}
# List all available packages defined in the repo's list file
get_available () {
printf "\e[1;34m-->\e[0m\e[1m Downloading list file from $repo_url\e[0m\n"
curl -sf "${repo_url}/list" || printf '\e[1;33m<!>\e[0m\e[1m Failed to download list file\e[0m\n'
exit 0
}
# Deploy a new or update an existing deployment
deploy () {
# target and version are optional, if not defined default to primary as defined in
# /arkdep/config and latest
if [[ -n $1 ]] && [[ $1 != '-' ]]; then
declare -r deploy_target=$1
else
declare -r deploy_target=$repo_default_image
fi
if [[ -n $2 ]]; then
declare -r deploy_version=$2
else
declare -r deploy_version='latest'
fi
# If cache requested version may not be latest
if [[ $1 == 'cache' ]] && [[ $deploy_version == 'latest' ]]; then
cleanup_and_quit '"latest" and undefined are not a valid version definitions for a cache source'
fi
printf "\e[1;34m-->\e[0m\e[1m Deploying $deploy_target $deploy_version\e[0m\n"
# Split latest_version at the delimiter, creating an array with data.0=package ver, data.1=compression method, data.2=sha1 hash
# only run if request target is not cache
if [[ $1 != 'cache' ]]; then
# If latest is requested grab database and get first line
printf '\e[1;34m-->\e[0m\e[1m Downloading database\e[0m\n'
if [[ $deploy_version == 'latest' ]]; then
declare curl_data=$(curl -sf "${repo_url}/${deploy_target}/database" | head -n 1)
elif [[ $deploy_target != 'cache' ]]; then
# Only return first hit
declare curl_data=$(curl -sf "${repo_url}/${deploy_target}/database" | grep -E "^$2" | head -1)
else
declare curl_data='cache'
fi
readarray -d : -t data <<< "$curl_data"
# If target is cache
else
# Find full name in cache, exclude sig files, if no hit quit with error
declare cache_hits=($(ls $arkdep_dir/cache | grep -E "^$deploy_version" | grep -v '.sig$'))
# Temporary var to store the delimited file found in cache
declare data_inter=()
# Check if none or more than a single hit, we only expect a single item to match
[[ ${#cache_hits[@]} -gt 1 ]] && cleanup_and_quit 'More than a single item in cache matched requested version'
[[ ${#cache_hits[@]} -lt 1 ]] && cleanup_and_quit 'No item in cache matched requested version'
# Split filename at delimiter
readarray -d . -t data_inter <<< "$cache_hits"
# Set expected vars for remainder of script
data[0]=${data_inter[0]}
data[1]=${data_inter[2]}
data[2]='-'
fi
# Ensure none of the vars contain whitespaces
data[0]=${data[0]//[$'\t\r\n']}
data[1]=${data[1]//[$'\t\r\n']}
data[2]=${data[2]//[$'\t\r\n']}
# Lets do a bunch of checks to ensure the data is all present
if [[ -z ${data[0]+x} ]] || [[ ! -n ${data[0]} ]]; then
printf '\e[1;31m<#>\e[0m\e[1m No target found\n\e[0m'
exit 1
fi
if [[ -z ${data[1]+x} ]] || [[ ! -n ${data[1]} ]]; then
printf '\e[1;31m<#>\e[0m\e[1m No compression method found\n\e[0m'
exit 1
fi
if [[ -z ${data[2]+x} ]] || [[ ! -n ${data[2]} ]]; then
# Do not trigger if hash is -, is used for cache deployments
if [[ $deploy_target != '-' ]]; then
printf '\e[1;31m<#>\e[0m\e[1m No checksum found\n\e[0m'
exit 1
fi
fi
# Lets ensure the requested image is not already deployed
if [[ -e $arkdep_dir/deployments/${data[0]} ]]; then
printf "\e[1;33m<!>\e[0m\e[1m ${data[0]} is already deployed, canceling deployment\e[0m\n"
exit 1
fi
# Check if requested version is already downloaded
if [[ -e $arkdep_dir/cache/${data[0]}.tar.${data[1]} ]]; then
printf "\e[1;34m-->\e[0m\e[1m ${data[0]} already in cache, skipping download\e[0m\n"
else
printf "\e[1;34m-->\e[0m\e[1m Downloading disk image\e[0m\n"
# Download the tarball if not yet downloaded
if [[ ! -e $arkdep_dir/cache/${data[0]}.tar.${data[1]} ]]; then
wget -q --show-progress -P $(readlink -m $arkdep_dir/cache/) "$repo_url/$deploy_target/${data[0]}.tar.${data[1]}" ||
cleanup_and_quit 'Failed to download tarball'
# Download GPG signature, only perform check if not disabled by user and keychain exists
if [[ ! $gpg_signature_check -eq 0 ]] && [[ -s $arkdep_dir/keys/trusted-keys ]]; then
# Download gpg signature if not yet in cache
if [[ ! -s $arkdep_dir/cache/${data[0]}.tar.${data[1]}.sig ]]; then
wget -q --show-progress -P $(readlink -m $arkdep_dir/cache/) "$repo_url/$deploy_target/${data[0]}.tar.${data[1]}.sig"
sig_exitcode=$?
fi
if [[ ! $sig_exitcode -eq 0 ]] && [[ $gpg_signature_check -eq 1 ]]; then
# Sig download is allowed to fail
printf "\e[1;33m<!>\e[0m\e[1m Failed to download GPG signature, signature check will be skipped\e[0m\n"
elif [[ ! $sig_exitcode -eq 0 ]] && [[ $gpg_signature_check -eq 2 ]]; then
# gpg_signature_check = 2, error and quit the program on fail
cleanup_and_quit 'GPG signature check configured to quit on download failure'
fi
fi
fi
fi
if [[ $gpg_signature_check -eq 2 ]] && [[ ! -s $arkdep_dir/cache/${data[0]}.tar.${data[1]}.sig ]]; then
# if GPG check required but file not present error and quit
cleanup_and_quit 'GPG signature expected but none were provided'
elif [[ ! -s $arkdep_dir/cache/${data[0]}.tar.${data[1]}.sig ]]; then
skip_gpg_check=1
fi
# If not configured to skip by previous error handeling check the signature to the downloaded image
if [[ ! $skip_gpg_check -eq 1 ]]; then
printf '\e[1;34m-->\e[0m\e[1m Checking GPG signature\e[0m\n'
# Perform GPG signature check
gpgv --keyring $arkdep_dir/keys/trusted-keys $arkdep_dir/cache/${data[0]}.tar.${data[1]}.sig $arkdep_dir/cache/${data[0]}.tar.${data[1]} ||
cleanup_and_quit 'gpg check failed'
elif [[ ${data[2]} != '-' ]]; then
# If GPG check not triggered instead check hash, unless defined as -
printf '\e[1;34m-->\e[0m\e[1m Validating integrity\e[0m\n'
sha1sum "$(readlink -m $arkdep_dir/cache/${data[0]}.tar.${data[1]})" |
grep "${data[2]}" ||
cleanup_and_quit "Checksum does not match the one defined in database\e[0m\n"
fi
# Extract the root image if not yet extracted
printf '\e[1;34m-->\e[0m\e[1m Writing root\e[0m\n'
# Create directory using unique deployment name
mkdir -pv $(readlink -m $arkdep_dir/deployments/${data[0]}) || cleanup_and_quit 'Failed to create deployment directory'
if [[ ! -e $arkdep_dir/cache/${data[0]}-rootfs.img ]]; then
tar -xf $(readlink -m $arkdep_dir/cache/${data[0]}.tar.${data[1]}) -C $(readlink -m $arkdep_dir/cache/) "./${data[0]}-rootfs.img" ||
cleanup_and_quit 'Failed to extract root'
fi
# Write the root image
btrfs receive -f $(readlink -m $arkdep_dir/cache/${data[0]}-rootfs.img) $(readlink -m $arkdep_dir/deployments/${data[0]}) ||
cleanup_and_quit 'Failed to receive root'
# Cleanup root image
rm $(readlink -m $arkdep_dir/cache/${data[0]}-rootfs.img)
# Extract the etc image if not yet extracted
printf '\e[1;34m-->\e[0m\e[1m Writing etc\e[0m\n'
if [[ ! -e $arkdep_dir/cache/${data[0]}-etc.img ]]; then
tar -xf $(readlink -m $arkdep_dir/cache/${data[0]}.tar.${data[1]}) -C $(readlink -m $arkdep_dir/cache/) "./${data[0]}-etc.img" ||
cleanup_and_quit 'failed to extract etc'
fi
# Write the etc image and create var directory, we have to unlock rootfs temporarily to do this
btrfs property set -f -ts $(readlink -m $arkdep_dir/deployments/${data[0]}/rootfs) ro false ||
cleanup_and_quit 'Failed to unlock root to write etc'
btrfs receive -f $(readlink -m $arkdep_dir/cache/${data[0]}-etc.img) $(readlink -m $arkdep_dir/deployments/${data[0]}/rootfs/) ||
cleanup_and_quit 'Failed to receive etc'
printf '\e[1;34m-->\e[0m\e[1m Ensure var, root and arkdep mountpoints exist\e[0m\n'
mkdir -pv $(readlink -m $arkdep_dir/deployments/${data[0]}/rootfs/var)
mkdir -pv $(readlink -m $arkdep_dir/deployments/${data[0]}/rootfs/arkdep)
# Lock the root volume again
btrfs property set -f -ts $(readlink -m $arkdep_dir/deployments/${data[0]}/rootfs) ro true ||
cleanup_and_quit 'Failed to lock root'
# Unlock the etc deployment
btrfs property set -f -ts $(readlink -m $arkdep_dir/deployments/${data[0]}/rootfs/etc) ro false ||
cleanup_and_quit 'Failed to unlock root to write etc'
# Cleanup etc image
rm $(readlink -m $arkdep_dir/cache/${data[0]}-etc.img)
# Write the var image
if [[ ! -e $arkdep_dir/shared/var ]]; then
printf '\e[1;34m-->\e[0m\e[1m Writing var\e[0m\n'
# Extract the var image if not yet extracted
if [[ ! -e $arkdep_dir/cache/${data[0]}-var.img ]]; then
tar -xf $(readlink -m $arkdep_dir/cache/${data[0]}.tar.${data[1]}) -C $(readlink -m $arkdep_dir/cache/) "./${data[0]}-var.img" ||
cleanup_and_quit 'failed to extract var'
fi
btrfs receive -f $(readlink -m $arkdep_dir/cache/${data[0]}-var.img) $(readlink -m $arkdep_dir/shared/) ||
cleanup_and_quit 'Failed to receive var'
else
# Notify if var is not deployed
printf "\e[1;33m<!>\e[0m\e[1m var is already preset, skipping var deployment\e[0m\n"
fi
# Make var writable
btrfs property set -f -ts $(readlink -m $arkdep_dir/shared/var) ro false ||
cleanup_and_quit 'Failed to unlock var'
# Cleanup var image
rm $(readlink -m $arkdep_dir/cache/${data[0]}-var.img)
# Add overlay if enabled
if [[ $enable_overlay -eq 1 ]]; then
printf '\e[1;34m-->\e[0m\e[1m Copying overlay to deployment\e[0m\n'
declare -r overlay_files=($(ls $arkdep_dir/overlay/))
# Check if only /etc is present, if it is we do not have to unlock the root volume
for file in ${overlay_files[*]}; do
if [[ ! $file == 'etc' ]]; then
printf "\e[1;33m<!>\e[0m\e[1m ${data[0]} Non /etc file or directory detected, root will be temporarily unlocked\e[0m\n"
overlay_unlock_root=1
fi
done
# Unlock root if required
if [[ $overlay_unlock_root -eq 1 ]]; then
btrfs property set -f -ts $(readlink -m $arkdep_dir/deployments/${data[0]}) ro false
fi
cp -rv $(readlink -m $arkdep_dir/overlay/*) $(readlink -m /$arkdep_dir/deployments/${data[0]}/rootfs/)
# Lock root again if required
if [[ $overlay_unlock_root -eq 1 ]]; then
btrfs property set -f -ts $(readlink -m $arkdep_dir/deployments/${data[0]}) ro true
fi
fi
printf '\e[1;34m-->\e[0m\e[1m Copying kernel image\e[0m\n'
# Get list of all available kernels
kernels_installed=($(ls $arkdep_dir/deployments/${data[0]}/rootfs/usr/lib/modules/))
mkdir -pv /boot/arkdep/${data[0]}
# Deploy kernel to /boot, deploy first hit of kernels_installed
cp -v $(readlink -m $arkdep_dir/deployments/${data[0]}/rootfs/usr/lib/modules/${kernels_installed[0]}/vmlinuz) /boot/arkdep/${data[0]}/ ||
cleanup_and_quit 'Failed to copy kernel image'
# Install kernel and generate initramfs
printf '\e[1;34m-->\e[0m\e[1m Generating initramfs\e[0m\n'
dracut -q -k $(readlink -m $arkdep_dir/deployments/${data[0]}/rootfs/usr/lib/modules/${kernels_installed[0]}) \
--kernel-image /boot/arkdep/${data[0]}/vmlinuz \
--kver ${kernels_installed[0]} \
--force \
/boot/arkdep/${data[0]}/initramfs-linux.img || cleanup_and_quit 'Failed to generate initramfs'
# Add to database
printf '\e[1;34m-->\e[0m\e[1m Updating database\e[0m\n'
printf "${data[0]}\n$(cat $(readlink -m $arkdep_dir/tracker))" |
tee $arkdep_dir/tracker.tmp
mv $arkdep_dir/tracker.tmp $arkdep_dir/tracker
# Deploy bootloader configuration
# also insert newline
printf '\n\e[1;34m-->\e[0m\e[1m Adding bootloader entry\e[0m\n'
sed "s/%target%/${data[0]}/" $arkdep_dir/templates/systemd-boot > /boot/loader/entries/${data[0]}.conf
# Set new deployment as default bootloader entry
printf '\e[1;34m-->\e[0m\e[1m Setting new bootloader entry as default\e[0m\n'
bootctl set-default ${data[0]}.conf || cleanup_and_quit "Failed to set default bootloader entry"
# Remove entries outside of keep
declare -r remove_deployments=($(tail -n +$deploy_keep $arkdep_dir/tracker))
# Remove old deployments
for deployment in ${remove_deployments[@]}; do
printf "\e[1;34m-->\e[0m\e[1m Cleaning up old deployment $deployment\e[0m\n"
remove_deployment $deployment
grep -v $deployment $arkdep_dir/tracker |
tee $arkdep_dir/tracker.tmp
mv $arkdep_dir/tracker.tmp $arkdep_dir/tracker
done
exit 0
}
[[ $1 == 'init' ]] && init
[[ $1 == 'teardown' ]] && teardown
[[ $1 == 'get-available' ]] && get_available
[[ $1 == 'deploy' ]] && deploy $2 $3
[[ $1 == 'remove' ]] && remove_deployment $2 $3
[[ $1 == 'healthcheck' ]] && healthcheck $1
# No valid params were provided
exit 3