176 lines
5.1 KiB
Bash
Executable File
176 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Generate one untracked, installation-specific Compose override from explicit THT_WS_* bindings.
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat >&2 <<'EOF'
|
|
usage: $0 --bindings-env <workspace-bindings.env> --operator-env <operator.env> --output <override.yaml> \
|
|
[--service <core|workspace-maintenance>]... [--role <all|dwh|evidence>]...
|
|
EOF
|
|
exit 2
|
|
}
|
|
|
|
trim() {
|
|
local value="$1"
|
|
value="${value#"${value%%[![:space:]]*}"}"
|
|
value="${value%"${value##*[![:space:]]}"}"
|
|
printf '%s' "$value"
|
|
}
|
|
|
|
is_safe_absolute_path() {
|
|
local value="$1" segment
|
|
local -a segments
|
|
[[ "$value" == /* && "$value" != *//* ]] || return 1
|
|
IFS=/ read -r -a segments <<<"$value"
|
|
for segment in "${segments[@]}"; do
|
|
[[ "$segment" != . && "$segment" != .. ]] || return 1
|
|
done
|
|
}
|
|
|
|
read_env_value() {
|
|
local source="$1" wanted="$2" line trimmed name value result=""
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
trimmed="$(trim "$line")"
|
|
[[ -n "$trimmed" && "$trimmed" != \#* && "$trimmed" == *=* ]] || continue
|
|
name="$(trim "${trimmed%%=*}")"
|
|
[[ "$name" == "$wanted" ]] || continue
|
|
value="$(trim "${trimmed#*=}")"
|
|
value="$(trim "${value%%#*}")"
|
|
value="${value#\"}"; value="${value%\"}"
|
|
value="${value#\'}"; value="${value%\'}"
|
|
result="$value"
|
|
done <"$source"
|
|
printf '%s' "$result"
|
|
}
|
|
|
|
binding_matches_roles() {
|
|
local name="$1" role
|
|
for role in "${roles[@]}"; do
|
|
case "$role" in
|
|
all) return 0 ;;
|
|
dwh)
|
|
[[ "$name" == *"_DWH_"* ]] && return 0
|
|
;;
|
|
evidence)
|
|
[[ "$name" == *"_EVIDENCE_"* ]] && return 0
|
|
;;
|
|
*)
|
|
echo "unsupported role filter: $role" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
return 1
|
|
}
|
|
|
|
bindings_env=""
|
|
operator_env=""
|
|
output=""
|
|
services=()
|
|
roles=()
|
|
while (($#)); do
|
|
case "$1" in
|
|
--bindings-env) bindings_env="${2:-}"; shift 2 ;;
|
|
--operator-env) operator_env="${2:-}"; shift 2 ;;
|
|
--output) output="${2:-}"; shift 2 ;;
|
|
--service) services+=("${2:-}"); shift 2 ;;
|
|
--role)
|
|
roles+=("$(printf '%s' "${2:-}" | tr '[:upper:]' '[:lower:]')")
|
|
shift 2
|
|
;;
|
|
*) usage ;;
|
|
esac
|
|
done
|
|
|
|
[[ -f "$bindings_env" && -f "$operator_env" && -n "$output" ]] || usage
|
|
[[ ! -e "$output" ]] || { echo "refusing to overwrite connector override: $output" >&2; exit 2; }
|
|
((${#services[@]})) || services=(core)
|
|
((${#roles[@]})) || roles=(all)
|
|
|
|
for service in "${services[@]}"; do
|
|
case "$service" in
|
|
core|workspace-maintenance) ;;
|
|
*)
|
|
echo "unsupported service target: $service" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
names=()
|
|
targets=()
|
|
sources=()
|
|
while IFS=$'\t' read -r name target; do
|
|
[[ "$name" =~ ^THT_WS_[A-Za-z0-9_]+_FILE$ ]] || continue
|
|
if [[ "$name" == *"_VECTOR_"* || "$name" == *"_EMBEDDING_"* ]]; then
|
|
echo "retired semantic secret binding is not supported: ${name%_FILE}_SOURCE" >&2
|
|
exit 2
|
|
fi
|
|
binding_matches_roles "$name" || continue
|
|
[[ "$target" =~ ^/run/secrets/[A-Za-z0-9][A-Za-z0-9_.-]*$ && "$target" != *..* ]] || {
|
|
echo "invalid connector secret target for $name: $target" >&2
|
|
exit 2
|
|
}
|
|
source_name="${name%_FILE}_SOURCE"
|
|
source_path="$(read_env_value "$operator_env" "$source_name")"
|
|
if [[ -n "${!source_name+x}" ]]; then
|
|
source_path="${!source_name}"
|
|
fi
|
|
if [[ -z "$source_path" ]]; then
|
|
echo "set $source_name in $operator_env" >&2
|
|
exit 2
|
|
fi
|
|
if ! is_safe_absolute_path "$source_path"; then
|
|
echo "unsafe source path for $source_name in $operator_env" >&2
|
|
exit 2
|
|
fi
|
|
names+=("$name")
|
|
targets+=("${target#/run/secrets/}")
|
|
sources+=("$source_name")
|
|
done < <(
|
|
awk '
|
|
function trim(value) { sub(/^[[:space:]]+/, "", value); sub(/[[:space:]]+$/, "", value); return value }
|
|
{
|
|
line = trim($0)
|
|
if (line == "" || line ~ /^#/) next
|
|
equals = index(line, "=")
|
|
if (!equals) next
|
|
name = trim(substr(line, 1, equals - 1))
|
|
value = trim(substr(line, equals + 1))
|
|
sub(/[[:space:]]+#.*/, "", value)
|
|
if (value ~ /^".*"$/ || value ~ /^\047.*\047$/) value = substr(value, 2, length(value) - 2)
|
|
print name "\t" value
|
|
}
|
|
' "$bindings_env"
|
|
)
|
|
|
|
((${#names[@]})) || {
|
|
echo "no THT_WS_*_FILE connector bindings matched the selected roles in $bindings_env" >&2
|
|
exit 2
|
|
}
|
|
|
|
{
|
|
printf '%s\n' '# Generated by scripts/generate-connector-secrets-override.sh; keep this file untracked.'
|
|
printf '%s\n' 'services:'
|
|
for service in "${services[@]}"; do
|
|
printf ' %s:\n' "$service"
|
|
printf '%s\n' \
|
|
' env_file:' \
|
|
' - path: ${THT_WORKSPACE_BINDINGS_ENV_FILE:?set THT_WORKSPACE_BINDINGS_ENV_FILE}' \
|
|
' required: true' \
|
|
' secrets:'
|
|
for ((index = 0; index < ${#names[@]}; index += 1)); do
|
|
printf ' - source: connector_secret_%d\n' "$((index + 1))"
|
|
printf ' target: %s\n' "${targets[index]}"
|
|
done
|
|
done
|
|
printf '%s\n' '' 'secrets:'
|
|
for ((index = 0; index < ${#names[@]}; index += 1)); do
|
|
printf ' connector_secret_%d:\n' "$((index + 1))"
|
|
printf ' file: ${%s:?set %s}\n' "${sources[index]}" "${sources[index]}"
|
|
done
|
|
} >"$output"
|
|
|
|
printf 'generated %s connector secret mount(s) for %s at %s\n' \
|
|
"${#names[@]}" "$(IFS=,; printf '%s' "${services[*]}")" "$output"
|