|
Bogdan Timofte
authored
2 weeks ago
|
1
|
#!/usr/bin/env bash
|
|
|
2
|
set -euo pipefail
|
|
|
3
|
|
|
|
4
|
project_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
|
|
5
|
|
|
|
6
|
inventory_path=${INVENTORY_PATH:-"$project_root/inventory/hosts.yaml"}
|
|
|
7
|
upstream_hosts_file=${UPSTREAM_HOSTS_FILE:-}
|
|
|
8
|
upstream_ssh_target=${UPSTREAM_SSH_TARGET:-nextgen@192.168.2.103}
|
|
|
9
|
upstream_hosts_path=${UPSTREAM_HOSTS_PATH:-/home/nextgen/projects/ssh-infrastructure/inventory/hosts.yaml}
|
|
|
10
|
local_is_jumper_identity_file=${LOCAL_IS_JUMPER_IDENTITY_FILE:-}
|
|
|
11
|
if [[ -z "$local_is_jumper_identity_file" ]]; then
|
|
|
12
|
local_is_jumper_identity_file='~/.ssh/keys/is-jumper_ed25519'
|
|
|
13
|
fi
|
|
|
14
|
deploy_after_sync=${DEPLOY_AFTER_SYNC:-1}
|
|
|
15
|
force_deploy=${FORCE_DEPLOY:-0}
|
|
|
16
|
|
|
|
17
|
tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/ssh-infra-sync.XXXXXX")
|
|
|
18
|
trap 'rm -rf "$tmpdir"' EXIT
|
|
|
19
|
|
|
|
20
|
tmp_hosts="$tmpdir/hosts.yaml"
|
|
|
21
|
tmp_generated="$tmpdir/generated"
|
|
|
22
|
|
|
|
23
|
if [[ -n "$upstream_hosts_file" ]]; then
|
|
|
24
|
cp "$upstream_hosts_file" "$tmp_hosts"
|
|
|
25
|
else
|
|
|
26
|
/usr/bin/scp -q "${upstream_ssh_target}:${upstream_hosts_path}" "$tmp_hosts"
|
|
|
27
|
fi
|
|
|
28
|
|
|
|
29
|
python3 - "$tmp_hosts" "$local_is_jumper_identity_file" <<'PY'
|
|
|
30
|
from pathlib import Path
|
|
|
31
|
import sys
|
|
|
32
|
|
|
|
33
|
import yaml
|
|
|
34
|
|
|
|
35
|
path = Path(sys.argv[1])
|
|
|
36
|
identity_file = sys.argv[2]
|
|
|
37
|
text = path.read_text(encoding="utf-8")
|
|
|
38
|
|
|
|
39
|
with path.open("r", encoding="utf-8") as handle:
|
|
|
40
|
data = yaml.safe_load(handle)
|
|
|
41
|
|
|
|
42
|
is_jumper = data.get("entrypoints", {}).get("is_jumper", {})
|
|
|
43
|
if (
|
|
|
44
|
is_jumper.get("identity_file") == identity_file
|
|
|
45
|
and is_jumper.get("identities_only") is True
|
|
|
46
|
):
|
|
|
47
|
raise SystemExit(0)
|
|
|
48
|
|
|
|
49
|
lines = text.splitlines(keepends=True)
|
|
|
50
|
|
|
|
51
|
entrypoints_idx = None
|
|
|
52
|
for idx, line in enumerate(lines):
|
|
|
53
|
if line.strip() == "entrypoints:" and not line.startswith((" ", "\t")):
|
|
|
54
|
entrypoints_idx = idx
|
|
|
55
|
break
|
|
|
56
|
if entrypoints_idx is None:
|
|
|
57
|
raise SystemExit("missing entrypoints section")
|
|
|
58
|
|
|
|
59
|
is_jumper_idx = None
|
|
|
60
|
for idx in range(entrypoints_idx + 1, len(lines)):
|
|
|
61
|
line = lines[idx]
|
|
|
62
|
if line and not line.startswith((" ", "\t")) and line.strip():
|
|
|
63
|
break
|
|
|
64
|
if line.startswith(" is_jumper:"):
|
|
|
65
|
is_jumper_idx = idx
|
|
|
66
|
break
|
|
|
67
|
if is_jumper_idx is None:
|
|
|
68
|
raise SystemExit("missing entrypoints.is_jumper section")
|
|
|
69
|
|
|
|
70
|
block_end = len(lines)
|
|
|
71
|
for idx in range(is_jumper_idx + 1, len(lines)):
|
|
|
72
|
line = lines[idx]
|
|
|
73
|
if line.strip() and not line.startswith(" "):
|
|
|
74
|
block_end = idx
|
|
|
75
|
break
|
|
|
76
|
|
|
|
77
|
identity_line = f" identity_file: {identity_file}\n"
|
|
|
78
|
identities_only_line = " identities_only: true\n"
|
|
|
79
|
identity_idx = None
|
|
|
80
|
identities_only_idx = None
|
|
|
81
|
for idx in range(is_jumper_idx + 1, block_end):
|
|
|
82
|
stripped = lines[idx].strip()
|
|
|
83
|
if stripped.startswith("identity_file:"):
|
|
|
84
|
identity_idx = idx
|
|
|
85
|
elif stripped.startswith("identities_only:"):
|
|
|
86
|
identities_only_idx = idx
|
|
|
87
|
|
|
|
88
|
if identity_idx is None:
|
|
|
89
|
insert_at = identities_only_idx if identities_only_idx is not None else block_end
|
|
|
90
|
lines.insert(insert_at, identity_line)
|
|
|
91
|
if identities_only_idx is not None:
|
|
|
92
|
identities_only_idx += 1
|
|
|
93
|
block_end += 1
|
|
|
94
|
else:
|
|
|
95
|
lines[identity_idx] = identity_line
|
|
|
96
|
|
|
|
97
|
if identities_only_idx is None:
|
|
|
98
|
insert_at = (identity_idx + 1) if identity_idx is not None else block_end
|
|
|
99
|
lines.insert(insert_at, identities_only_line)
|
|
|
100
|
else:
|
|
|
101
|
lines[identities_only_idx] = identities_only_line
|
|
|
102
|
|
|
|
103
|
path.write_text("".join(lines), encoding="utf-8")
|
|
|
104
|
PY
|
|
|
105
|
|
|
|
106
|
python3 "$project_root/tools/generate-configs.py" \
|
|
|
107
|
--inventory "$tmp_hosts" \
|
|
|
108
|
--output-dir "$tmp_generated"
|
|
|
109
|
|
|
|
110
|
if cmp -s "$tmp_hosts" "$inventory_path"; then
|
|
|
111
|
printf 'inventory unchanged: %s\n' "$inventory_path"
|
|
|
112
|
if [[ "$deploy_after_sync" == "1" && "$force_deploy" == "1" ]]; then
|
|
|
113
|
"$project_root/tools/deploy-local.sh"
|
|
|
114
|
fi
|
|
|
115
|
exit 0
|
|
|
116
|
fi
|
|
|
117
|
|
|
|
118
|
install -m 644 "$tmp_hosts" "$inventory_path"
|
|
|
119
|
printf 'updated inventory from upstream: %s\n' "$inventory_path"
|
|
|
120
|
|
|
|
121
|
if [[ "$deploy_after_sync" == "1" ]]; then
|
|
|
122
|
"$project_root/tools/deploy-local.sh"
|
|
|
123
|
else
|
|
|
124
|
printf 'skipped deploy because DEPLOY_AFTER_SYNC=%s\n' "$deploy_after_sync"
|
|
|
125
|
fi
|