217 lines
7 KiB
Bash
Executable file
217 lines
7 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.
|
|
|
|
# Print manual if no parameters provided or invalid amount of parameters is provided
|
|
if [[ ! -n $1 || -n $2 ]]; then
|
|
cat <<- END
|
|
Usage: bttrfs-deploy <action> [target]
|
|
Actions:
|
|
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
|
|
init Initialize bttrfs on a new system
|
|
teardown Remove all bttrfs-deploy related files and folders
|
|
get-available List available packages in repo
|
|
END
|
|
exit 0
|
|
fi
|
|
|
|
## Set common variables
|
|
#
|
|
declare -r bttrfs_dir='/bttrfs/'
|
|
declare -r bttrfs_deployment_dir="$bttrfs_dir/deployments"
|
|
# FIXME: Automatically filter trailing /
|
|
declare -r repo_url='https://repo.arkanelinux.org/bttrfs-test'
|
|
declare -r repo_default_image='arkanelinux'
|
|
|
|
## 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?
|
|
#[[ ! -n $BTTRFS_NO_CLEANUP || ! -n $subvol_created ]] &&
|
|
# 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
|
|
for prog in btrfs wget; 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"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
## Core functions
|
|
#
|
|
# Initialize the system for bttrfs
|
|
init () {
|
|
|
|
printf '\e[1;34m-->\e[0m\e[1m Initializing bttrfs\e[0m\n'
|
|
|
|
[[ -d $bttrfs_dir ]] && cleanup_and_quit "$bttrfs_dir already exists"
|
|
|
|
# Create the /bttrfs subvolume
|
|
printf "\e[1;34m-->\e[0m\e[1m Creating $(readlink -m $bttrfs_dir) subvolume\e[0m\n"
|
|
btrfs subvolume create $bttrfs_dir || cleanup_and_quit "Failed to create btrfs subvolume $(readlink -m $bttrfs_dir)"
|
|
|
|
# Create directory structure
|
|
printf "\e[1;34m-->\e[0m\e[1m Creating directory structure\e[0m\n"
|
|
mkdir -pv $(readlink -m $bttrfs_dir/deployments) \
|
|
$(readlink -m $bttrfs_dir/deployments/primary_a) \
|
|
$(readlink -m $bttrfs_dir/deployments/primary_b) \
|
|
$(readlink -m $bttrfs_dir/data) \
|
|
$(readlink -m $bttrfs_dir/cache) \
|
|
$(readlink -m $bttrfs_dir/shared) ||
|
|
cleanup_and_quit "Failed to create /bttrfs and related directories"
|
|
|
|
# Write default config file
|
|
printf "\e[1;34m-->\e[0m\e[1m Adding default config file\e[0m\n"
|
|
cat <<- END > $bttrfs_dir/config
|
|
# Primary filesystem operating system
|
|
primary_os='arkanelinux'
|
|
|
|
# Do not copy custom root filesystem to new deployments
|
|
custom_rootfs=0
|
|
|
|
# Do not install additional packages defined customize
|
|
custom_packages=0
|
|
END
|
|
|
|
exit 0
|
|
}
|
|
|
|
teardown () {
|
|
|
|
cat <<- END
|
|
WARNING: Removing bttrfs 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 $bttrfs_dir will be deleted
|
|
|
|
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
|
|
|
|
printf '\e[1;34m-->\e[0m\e[1m Tearing down bttrfs\e[0m\n'
|
|
|
|
# Quit with error if $bttrfs_dir does not exist
|
|
if [[ ! -d $bttrfs_dir ]]; then
|
|
printf "\e[1;31m<#>\e[0m $(readlink -m $bttrfs_dir) does not exist, there is nothing to tear down"
|
|
exit 1
|
|
fi
|
|
|
|
# Remove all nested subvolumes in $bttrfs_dir
|
|
for volume in $(btrfs subvolume list $bttrfs_dir | grep -oE '[^ ]+$'); do
|
|
btrfs property set -f -ts $(readlink -m $bttrfs_dir/$volume) ro false
|
|
btrfs subvolume delete $(readlink -m $bttrfs_dir/$volume)
|
|
done
|
|
|
|
# Remove $bttrfs_dir itself
|
|
btrfs property set -f -ts $(readlink -m $bttrfs_dir) ro false
|
|
btrfs subvolume delete $(readlink -m $bttrfs_dir)
|
|
|
|
else
|
|
printf '\e[1;34m-->\e[0m\e[1m Teardown canceled, no changes made to system\e[0m\n'
|
|
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" || cleanup_and_quit 'Failed to download repo file'
|
|
|
|
}
|
|
|
|
# Deploy a new or update an existing deployment
|
|
deploy () {
|
|
|
|
# Load defaults from config file
|
|
source $bttrfs_dir/config
|
|
|
|
# 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}") &&
|
|
# Split latest_version at the delimiter, creating an array with data.1=package ver, data.2=compression method
|
|
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
|
|
if [[ -e $(readlink -m $bttrfs_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
|
|
|
|
# Download the tarball
|
|
wget -P $(readlink -m $bttrfs_dir/cache/) "${repo_url}/${repo_default_image}/${data[0]}.tar.${data[1]}" ||
|
|
(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"
|
|
sha1sum "$(readlink -m ${bttrfs_dir}/cache/${data[0]}.tar.${data[1]})" |
|
|
grep "${data[3]}" ||
|
|
(printf "\e[1;31m<#>\e[0m Checksum does not match repo file, got $chksum\e[0m\n"; exit 1)
|
|
|
|
# Extract the root image
|
|
printf "\e[1;34m-->\e[0m\e[1m Writing root\e[0m\n"
|
|
[[ ! -e $(readlink -m $bttrfs_dir/cache/${data[0]}.img) ]] &&
|
|
(tar -xf $(readlink -m ${bttrfs_dir}/cache/${data[0]}.tar.${data[1]}) -C $(readlink -m ${bttrfs_dir}/cache/) "${data[0]}.img" ||
|
|
(printf "\e[1;31m<#>\e[0m Failed extracting root image from tarball\e[0m\n"; exit 1))
|
|
|
|
# Write the root image
|
|
btrfs receive -f $(readlink -m ${bttrfs_dir}/cache/${data[0]}.img) $(readlink -m ${bttrfs_dir}/deployments/primary_a/)
|
|
|
|
#printf "\e[1;34m-->\e[0m\e[1m Writing etc\e[0m\n"
|
|
#printf "\e[1;34m-->\e[0m\e[1m Writing var\e[0m\n"
|
|
|
|
# Remove uncompressed img files to save space
|
|
#printf "\e[1;34m-->\e[0m\e[1m Cleaning up extracted images\e[0m\n"
|
|
#rm ${bttrfs_dir}/
|
|
|
|
}
|
|
|
|
# If $1 is init run init
|
|
[[ $1 == 'init' ]] && init
|
|
[[ $1 == 'teardown' ]] && teardown
|
|
[[ $1 == 'update' ]] && check_for_updates
|
|
[[ $1 == 'get-available' ]] && get_available
|
|
[[ $1 == 'deploy' ]] && deploy
|