#!/usr/bin/env bash # Gateway routes, projected from the estate onto one target. # # Usage: # ./services.sh # list # ./services.sh render aws # -> render/out/aws/*.conf (nginx vhosts) # ./services.sh render local # -> render/out/local/Caddyfile # ./services.sh deploy # refuses; ppl/ctrl/deploy.sh ships config # # Each target is a projection with its own rules, not a format conversion. # The nine axes they disagree on, and the install order: ../README.md set -euo pipefail cd "$(dirname "$0")" source ./lib/config.sh source ./lib/estate.sh load_config OUT_ROOT="./render/out" list() { printf '%-12s %-16s %-22s %-9s %s\n' NAME FQDN UPSTREAM PLACEMENT SOURCE local name host up kind raw while IFS=$'\x1f' read -r name host up kind raw placement peer port lhost; do [ -z "$name" ] && continue # A placed service has no literal `upstream` — it is derived from the # peer's overlay address, so show what it actually resolves to. local shown; shown="$(service_upstream "$up" "$placement" "$peer" "$port")" || shown="" printf '%-12s %-16s %-22s %-9s %s\n' \ "$name" "${host}.${DOMAIN}" "${shown:--}" "$placement" \ "$([ -n "$raw" ] && echo 'hand-written' || echo 'generated')" done < <(estate_services "$TARGET") echo echo "hand-written entries are NOT generated and NOT overwritten." echo "run 'make estate show' to see why each one is an exception." } render_cloud() { # Two statements: `local a="$1" b="$a"` expands all arguments before any # assignment, so $a would still be unset. local target="$1" local out="$OUT_ROOT/$target" rm -rf "$out"; mkdir -p "$out" local name host up kind raw uhost uport n=0 skipped=0 while IFS=$'\x1f' read -r name host up kind raw placement peer port lhost; do [ -z "$name" ] && continue if [ -n "$raw" ]; then skipped=$((skipped + 1)) continue fi # Placement picks the rendering. A container on the estate's own network # is reached by NAME through docker's resolver; anything on the overlay # is reached by ADDRESS and needs no DNS. That is one decision, not the # three properties (upstream{}, no resolver, no set $var) it produces. local tmpl case "$placement" in local|instance) tmpl=./render/nginx-upstream.tmpl uhost="$(overlay_get estate "peers.${peer}.address")" uport="$port" # resolved via service_upstream's same rule if [ -z "$uhost" ]; then echo " ! $name: placement '$placement' names peer '$peer', which has no address" >&2 continue fi ;; hosted) echo " ! $name: placement 'hosted' is declared but not rendered yet" >&2 continue ;; *) if [ "$kind" = "static" ]; then tmpl=./render/nginx-static.tmpl uhost=""; uport="" else tmpl=./render/nginx-proxy.tmpl uhost="${up%%:*}"; uport="${up##*:}" fi ;; esac sed -e "s|\${NAME}|${name}|g" \ -e "s|\${ESTATE}|${ESTATE}|g" \ -e "s|\${FQDN}|${host}.${DOMAIN}|g" \ -e "s|\${DOMAIN}|${DOMAIN}|g" \ -e "s|\${PLACEMENT}|${placement}|g" \ -e "s|\${PEER}|${peer}|g" \ -e "s|\${UPSTREAM_HOST}|${uhost}|g" \ -e "s|\${UPSTREAM_PORT}|${uport}|g" \ "$tmpl" > "$out/${name}.conf" n=$((n + 1)) done < <(estate_services "$target") echo "wrote $n vhost(s) to $out/ ($skipped hand-written, left alone)" cat < "$out/Caddyfile" echo "wrote $out/Caddyfile" echo echo "rig is not consulted and does not know this exists — its handover" echo "scrub refuses the string '${ld}'. Where a port belongs to a rig," echo "'make ports verify' RECOMPUTES rig's formula to check it rather than" echo "importing rig's code. Convention, verified; not a dependency." } case "${1:-list}" in list) list ;; render) shift # `case "${1:-X}"` defaults the match but leaves $1 empty. t="${1:-$TARGET}" case "$t" in local) render_local ;; aws|gcp) render_cloud "$t" ;; *) echo "usage: $0 render [aws|gcp|local]" >&2; exit 1 ;; esac ;; deploy) echo "berth does not ship config; ppl/ctrl/deploy.sh does." >&2 echo " berth's half is the DESCRIPTION and the render. Shipping is" >&2 echo " rsync + compose against a live box, and it belongs where the" >&2 echo " credentials are: berth is the tool, ppl is the estate that" >&2 echo " holds the secrets." >&2 exit 1 ;; *) echo "usage: $0 [list|render [aws|gcp|local]|deploy]" >&2; exit 1 ;; esac