arkdep/arkanium-deploy

390 lines
14 KiB
Text
Raw Normal View History

2023-07-05 01:44:37 +02:00
#!/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.
2023-08-15 09:54:08 +02:00
set -o pipefail
2023-07-05 01:44:37 +02:00
# Print manual if no parameters provided or invalid amount of parameters is provided
2023-08-15 09:54:08 +02:00
if [[ ! -n $1 ]]; then
2023-07-05 01:44:37 +02:00
cat <<- END
2023-08-09 08:09:20 +02:00
Usage: arkanium-deploy <action> [target]
2023-07-05 01:44:37 +02:00
Actions:
2023-07-22 18:15:44 +02:00
update Check for updates, optionally provide a target, if no target provided it defaults to primary
deploy Deploy a new or update an existing deployment
2023-08-09 08:09:20 +02:00
init Initialize Arkanium on a new system
teardown Remove all Arkanium-deploy related files and folders
2023-07-22 18:15:44 +02:00
get-available List available packages in repo
2023-07-05 01:44:37 +02:00
END
exit 0
fi
## Set common variables
#
2023-08-09 08:09:20 +02:00
declare -r arkanium_dir='/arkanium/'
2023-07-05 01:44:37 +02:00
2023-09-11 19:12:40 +02:00
## Load config file
#
source $arkanium_dir/config
2023-07-05 01:44:37 +02:00
## Common functions
#
# Cleanup and quit if error
cleanup_and_quit () {
# If any paramters are passed we will assume it to be an error
[[ -n $1 ]] && printf "\e[1;31m<#>\e[0m $*\e[0m\n" >&2
2023-09-14 19:10:20 +02:00
# 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 $arkanium_dir/deployments/${data[0]}/rootfs ro false
rm -rf $arkanium_dir/deployments/${data[0]}
rm -rf /boot/arkanium/${data[0]}
rm /boot/loader/entries/${data[0]}.conf
fi
2023-07-05 01:44:37 +02:00
# Quit program if argument provided to function
[[ -n $1 ]] && exit 1
# Otherwise just quit, there is no error
exit 0
}
## Error checking
#
# Quit if not root
[[ ! $EUID -eq 0 ]] &&
printf '\e[1;31m<#>\e[0m\e[1m This program has to be run as root\n\e[0m' &&
exit 1
# Check if all dependencies are installed, quit if not
2023-09-14 19:10:20 +02:00
for prog in btrfs wget dracut bootctl; do
2023-07-05 01:44:37 +02:00
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"
exit 1
fi
done
## Core functions
#
2023-08-09 08:09:20 +02:00
# Initialize the system for arkanium
2023-07-05 01:44:37 +02:00
init () {
2023-07-31 03:07:34 +02:00
# 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'
2023-08-09 08:09:20 +02:00
printf '\e[1;34m-->\e[0m\e[1m Initializing arkanium\e[0m\n'
2023-07-05 01:44:37 +02:00
2023-08-09 08:09:20 +02:00
[[ -d $arkanium_dir ]] && cleanup_and_quit "$arkanium_dir already exists"
2023-07-05 01:44:37 +02:00
2023-08-09 08:09:20 +02:00
# Create the /arkanium subvolume
printf "\e[1;34m-->\e[0m\e[1m Creating $(readlink -m $arkanium_dir) subvolume\e[0m\n"
btrfs subvolume create $arkanium_dir || cleanup_and_quit "Failed to create btrfs subvolume"
2023-07-05 01:44:37 +02:00
# Create directory structure
printf "\e[1;34m-->\e[0m\e[1m Creating directory structure\e[0m\n"
2023-08-09 08:09:20 +02:00
mkdir -pv $(readlink -m $arkanium_dir/deployments) \
2023-08-10 06:44:20 +02:00
$(readlink -m $arkanium_dir/deployments) \
2023-08-09 08:09:20 +02:00
$(readlink -m $arkanium_dir/cache) \
2023-08-10 06:44:20 +02:00
$(readlink -m $arkanium_dir/templates) \
$(readlink -m $arkanium_dir/overlay) \
2023-08-09 08:09:20 +02:00
$(readlink -m $arkanium_dir/shared) ||
cleanup_and_quit "Failed to create /arkanium and related directories"
2023-07-05 01:44:37 +02:00
2023-08-25 00:15:23 +02:00
# Create empty database files
touch $(readlink -m $arkanium_dir/tracker)
2023-07-31 03:07:34 +02:00
# Add home shared subvolume and make writable
2023-08-14 17:55:50 +02:00
btrfs subvolume create $(readlink -m $arkanium_dir/shared/home) || cleanup_and_quit "Failed to create home subvolume"
2023-09-16 18:31:55 +02:00
btrfs subvolume create $(readlink -m $arkanium_dir/shared/root) || cleanup_and_quit "Failed to create root subvolume"
2023-08-14 17:55:50 +02:00
btrfs property set -f -ts $(readlink -m $arkanium_dir/shared/home) ro false
2023-09-16 18:31:55 +02:00
btrfs property set -f -ts $(readlink -m $arkanium_dir/shared/root) ro false
2023-07-31 03:07:34 +02:00
2023-07-05 01:44:37 +02:00
# Write default config file
printf "\e[1;34m-->\e[0m\e[1m Adding default config file\e[0m\n"
2023-08-09 08:09:20 +02:00
cat <<- END > $arkanium_dir/config
# Write /arkanium/overlay overlay to root or etc
2023-09-16 11:53:45 +02:00
enable_overlay=1
2023-07-05 01:44:37 +02:00
# Do not install additional packages defined customize
custom_packages=0
2023-08-14 18:01:33 +02:00
# URL to image repository, do not add trailing slash
repo_url='https://repo.arkanelinux.org/arkanium'
# Default image pulled from repo if nothing defined
repo_default_image='arkanelinux'
2023-08-25 00:15:23 +02:00
# Keep the latest n+1 deployments, remove anything older
deploy_keep=2
2023-07-05 01:44:37 +02:00
END
2023-08-10 06:44:20 +02:00
# Add default bootloader config file
cat <<- END > $arkanium_dir/templates/systemd-boot
2023-08-14 20:08:38 +02:00
title Arkane GNU/Linux - arkanium
linux /arkanium/%target%/vmlinuz
2023-07-31 03:07:34 +02:00
initrd /amd-ucode.img
initrd /intel-ucode.img
initrd /arkanium/%target%/initramfs-linux.img
2023-08-27 02:07:37 +02:00
options root="LABEL=arkane_root" rootflags=subvol=/arkanium/deployments/%target%/rootfs rw
2023-07-31 03:07:34 +02:00
END
2023-07-05 01:44:37 +02:00
exit 0
2023-07-31 03:07:34 +02:00
2023-07-05 01:44:37 +02:00
}
teardown () {
cat <<- END
2023-08-09 08:09:20 +02:00
WARNING: Removing arkanium may leave your system in an unbootable state and you
2023-07-05 01:44:37 +02:00
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;
2023-08-09 08:09:20 +02:00
- All subvolumes under $arkanium_dir will be deleted
2023-07-05 01:44:37 +02:00
END
read -p 'Type "I KNOW WHAT I AM GOING" in uppercase to confirm that you know what you are doing: ' input_confirm
if [[ $input_confirm == 'I KNOW WHAT I AM DOING' ]]; then
2023-08-09 08:09:20 +02:00
printf '\e[1;34m-->\e[0m\e[1m Tearing down arkanium\e[0m\n'
2023-07-05 01:44:37 +02:00
2023-08-09 08:09:20 +02:00
# Quit with error if $arkanium_dir does not exist
if [[ ! -d $arkanium_dir ]]; then
printf "\e[1;31m<#>\e[0m $(readlink -m $arkanium_dir) does not exist, there is nothing to tear down"
2023-07-05 01:44:37 +02:00
exit 1
fi
2023-08-09 08:09:20 +02:00
# Remove all nested subvolumes in $arkanium_dir
for volume in $(btrfs subvolume list $arkanium_dir | grep -oE '[^ ]+$'); do
btrfs property set -f -ts $(readlink -m $arkanium_dir/$volume) ro false
btrfs subvolume delete $(readlink -m $arkanium_dir/$volume)
2023-07-05 01:44:37 +02:00
done
2023-08-09 08:09:20 +02:00
# Remove $arkanium_dir itself
btrfs property set -f -ts $(readlink -m $arkanium_dir) ro false
btrfs subvolume delete $(readlink -m $arkanium_dir)
2023-07-05 01:44:37 +02:00
else
printf '\e[1;34m-->\e[0m\e[1m Teardown canceled, no changes made to system\e[0m\n'
fi
exit 0
}
2023-07-22 18:15:44 +02:00
# 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" || cleanup_and_quit 'Failed to download repo file'
2023-07-05 01:44:37 +02:00
}
2023-07-22 18:15:44 +02:00
# Deploy a new or update an existing deployment
deploy () {
2023-08-14 20:08:38 +02:00
# target and version are optional, if not defined default to primary as defined in
# /arkanium/config and latest
2023-08-15 10:12:02 +02:00
if [[ -n $2 ]]; then
declare -r deploy_target=$2
else
declare -r deploy_target="$repo_default_image"
fi
if [[ -n $3 ]]; then
declare -r deploy_version=$3
else
declare -r deploy_version='latest'
fi
2023-07-22 18:15:44 +02:00
printf "\e[1;34m-->\e[0m\e[1m Deploying $deploy_target $deploy_version\e[0m\n"
2023-08-15 09:54:08 +02:00
# If latest is requested grab database and get first line
2023-09-14 19:46:34 +02:00
printf "\e[1;34m-->\e[0m\e[1m Downloading database from repo\e[0m\n"
2023-08-15 09:54:08 +02:00
if [[ $deploy_version == 'latest' ]]; then
declare curl_data=$(curl -sf "${repo_url}/${deploy_target}/database" | head -n 1)
else
declare curl_data=$(curl -sf "${repo_url}/${deploy_target}/database" | grep $3)
fi
2023-09-14 19:10:20 +02:00
# Split latest_version at the delimiter, creating an array with data.0=package ver, data.1=compression method, data.2=sha1 hash
2023-08-15 09:54:08 +02:00
readarray -d : -t data <<< "$curl_data"
# A carriage feed is inserted for some reason, lets remove it
data[2]=${data[2]//[$'\t\r\n']}
# Lets ensure the requested image is not already deployed
if [[ -e $arkanium_dir/deployments/${data[0]} ]]; then
printf "\e[1;33m<!>\e[0m\e[1m ${data[0]} is already deployed, canceling deployment\e[0m\n"
exit 0
fi
2023-07-22 18:15:44 +02:00
# Check if requested version is already downloaded
2023-08-25 00:15:23 +02:00
if [[ -e $arkanium_dir/cache/${data[0]}.tar.${data[1]} ]]; then
2023-07-22 18:15:44 +02:00
printf "\e[1;34m-->\e[0m\e[1m ${data[0]} already in cache, skipping download\e[0m\n"
else
2023-08-14 20:08:38 +02:00
# Download the tarball if not yet downloaded
if [[ ! -e $arkanium_dir/cache/${data[0]}.tar.${data[1]} ]]; then
wget -P $(readlink -m $arkanium_dir/cache/) "$repo_url/$repo_default_image/${data[0]}.tar.${data[1]}" ||
cleanup_and_quit 'Failed to download tarball'
fi
2023-07-22 18:15:44 +02:00
fi
printf "\e[1;34m-->\e[0m\e[1m Validating integrity\e[0m\n"
2023-08-14 17:55:50 +02:00
sha1sum "$(readlink -m $arkanium_dir/cache/${data[0]}.tar.${data[1]})" |
2023-07-22 18:15:44 +02:00
grep "${data[3]}" ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit "Checksum does not match repo file, got $chksum\e[0m\n"
2023-07-22 18:15:44 +02:00
2023-09-16 18:31:55 +02:00
# Extract the root image if not yet extracted
printf "\e[1;34m-->\e[0m\e[1m Writing root\e[0m\n"
2023-08-14 20:08:38 +02:00
# Create directory using unique deployment name
2023-09-14 19:10:20 +02:00
mkdir -pv $(readlink -m $arkanium_dir/deployments/${data[0]}) || cleanup_and_quit 'Failed to create deployment directory'
2023-08-14 20:08:38 +02:00
2023-08-25 00:15:23 +02:00
if [[ ! -e $arkanium_dir/cache/${data[0]}-rootfs.img ]]; then
2023-08-14 20:08:38 +02:00
tar -xf $(readlink -m $arkanium_dir/cache/${data[0]}.tar.${data[1]}) -C $(readlink -m $arkanium_dir/cache/) "./${data[0]}-rootfs.img" ||
cleanup_and_quit 'Failed to extract root'
fi
2023-07-22 18:15:44 +02:00
# Write the root image
2023-08-14 20:08:38 +02:00
btrfs receive -f $(readlink -m $arkanium_dir/cache/${data[0]}-rootfs.img) $(readlink -m $arkanium_dir/deployments/${data[0]}) ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to receive root'
# Cleanup root image
2023-08-14 20:08:38 +02:00
rm $(readlink -m $arkanium_dir/cache/${data[0]}-rootfs.img)
2023-07-31 03:07:34 +02:00
# Extract the etc image if not yet extracted
printf "\e[1;34m-->\e[0m\e[1m Writing etc\e[0m\n"
2023-08-25 00:15:23 +02:00
if [[ ! -e $arkanium_dir/cache/${data[0]}-etc.img ]]; then
2023-08-14 20:08:38 +02:00
tar -xf $(readlink -m $arkanium_dir/cache/${data[0]}.tar.${data[1]}) -C $(readlink -m $arkanium_dir/cache/) "./${data[0]}-etc.img" ||
cleanup_and_quit 'failed to extract etc'
fi
2023-07-31 03:07:34 +02:00
2023-08-14 20:08:38 +02:00
# Write the etc image and create var directory, we have to unlock rootfs temporarily to do this
btrfs property set -f -ts $(readlink -m $arkanium_dir/deployments/${data[0]}/rootfs) ro false ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to unlock root to write etc'
2023-08-14 20:08:38 +02:00
btrfs receive -f $(readlink -m $arkanium_dir/cache/${data[0]}-etc.img) $(readlink -m $arkanium_dir/deployments/${data[0]}/rootfs/) ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to receive etc'
2023-08-14 20:08:38 +02:00
2023-09-16 18:31:55 +02:00
printf "\e[1;34m-->\e[0m\e[1m Ensure var and root mountpoints exist\e[0m\n"
2023-08-14 20:08:38 +02:00
mkdir -pv $(readlink -m $arkanium_dir/deployments/${data[0]}/rootfs/var)
2023-09-16 18:31:55 +02:00
mkdir -pv $(readlink -m $arkanium_dir/deployments/${data[0]}/rootfs/root)
2023-08-14 20:08:38 +02:00
2023-09-16 18:31:55 +02:00
# Lock the root volume again
2023-08-14 20:08:38 +02:00
btrfs property set -f -ts $(readlink -m $arkanium_dir/deployments/${data[0]}/rootfs) ro true ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to lock root'
2023-08-14 20:08:38 +02:00
# Unlock the etc deployment
btrfs property set -f -ts $(readlink -m $arkanium_dir/deployments/${data[0]}/rootfs/etc) ro false ||
cleanup_and_quit 'Failed to unlock root to write etc'
2023-08-25 00:15:23 +02:00
# Cleanup etc image
rm $(readlink -m $arkanium_dir/cache/${data[0]}-etc.img)
2023-07-31 03:07:34 +02:00
# Write the var image
2023-08-25 00:15:23 +02:00
if [[ ! -e $arkanium_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 $arkanium_dir/cache/${data[0]}-var.img ]]; then
tar -xf $(readlink -m $arkanium_dir/cache/${data[0]}.tar.${data[1]}) -C $(readlink -m $arkanium_dir/cache/) "./${data[0]}-var.img" ||
cleanup_and_quit 'failed to extract var'
fi
2023-08-25 00:15:23 +02:00
btrfs receive -f $(readlink -m $arkanium_dir/cache/${data[0]}-var.img) $(readlink -m $arkanium_dir/shared/) ||
cleanup_and_quit 'Failed to receive var'
else
# Notify if var is not deployed
printf "\e[1;33m<!>\e[0m\e[1m ${data[0]} var is already preset, skipping var deployment\e[0m\n"
2023-08-25 00:15:23 +02:00
fi
2023-07-31 03:07:34 +02:00
# Make var writable
2023-08-14 17:55:50 +02:00
btrfs property set -f -ts $(readlink -m $arkanium_dir/shared/var) ro false ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to unlock var'
2023-07-22 18:15:44 +02:00
2023-08-14 20:08:38 +02:00
# Cleanup var image
rm $(readlink -m $arkanium_dir/cache/${data[0]}-var.img)
2023-09-16 11:53:45 +02:00
# 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"
overlay_files=$(ls $arkanium_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 -eq 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
2023-09-16 12:02:54 +02:00
btrfs property set -f -ts $(readlink -m $arkanium_dir/deployments/${data[0]}) ro false
2023-09-16 11:53:45 +02:00
fi
2023-09-16 12:02:54 +02:00
cp -rv $(readlink -m $arkanium_dir/overlay/*) $(readlink -m /$arkanium_dir/deployments/${data[0]}/rootfs/)
2023-09-16 11:53:45 +02:00
# Lock root again if required
if [[ $overlay_unlock_root -eq 1 ]]; then
2023-09-16 12:02:54 +02:00
btrfs property set -f -ts $(readlink -m $arkanium_dir/deployments/${data[0]}) ro true
2023-09-16 11:53:45 +02:00
fi
fi
2023-09-14 19:31:19 +02:00
printf "\e[1;34m-->\e[0m\e[1m Copying kernel image\e[0m\n"
mkdir -pv $(readlink -m /boot/arkanium/${data[0]})
cp -v $arkanium_dir/deployments/${data[0]}/rootfs/usr/lib/modules/*/vmlinuz /boot/arkanium/${data[0]}/ ||
cleanup_and_quit 'Failed to copy kernel image'
# Install kernel and generate initramfs
2023-09-14 19:10:20 +02:00
printf "\e[1;34m-->\e[0m\e[1m Generating initramfs\e[0m\n"
dracut -k $(cd /arkanium/deployments/${data[0]}/rootfs/usr/lib/modules/*; pwd) \
--kernel-image /boot/arkanium/${data[0]}/vmlinuz \
--force \
/boot/arkanium/${data[0]}/initramfs-linux.img || cleanup_and_quit 'Failed to generate initramfs'
2023-08-25 00:15:23 +02:00
# Add to database
# TODO: If this step is never reached ensure cleanup, maybe write a "busy file" somewhere
2023-08-25 00:15:23 +02:00
printf "\e[1;34m-->\e[0m\e[1m Updating database\e[0m\n"
printf "${data[0]}\n$(cat $(readlink -m $arkanium_dir/tracker | head -$deploy_keep))" > $arkanium_dir/tracker
2023-08-27 02:07:37 +02:00
# Deploy bootloader configuration
2023-09-14 19:10:20 +02:00
printf "\e[1;34m-->\e[0m\e[1m Adding bootloader entry\e[0m\n"
2023-08-27 02:07:37 +02:00
sed "s/%target%/${data[0]}/" $arkanium_dir/templates/systemd-boot > /boot/loader/entries/${data[0]}.conf
2023-09-14 19:10:20 +02:00
printf "\e[1;34m-->\e[0m\e[1m Setting new bootloader entry as default\e[0m\n"
2023-08-27 02:07:37 +02:00
# Configuring it with a oneshot for now, for testing
2023-09-14 19:10:20 +02:00
#bootctl set-default ${data[0]}.conf || cleanup_and_quit "Failed to set default bootloader entry"
bootctl set-oneshot ${data[0]}.conf || cleanup_and_quit "Failed to set default bootloader entry"
2023-08-27 02:07:37 +02:00
2023-08-25 00:15:23 +02:00
# Remove entries outside of keep
declare -r remove_deployments="$(cat $arkanium_dir/tracker | head -$deploy_keep | grep -rvf - $(readlink -m $arkanium_dir/tracker))"
# Remove old deployments
for deployment in $remove_deployments; do
printf "\e[1;34m-->\e[0m\e[1m Removing old deployment $deployment\e[0m\n"
rm -v /boot/loader/entries/$deployment.conf
2023-08-25 00:15:23 +02:00
btrfs property set -f -ts $(readlink -m $arkanium_dir/deployments/$deployment) ro false
rm -rfv $(readlink -m $arkanium_dir/deployments/$deployment)
rm -rfv $(readlink -m /boot/arkanium/$deployment)
2023-08-25 00:15:23 +02:00
grep -rv $deployment $(readline -m $arkanium_dir/tracker) > $arkanium_dir/tracker
done
2023-08-27 02:07:37 +02:00
2023-07-05 01:44:37 +02:00
}
[[ $1 == 'init' ]] && init
[[ $1 == 'teardown' ]] && teardown
2023-07-22 18:15:44 +02:00
[[ $1 == 'update' ]] && check_for_updates
[[ $1 == 'get-available' ]] && get_available
2023-08-15 10:12:02 +02:00
[[ $1 == 'deploy' ]] && deploy $1 $2 $3