arkdep/arkanium-deploy

276 lines
9.6 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.
# Print manual if no parameters provided or invalid amount of parameters is provided
if [[ ! -n $1 || -n $2 ]]; then
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/'
declare -r arkanium_deployment_dir="$arkanium_dir/deployments"
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
# Remove temporary btrfs volumes
# ONLY IF CUSTOM DEFINED?
2023-08-09 08:09:20 +02:00
#[[ ! -n $arkanium_NO_CLEANUP || ! -n $subvol_created ]] &&
2023-07-05 01:44:37 +02:00
# btrfs subvolume delete $workdir 2> /dev/null
# 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-07-22 18:15:44 +02:00
for prog in btrfs wget; 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-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"
btrfs property set -f -ts $(readlink -m $arkanium_dir/shared/home) 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
2023-07-05 01:44:37 +02:00
# Primary filesystem operating system
2023-07-31 03:07:34 +02:00
primary_os='${repo_default_image}'
2023-07-05 01:44:37 +02:00
2023-08-09 08:09:20 +02:00
# Write /arkanium/overlay overlay to root or etc
2023-07-31 03:07:34 +02:00
enable_overlay=0
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-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-07-31 03:07:34 +02:00
title Arkane GNU/Linux - Primary A
linux /vmlinuz-linux
initrd /amd-ucode.img
initrd /intel-ucode.img
initrd /initramfs-linux.img
2023-08-14 17:55:50 +02:00
options root="LABEL=arkane_root" rootflags=subvol=$arkanium_dir/${data} 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 () {
# Load defaults from config file
2023-08-09 08:09:20 +02:00
source $arkanium_dir/config
2023-07-22 18:15:44 +02:00
# target and version are optional, if not defined default to primary latest
[[ -n $2 ]] && declare -r deploy_target=$2 || declare -r deploy_target="$primary_os"
[[ -n $3 ]] && declare -r deploy_version=$3 || declare -r deploy_version='latest'
printf "\e[1;34m-->\e[0m\e[1m Deploying $deploy_target $deploy_version\e[0m\n"
# If latest is requested grab latest file
[[ $deploy_version == 'latest' ]] &&
declare latest_data=$(curl -sf "${repo_url}/${deploy_target}/${deploy_version}") &&
2023-08-10 06:44:20 +02:00
# Split latest_version at the delimiter, creating an array with data.1=package ver, data.2=compression method, data.3=sha1 hash
2023-07-22 18:15:44 +02:00
readarray -d : -t data <<< "$latest_data"
# A carriage feed is inserted for some reason, lets remove it
data[2]=${data[2]//[$'\t\r\n']}
# Check if requested version is already downloaded
2023-08-09 08:09:20 +02:00
if [[ -e $(readlink -m $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
# Download the tarball
2023-08-14 17:55:50 +02:00
wget -P $(readlink -m $arkanium_dir/cache/) "$repo_url/$repo_default_image/${data[0]}.tar.${data[1]}" ||
2023-07-22 18:15:44 +02:00
(printf "\e[1;31m<#>\e[0m Failed to download tarball\e[0m\n"; exit 1)
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-07-31 03:07:34 +02:00
# TODO: Just for loop this?
# TODO: Add a bunch of control flow to check if stuff already exists and if a or b needs to be targeted
#
# Extract the root image if not yet extracted
2023-07-22 18:15:44 +02:00
printf "\e[1;34m-->\e[0m\e[1m Writing root\e[0m\n"
2023-08-14 17:55:50 +02:00
[[ ! -e $(readlink -m $arkanium_dir/cache/${data[0]}.img) ]] &&
tar -xf $(readlink -m $arkanium_dir/cache/${data[0]}.tar.${data[1]}) -C $(readlink -m $arkanium_dir/cache/) "${data[0]}.img" ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to extract root'
2023-07-22 18:15:44 +02:00
# Write the root image
2023-08-14 17:55:50 +02:00
btrfs receive -f $(readlink -m $arkanium_dir/cache/${data[0]}.img) $(readlink -m $arkanium_dir/deployments/primary_a/) ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to receive root'
# Cleanup root image
2023-08-14 17:55:50 +02:00
rm $(readlink -m $arkanium_dir/cache/${data[0]}.img)
2023-07-31 03:07:34 +02:00
# Ensure /var exists for mounting
2023-08-14 17:55:50 +02:00
mkdir $(readlink -m $arkanium_dir/)
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-14 17:55:50 +02:00
[[ ! -e $(readlink -m $arkanium_dir/cache/${data[0]}-etc.img) ]] &&
tar -xf $(readlink -m $arkanium_dir/cache/${data[0]}.tar.${data[1]}) -C $(readlink -m $arkanium_dir/cache/) "${data[0]}-etc.img" ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'failed to extract etc'
# Write the etc image, we have to unlock rootfs temporarily to do this
2023-08-14 17:55:50 +02:00
btrfs property set -f -ts $(readlink -m $arkanium_dir/deployments/primary_a/rootfs) ro false ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to unlock root to write etc'
2023-08-14 17:55:50 +02:00
btrfs receive -f $(readlink -m $arkanium_dir/cache/${data[0]}-etc.img) $(readlink -m $arkanium_dir/deployments/primary_a/rootfs/) ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to receive etc'
2023-08-14 17:55:50 +02:00
btrfs property set -f -ts $(readlink -m $arkanium_dir/deployments/primary_a/rootfs) ro true ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to lock root'
# Cleanup etc image
2023-08-14 17:55:50 +02:00
rm $(readlink -m $arkanium_dir/cache/${data[0]}-etc.img)
2023-07-31 03:07:34 +02:00
# Overlay customizations to etc
# TODO: overlay here if enabled in config file
# Extract the var image if not yet extracted
printf "\e[1;34m-->\e[0m\e[1m Writing var\e[0m\n"
2023-08-14 17:55:50 +02:00
[[ ! -e $(readlink -m $arkanium_dir/cache/${data[0]}-var.img) ]] &&
tar -xf $(readlink -m $arkanium_dir/cache/${data[0]}.tar.${data[1]}) -C $(readlink -m $arkanium_dir/cache/) "${data[0]}-var.img" ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'failed to extract etc'
# Write the var image
2023-08-14 17:55:50 +02:00
btrfs receive -f $(readlink -m $arkanium_dir/cache/${data[0]}-var.img) $(readlink -m $arkanium_dir/shared/) ||
2023-07-31 03:07:34 +02:00
cleanup_and_quit 'Failed to receive var'
# 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-07-05 01:44:37 +02:00
}
# If $1 is init run init
[[ $1 == 'init' ]] && init
[[ $1 == 'teardown' ]] && teardown
2023-07-22 18:15:44 +02:00
[[ $1 == 'update' ]] && check_for_updates
[[ $1 == 'get-available' ]] && get_available
[[ $1 == 'deploy' ]] && deploy