|
|
|
@ -14,143 +14,153 @@ CONTAINER= |
|
|
|
# FUNCTIONS |
|
|
|
|
|
|
|
function usage { |
|
|
|
echo "$(basename "$0") <CONTAINER_NAME>" |
|
|
|
echo "$(basename "$0") <CONTAINER_NAME>" |
|
|
|
} |
|
|
|
|
|
|
|
function humanize_ago { |
|
|
|
local seconds=$(($(date +%s) - $(date -d "$1" +%s))) |
|
|
|
|
|
|
|
local days=$((seconds / 86400)) |
|
|
|
local hours=$(((seconds % 86400) / 3600)) |
|
|
|
local minutes=$(((seconds % 3600) / 60)) |
|
|
|
|
|
|
|
if ((days > 365)); then |
|
|
|
echo "$((days / 365)) year(s) ago" |
|
|
|
elif ((days > 30)); then |
|
|
|
echo "$((days / 30)) month(s) ago" |
|
|
|
elif ((days > 0)); then |
|
|
|
echo "$days day(s) ago" |
|
|
|
elif ((hours > 0)); then |
|
|
|
echo "$hours hour(s) ago" |
|
|
|
elif ((minutes > 0)); then |
|
|
|
echo "$minutes minute(s) ago" |
|
|
|
else |
|
|
|
echo "$seconds second(s) ago" |
|
|
|
fi |
|
|
|
local seconds=$(($(date +%s) - $(date -d "$1" +%s))) |
|
|
|
|
|
|
|
local days=$((seconds / 86400)) |
|
|
|
local hours=$(((seconds % 86400) / 3600)) |
|
|
|
local minutes=$(((seconds % 3600) / 60)) |
|
|
|
|
|
|
|
if ((days > 365)); then |
|
|
|
echo "$((days / 365)) year(s) ago" |
|
|
|
elif ((days > 30)); then |
|
|
|
echo "$((days / 30)) month(s) ago" |
|
|
|
elif ((days > 0)); then |
|
|
|
echo "$days day(s) ago" |
|
|
|
elif ((hours > 0)); then |
|
|
|
echo "$hours hour(s) ago" |
|
|
|
elif ((minutes > 0)); then |
|
|
|
echo "$minutes minute(s) ago" |
|
|
|
else |
|
|
|
echo "$seconds second(s) ago" |
|
|
|
fi |
|
|
|
} |
|
|
|
|
|
|
|
function parse_options { |
|
|
|
while [[ $# -gt 0 ]]; do |
|
|
|
case "$1" in |
|
|
|
--help | -h) |
|
|
|
usage && exit 0 |
|
|
|
;; |
|
|
|
*) |
|
|
|
if [[ -z $CONTAINER ]]; then |
|
|
|
CONTAINER=$1 |
|
|
|
else |
|
|
|
echo >&2 "Unknown option: $1" && usage && exit 2 |
|
|
|
fi |
|
|
|
;; |
|
|
|
esac |
|
|
|
|
|
|
|
shift 1 # Move to the next argument |
|
|
|
done |
|
|
|
[[ -n $CONTAINER ]] || (usage && exit 1) |
|
|
|
while [[ $# -gt 0 ]]; do |
|
|
|
case "$1" in |
|
|
|
--help | -h) |
|
|
|
usage && exit 0 |
|
|
|
;; |
|
|
|
*) |
|
|
|
if [[ -z $CONTAINER ]]; then |
|
|
|
CONTAINER=$1 |
|
|
|
else |
|
|
|
echo >&2 "Unknown option: $1" && usage && exit 2 |
|
|
|
fi |
|
|
|
;; |
|
|
|
esac |
|
|
|
|
|
|
|
shift 1 # Move to the next argument |
|
|
|
done |
|
|
|
[[ -n $CONTAINER ]] || (usage && exit 1) |
|
|
|
} |
|
|
|
|
|
|
|
function mount_miaou_bash { |
|
|
|
printenv MIAOU_BASH_DIR > /dev/null || (echo >&2 "Warn: environment variable MIAOU_BASH_DIR is missing!" && return) |
|
|
|
printenv MIAOU_BASH_DIR >/dev/null || (echo >&2 "Warn: environment variable MIAOU_BASH_DIR is missing!" && return) |
|
|
|
|
|
|
|
local target="$1" |
|
|
|
local optional_project=${2:-} |
|
|
|
[[ -n $optional_project ]] && optional_project="--project $optional_project" |
|
|
|
local target="$1" |
|
|
|
local optional_project=${2:-} |
|
|
|
[[ -n $optional_project ]] && optional_project="--project $optional_project" |
|
|
|
|
|
|
|
incus $optional_project config device add $target MIAOU-BASH disk source=$MIAOU_BASH_DIR path=/opt/miaou-bash readonly=true | grep -q 'Device MIAOU-BASH added' |
|
|
|
incus $optional_project config device add $target MIAOU-BASH disk source=$MIAOU_BASH_DIR path=/opt/miaou-bash readonly=true | grep -q 'Device MIAOU-BASH added' |
|
|
|
} |
|
|
|
|
|
|
|
function infocmp_xterm_ghostty { |
|
|
|
local target="$1" |
|
|
|
local optional_project=${2:-} |
|
|
|
[[ -n $optional_project ]] && optional_project="--project $optional_project" |
|
|
|
|
|
|
|
echo "export GHOSTTY infocmp from host to container" |
|
|
|
infocmp -x xterm-ghostty | incus exec $optional_project $target -- tic -x - 2>/dev/null |
|
|
|
} |
|
|
|
|
|
|
|
function build_sandbox_project { |
|
|
|
if ! incus project show $PROJECT_SANDBOX > /dev/null; then |
|
|
|
incus project create $PROJECT_SANDBOX |
|
|
|
incus --project $PROJECT_SANDBOX profile device add default root disk path=/ pool=default |
|
|
|
incus --project $PROJECT_SANDBOX profile device add default eth0 nic name=eth0 network=incusbr0 |
|
|
|
fi |
|
|
|
if ! incus project show $PROJECT_SANDBOX >/dev/null; then |
|
|
|
incus project create $PROJECT_SANDBOX |
|
|
|
incus --project $PROJECT_SANDBOX profile device add default root disk path=/ pool=default |
|
|
|
incus --project $PROJECT_SANDBOX profile device add default eth0 nic name=eth0 network=incusbr0 |
|
|
|
fi |
|
|
|
} |
|
|
|
|
|
|
|
function refresh_template { |
|
|
|
if incus --project "$PROJECT_SANDBOX" info "$TEMPLATE_SANDBOX" | grep '^Status: STOPPED'; then |
|
|
|
incus --project "$PROJECT_SANDBOX" start "$TEMPLATE_SANDBOX" |
|
|
|
fi |
|
|
|
incus --project "$PROJECT_SANDBOX" exec "$TEMPLATE_SANDBOX" -- bash << EOF |
|
|
|
apt-get update |
|
|
|
apt-get dist-upgrade -y |
|
|
|
apt-get install -y curl |
|
|
|
/opt/miaou-bash/install.sh |
|
|
|
if incus --project "$PROJECT_SANDBOX" info "$TEMPLATE_SANDBOX" | grep '^Status: STOPPED'; then |
|
|
|
incus --project "$PROJECT_SANDBOX" start "$TEMPLATE_SANDBOX" |
|
|
|
fi |
|
|
|
incus --project "$PROJECT_SANDBOX" exec "$TEMPLATE_SANDBOX" -- bash <<EOF |
|
|
|
apt-get update |
|
|
|
apt-get dist-upgrade -y |
|
|
|
apt-get install -y curl |
|
|
|
/opt/miaou-bash/install.sh 2>/dev/null |
|
|
|
EOF |
|
|
|
incus --project "$PROJECT_SANDBOX" stop "$TEMPLATE_SANDBOX" |
|
|
|
local image_date=$(incus --project "$PROJECT_SANDBOX" list "$TEMPLATE_SANDBOX" -f compact,noheader -cl) |
|
|
|
echo "template refreshed at: $image_date" |
|
|
|
incus --project "$PROJECT_SANDBOX" stop "$TEMPLATE_SANDBOX" |
|
|
|
local image_date=$(incus --project "$PROJECT_SANDBOX" list "$TEMPLATE_SANDBOX" -f compact,noheader -cl) |
|
|
|
echo "template refreshed at: $image_date" |
|
|
|
} |
|
|
|
|
|
|
|
function build_template_from_scratch { |
|
|
|
build_sandbox_project |
|
|
|
echo "building template from scratch..." |
|
|
|
incus --project "$PROJECT_SANDBOX" launch "$MIAOU_IMAGE" "$TEMPLATE_SANDBOX" |
|
|
|
incus --project "$PROJECT_SANDBOX" file delete "$TEMPLATE_SANDBOX"/etc/apt/sources.list |
|
|
|
incus --project "$PROJECT_SANDBOX" file push --uid 0 --gid 0 --mode 644 --create-dirs /etc/apt/sources.list.d/debian.sources "$TEMPLATE_SANDBOX"/etc/apt/sources.list.d/debian.sources |
|
|
|
cat << EOF | incus --project "$PROJECT_SANDBOX" file push --uid 0 --gid 0 --mode 644 --create-dirs - "$TEMPLATE_SANDBOX"/etc/systemd/resolved.conf.d/10-disable-ipv4-listener.conf |
|
|
|
build_sandbox_project |
|
|
|
echo "building template from scratch..." |
|
|
|
incus --project "$PROJECT_SANDBOX" launch "$MIAOU_IMAGE" "$TEMPLATE_SANDBOX" |
|
|
|
incus --project "$PROJECT_SANDBOX" file delete "$TEMPLATE_SANDBOX"/etc/apt/sources.list |
|
|
|
incus --project "$PROJECT_SANDBOX" file push --uid 0 --gid 0 --mode 644 --create-dirs /etc/apt/sources.list.d/debian.sources "$TEMPLATE_SANDBOX"/etc/apt/sources.list.d/debian.sources |
|
|
|
cat <<EOF | incus --project "$PROJECT_SANDBOX" file push --uid 0 --gid 0 --mode 644 --create-dirs - "$TEMPLATE_SANDBOX"/etc/systemd/resolved.conf.d/10-disable-ipv4-listener.conf |
|
|
|
[Resolve] |
|
|
|
LLMNR=no |
|
|
|
DNSStubListener=no |
|
|
|
EOF |
|
|
|
mount_miaou_bash "$TEMPLATE_SANDBOX" "$PROJECT_SANDBOX" |
|
|
|
refresh_template |
|
|
|
echo "template:"$TEMPLATE_SANDBOX" from project:$PROJECT_SANDBOX built successfully!" |
|
|
|
mount_miaou_bash "$TEMPLATE_SANDBOX" "$PROJECT_SANDBOX" |
|
|
|
command -v ghostty >/dev/null && infocmp_xterm_ghostty "$TEMPLATE_SANDBOX" "$PROJECT_SANDBOX" |
|
|
|
refresh_template |
|
|
|
echo "template:"$TEMPLATE_SANDBOX" from project:$PROJECT_SANDBOX built successfully!" |
|
|
|
} |
|
|
|
|
|
|
|
function prepare_template_for_quick_creation { |
|
|
|
local image_date=$(incus list "$TEMPLATE_SANDBOX" -f compact,noheader -cl --project "$PROJECT_SANDBOX") |
|
|
|
if [[ -z $image_date ]]; then |
|
|
|
build_template_from_scratch |
|
|
|
else |
|
|
|
local true_date=$(date -d "$image_date" +%s) |
|
|
|
local expiration_date=$(date -d "$TEMPLATE_REFRESH_DELAY ago" +%s) |
|
|
|
if ((true_date < expiration_date)); then |
|
|
|
echo "template exists $(humanize_ago "$image_date"), needs a refresh..." |
|
|
|
refresh_template |
|
|
|
else |
|
|
|
: |
|
|
|
# echo "template already exists $(humanize_ago "$image_date")!" |
|
|
|
fi |
|
|
|
fi |
|
|
|
local image_date=$(incus list "$TEMPLATE_SANDBOX" -f compact,noheader -cl --project "$PROJECT_SANDBOX") |
|
|
|
if [[ -z $image_date ]]; then |
|
|
|
build_template_from_scratch |
|
|
|
else |
|
|
|
local true_date=$(date -d "$image_date" +%s) |
|
|
|
local expiration_date=$(date -d "$TEMPLATE_REFRESH_DELAY ago" +%s) |
|
|
|
if ((true_date < expiration_date)); then |
|
|
|
echo "template exists $(humanize_ago "$image_date"), needs a refresh..." |
|
|
|
refresh_template |
|
|
|
else |
|
|
|
: |
|
|
|
# echo "template already exists $(humanize_ago "$image_date")!" |
|
|
|
fi |
|
|
|
fi |
|
|
|
} |
|
|
|
|
|
|
|
function customize_host { |
|
|
|
local target domain |
|
|
|
target="$1" |
|
|
|
domain=$(incus network get incusbr0 dns.domain) |
|
|
|
domain=${domain:-incus} |
|
|
|
local target domain |
|
|
|
target="$1" |
|
|
|
domain=$(incus network get incusbr0 dns.domain) |
|
|
|
domain=${domain:-incus} |
|
|
|
|
|
|
|
printf "127.0.1.1\t$target.$domain $target\n127.0.0.1\tlocalhost\n" | incus file push - $target/etc/hosts |
|
|
|
echo "$target.$domain" | incus file push - $target/etc/hostname |
|
|
|
printf "127.0.1.1\t$target.$domain $target\n127.0.0.1\tlocalhost\n" | incus file push - $target/etc/hosts |
|
|
|
echo "$target.$domain" | incus file push - $target/etc/hostname |
|
|
|
} |
|
|
|
|
|
|
|
function assert_not_sandboxing { |
|
|
|
[[ $(incus project get-current) == "$PROJECT_SANDBOX" ]] \ |
|
|
|
&& echo ERROR: actual sandboxing project, please switch back to your 'default' project >&2 \ |
|
|
|
&& exit 30 \ |
|
|
|
|| true |
|
|
|
[[ $(incus project get-current) == "$PROJECT_SANDBOX" ]] && |
|
|
|
echo ERROR: actual sandboxing project, please switch back to your 'default' project >&2 && |
|
|
|
exit 30 || |
|
|
|
true |
|
|
|
} |
|
|
|
|
|
|
|
function create { |
|
|
|
if incus --project "$PROJECT_SANDBOX" info "$TEMPLATE_SANDBOX" | grep '^Status: RUNNING'; then |
|
|
|
incus --project "$PROJECT_SANDBOX" stop "$TEMPLATE_SANDBOX" |
|
|
|
fi |
|
|
|
incus --project "$PROJECT_SANDBOX" copy --instance-only "$TEMPLATE_SANDBOX" "$CONTAINER" --target-project $(incus project get-current) |
|
|
|
incus file delete "$CONTAINER/etc/machine-id" |
|
|
|
incus config unset "$CONTAINER" volatile.eth0.hwaddr |
|
|
|
incus start "$CONTAINER" |
|
|
|
customize_host "$CONTAINER" |
|
|
|
if incus --project "$PROJECT_SANDBOX" info "$TEMPLATE_SANDBOX" | grep '^Status: RUNNING'; then |
|
|
|
incus --project "$PROJECT_SANDBOX" stop "$TEMPLATE_SANDBOX" |
|
|
|
fi |
|
|
|
incus --project "$PROJECT_SANDBOX" copy --instance-only "$TEMPLATE_SANDBOX" "$CONTAINER" --target-project $(incus project get-current) |
|
|
|
incus file delete "$CONTAINER/etc/machine-id" |
|
|
|
incus config unset "$CONTAINER" volatile.eth0.hwaddr |
|
|
|
incus start "$CONTAINER" |
|
|
|
customize_host "$CONTAINER" |
|
|
|
} |
|
|
|
|
|
|
|
# MAIN |
|
|
|
|