kampodine 0.2.2 → 0.4.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.
@@ -14,32 +14,42 @@
14
14
  # Auth: ~/.oci/config (brew install oci-cli; oci setup config) by default, or
15
15
  # --from-pass to source API credentials from the local pass store — the key
16
16
  # NEVER enters this repo, the image, or any log (values only go into env vars).
17
- # Every oci call pins --profile $PROFILE (OCI_PROFILE, default esellar-api) —
18
- # the CLI's DEFAULT profile is NOT the esellar tenancy.
17
+ # Every oci call pins --profile $PROFILE (OCI_PROFILE, default "default") —
18
+ # the CLI's DEFAULT profile is NOT the kampodine tenancy.
19
19
  # Placeholder pass paths (create yours to match):
20
- # esellar/oci/user user OCID
21
- # esellar/oci/tenancy tenancy OCID
22
- # esellar/oci/fingerprint API key fingerprint
23
- # esellar/oci/api-key PEM private key
20
+ # kampodine/oci/user user OCID
21
+ # kampodine/oci/tenancy tenancy OCID
22
+ # kampodine/oci/fingerprint API key fingerprint
23
+ # kampodine/oci/api-key PEM private key
24
24
  #
25
25
  # Usage:
26
26
  # kampodine image-import # defaults below
27
27
  # kampodine image-import --from-pass
28
- # kampodine image-import --image build/esellar-alpine-3.22.6-aarch64.qcow2
28
+ # kampodine image-import --image build/kampodine-alpine-3.22.6-aarch64.qcow2
29
29
  set -euo pipefail
30
30
 
31
31
  REPO_ROOT="$(git rev-parse --show-toplevel)"
32
32
  IMAGE=""
33
- BUCKET="${OCI_IMPORT_BUCKET:-esellar-image-import}"
34
- NAME_PREFIX="${OCI_IMPORT_NAME:-esellar-alpine}"
35
- COMPARTMENT_NAME="${OCI_COMPARTMENT:-esellar}"
36
- PROFILE="${OCI_PROFILE:-esellar-api}"
33
+ BUCKET="${OCI_IMPORT_BUCKET:-kampodine-image-import}"
34
+ NAME_PREFIX="${OCI_IMPORT_NAME:-kampodine-alpine}"
35
+ COMPARTMENT_NAME="${OCI_COMPARTMENT:-kampodine}"
36
+ PROFILE="${OCI_PROFILE:-default}"
37
37
  FROM_PASS=0
38
38
  KEEP_OBJECT=0
39
39
 
40
40
  say() { printf '[import] %s\n' "$*"; }
41
41
  die() { printf '[import][FAIL] %s\n' "$*" >&2; exit 1; }
42
- usage() { grep '^# kampodine image-import' "$0" | sed 's/^# //'; exit 0; }
42
+ usage() {
43
+ cat <<'EOF'
44
+ Usage:
45
+ kampodine image-import [--image <qcow2>] [--bucket <name>] [--name-prefix <p>]
46
+ [--compartment <name>] [--from-pass] [--keep-object]
47
+
48
+ Examples:
49
+ EOF
50
+ grep '^# kampodine image-import' "$0" | sed 's/^# //'
51
+ exit 0
52
+ }
43
53
 
