加固生产运维定时器绑定(07-26-internal-ops-recovery-baseline)
这个提交包含在:
@@ -619,6 +619,9 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
||||
- Application storage mount: `${STORAGE_HOST_DIR:-./data/storage}`.
|
||||
- Formal deploy inputs: absolute `POSTGRES_DATA_DIR`, absolute
|
||||
`STORAGE_HOST_DIR`, `DEPLOY_DATA_MODE=existing|initialize`.
|
||||
- Timer install inputs: `COMPOSE_PROJECT_NAME`, absolute
|
||||
`POSTGRES_DATA_DIR`, absolute `STORAGE_HOST_DIR`, full 40–64 hex
|
||||
`BUILD_SHA` and its exact 12-hex `VITE_BUILD_SHA` prefix.
|
||||
- DB has no host `ports`; server remains loopback-only.
|
||||
|
||||
### 3. Contracts
|
||||
@@ -639,6 +642,16 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
||||
only that mode may create data directories.
|
||||
- Remote readiness may use host curl, but build-SHA JSON validation runs with
|
||||
Node inside the server container; the Docker host does not need Node.
|
||||
- The timer installer must validate the same non-root, non-overlapping absolute
|
||||
data-path shape as formal deploy, require an existing `PG_VERSION` and
|
||||
storage directory, and write both paths plus both build identifiers into the
|
||||
systemd service `Environment=` entries. Project name alone is insufficient:
|
||||
a later timer invocation would otherwise interpolate Compose defaults and
|
||||
operate against a non-active data pair.
|
||||
- Timer installation may enable/start the timer unit, but verification must not
|
||||
manually start the daily service because that path can send real alerts.
|
||||
Verify the unit environment against active container mounts, then run
|
||||
Compose `config --quiet` and `exec -T server true` with the same values.
|
||||
- For an existing cutover, stop the old PostgreSQL completely, copy PGDATA and
|
||||
storage into pre-verified empty absolute targets while preserving ownership,
|
||||
permissions, links and extended attributes, and retain the old directories
|
||||
@@ -651,6 +664,13 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
||||
- Any fixed `container_name` appears -> environment contract fails.
|
||||
- Required data mount loses its parameter -> environment contract fails.
|
||||
- Deploy/timer omits `-p` -> environment contract fails.
|
||||
- Timer install omits either data path/build identifier, receives nested paths,
|
||||
a PGDATA without `PG_VERSION`, a missing storage directory, an invalid full
|
||||
SHA or a 12-character value that is not its prefix -> fail before writing the
|
||||
systemd unit.
|
||||
- A path-segment regex can match an empty string at the leading `/` -> contract
|
||||
failure; use explicit root, trailing slash, `//`, `/./` and `/../` checks so
|
||||
valid absolute paths remain installable.
|
||||
- Formal deploy omits paths/mode, uses a relative/broad/overlapping path, or an
|
||||
`existing` PGDATA lacks `PG_VERSION` -> fail before DB start.
|
||||
- New project resolves the same PGDATA while the old DB runs -> stop before
|
||||
@@ -664,17 +684,26 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
||||
- The isolated backup/restore drill mutates Compose with a DB port and fixed
|
||||
name and proves the contract rejects it.
|
||||
- `bash -n` covers deploy, timer, backup and restore scripts.
|
||||
- Timer contract assertions cover all four explicit inputs, path isolation,
|
||||
PGDATA marker, SHA/prefix validation and the four systemd `Environment=`
|
||||
entries. On the deployment host, assert timer enabled/active, daily service
|
||||
inactive, exact active-environment equality and non-mutating Compose resolve.
|
||||
|
||||
### 6. Good/Base/Bad Cases
|
||||
|
||||
- Good: one approved project resolves one stopped-and-migrated PGDATA and the
|
||||
matching active storage path; deploy, timer and backup all use that identity.
|
||||
- Good: the timer unit contains the current project, active mount sources and
|
||||
deployed full/short SHA pair; Compose config and a non-mutating server exec
|
||||
resolve under those exact values while the daily service stays inactive.
|
||||
- Base: direct local Compose uses the documented relative defaults under one
|
||||
project; those defaults are never accepted by formal `deploy.sh`.
|
||||
- Bad: start a renamed project before the old DB stops, or let it create a new
|
||||
empty directory and accept a green health check as proof of data continuity.
|
||||
- Bad: move the only PGDATA into the new path, migrate it in place, then assume
|
||||
the old container name alone is a rollback point.
|
||||
- Bad: reinstall the timer with only `COMPOSE_PROJECT_NAME`, allowing systemd
|
||||
Compose interpolation to fall back to repository-relative data directories.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
@@ -697,6 +726,22 @@ DB_TOOL_MODE=docker DB_CONTAINER="${DB_CONTAINERS[0]}" \
|
||||
STORAGE_DIR_HOST=<verified-active-storage> ./scripts/backup-all.sh
|
||||
```
|
||||
|
||||
For timer installation, keep the active deployment identity explicit:
|
||||
|
||||
```bash
|
||||
# Wrong: project identity alone still permits relative data-path fallback.
|
||||
COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT_NAME" \
|
||||
bash deploy/trialrun/install-warch3-ops-timer.sh install
|
||||
|
||||
# Correct: use the verified active mounts and deployed SHA pair.
|
||||
COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT_NAME" \
|
||||
POSTGRES_DATA_DIR=<verified-active-pgdata> \
|
||||
STORAGE_HOST_DIR=<verified-active-storage> \
|
||||
BUILD_SHA=<verified-full-build-sha> \
|
||||
VITE_BUILD_SHA=<verified-12-char-prefix> \
|
||||
bash deploy/trialrun/install-warch3-ops-timer.sh install
|
||||
```
|
||||
|
||||
## Scenario: Paired Backup And Shadow-Only Restore
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
在新工单中引用
屏蔽一个用户