workspaces/gitops_router.sh

238 lines
6.8 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -euo pipefail
PERSON="${1:?Missing PERSON argument}"
HOST="alps:3222"
PROTOCOL="http"
REPO="babbarc/workspaces"
BRANCH="master"
LOG_FILE="/tmp/.gitops-router-${PERSON}.log"
# ─────────────────────────────────────────────
# ANSI color codes
readonly C_RESET='\033[0m'
readonly C_INFO='\033[1;34m' # bold blue
readonly C_WARN='\033[1;33m' # bold yellow
readonly C_ERROR='\033[1;31m' # bold red
# ─────────────────────────────────────────────
# log <level> <message...> with emojis
log() {
local lvl="${1^^}"
shift
local icon color
case "$lvl" in
INFO) icon="" color="$C_INFO" ;;
WARN) icon="⚠️" color="$C_WARN" ;;
ERROR) icon="❌" color="$C_ERROR" ;;
*) icon="🔹" color="$C_RESET" ;;
esac
local ts
ts="$(date '+%Y-%m-%d %H:%M:%S')"
printf '%b%s [%s] [%s] %s%b\n' \
"$color" "$icon" "$ts" "$lvl" "$*" "$C_RESET" |
tee -a "$LOG_FILE"
}
# ─────────────────────────────────────────────
# Build the raw URL for fetching files
geturl() {
local type="$1" file="$2"
printf '%s://%s/%s/%s/branch/%s/%s\n' \
"$PROTOCOL" "$HOST" "$REPO" "$type" "$BRANCH" "$file"
}
# ─────────────────────────────────────────────
# Run a local script
run() {
local script="$1"
"$HOME/.local/bin/$script"
}
# ─────────────────────────────────────────────
# Download & install an artifact
# update <file> <target-dir> <mode> [<type>]
update() {
local file="$1" dir="$2" mode="$3" type="${4:-raw}"
local url out
out="$HOME/$dir/$(basename "$file")"
url="$(geturl "$type" "$file")"
[[ -f "$out" ]] && chmod 700 "$out"
if curl -fsSL "$url" -o "$out"; then
log INFO "Downloaded $url$out"
chmod "$mode" "$out"
else
log ERROR "Failed to download $url"
return 1
fi
}
# ─────────────────────────────────────────────
# Clean up dangling podman images
clean_images() {
local dangling
dangling="$(podman images -f dangling=true -q)"
if [[ -z "$dangling" ]]; then
log INFO "No dangling images to remove."
else
log WARN "Removing dangling images..."
echo "$dangling" | xargs podman rmi
log INFO "Dangling images removed."
fi
}
# ─────────────────────────────────────────────
# Remove host podman containers
remove_containers() {
local tokens=("$@")
local flags=() patterns=() containers=()
local valid='^[A-Za-z0-9._-]+$'
# allow unmatched globs to disappear
shopt -s nullglob
# separate flags (-f, etc.) from name patterns
for tok in "${tokens[@]}"; do
if [[ "$tok" == -* ]]; then
flags+=("$tok")
else
patterns+=("$tok")
fi
done
# validate & expand each pattern
for pat in "${patterns[@]}"; do
if [[ ! "$pat" =~ $valid ]]; then
log ERROR "Invalid container name: '$pat'"
shopt -u nullglob
return 1
fi
containers+=("$pat")
done
shopt -u nullglob
if ((${#containers[@]} == 0)); then
log WARN "No containers matched: ${patterns[*]}"
return 0
fi
# pass flags *then* containers to podman rm
podman rm "${flags[@]}" "${containers[@]}"
}
# ─────────────────────────────────────────────
# validate_command <cmd> [<tok1> <tok2> …]
validate_command() {
local cmd="$1"
shift
local tokens=("$@")
local yaml="$HOME/access.yml"
# 1) Is command allowed at all?
if [[ "$(yq e ".\"$PERSON\".commands | has(\"$cmd\")" "$yaml")" != "true" ]]; then
log ERROR "Unauthorized command: '$cmd'"
exit 1
fi
# 2) Load allowed args for this cmd (may be empty array)
mapfile -t allowed < <(yq e ".\"$PERSON\".commands.${cmd}[]" "$yaml")
if [[ "${#allowed[@]}" -eq 0 ]]; then
log ERROR "No allowed arguments for command '$cmd' in $yaml"
exit 1
fi
# 3) Extract just the non-flag tokens
local args=()
for tok in "${tokens[@]}"; do
[[ "$tok" == -* ]] && continue
args+=("$tok")
done
if [[ "$cmd" == "remove" ]]; then
# ─ remove: must have at least one arg
if ((${#args[@]} == 0)); then
log ERROR "Command '$cmd' requires at least one argument: ${allowed[*]}"
exit 1
fi
# Validate each against allowed[]
for a in "${args[@]}"; do
local ok=false
for want in "${allowed[@]}"; do
[[ "$a" == "$want" ]] && ok=true && break
done
if ! $ok; then
log ERROR "Invalid argument '$a' for '$cmd'; allowed: ${allowed[*]}"
exit 1
fi
done
else
# ─ all other cmds: must have exactly one arg
if ((${#args[@]} != 1)); then
log ERROR "Command '$cmd' requires exactly one argument: ${allowed[*]}"
exit 1
fi
# And that single arg must be allowed
local a="${args[0]}"
local ok=false
for want in "${allowed[@]}"; do
[[ "$a" == "$want" ]] && ok=true && break
done
if ! $ok; then
log ERROR "Invalid argument '$a' for '$cmd'; allowed: ${allowed[*]}"
exit 1
fi
fi
}
# ─────────────────────────────────────────────
# Entry & command parsing
if [[ -z "${SSH_ORIGINAL_COMMAND:-}" ]]; then
log ERROR "No SSH_ORIGINAL_COMMAND provided."
exit 1
fi
log INFO "SSH_ORIGINAL_COMMAND: $SSH_ORIGINAL_COMMAND"
read -ra parts <<<"$SSH_ORIGINAL_COMMAND"
cmd="${parts[0]}"
args=("${parts[@]:1}")
validate_command "$cmd" "${args[@]}"
# ─────────────────────────────────────────────
# Dispatch
case "$cmd" in
build)
case "${args[0]}" in
base) podman build --target base -t analytics-backend-base . ;;
workspace) podman build --target base -t analytics-backend-base . ;;
all) podman build -t analytics-backend-workspace . ;;
*) log ERROR "build: invalid arg '${args[0]}'" ;;
esac
;;
update)
case "${args[0]}" in
containerfile) update Containerfile . 500 ;;
access) update access.yml . 400 ;;
ssh_router) update ssh_router.sh .local/bin 500 ;;
gitops_router) update gitops_router.sh .local/bin 500 ;;
home_tar) update home.tar.gz . 500 media ;;
gitconfig) update gitconfig.template . 500 ;;
*) log ERROR "update: invalid arg '${args[0]}'" ;;
esac
;;
clean) clean_images ;;
status) podman images ;;
remove) remove_containers "${args[@]}" ;;
*)
log ERROR "Unknown command: '$cmd'"
exit 127
;;
esac