44
54
  while [[ $# -gt 0 ]]; do
45
55
  case "$1" in
@@ -55,18 +65,19 @@ while [[ $# -gt 0 ]]; do
55
65
  done
56
66
 
57
67
  command -v oci >/dev/null 2>&1 || die "oci CLI not found — brew install oci-cli, then: oci setup config (infra/oci/README.md § CLI setup)"
68
+ [[ -n "${OCI_COMPARTMENT:-}" ]] || die "set OCI_COMPARTMENT=<compartment name or ocid> — no default: compartments are account-specific"
58
69
  command -v pass >/dev/null 2>&1 || true
59
70
 
60
71
  if [[ $FROM_PASS -eq 1 ]]; then
61
72
  command -v pass >/dev/null 2>&1 || die "pass not installed (brew install pass)"
62
73
  say "sourcing OCI API credentials from pass (values never printed/logged)…"
63
74
  for entry in user tenancy fingerprint api-key; do
64
- pass show "esellar/oci/$entry" >/dev/null 2>&1 || die "missing pass entry esellar/oci/$entry"
75
+ pass show "kampodine/oci/$entry" >/dev/null 2>&1 || die "missing pass entry kampodine/oci/$entry"
65
76
  done
66
- OCI_CLI_USER="$(pass show esellar/oci/user)"
67
- OCI_CLI_TENANCY="$(pass show esellar/oci/tenancy)"
68
- OCI_CLI_FINGERPRINT="$(pass show esellar/oci/fingerprint)"
69
- OCI_CLI_KEY_CONTENT="$(pass show esellar/oci/api-key)"
77
+ OCI_CLI_USER="$(pass show kampodine/oci/user)"
78
+ OCI_CLI_TENANCY="$(pass show kampodine/oci/tenancy)"
79
+ OCI_CLI_FINGERPRINT="$(pass show kampodine/oci/fingerprint)"
80
+ OCI_CLI_KEY_CONTENT="$(pass show kampodine/oci/api-key)"
70
81
  export OCI_CLI_USER OCI_CLI_TENANCY OCI_CLI_FINGERPRINT OCI_CLI_KEY_CONTENT
71
82
  fi
72
83
 
@@ -74,7 +85,7 @@ fi
74
85
  if [[ -z "$IMAGE" ]]; then
75
86
  # Constrained glob (our own build output names) — ls is fine here.
76
87
  # shellcheck disable=SC2012
77
- IMAGE="$(ls -t "$REPO_ROOT/infra/alpine-host/build"/esellar-alpine-*.qcow2 2>/dev/null | head -1 || true)"
88
+ IMAGE="$(ls -t "$REPO_ROOT/infra/alpine-host/build"/kampodine-alpine-*.qcow2 2>/dev/null | head -1 || true)"
78
89
  [[ -n "$IMAGE" ]] || die "no qcow2 under infra/alpine-host/build/ — run: (cd infra/alpine-host && packer build .)"
79
90
  fi
80
91
  [[ -f "$IMAGE" ]] || die "image not found: $IMAGE"
@@ -86,7 +97,7 @@ NAMESPACE="$(oci os ns get --profile "$PROFILE" --query data --raw-output)"
86
97
 
87
98
  STAMP="$(date +%Y%m%d-%H%M%S)"
88
99
  OBJECT_NAME="${NAME_PREFIX}-${STAMP}.qcow2"
89
- IMAGE_NAME="${NAME_PREFIX}-$(basename "$IMAGE" .qcow2 | sed 's/^esellar-alpine-//')-${STAMP}"
100
+ IMAGE_NAME="${NAME_PREFIX}-$(basename "$IMAGE" .qcow2 | sed 's/^kampodine-alpine-//')-${STAMP}"
90
101
 
91
102
  say "uploading $(basename "$IMAGE") -> os://$BUCKET/$OBJECT_NAME"
92
103
  oci os object put -bn "$BUCKET" --profile "$PROFILE" --file "$IMAGE" --name "$OBJECT_NAME" --force \
@@ -152,7 +163,7 @@ say " display : $IMAGE_NAME"
152
163
  say " firmware : $IMPORT_FIRMWARE"
153
164
  say "next:"
154
165
  if [[ "$IMPORT_FIRMWARE" == "UEFI_64" ]]; then
155
- say " A1-ready. kampodine bluegreen provision <blue|green> picks this image (newest esellar-alpine*)."
166
+ say " A1-ready. kampodine bluegreen provision <blue|green> picks this image (newest kampodine-alpine*)."
156
167
  else
157
168
  say " A1 launch will REJECT this image (firmware $IMPORT_FIRMWARE) — see the WARN above."
158
169
  fi
@@ -15,17 +15,33 @@ set -euo pipefail
15
15
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
16
  REPO_ROOT="${REPO_ROOT:-$(cd "${SCRIPT_DIR}/../../.." && pwd)}"
17
17
  TENANT_DIR="${TENANT_DIR:-/data/tenants}"
18
- SERVICE="${SERVICE:-esellar-api}"
18
+ SERVICE="${SERVICE:-kampodine-api}"
19
19
 
20
20
  log() { printf '[migrate-all] %s\n' "$*"; }
21
21
  fail() { printf '[migrate-all][FAIL] %s\n' "$*" >&2; exit 1; }
22
+ usage() {
23
+ cat <<'EOF'
24
+ Usage:
25
+ kampodine migrate [--allow-running]
26
+
27
+ Examples:
28
+ kampodine migrate # stop the API first (rc-service kampodine-api stop)
29
+ kampodine migrate --allow-running # deliberate: migrate while the API serves (SQLITE_BUSY risk)
30
+
31
+ Runs drizzle migrations over the libSQL dbs under TENANT_DIR (default
32
+ /data/tenants): root.db first (auth/org plane), then tenant_*.db sorted,
33
+ bounded-parallel (MIGRATE_JOBS, default 4). Per-file failures are collected;
34
+ exits 1 if any failed.
35
+ EOF
36
+ exit 0
37
+ }
22
38
 
23
39
  ALLOW_RUNNING=0
24
40
  for arg in "$@"; do
25
41
  case "$arg" in
26
42
  --allow-running) ALLOW_RUNNING=1 ;;
27
- -h|--help) echo "usage: $0 [--allow-running]"; exit 0 ;;
28
- *) fail "unknown argument: $arg" ;;
43
+ -h|--help) usage ;;
44
+ *) fail "unknown argument: $arg (--help)" ;;
29
45
  esac
30
46
  done
31
47
 
@@ -35,11 +51,11 @@ command -v pnpm >/dev/null 2>&1 || fail "pnpm not found in PATH"
35
51
  [[ -d "$TENANT_DIR" ]] || fail "tenant dir ${TENANT_DIR} does not exist"
36
52
 
37
53
  # Writing schema while the API serves traffic risks SQLITE_BUSY on a single-writer
38
- # engine — the service must be stopped first (rc-service esellar-api stop). Override exists
54
+ # engine — the service must be stopped first (rc-service kampodine-api stop). Override exists
39
55
  # for deliberate local use.
40
56
  if systemctl is-active --quiet "$SERVICE" 2>/dev/null; then
