SSH-Infrastructure / tools / sync-hosts-from-upstream.sh
1 contributor
125 lines | 3.92kb
#!/usr/bin/env bash
set -euo pipefail

project_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)

inventory_path=${INVENTORY_PATH:-"$project_root/inventory/hosts.yaml"}
upstream_hosts_file=${UPSTREAM_HOSTS_FILE:-}
upstream_ssh_target=${UPSTREAM_SSH_TARGET:-nextgen@192.168.2.103}
upstream_hosts_path=${UPSTREAM_HOSTS_PATH:-/home/nextgen/projects/ssh-infrastructure/inventory/hosts.yaml}
local_is_jumper_identity_file=${LOCAL_IS_JUMPER_IDENTITY_FILE:-}
if [[ -z "$local_is_jumper_identity_file" ]]; then
    local_is_jumper_identity_file='~/.ssh/keys/is-jumper_ed25519'
fi
deploy_after_sync=${DEPLOY_AFTER_SYNC:-1}
force_deploy=${FORCE_DEPLOY:-0}

tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/ssh-infra-sync.XXXXXX")
trap 'rm -rf "$tmpdir"' EXIT

tmp_hosts="$tmpdir/hosts.yaml"
tmp_generated="$tmpdir/generated"

if [[ -n "$upstream_hosts_file" ]]; then
    cp "$upstream_hosts_file" "$tmp_hosts"
else
    /usr/bin/scp -q "${upstream_ssh_target}:${upstream_hosts_path}" "$tmp_hosts"
fi

python3 - "$tmp_hosts" "$local_is_jumper_identity_file" <<'PY'
from pathlib import Path
import sys

import yaml

path = Path(sys.argv[1])
identity_file = sys.argv[2]
text = path.read_text(encoding="utf-8")

with path.open("r", encoding="utf-8") as handle:
    data = yaml.safe_load(handle)

is_jumper = data.get("entrypoints", {}).get("is_jumper", {})
if (
    is_jumper.get("identity_file") == identity_file
    and is_jumper.get("identities_only") is True
):
    raise SystemExit(0)

lines = text.splitlines(keepends=True)

entrypoints_idx = None
for idx, line in enumerate(lines):
    if line.strip() == "entrypoints:" and not line.startswith((" ", "\t")):
        entrypoints_idx = idx
        break
if entrypoints_idx is None:
    raise SystemExit("missing entrypoints section")

is_jumper_idx = None
for idx in range(entrypoints_idx + 1, len(lines)):
    line = lines[idx]
    if line and not line.startswith((" ", "\t")) and line.strip():
        break
    if line.startswith("  is_jumper:"):
        is_jumper_idx = idx
        break
if is_jumper_idx is None:
    raise SystemExit("missing entrypoints.is_jumper section")

block_end = len(lines)
for idx in range(is_jumper_idx + 1, len(lines)):
    line = lines[idx]
    if line.strip() and not line.startswith("    "):
        block_end = idx
        break

identity_line = f"    identity_file: {identity_file}\n"
identities_only_line = "    identities_only: true\n"
identity_idx = None
identities_only_idx = None
for idx in range(is_jumper_idx + 1, block_end):
    stripped = lines[idx].strip()
    if stripped.startswith("identity_file:"):
        identity_idx = idx
    elif stripped.startswith("identities_only:"):
        identities_only_idx = idx

if identity_idx is None:
    insert_at = identities_only_idx if identities_only_idx is not None else block_end
    lines.insert(insert_at, identity_line)
    if identities_only_idx is not None:
        identities_only_idx += 1
        block_end += 1
else:
    lines[identity_idx] = identity_line

if identities_only_idx is None:
    insert_at = (identity_idx + 1) if identity_idx is not None else block_end
    lines.insert(insert_at, identities_only_line)
else:
    lines[identities_only_idx] = identities_only_line

path.write_text("".join(lines), encoding="utf-8")
PY

python3 "$project_root/tools/generate-configs.py" \
    --inventory "$tmp_hosts" \
    --output-dir "$tmp_generated"

if cmp -s "$tmp_hosts" "$inventory_path"; then
    printf 'inventory unchanged: %s\n' "$inventory_path"
    if [[ "$deploy_after_sync" == "1" && "$force_deploy" == "1" ]]; then
        "$project_root/tools/deploy-local.sh"
    fi
    exit 0
fi

install -m 644 "$tmp_hosts" "$inventory_path"
printf 'updated inventory from upstream: %s\n' "$inventory_path"

if [[ "$deploy_after_sync" == "1" ]]; then
    "$project_root/tools/deploy-local.sh"
else
    printf 'skipped deploy because DEPLOY_AFTER_SYNC=%s\n' "$deploy_after_sync"
fi