kampodine 0.3.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +139 -0
- package/README.md +74 -11
- package/cli.js +22 -2
- package/kampodine.md +13 -3
- package/package.json +1 -1
- package/scripts/backup.sh +290 -0
- package/scripts/bluegreen.sh +78 -57
- package/scripts/common.sh +644 -0
- package/scripts/config.sh +247 -0
- package/scripts/deploy-lifecycle.sh +266 -0
- package/scripts/deploy.sh +148 -31
- package/scripts/dns.sh +27 -8
- package/scripts/env.sh +35 -21
- package/scripts/image-import.sh +29 -19
- package/scripts/metrics.sh +127 -0
- package/scripts/migrate.sh +41 -7
- package/scripts/status.sh +132 -12
- package/scripts/vm-prepare.sh +103 -89
package/scripts/vm-prepare.sh
CHANGED
|
@@ -12,21 +12,21 @@
|
|
|
12
12
|
# 2. apk repositories: ensure the v3.22 community repo (podman lives there)
|
|
13
13
|
# 3. podman stack: podman podman-docker crun catatonit netavark
|
|
14
14
|
# aardvark-dns fuse-overlayfs
|
|
15
|
-
# 4. /etc/
|
|
15
|
+
# 4. /etc/kampodine/ (kampodine deploy writes /etc/kampodine/env here, 0600 root)
|
|
16
16
|
# 5. /etc/containers/registries.conf (insecure 127.0.0.1:5000; search docker.io)
|
|
17
17
|
# 6. sysctl net.ipv4.ip_unprivileged_port_start=80 (persisted + applied live)
|
|
18
|
-
# 7. OpenRC services /etc/init.d/
|
|
18
|
+
# 7. OpenRC services /etc/init.d/kampodine-api + /etc/init.d/kamal-proxy
|
|
19
19
|
# (supervise-daemon around plain `podman run`), rc-update'd into the
|
|
20
20
|
# default runlevel — that IS boot survival (no quadlets, no systemctl)
|
|
21
|
-
# 8.
|
|
22
|
-
# busybox watcher (no container) that polls /etc/
|
|
21
|
+
# 8. kampodine-anchor: the blue-green reserved-ip flip's GUEST half — a
|
|
22
|
+
# busybox watcher (no container) that polls /etc/kampodine/anchor.conf and
|
|
23
23
|
# `ip addr add`s the anchor address at flip time. Started+enabled on every
|
|
24
24
|
# VM, inert without the conf (flip tooling writes it over ssh).
|
|
25
25
|
# 9. kamal-proxy image pulled + service UP — the first deploy execs into it
|
|
26
26
|
# to issue the fresh ACME certificate
|
|
27
27
|
#
|
|
28
|
-
# The
|
|
29
|
-
# the first deploy over the registry tunnel) nor /etc/
|
|
28
|
+
# The kampodine-api container is NOT started here: neither its image (pulled by
|
|
29
|
+
# the first deploy over the registry tunnel) nor /etc/kampodine/env (written by
|
|
30
30
|
# kampodine deploy) exists yet on a fresh VM.
|
|
31
31
|
#
|
|
32
32
|
# Idempotent: safe to re-run. apk add is a no-op when satisfied; managed files
|
|
@@ -35,42 +35,56 @@
|
|
|
35
35
|
# not a restart).
|
|
36
36
|
#
|
|
37
37
|
# kampodine vm-prepare --host root@<new-ip>
|
|
38
|
+
# kampodine vm-prepare --profile <name> # host/key from ~/.kampodine/config.json
|
|
38
39
|
# ... --pull-images # + pre-pull the app image over the registry tunnel
|
|
39
|
-
# ... --ssh-key <path> # ssh identity (default: agent /
|
|
40
|
+
# ... --ssh-key <path> # ssh identity (default: agent / KAMPODINE_SSH_KEY)
|
|
40
41
|
set -euo pipefail
|
|
41
42
|
|
|
42
43
|
HOST=""
|
|
43
44
|
DO_PULL=0
|
|
44
|
-
SSH_KEY="${
|
|
45
|
+
SSH_KEY="${KAMPODINE_SSH_KEY:-}"
|
|
46
|
+
PROFILE_FLAG=""
|
|
47
|
+
HOST_FLAG=""
|
|
48
|
+
KEY_FLAG=""
|
|
49
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
50
|
+
# shellcheck source=common.sh
|
|
51
|
+
source "$HERE/common.sh"
|
|
45
52
|
|
|
46
53
|
say() { printf '\033[1;32m[vm-prepare]\033[0m %s\n' "$*"; }
|
|
47
54
|
die() { printf '\033[1;31m[vm-prepare] FAIL:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
48
55
|
usage() {
|
|
49
56
|
cat <<'EOF'
|
|
50
57
|
Usage:
|
|
51
|
-
kampodine vm-prepare --host root@<new-ip> [--pull-images] [--ssh-key <path>]
|
|
58
|
+
kampodine vm-prepare --host root@<new-ip> [--pull-images] [--ssh-key <path>] [--profile <name>]
|
|
52
59
|
|
|
53
60
|
Examples:
|
|
54
61
|
EOF
|
|
55
62
|
grep '^# kampodine vm-prepare' "$0" | sed 's/^# //'
|
|
56
63
|
cat <<'EOF'
|
|
57
64
|
|
|
58
|
-
Host/key resolution: --host |
|
|
59
|
-
--ssh-key |
|
|
65
|
+
Host/key resolution: --host | --profile <name> | KAMPODINE_PROFILE |
|
|
66
|
+
config defaultProfile; --ssh-key | profile sshKey | KAMPODINE_SSH_KEY |
|
|
67
|
+
ssh-agent / ~/.ssh/config.
|
|
60
68
|
EOF
|
|
61
69
|
exit 0
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
while [[ $# -gt 0 ]]; do
|
|
65
73
|
case "$1" in
|
|
66
|
-
--host) HOST="$2"; shift 2 ;;
|
|
74
|
+
--host) HOST_FLAG="$2"; HOST="$2"; shift 2 ;;
|
|
67
75
|
--pull-images) DO_PULL=1; shift ;;
|
|
68
|
-
--ssh-key) SSH_KEY="$2"; shift 2 ;;
|
|
76
|
+
--ssh-key) KEY_FLAG="$2"; SSH_KEY="$2"; shift 2 ;;
|
|
77
|
+
--profile) PROFILE_FLAG="$2"; shift 2 ;;
|
|
69
78
|
-h|--help) usage ;;
|
|
70
79
|
*) die "unknown argument: $1 (--help)" ;;
|
|
71
80
|
esac
|
|
72
81
|
done
|
|
73
|
-
|
|
82
|
+
# profile resolution (flag > KAMPODINE_PROFILE > defaultProfile); the explicit
|
|
83
|
+
# --host/--ssh-key flags always beat the profile
|
|
84
|
+
profile_resolve "$PROFILE_FLAG" "${HOST_FLAG:-}" "${KEY_FLAG:-}"
|
|
85
|
+
SSH_KEY="${KEY_FLAG:-${KAMPODINE_SSH_KEY:-}}"
|
|
86
|
+
[[ -z "$HOST" ]] && HOST="${KAMPODINE_HOST:-}"
|
|
87
|
+
[[ -n "$HOST" ]] || die "--host root@<ip> required (--profile <name> or KAMPODINE_HOST also work; --help for usage)"
|
|
74
88
|
|
|
75
89
|
SSH_ARGS=(-o ConnectTimeout=10 -o BatchMode=yes -o StrictHostKeyChecking=accept-new)
|
|
76
90
|
[[ -n "$SSH_KEY" ]] && SSH_ARGS+=(-i "$SSH_KEY")
|
|
@@ -87,7 +101,7 @@ if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
|
87
101
|
fi
|
|
88
102
|
|
|
89
103
|
# --- managed file payloads (built Mac-side, scp'd) ----------------------------
|
|
90
|
-
TMPDIR_LOCAL="$(mktemp -d /tmp/
|
|
104
|
+
TMPDIR_LOCAL="$(mktemp -d /tmp/kampodine-vm-prepare.XXXXXX)"
|
|
91
105
|
trap 'rm -rf "$TMPDIR_LOCAL"' EXIT
|
|
92
106
|
|
|
93
107
|
cat > "$TMPDIR_LOCAL/registries.conf" <<'REGISTRIES'
|
|
@@ -102,17 +116,17 @@ location = "127.0.0.1:5000"
|
|
|
102
116
|
insecure = true
|
|
103
117
|
REGISTRIES
|
|
104
118
|
|
|
105
|
-
cat > "$TMPDIR_LOCAL/60-
|
|
119
|
+
cat > "$TMPDIR_LOCAL/60-kampodine.conf" <<'SYSCTL'
|
|
106
120
|
# Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
|
|
107
121
|
# OpenRC contract: let unprivileged/container paths bind port 80
|
|
108
122
|
# (kamal-proxy publishes 80+443; belt-and-braces for a future rootless move).
|
|
109
123
|
net.ipv4.ip_unprivileged_port_start=80
|
|
110
124
|
SYSCTL
|
|
111
125
|
|
|
112
|
-
cat > "$TMPDIR_LOCAL/
|
|
126
|
+
cat > "$TMPDIR_LOCAL/kampodine-api" <<'INITD_API'
|
|
113
127
|
#!/sbin/openrc-run
|
|
114
128
|
# Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
|
|
115
|
-
# KEEP IN SYNC with
|
|
129
|
+
# KEEP IN SYNC with the golden image's container-service init template
|
|
116
130
|
# (rendered with role defaults): vm-prepare bootstraps a fresh VM BEFORE the
|
|
117
131
|
# first ansible converge; ansible owns the file afterwards (drift repair).
|
|
118
132
|
#
|
|
@@ -122,7 +136,7 @@ cat > "$TMPDIR_LOCAL/esellar-api" <<'INITD_API'
|
|
|
122
136
|
# dies, the WHOLE run restarts against the local :latest (no pull inside the
|
|
123
137
|
# run — restarts are offline-safe; the registry tunnel is down between deploys).
|
|
124
138
|
#
|
|
125
|
-
# Env: /etc/
|
|
139
|
+
# Env: /etc/kampodine/env (0600 root, written by kampodine deploy per deploy) is
|
|
126
140
|
# the single SECRETS source (podman --env-file reads KEY=VALUE lines directly).
|
|
127
141
|
# The five CLEAR vars (NODE_ENV, PORT, LIBSQL_TENANT_DIR, LIBSQL_API_MOUNT,
|
|
128
142
|
# STATIC_SPA_MOUNT) are owned HERE via -e — deploy filters them out of the env
|
|
@@ -130,16 +144,16 @@ cat > "$TMPDIR_LOCAL/esellar-api" <<'INITD_API'
|
|
|
130
144
|
# (tenant sqlite files) — without the bind mount, tenant data would be
|
|
131
145
|
# container-ephemeral and lost on every restart.
|
|
132
146
|
|
|
133
|
-
name="
|
|
134
|
-
description="
|
|
147
|
+
name="kampodine-api"
|
|
148
|
+
description="kampodine API + SPA container (127.0.0.1:5000/kampodine-api:latest on 8080)"
|
|
135
149
|
|
|
136
150
|
supervisor=supervise-daemon
|
|
137
151
|
command="/usr/bin/podman"
|
|
138
|
-
command_args="run --rm --name
|
|
152
|
+
command_args="run --rm --name kampodine-api --network kamal -p 8080:8080"
|
|
139
153
|
command_args="$command_args -v /data/tenants:/data/tenants"
|
|
140
154
|
command_args="$command_args -e NODE_ENV=production -e PORT=8080"
|
|
141
155
|
command_args="$command_args -e LIBSQL_TENANT_DIR=/data/tenants -e LIBSQL_API_MOUNT=1 -e STATIC_SPA_MOUNT=1"
|
|
142
|
-
command_args="$command_args --env-file /etc/
|
|
156
|
+
command_args="$command_args --env-file /etc/kampodine/env 127.0.0.1:5000/kampodine-api:latest"
|
|
143
157
|
|
|
144
158
|
# Respawn forever (blue's restart=always precedent): a crash loop self-heals at
|
|
145
159
|
# the next deploy's restart; the 10s delay bounds log noise.
|
|
@@ -147,26 +161,26 @@ respawn_delay=10
|
|
|
147
161
|
respawn_max=0
|
|
148
162
|
|
|
149
163
|
# podman run's OWN stderr (missing image, name conflicts, netavark failures);
|
|
150
|
-
# container stdout/stderr go to `podman logs
|
|
151
|
-
supervise_daemon_args="--stderr /var/log/
|
|
164
|
+
# container stdout/stderr go to `podman logs kampodine-api`.
|
|
165
|
+
supervise_daemon_args="--stderr /var/log/kampodine-service.log"
|
|
152
166
|
|
|
153
167
|
depend() {
|
|
154
168
|
need net
|
|
155
|
-
after
|
|
169
|
+
after kampodine-datamount
|
|
156
170
|
}
|
|
157
171
|
|
|
158
172
|
start_pre() {
|
|
159
173
|
# SIGKILLed runs leave the container name holding port 8080 (--rm only
|
|
160
174
|
# cleans CLEAN exits) — sweep any leftover before the supervised run.
|
|
161
|
-
podman container exists
|
|
175
|
+
podman container exists kampodine-api 2>/dev/null && podman rm -f kampodine-api >/dev/null 2>&1
|
|
162
176
|
return 0
|
|
163
177
|
}
|
|
164
178
|
|
|
165
179
|
stop_post() {
|
|
166
180
|
# supervise-daemon killed the podman client; make sure the container goes
|
|
167
181
|
# down too (TERM -> graceful shutdown, then a tolerant sweep).
|
|
168
|
-
podman stop --time 10
|
|
169
|
-
podman rm -f --time 0
|
|
182
|
+
podman stop --time 10 kampodine-api >/dev/null 2>&1 || true
|
|
183
|
+
podman rm -f --time 0 kampodine-api >/dev/null 2>&1 || true
|
|
170
184
|
}
|
|
171
185
|
INITD_API
|
|
172
186
|
|
|
@@ -174,7 +188,7 @@ cat > "$TMPDIR_LOCAL/kamal-proxy" <<'INITD_PROXY'
|
|
|
174
188
|
#!/sbin/openrc-run
|
|
175
189
|
# Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
|
|
176
190
|
# KEEP IN SYNC with ansible/roles/container-service/templates/kamal-proxy.initd.j2
|
|
177
|
-
# (see the
|
|
191
|
+
# (see the kampodine-api header for the vm-prepare/ansible split).
|
|
178
192
|
#
|
|
179
193
|
# TLS edge (kamal-proxy, Let's Encrypt HTTP-01 on :80). Publishes 80+443; the
|
|
180
194
|
# LE certs + host->target registrations persist in the kamal-proxy-config named
|
|
@@ -182,7 +196,7 @@ cat > "$TMPDIR_LOCAL/kamal-proxy" <<'INITD_PROXY'
|
|
|
182
196
|
# the first deploy survives container recreation AND reboots. Start-fresh:
|
|
183
197
|
# nothing is carried from the retired blue VM. Target registration happens at
|
|
184
198
|
# deploy time (kampodine deploy: podman exec kamal-proxy kamal-proxy deploy
|
|
185
|
-
#
|
|
199
|
+
# kampodine-api --host=$PROXY_HOST --target=kampodine-api:8080 --tls
|
|
186
200
|
# --health-check-path=/api/auth/ok).
|
|
187
201
|
|
|
188
202
|
name="kamal-proxy"
|
|
@@ -212,19 +226,19 @@ stop_post() {
|
|
|
212
226
|
}
|
|
213
227
|
INITD_PROXY
|
|
214
228
|
|
|
215
|
-
cat > "$TMPDIR_LOCAL/
|
|
229
|
+
cat > "$TMPDIR_LOCAL/kampodine-anchor.sh" <<'ANCHOR_WATCHER'
|
|
216
230
|
#!/bin/sh
|
|
217
|
-
# KEEP IN SYNC with ansible/roles/container-service/files/
|
|
231
|
+
# KEEP IN SYNC with ansible/roles/container-service/files/kampodine-anchor.sh
|
|
218
232
|
# (vm-prepare rendered-content convention: vm-prepare bootstraps a fresh VM
|
|
219
233
|
# BEFORE the first ansible converge; ansible owns the file afterwards).
|
|
220
234
|
#
|
|
221
|
-
#
|
|
235
|
+
# kampodine-anchor.sh — guest half of the blue-green reserved-ip flip.
|
|
222
236
|
#
|
|
223
237
|
# OCI assigns the reserved PUBLIC ip to a SECONDARY private ip ("the anchor")
|
|
224
238
|
# on the instance VNIC, but this image has NO oracle-cloud-agent: nothing
|
|
225
239
|
# configures that private ip inside the guest, so packets to the reserved ip
|
|
226
240
|
# die until the address exists on the interface. `kampodine bluegreen flip` writes
|
|
227
|
-
# /etc/
|
|
241
|
+
# /etc/kampodine/anchor.conf over ssh at flip time; this watcher polls it and
|
|
228
242
|
# runs `ip addr add` within one interval. The unit is enabled+started on every
|
|
229
243
|
# VM by default and is INERT without the conf — a VM that never flips never
|
|
230
244
|
# touches its addresses.
|
|
@@ -236,13 +250,13 @@ cat > "$TMPDIR_LOCAL/esellar-anchor.sh" <<'ANCHOR_WATCHER'
|
|
|
236
250
|
# ADD-ONLY BY DESIGN: this script NEVER removes or flushes addresses (rollback
|
|
237
251
|
# cleanup is the flip tool's explicit ssh job). Idempotent: a present address
|
|
238
252
|
# is a no-op. Only state TRANSITIONS are logged (bounded log noise), to
|
|
239
|
-
# stdout — supervise-daemon tees it to /var/log/
|
|
253
|
+
# stdout — supervise-daemon tees it to /var/log/kampodine-anchor.log.
|
|
240
254
|
|
|
241
|
-
CONF="/etc/
|
|
242
|
-
INTERVAL="${
|
|
255
|
+
CONF="/etc/kampodine/anchor.conf"
|
|
256
|
+
INTERVAL="${KAMPODINE_ANCHOR_INTERVAL:-5}"
|
|
243
257
|
STATE="boot" # last logged transition (boot | idle | added | error)
|
|
244
258
|
|
|
245
|
-
log() { printf '%s
|
|
259
|
+
log() { printf '%s kampodine-anchor: %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "$*"; }
|
|
246
260
|
|
|
247
261
|
# supervise-daemon SIGTERMs on stop — die cleanly with the current tick.
|
|
248
262
|
trap 'exit 0' TERM INT
|
|
@@ -283,33 +297,33 @@ while :; do
|
|
|
283
297
|
done
|
|
284
298
|
ANCHOR_WATCHER
|
|
285
299
|
|
|
286
|
-
cat > "$TMPDIR_LOCAL/
|
|
300
|
+
cat > "$TMPDIR_LOCAL/kampodine-anchor" <<'INITD_ANCHOR'
|
|
287
301
|
#!/sbin/openrc-run
|
|
288
302
|
# Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
|
|
289
|
-
# KEEP IN SYNC with ansible/roles/container-service/templates/
|
|
290
|
-
# (rendered with role defaults — see the
|
|
303
|
+
# KEEP IN SYNC with ansible/roles/container-service/templates/kampodine-anchor.initd.j2
|
|
304
|
+
# (rendered with role defaults — see the kampodine-api header for the
|
|
291
305
|
# vm-prepare/ansible split).
|
|
292
306
|
#
|
|
293
307
|
# Guest half of the blue-green reserved-ip flip: supervise-daemon runs the
|
|
294
|
-
#
|
|
308
|
+
# kampodine-anchor.sh watcher, which polls /etc/kampodine/anchor.conf (written by
|
|
295
309
|
# `kampodine bluegreen flip` over ssh at flip time) and `ip addr add`s the
|
|
296
310
|
# anchor address when it appears. Started+enabled on EVERY vm by default and
|
|
297
311
|
# INERT without the conf: a VM that never flips never touches its addresses —
|
|
298
|
-
# unlike the container units, there is no
|
|
299
|
-
# Transitions land in /var/log/
|
|
312
|
+
# unlike the container units, there is no kampodine_start_containers gate.
|
|
313
|
+
# Transitions land in /var/log/kampodine-anchor.log.
|
|
300
314
|
|
|
301
|
-
name="
|
|
315
|
+
name="kampodine-anchor"
|
|
302
316
|
description="Blue-green reserved-ip anchor address watcher (guest half of the flip)"
|
|
303
317
|
|
|
304
318
|
supervisor=supervise-daemon
|
|
305
|
-
command="/usr/local/sbin/
|
|
319
|
+
command="/usr/local/sbin/kampodine-anchor.sh"
|
|
306
320
|
|
|
307
321
|
# Respawn forever: the watcher itself never exits (trap TERM/INT -> exit 0 on
|
|
308
322
|
# stop), so a respawn means the script died abnormally — retry gently.
|
|
309
323
|
respawn_delay=5
|
|
310
324
|
respawn_max=0
|
|
311
325
|
|
|
312
|
-
supervise_daemon_args="--stdout /var/log/
|
|
326
|
+
supervise_daemon_args="--stdout /var/log/kampodine-anchor.log --stderr /var/log/kampodine-anchor.log"
|
|
313
327
|
|
|
314
328
|
depend() {
|
|
315
329
|
need net
|
|
@@ -366,7 +380,7 @@ say "sshd hardening: ensure drop-in + Include + restart (golden image predates t
|
|
|
366
380
|
vm 'grep -q "^Include /etc/ssh/sshd_config.d/\*.conf" /etc/ssh/sshd_config || sed -i "1i Include /etc/ssh/sshd_config.d/*.conf" /etc/ssh/sshd_config' \
|
|
367
381
|
|| die "could not ensure the sshd_config Include line"
|
|
368
382
|
vm 'mkdir -p /etc/ssh/sshd_config.d && chmod 700 /etc/ssh/sshd_config.d'
|
|
369
|
-
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/sshd-hardening.conf" "$HOST:/etc/ssh/sshd_config.d/99-
|
|
383
|
+
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/sshd-hardening.conf" "$HOST:/etc/ssh/sshd_config.d/99-kampodine-hardening.conf"
|
|
370
384
|
vm 'rc-service sshd restart' || die "sshd restart failed after hardening ensure"
|
|
371
385
|
|
|
372
386
|
say "gate: sshd hardening effective"
|
|
@@ -404,50 +418,50 @@ PODSTACK_GATE
|
|
|
404
418
|
vm 'rc-update show boot | grep -q cgroups || rc-update add cgroups boot' || die "rc-update cgroups failed"
|
|
405
419
|
vm 'rc-service cgroups status >/dev/null 2>&1 || rc-service cgroups start' || die "cgroups start failed"
|
|
406
420
|
|
|
407
|
-
# --- 3. /etc/
|
|
408
|
-
say "creating /etc/
|
|
409
|
-
vm 'mkdir -p /etc/
|
|
421
|
+
# --- 3. /etc/kampodine + managed config files ------------------------------------
|
|
422
|
+
say "creating /etc/kampodine (env file lands here via kampodine deploy, 0600 root)…"
|
|
423
|
+
vm 'mkdir -p /etc/kampodine && chmod 700 /etc/kampodine'
|
|
410
424
|
|
|
411
425
|
say "writing /etc/containers/registries.conf (insecure 127.0.0.1:5000; search docker.io)…"
|
|
412
426
|
vm 'mkdir -p /etc/containers'
|
|
413
427
|
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/registries.conf" "$HOST:/etc/containers/registries.conf"
|
|
414
428
|
|
|
415
|
-
say "writing /etc/sysctl.d/60-
|
|
416
|
-
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/60-
|
|
429
|
+
say "writing /etc/sysctl.d/60-kampodine.conf + applying live…"
|
|
430
|
+
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/60-kampodine.conf" "$HOST:/etc/sysctl.d/60-kampodine.conf"
|
|
417
431
|
vm_sh <<'SYSCTL_APPLY' || die "sysctl ensure/apply failed"
|
|
418
432
|
rc-update show boot | grep -Eq '^[[:space:]]*sysctl[[:space:]]*\|' || rc-update add sysctl boot
|
|
419
433
|
sysctl -w net.ipv4.ip_unprivileged_port_start=80 > /dev/null
|
|
420
434
|
SYSCTL_APPLY
|
|
421
435
|
|
|
422
436
|
# --- 4. OpenRC services (canonical: ansible container-service templates) --------
|
|
423
|
-
say "ensuring the 'kamal' podman network (deploy's proxy re-point resolves
|
|
437
|
+
say "ensuring the 'kamal' podman network (deploy's proxy re-point resolves kampodine-api:8080 by network DNS)…"
|
|
424
438
|
# shellcheck disable=SC2016
|
|
425
439
|
vm 'podman network exists kamal 2>/dev/null || podman network create kamal' \
|
|
426
440
|
|| die "podman network create kamal failed"
|
|
427
|
-
say "installing OpenRC services
|
|
428
|
-
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/
|
|
441
|
+
say "installing OpenRC services kampodine-api + kamal-proxy (supervise-daemon around podman run)…"
|
|
442
|
+
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kampodine-api" "$HOST:/etc/init.d/kampodine-api"
|
|
429
443
|
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kamal-proxy" "$HOST:/etc/init.d/kamal-proxy"
|
|
430
|
-
vm 'chmod 755 /etc/init.d/
|
|
444
|
+
vm 'chmod 755 /etc/init.d/kampodine-api /etc/init.d/kamal-proxy'
|
|
431
445
|
|
|
432
|
-
# --- 4b.
|
|
446
|
+
# --- 4b. kampodine-anchor (blue-green flip guest half — watcher, no container) ----
|
|
433
447
|
# Ships on EVERY vm: started + enabled now, INERT until a flip writes
|
|
434
|
-
# /etc/
|
|
448
|
+
# /etc/kampodine/anchor.conf over ssh (ansible container-service owns both files
|
|
435
449
|
# afterwards — drift repair keeps them in sync).
|
|
436
|
-
say "installing the
|
|
450
|
+
say "installing the kampodine-anchor watcher service (blue-green flip guest half)…"
|
|
437
451
|
# /usr/local/sbin does NOT exist on the golden image (fresh Alpine ships no
|
|
438
452
|
# /usr/local hierarchy; the alpine-base role creates it — vm-prepare runs
|
|
439
453
|
# BEFORE any ansible)
|
|
440
454
|
vm 'mkdir -p /usr/local/sbin' || die "mkdir /usr/local/sbin failed"
|
|
441
|
-
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/
|
|
442
|
-
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/
|
|
443
|
-
vm 'chmod 755 /usr/local/sbin/
|
|
444
|
-
vm 'sh -n /usr/local/sbin/
|
|
445
|
-
vm 'sh -n /etc/init.d/
|
|
446
|
-
vm 'rc-update show default | grep -qE "^[[:space:]]*
|
|
447
|
-
|| die "rc-update add
|
|
448
|
-
vm 'rc-service
|
|
449
|
-
|
|
450
|
-
for SVC in
|
|
455
|
+
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kampodine-anchor.sh" "$HOST:/usr/local/sbin/kampodine-anchor.sh"
|
|
456
|
+
scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kampodine-anchor" "$HOST:/etc/init.d/kampodine-anchor"
|
|
457
|
+
vm 'chmod 755 /usr/local/sbin/kampodine-anchor.sh /etc/init.d/kampodine-anchor' || die "chmod kampodine-anchor files failed"
|
|
458
|
+
vm 'sh -n /usr/local/sbin/kampodine-anchor.sh' || die "kampodine-anchor.sh does not parse (busybox sh)"
|
|
459
|
+
vm 'sh -n /etc/init.d/kampodine-anchor' || die "/etc/init.d/kampodine-anchor does not parse"
|
|
460
|
+
vm 'rc-update show default | grep -qE "^[[:space:]]*kampodine-anchor[[:space:]]*\\|" || rc-update add kampodine-anchor default' \
|
|
461
|
+
|| die "rc-update add kampodine-anchor default failed"
|
|
462
|
+
vm 'rc-service kampodine-anchor start' || die "rc-service kampodine-anchor start failed"
|
|
463
|
+
|
|
464
|
+
for SVC in kampodine-api kamal-proxy; do
|
|
451
465
|
vm "rc-update show default | grep -qE '^[[:space:]]*${SVC}[[:space:]]*\\|' || rc-update add ${SVC} default" \
|
|
452
466
|
|| die "rc-update add $SVC default failed"
|
|
453
467
|
done
|
|
@@ -456,7 +470,7 @@ done
|
|
|
456
470
|
# re-runs after a deploy must not bounce production.
|
|
457
471
|
# shellcheck disable=SC2016
|
|
458
472
|
vm_sh <<'UNIT_DRIFT' || true
|
|
459
|
-
for SVC in
|
|
473
|
+
for SVC in kampodine-api kamal-proxy kampodine-anchor; do
|
|
460
474
|
if rc-service "$SVC" status > /dev/null 2>&1; then
|
|
461
475
|
echo "RUNNING: $SVC (unit file was overwritten — rc-service $SVC restart to apply, on your call)"
|
|
462
476
|
fi
|
|
@@ -483,26 +497,26 @@ if [[ $DO_PULL -eq 1 ]]; then
|
|
|
483
497
|
# installs it later; vm-prepare runs BEFORE any ansible)
|
|
484
498
|
vm 'busybox wget -q -O /dev/null http://127.0.0.1:5000/v2/' || {
|
|
485
499
|
pkill -f "ssh.*-R 5000" 2>/dev/null || true; sleep 1
|
|
486
|
-
nohup ssh -R 5000:127.0.0.1:5000 -N -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes "$HOST" >/tmp/
|
|
500
|
+
nohup ssh -R 5000:127.0.0.1:5000 -N -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes "$HOST" >/tmp/kampodine-tunnel.log 2>&1 &
|
|
487
501
|
sleep 3
|
|
488
502
|
}
|
|
489
|
-
vm 'busybox wget -q -O /dev/null http://127.0.0.1:5000/v2/' || die "registry tunnel did not come up (/tmp/
|
|
490
|
-
vm 'podman pull --tls-verify=false 127.0.0.1:5000/
|
|
503
|
+
vm 'busybox wget -q -O /dev/null http://127.0.0.1:5000/v2/' || die "registry tunnel did not come up (/tmp/kampodine-tunnel.log)"
|
|
504
|
+
vm 'podman pull --tls-verify=false 127.0.0.1:5000/kampodine-api:latest' || die "app image pull failed"
|
|
491
505
|
else
|
|
492
506
|
say "skipping app-image pre-pull (pass --pull-images, or let the first deploy pull)"
|
|
493
507
|
fi
|
|
494
508
|
|
|
495
|
-
# --- 7. converge
|
|
509
|
+
# --- 7. converge kampodine-api ONLY if a previous deploy left image + env ---------
|
|
496
510
|
# On a truly fresh VM neither exists — the FIRST DEPLOY provides both and
|
|
497
511
|
# starts the service. Re-runs of this script after a deploy heal drift.
|
|
498
|
-
say "
|
|
512
|
+
say "kampodine-api start check (needs image + /etc/kampodine/env — first deploy provides both)…"
|
|
499
513
|
# shellcheck disable=SC2016
|
|
500
|
-
vm_sh <<'API_CONVERGE' || die "
|
|
501
|
-
if [ -f /etc/
|
|
502
|
-
rc-service
|
|
503
|
-
echo "
|
|
514
|
+
vm_sh <<'API_CONVERGE' || die "kampodine-api start failed (image + env present — investigate: podman logs kampodine-api)"
|
|
515
|
+
if [ -f /etc/kampodine/env ] && podman image exists 127.0.0.1:5000/kampodine-api:latest; then
|
|
516
|
+
rc-service kampodine-api start
|
|
517
|
+
echo "kampodine-api started (image + env present)"
|
|
504
518
|
else
|
|
505
|
-
echo "
|
|
519
|
+
echo "kampodine-api deferred: no image and/or /etc/kampodine/env yet (normal on a fresh VM — first deploy handles it)"
|
|
506
520
|
fi
|
|
507
521
|
API_CONVERGE
|
|
508
522
|
|
|
@@ -522,16 +536,16 @@ vm 'grep -q "127.0.0.1:5000" /etc/containers/registries.conf && grep -q "insecur
|
|
|
522
536
|
|| die "registries.conf incomplete (127.0.0.1:5000 insecure + docker.io search)"
|
|
523
537
|
|
|
524
538
|
say "gate: services enabled in the default runlevel"
|
|
525
|
-
vm 'rc-update show default | grep -qE "^[[:space:]]*
|
|
539
|
+
vm 'rc-update show default | grep -qE "^[[:space:]]*kampodine-api[[:space:]]*\\|"' || die "kampodine-api not in the default runlevel"
|
|
526
540
|
vm 'rc-update show default | grep -qE "^[[:space:]]*kamal-proxy[[:space:]]*\\|"' || die "kamal-proxy not in the default runlevel"
|
|
527
541
|
vm 'rc-service kamal-proxy status >/dev/null' || die "kamal-proxy service not started"
|
|
528
542
|
|
|
529
|
-
say "gate:
|
|
530
|
-
vm 'rc-update show default | grep -qE "^[[:space:]]*
|
|
531
|
-
vm 'rc-service
|
|
532
|
-
vm '! test -e /etc/
|
|
533
|
-
vm 'grep -q "no anchor conf" /var/log/
|
|
534
|
-
|| die "
|
|
543
|
+
say "gate: kampodine-anchor watcher running + INERT (no anchor conf on a fresh VM)"
|
|
544
|
+
vm 'rc-update show default | grep -qE "^[[:space:]]*kampodine-anchor[[:space:]]*\\|"' || die "kampodine-anchor not in the default runlevel"
|
|
545
|
+
vm 'rc-service kampodine-anchor status >/dev/null' || die "kampodine-anchor service not started"
|
|
546
|
+
vm '! test -e /etc/kampodine/anchor.conf' || die "/etc/kampodine/anchor.conf already exists on a fresh VM (wrong machine?)"
|
|
547
|
+
vm 'grep -q "no anchor conf" /var/log/kampodine-anchor.log' \
|
|
548
|
+
|| die "kampodine-anchor is started but never logged its idle transition (watcher loop not running?)"
|
|
535
549
|
|
|
536
550
|
# --- summary ---------------------------------------------------------------------
|
|
537
551
|
IP="${HOST#*@}"
|