41
57
  if [[ $ALLOW_RUNNING -ne 1 ]]; then
42
- fail "${SERVICE} is running — stop it first (rc-service esellar-api stop) or pass --allow-running"
58
+ fail "${SERVICE} is running — stop it first (rc-service kampodine-api stop) or pass --allow-running"
43
59
  fi
44
60
  log "WARNING: migrating while ${SERVICE} is running (--allow-running)"
45
61
  fi
package/scripts/status.sh CHANGED
@@ -2,12 +2,35 @@
2
2
  # status.sh — one command: what is LIVE (through the proxy) + the blue/green
3
3
  # pair view (instances, reserved IP, health).
4
4
  #
5
+ # Usage:
6
+ # kampodine status
7
+ #
5
8
  # Env: APP_HOST_HEADER (default: app.example.com), OCI_PROFILE, OCI_COMPARTMENT.
6
9
  set -euo pipefail
7
10
  HERE="$(cd "$(dirname "$0")" && pwd)"
8
11
  HOST="${APP_HOST_HEADER:-app.example.com}"
9
12
 
10
13
  say() { printf '%s\n' "$*"; }
14
+ die() { printf '[status] FAIL: %s\n' "$*" >&2; exit 1; }
15
+ usage() {
16
+ cat <<'EOF'
17
+ Usage:
18
+ kampodine status
19
+
20
+ Examples:
21
+ kampodine status # live health (proxy host) + the blue/green pair view
22
+
23
+ Env: APP_HOST_HEADER (default app.example.com), OCI_PROFILE, OCI_COMPARTMENT.
24
+ EOF
25
+ exit 0
26
+ }
27
+
28
+ while [[ $# -gt 0 ]]; do
29
+ case "$1" in
30
+ -h|--help) usage ;;
31
+ *) die "unknown argument: $1 (--help)" ;;
32
+ esac
33
+ done
11
34
 
12
35
  say "== live (through the proxy: https://$HOST) =="
13
36
  if body="$(curl -sf -m 8 "https://$HOST/api/auth/ok" 2>/dev/null)"; then
@@ -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/esellar/ (kampodine deploy writes /etc/esellar/env here, 0600 root)
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/esellar-api + /etc/init.d/kamal-proxy
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. esellar-anchor: the blue-green reserved-ip flip's GUEST half — a
22
- # busybox watcher (no container) that polls /etc/esellar/anchor.conf and
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 esellar-api container is NOT started here: neither its image (pulled by
29
- # the first deploy over the registry tunnel) nor /etc/esellar/env (written by
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
@@ -36,16 +36,30 @@
36
36
  #
37
37
  # kampodine vm-prepare --host root@<new-ip>
38
38
  # ... --pull-images # + pre-pull the app image over the registry tunnel
39
- # ... --ssh-key <path> # ssh identity (default: agent / ESPELLAR_SSH_KEY)
39
+ # ... --ssh-key <path> # ssh identity (default: agent / KAMPODINE_SSH_KEY)
40
40
  set -euo pipefail
41
41
 
42
42
  HOST=""
43
43
  DO_PULL=0
44
- SSH_KEY="${ESPELLAR_SSH_KEY:-}"
44
+ SSH_KEY="${KAMPODINE_SSH_KEY:-}"
45
45
 
46
46
  say() { printf '\033[1;32m[vm-prepare]\033[0m %s\n' "$*"; }
47
47
  die() { printf '\033[1;31m[vm-prepare] FAIL:\033[0m %s\n' "$*" >&2; exit 1; }
48
- usage() { grep '^# kampodine vm-prepare' "$0" | sed 's/^# //'; exit 0; }
48
+ usage() {
49
+ cat <<'EOF'
50
+ Usage:
51
+ kampodine vm-prepare --host root@<new-ip> [--pull-images] [--ssh-key <path>]
52
+
53
+ Examples:
54
+ EOF
55
+ grep '^# kampodine vm-prepare' "$0" | sed 's/^# //'
56
+ cat <<'EOF'
57
+
58
+ Host/key resolution: --host | (no env default — explicit flag);
59
+ --ssh-key | KAMPODINE_SSH_KEY | ssh-agent / ~/.ssh/config.
60
+ EOF
61
+ exit 0
62
+ }
49
63
 
50
64
  while [[ $# -gt 0 ]]; do
51
65
  case "$1" in
@@ -73,7 +87,7 @@ if [[ "$(uname -s)" == "Darwin" ]]; then
73
87
  fi
74
88
 
75
89
  # --- managed file payloads (built Mac-side, scp'd) ----------------------------
76
- TMPDIR_LOCAL="$(mktemp -d /tmp/esellar-vm-prepare.XXXXXX)"
90
+ TMPDIR_LOCAL="$(mktemp -d /tmp/kampodine-vm-prepare.XXXXXX)"
77
91
  trap 'rm -rf "$TMPDIR_LOCAL"' EXIT
78
92
 
79
93
  cat > "$TMPDIR_LOCAL/registries.conf" <<'REGISTRIES'
@@ -88,17 +102,17 @@ location = "127.0.0.1:5000"
88
102
  insecure = true
89
103
  REGISTRIES
90
104
 
91
- cat > "$TMPDIR_LOCAL/60-esellar.conf" <<'SYSCTL'
105
+ cat > "$TMPDIR_LOCAL/60-kampodine.conf" <<'SYSCTL'
92
106
  # Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
93
107
  # OpenRC contract: let unprivileged/container paths bind port 80
94
108
  # (kamal-proxy publishes 80+443; belt-and-braces for a future rootless move).
95
109
  net.ipv4.ip_unprivileged_port_start=80
96
110
  SYSCTL
97
111
 
98
- cat > "$TMPDIR_LOCAL/esellar-api" <<'INITD_API'
112
+ cat > "$TMPDIR_LOCAL/kampodine-api" <<'INITD_API'
99
113
  #!/sbin/openrc-run
100
114
  # Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
101
- # KEEP IN SYNC with ansible/roles/container-service/templates/esellar-api.initd.j2
115
+ # KEEP IN SYNC with the golden image's container-service init template
102
116
  # (rendered with role defaults): vm-prepare bootstraps a fresh VM BEFORE the
103
117
  # first ansible converge; ansible owns the file afterwards (drift repair).
104
118
  #
@@ -108,7 +122,7 @@ cat > "$TMPDIR_LOCAL/esellar-api" <<'INITD_API'
108
122
  # dies, the WHOLE run restarts against the local :latest (no pull inside the
109
123
  # run — restarts are offline-safe; the registry tunnel is down between deploys).
110
124
  #
111
- # Env: /etc/esellar/env (0600 root, written by kampodine deploy per deploy) is
125
+ # Env: /etc/kampodine/env (0600 root, written by kampodine deploy per deploy) is
112
126
  # the single SECRETS source (podman --env-file reads KEY=VALUE lines directly).
113
127
  # The five CLEAR vars (NODE_ENV, PORT, LIBSQL_TENANT_DIR, LIBSQL_API_MOUNT,
114
128
  # STATIC_SPA_MOUNT) are owned HERE via -e — deploy filters them out of the env
@@ -116,16 +130,16 @@ cat > "$TMPDIR_LOCAL/esellar-api" <<'INITD_API'
116
130
  # (tenant sqlite files) — without the bind mount, tenant data would be
117
131
  # container-ephemeral and lost on every restart.
118
132
 
119
- name="esellar-api"
120
- description="esellar API + SPA container (127.0.0.1:5000/esellar-api:latest on 8080)"
133
+ name="kampodine-api"
134
+ description="kampodine API + SPA container (127.0.0.1:5000/kampodine-api:latest on 8080)"
121
135
 
122
136
  supervisor=supervise-daemon
123
137
  command="/usr/bin/podman"
124
- command_args="run --rm --name esellar-api --network kamal -p 8080:8080"
138
+ command_args="run --rm --name kampodine-api --network kamal -p 8080:8080"
125
139
  command_args="$command_args -v /data/tenants:/data/tenants"
126
140
  command_args="$command_args -e NODE_ENV=production -e PORT=8080"
127
141
  command_args="$command_args -e LIBSQL_TENANT_DIR=/data/tenants -e LIBSQL_API_MOUNT=1 -e STATIC_SPA_MOUNT=1"
128
- command_args="$command_args --env-file /etc/esellar/env 127.0.0.1:5000/esellar-api:latest"
142
+ command_args="$command_args --env-file /etc/kampodine/env 127.0.0.1:5000/kampodine-api:latest"
129
143
 
130
144
  # Respawn forever (blue's restart=always precedent): a crash loop self-heals at
131
145
  # the next deploy's restart; the 10s delay bounds log noise.
@@ -133,26 +147,26 @@ respawn_delay=10
133
147
  respawn_max=0
134
148
 
135
149
  # podman run's OWN stderr (missing image, name conflicts, netavark failures);
136
- # container stdout/stderr go to `podman logs esellar-api`.
137
- supervise_daemon_args="--stderr /var/log/esellar-api-service.log"
150
+ # container stdout/stderr go to `podman logs kampodine-api`.
151
+ supervise_daemon_args="--stderr /var/log/kampodine-service.log"
138
152
 
139
153
  depend() {
140
154
  need net
141
- after esellar-datamount
155
+ after kampodine-datamount
142
156
  }
143
157
 
144
158
  start_pre() {
145
159
  # SIGKILLed runs leave the container name holding port 8080 (--rm only
146
160
  # cleans CLEAN exits) — sweep any leftover before the supervised run.
147
- podman container exists esellar-api 2>/dev/null && podman rm -f esellar-api >/dev/null 2>&1
161
+ podman container exists kampodine-api 2>/dev/null && podman rm -f kampodine-api >/dev/null 2>&1
148
162
  return 0
149
163
  }
150
164
 
151
165
  stop_post() {
152
166
  # supervise-daemon killed the podman client; make sure the container goes
153
167
  # down too (TERM -> graceful shutdown, then a tolerant sweep).
154
- podman stop --time 10 esellar-api >/dev/null 2>&1 || true
155
- podman rm -f --time 0 esellar-api >/dev/null 2>&1 || true
168
+ podman stop --time 10 kampodine-api >/dev/null 2>&1 || true
169
+ podman rm -f --time 0 kampodine-api >/dev/null 2>&1 || true
156
170
  }
157
171
  INITD_API
158
172
 
@@ -160,7 +174,7 @@ cat > "$TMPDIR_LOCAL/kamal-proxy" <<'INITD_PROXY'
160
174
  #!/sbin/openrc-run
161
175
  # Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
162
176
  # KEEP IN SYNC with ansible/roles/container-service/templates/kamal-proxy.initd.j2
163
- # (see the esellar-api header for the vm-prepare/ansible split).
177
+ # (see the kampodine-api header for the vm-prepare/ansible split).
164
178
  #
165
179
  # TLS edge (kamal-proxy, Let's Encrypt HTTP-01 on :80). Publishes 80+443; the
166
180
  # LE certs + host->target registrations persist in the kamal-proxy-config named
@@ -168,7 +182,7 @@ cat > "$TMPDIR_LOCAL/kamal-proxy" <<'INITD_PROXY'
168
182
  # the first deploy survives container recreation AND reboots. Start-fresh:
169
183
  # nothing is carried from the retired blue VM. Target registration happens at
170
184
  # deploy time (kampodine deploy: podman exec kamal-proxy kamal-proxy deploy
171
- # esellar-api --host=$PROXY_HOST --target=esellar-api:8080 --tls
185
+ # kampodine-api --host=$PROXY_HOST --target=kampodine-api:8080 --tls
172
186
  # --health-check-path=/api/auth/ok).
173
187
 
174
188
  name="kamal-proxy"
@@ -198,19 +212,19 @@ stop_post() {
198
212
  }
199
213
  INITD_PROXY
200
214
 
201
- cat > "$TMPDIR_LOCAL/esellar-anchor.sh" <<'ANCHOR_WATCHER'
215
+ cat > "$TMPDIR_LOCAL/kampodine-anchor.sh" <<'ANCHOR_WATCHER'
202
216
  #!/bin/sh
203
- # KEEP IN SYNC with ansible/roles/container-service/files/esellar-anchor.sh
217
+ # KEEP IN SYNC with ansible/roles/container-service/files/kampodine-anchor.sh
204
218
  # (vm-prepare rendered-content convention: vm-prepare bootstraps a fresh VM
205
219
  # BEFORE the first ansible converge; ansible owns the file afterwards).
206
220
  #
207
- # esellar-anchor.sh — guest half of the blue-green reserved-ip flip.
221
+ # kampodine-anchor.sh — guest half of the blue-green reserved-ip flip.
208
222
  #
209
223
  # OCI assigns the reserved PUBLIC ip to a SECONDARY private ip ("the anchor")
210
224
  # on the instance VNIC, but this image has NO oracle-cloud-agent: nothing
211
225
  # configures that private ip inside the guest, so packets to the reserved ip
212
226
  # die until the address exists on the interface. `kampodine bluegreen flip` writes
213
- # /etc/esellar/anchor.conf over ssh at flip time; this watcher polls it and
227
+ # /etc/kampodine/anchor.conf over ssh at flip time; this watcher polls it and
214
228
  # runs `ip addr add` within one interval. The unit is enabled+started on every
215
229
  # VM by default and is INERT without the conf — a VM that never flips never
216
230
  # touches its addresses.
@@ -222,13 +236,13 @@ cat > "$TMPDIR_LOCAL/esellar-anchor.sh" <<'ANCHOR_WATCHER'
222
236
  # ADD-ONLY BY DESIGN: this script NEVER removes or flushes addresses (rollback
223
237
  # cleanup is the flip tool's explicit ssh job). Idempotent: a present address
224
238
  # is a no-op. Only state TRANSITIONS are logged (bounded log noise), to
225
- # stdout — supervise-daemon tees it to /var/log/esellar-anchor.log.
239
+ # stdout — supervise-daemon tees it to /var/log/kampodine-anchor.log.
226
240
 
227
- CONF="/etc/esellar/anchor.conf"
228
- INTERVAL="${ESPELLAR_ANCHOR_INTERVAL:-5}"
241
+ CONF="/etc/kampodine/anchor.conf"
242
+ INTERVAL="${KAMPODINE_ANCHOR_INTERVAL:-5}"
229
243
  STATE="boot" # last logged transition (boot | idle | added | error)
230
244
 
231
- log() { printf '%s esellar-anchor: %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "$*"; }
245
+ log() { printf '%s kampodine-anchor: %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "$*"; }
232
246
 
233
247
  # supervise-daemon SIGTERMs on stop — die cleanly with the current tick.
234
248
  trap 'exit 0' TERM INT
@@ -269,33 +283,33 @@ while :; do
269
283
  done
270
284
  ANCHOR_WATCHER
271
285
 
272
- cat > "$TMPDIR_LOCAL/esellar-anchor" <<'INITD_ANCHOR'
286
+ cat > "$TMPDIR_LOCAL/kampodine-anchor" <<'INITD_ANCHOR'
273
287
  #!/sbin/openrc-run
274
288
  # Managed by packages/kampodine/scripts/vm-prepare.sh — do not hand-edit.
275
- # KEEP IN SYNC with ansible/roles/container-service/templates/esellar-anchor.initd.j2
276
- # (rendered with role defaults — see the esellar-api header for the
289
+ # KEEP IN SYNC with ansible/roles/container-service/templates/kampodine-anchor.initd.j2
290
+ # (rendered with role defaults — see the kampodine-api header for the
277
291
  # vm-prepare/ansible split).
278
292
  #
279
293
  # Guest half of the blue-green reserved-ip flip: supervise-daemon runs the
280
- # esellar-anchor.sh watcher, which polls /etc/esellar/anchor.conf (written by
294
+ # kampodine-anchor.sh watcher, which polls /etc/kampodine/anchor.conf (written by
281
295
  # `kampodine bluegreen flip` over ssh at flip time) and `ip addr add`s the
282
296
  # anchor address when it appears. Started+enabled on EVERY vm by default and
283
297
  # INERT without the conf: a VM that never flips never touches its addresses —
284
- # unlike the container units, there is no esellar_start_containers gate.
285
- # Transitions land in /var/log/esellar-anchor.log.
298
+ # unlike the container units, there is no kampodine_start_containers gate.
299
+ # Transitions land in /var/log/kampodine-anchor.log.
286
300
 
287
- name="esellar-anchor"
301
+ name="kampodine-anchor"
288
302
  description="Blue-green reserved-ip anchor address watcher (guest half of the flip)"
289
303
 
290
304
  supervisor=supervise-daemon
291
- command="/usr/local/sbin/esellar-anchor.sh"
305
+ command="/usr/local/sbin/kampodine-anchor.sh"
292
306
 
293
307
  # Respawn forever: the watcher itself never exits (trap TERM/INT -> exit 0 on
294
308
  # stop), so a respawn means the script died abnormally — retry gently.
295
309
  respawn_delay=5
296
310
  respawn_max=0
297
311
 
298
- supervise_daemon_args="--stdout /var/log/esellar-anchor.log --stderr /var/log/esellar-anchor.log"
312
+ supervise_daemon_args="--stdout /var/log/kampodine-anchor.log --stderr /var/log/kampodine-anchor.log"
299
313
 
300
314
  depend() {
301
315
  need net
@@ -352,7 +366,7 @@ say "sshd hardening: ensure drop-in + Include + restart (golden image predates t
352
366
  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' \
353
367
  || die "could not ensure the sshd_config Include line"
354
368
  vm 'mkdir -p /etc/ssh/sshd_config.d && chmod 700 /etc/ssh/sshd_config.d'
355
- scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/sshd-hardening.conf" "$HOST:/etc/ssh/sshd_config.d/99-esellar-hardening.conf"
369
+ scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/sshd-hardening.conf" "$HOST:/etc/ssh/sshd_config.d/99-kampodine-hardening.conf"
356
370
  vm 'rc-service sshd restart' || die "sshd restart failed after hardening ensure"
357
371
 
358
372
  say "gate: sshd hardening effective"
@@ -390,50 +404,50 @@ PODSTACK_GATE
390
404
  vm 'rc-update show boot | grep -q cgroups || rc-update add cgroups boot' || die "rc-update cgroups failed"
391
405
  vm 'rc-service cgroups status >/dev/null 2>&1 || rc-service cgroups start' || die "cgroups start failed"
392
406
 
393
- # --- 3. /etc/esellar + managed config files ------------------------------------
394
- say "creating /etc/esellar (env file lands here via kampodine deploy, 0600 root)…"
395
- vm 'mkdir -p /etc/esellar && chmod 700 /etc/esellar'
407
+ # --- 3. /etc/kampodine + managed config files ------------------------------------
408
+ say "creating /etc/kampodine (env file lands here via kampodine deploy, 0600 root)…"
409
+ vm 'mkdir -p /etc/kampodine && chmod 700 /etc/kampodine'
396
410
 
397
411
  say "writing /etc/containers/registries.conf (insecure 127.0.0.1:5000; search docker.io)…"
398
412
  vm 'mkdir -p /etc/containers'
399
413
  scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/registries.conf" "$HOST:/etc/containers/registries.conf"
400
414
 
401
- say "writing /etc/sysctl.d/60-esellar.conf + applying live…"
402
- scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/60-esellar.conf" "$HOST:/etc/sysctl.d/60-esellar.conf"
415
+ say "writing /etc/sysctl.d/60-kampodine.conf + applying live…"
416
+ scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/60-kampodine.conf" "$HOST:/etc/sysctl.d/60-kampodine.conf"
403
417
  vm_sh <<'SYSCTL_APPLY' || die "sysctl ensure/apply failed"
404
418
  rc-update show boot | grep -Eq '^[[:space:]]*sysctl[[:space:]]*\|' || rc-update add sysctl boot
405
419
  sysctl -w net.ipv4.ip_unprivileged_port_start=80 > /dev/null
406
420
  SYSCTL_APPLY
407
421
 
408
422
  # --- 4. OpenRC services (canonical: ansible container-service templates) --------
409
- say "ensuring the 'kamal' podman network (deploy's proxy re-point resolves esellar-api:8080 by network DNS)…"
423
+ say "ensuring the 'kamal' podman network (deploy's proxy re-point resolves kampodine-api:8080 by network DNS)…"
410
424
  # shellcheck disable=SC2016
411
425
  vm 'podman network exists kamal 2>/dev/null || podman network create kamal' \
412
426
  || die "podman network create kamal failed"
413
- say "installing OpenRC services esellar-api + kamal-proxy (supervise-daemon around podman run)…"
414
- scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/esellar-api" "$HOST:/etc/init.d/esellar-api"
427
+ say "installing OpenRC services kampodine-api + kamal-proxy (supervise-daemon around podman run)…"
428
+ scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kampodine-api" "$HOST:/etc/init.d/kampodine-api"
415
429
  scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kamal-proxy" "$HOST:/etc/init.d/kamal-proxy"
416
- vm 'chmod 755 /etc/init.d/esellar-api /etc/init.d/kamal-proxy'
430
+ vm 'chmod 755 /etc/init.d/kampodine-api /etc/init.d/kamal-proxy'
417
431
 
418
- # --- 4b. esellar-anchor (blue-green flip guest half — watcher, no container) ----
432
+ # --- 4b. kampodine-anchor (blue-green flip guest half — watcher, no container) ----
419
433
  # Ships on EVERY vm: started + enabled now, INERT until a flip writes
420
- # /etc/esellar/anchor.conf over ssh (ansible container-service owns both files
434
+ # /etc/kampodine/anchor.conf over ssh (ansible container-service owns both files
421
435
  # afterwards — drift repair keeps them in sync).
422
- say "installing the esellar-anchor watcher service (blue-green flip guest half)…"
436
+ say "installing the kampodine-anchor watcher service (blue-green flip guest half)…"
423
437
  # /usr/local/sbin does NOT exist on the golden image (fresh Alpine ships no
424
438
  # /usr/local hierarchy; the alpine-base role creates it — vm-prepare runs
425
439
  # BEFORE any ansible)
426
440
  vm 'mkdir -p /usr/local/sbin' || die "mkdir /usr/local/sbin failed"
427
- scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/esellar-anchor.sh" "$HOST:/usr/local/sbin/esellar-anchor.sh"
428
- scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/esellar-anchor" "$HOST:/etc/init.d/esellar-anchor"
429
- vm 'chmod 755 /usr/local/sbin/esellar-anchor.sh /etc/init.d/esellar-anchor' || die "chmod esellar-anchor files failed"
430
- vm 'sh -n /usr/local/sbin/esellar-anchor.sh' || die "esellar-anchor.sh does not parse (busybox sh)"
431
- vm 'sh -n /etc/init.d/esellar-anchor' || die "/etc/init.d/esellar-anchor does not parse"
432
- vm 'rc-update show default | grep -qE "^[[:space:]]*esellar-anchor[[:space:]]*\\|" || rc-update add esellar-anchor default' \
433
- || die "rc-update add esellar-anchor default failed"
434
- vm 'rc-service esellar-anchor start' || die "rc-service esellar-anchor start failed"
435
-
436
- for SVC in esellar-api kamal-proxy; do
441
+ scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kampodine-anchor.sh" "$HOST:/usr/local/sbin/kampodine-anchor.sh"
442
+ scp -q "${SSH_ARGS[@]}" "$TMPDIR_LOCAL/kampodine-anchor" "$HOST:/etc/init.d/kampodine-anchor"
443
+ vm 'chmod 755 /usr/local/sbin/kampodine-anchor.sh /etc/init.d/kampodine-anchor' || die "chmod kampodine-anchor files failed"
444
+ vm 'sh -n /usr/local/sbin/kampodine-anchor.sh' || die "kampodine-anchor.sh does not parse (busybox sh)"
445
+ vm 'sh -n /etc/init.d/kampodine-anchor' || die "/etc/init.d/kampodine-anchor does not parse"
446
+ vm 'rc-update show default | grep -qE "^[[:space:]]*kampodine-anchor[[:space:]]*\\|" || rc-update add kampodine-anchor default' \
447
+ || die "rc-update add kampodine-anchor default failed"
448
+ vm 'rc-service kampodine-anchor start' || die "rc-service kampodine-anchor start failed"
449
+
450
+ for SVC in kampodine-api kamal-proxy; do
437
451
  vm "rc-update show default | grep -qE '^[[:space:]]*${SVC}[[:space:]]*\\|' || rc-update add ${SVC} default" \
438
452
  || die "rc-update add $SVC default failed"
439
453
  done
@@ -442,7 +456,7 @@ done
442
456
  # re-runs after a deploy must not bounce production.
443
457
  # shellcheck disable=SC2016
444
458
  vm_sh <<'UNIT_DRIFT' || true
445
- for SVC in esellar-api kamal-proxy esellar-anchor; do
459
+ for SVC in kampodine-api kamal-proxy kampodine-anchor; do
446
460
  if rc-service "$SVC" status > /dev/null 2>&1; then
447
461
  echo "RUNNING: $SVC (unit file was overwritten — rc-service $SVC restart to apply, on your call)"
448
462
  fi
@@ -469,26 +483,26 @@ if [[ $DO_PULL -eq 1 ]]; then
469
483
  # installs it later; vm-prepare runs BEFORE any ansible)
470
484
  vm 'busybox wget -q -O /dev/null http://127.0.0.1:5000/v2/' || {
471
485
  pkill -f "ssh.*-R 5000" 2>/dev/null || true; sleep 1
472
- nohup ssh -R 5000:127.0.0.1:5000 -N -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes "$HOST" >/tmp/esellar-tunnel.log 2>&1 &
486
+ nohup ssh -R 5000:127.0.0.1:5000 -N -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes "$HOST" >/tmp/kampodine-tunnel.log 2>&1 &
473
487
  sleep 3
474
488
  }
475
- vm 'busybox wget -q -O /dev/null http://127.0.0.1:5000/v2/' || die "registry tunnel did not come up (/tmp/esellar-tunnel.log)"
476
- vm 'podman pull --tls-verify=false 127.0.0.1:5000/esellar-api:latest' || die "app image pull failed"
489
+ 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)"
490
+ vm 'podman pull --tls-verify=false 127.0.0.1:5000/kampodine-api:latest' || die "app image pull failed"
477
491
  else
478
492
  say "skipping app-image pre-pull (pass --pull-images, or let the first deploy pull)"
479
493
  fi
480
494
 
481
- # --- 7. converge esellar-api ONLY if a previous deploy left image + env ---------
495
+ # --- 7. converge kampodine-api ONLY if a previous deploy left image + env ---------
482
496
  # On a truly fresh VM neither exists — the FIRST DEPLOY provides both and
483
497
  # starts the service. Re-runs of this script after a deploy heal drift.
484
- say "esellar-api start check (needs image + /etc/esellar/env — first deploy provides both)…"
498
+ say "kampodine-api start check (needs image + /etc/kampodine/env — first deploy provides both)…"
485
499
  # shellcheck disable=SC2016
486
- vm_sh <<'API_CONVERGE' || die "esellar-api start failed (image + env present — investigate: podman logs esellar-api)"
487
- if [ -f /etc/esellar/env ] && podman image exists 127.0.0.1:5000/esellar-api:latest; then
488
- rc-service esellar-api start
489
- echo "esellar-api started (image + env present)"
500
+ vm_sh <<'API_CONVERGE' || die "kampodine-api start failed (image + env present — investigate: podman logs kampodine-api)"
501
+ if [ -f /etc/kampodine/env ] && podman image exists 127.0.0.1:5000/kampodine-api:latest; then
502
+ rc-service kampodine-api start
503
+ echo "kampodine-api started (image + env present)"
490
504
  else
491
- echo "esellar-api deferred: no image and/or /etc/esellar/env yet (normal on a fresh VM — first deploy handles it)"
505
+ echo "kampodine-api deferred: no image and/or /etc/kampodine/env yet (normal on a fresh VM — first deploy handles it)"
492
506
  fi
493
507
  API_CONVERGE
494
508
 
@@ -508,16 +522,16 @@ vm 'grep -q "127.0.0.1:5000" /etc/containers/registries.conf && grep -q "insecur
508
522
  || die "registries.conf incomplete (127.0.0.1:5000 insecure + docker.io search)"
509
523
 
510
524
  say "gate: services enabled in the default runlevel"
511
- vm 'rc-update show default | grep -qE "^[[:space:]]*esellar-api[[:space:]]*\\|"' || die "esellar-api not in the default runlevel"
525
+ vm 'rc-update show default | grep -qE "^[[:space:]]*kampodine-api[[:space:]]*\\|"' || die "kampodine-api not in the default runlevel"
512
526
  vm 'rc-update show default | grep -qE "^[[:space:]]*kamal-proxy[[:space:]]*\\|"' || die "kamal-proxy not in the default runlevel"
513
527
  vm 'rc-service kamal-proxy status >/dev/null' || die "kamal-proxy service not started"
514
528
 
515
- say "gate: esellar-anchor watcher running + INERT (no anchor conf on a fresh VM)"
516
- vm 'rc-update show default | grep -qE "^[[:space:]]*esellar-anchor[[:space:]]*\\|"' || die "esellar-anchor not in the default runlevel"
517
- vm 'rc-service esellar-anchor status >/dev/null' || die "esellar-anchor service not started"
518
- vm '! test -e /etc/esellar/anchor.conf' || die "/etc/esellar/anchor.conf already exists on a fresh VM (wrong machine?)"
519
- vm 'grep -q "no anchor conf" /var/log/esellar-anchor.log' \
520
- || die "esellar-anchor is started but never logged its idle transition (watcher loop not running?)"
529
+ say "gate: kampodine-anchor watcher running + INERT (no anchor conf on a fresh VM)"
530
+ vm 'rc-update show default | grep -qE "^[[:space:]]*kampodine-anchor[[:space:]]*\\|"' || die "kampodine-anchor not in the default runlevel"
531
+ vm 'rc-service kampodine-anchor status >/dev/null' || die "kampodine-anchor service not started"
532
+ vm '! test -e /etc/kampodine/anchor.conf' || die "/etc/kampodine/anchor.conf already exists on a fresh VM (wrong machine?)"
533
+ vm 'grep -q "no anchor conf" /var/log/kampodine-anchor.log' \
534
+ || die "kampodine-anchor is started but never logged its idle transition (watcher loop not running?)"
521
535
 
522
536
  # --- summary ---------------------------------------------------------------------
523
537
  IP="${HOST#*@}"