fix(api): wake 与回档/备份/改文件按服务器互斥
This commit is contained in:
27 files changed
+1300
-36
No files matched your search
+14
-4
@@ -741,6 +741,11 @@ paths:
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'409':
|
||||
description: A restore, backup or file write holds the server's world volume (maintenance_in_progress); nothing was started.
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'429':
|
||||
description: Wake cooldown is still active for this server.
|
||||
content:
|
||||
@@ -1193,7 +1198,7 @@ paths:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'409':
|
||||
description: Server is not stopped (its world PVC is still mounted).
|
||||
description: Server is not stopped (not_stopped), or a restore, backup or file write already holds its world volume (maintenance_in_progress).
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
@@ -1228,6 +1233,11 @@ paths:
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'409':
|
||||
description: A restore, backup or file write holds the server's world volume (maintenance_in_progress); nothing was started.
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'429':
|
||||
description: Wake cooldown is still active.
|
||||
content:
|
||||
@@ -2828,7 +2838,7 @@ paths:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'409':
|
||||
description: Submission has already been reviewed.
|
||||
description: Server is not stopped (not_stopped), or a restore, backup or file write already holds its world volume (maintenance_in_progress).
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
@@ -2873,7 +2883,7 @@ paths:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'409':
|
||||
description: Server is not stopped (its world PVC is still mounted).
|
||||
description: Server is not stopped (not_stopped), or a restore, backup or file write already holds its world volume (maintenance_in_progress).
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
@@ -3122,7 +3132,7 @@ paths:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'409':
|
||||
description: Server is not stopped (its world PVC is still mounted).
|
||||
description: Server is not stopped (not_stopped), or a restore, backup or file write already holds its world volume (maintenance_in_progress).
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
|
||||
+47
-2
@@ -185,6 +185,7 @@ per-server cooldown → global running cap**. Map the API result:
|
||||
| HTTP | Code | Cause | Fix |
|
||||
|---|---|---|---|
|
||||
| `403` | `forbidden` | `autostartPolicy=allowlist` and UUID not allowlisted, or `ownerOnly` and caller is not owner | Add the UUID / claim the server / set `autostartPolicy=public` |
|
||||
| `409` | `maintenance_in_progress` | A restore, backup or file write holds the server's world volume (§3b) | Wait for the Job to finish |
|
||||
| `429` | (cooldown) | Wake retried within the 30s per-server `WakeCooldown` | Wait out the cooldown |
|
||||
| `503` | `at_capacity` | Global `MaxRunningServers` cap reached | Stop another server or raise the cap |
|
||||
|
||||
@@ -194,11 +195,54 @@ gate; the proxy polls `GET /api/v1/internal/servers/{name}/status` every ~2s and
|
||||
teleports when `ready=true`.
|
||||
|
||||
The Velocity-side consumption of these codes (`403` → "You're not allowed to
|
||||
start «server»"; `429` → re-queue; other → "Couldn't start … Try again
|
||||
start «server»"; `409 maintenance_in_progress` → "«server» is under
|
||||
maintenance", not queued; `429` → re-queue; other → "Couldn't start … Try again
|
||||
shortly.") lives in the Java plugin and is **[CODE-ONLY]** — the codes it reacts
|
||||
to are produced by the Go-tested `authorizeWakeByUUID` / cooldown limiter, so
|
||||
grade the two halves separately.
|
||||
|
||||
### 3b. Wake, restore, backup or file save refused with `maintenance_in_progress`
|
||||
|
||||
A server's world volume is ReadWriteOnce, and on a single node RWO lets a game
|
||||
pod and a restore Job mount it side by side. So felis-api serialises them per
|
||||
server: a restore, a backup, or a file write takes the world, and until its Job
|
||||
finishes every wake (panel or join) and every other world operation on that
|
||||
server gets `409 maintenance_in_progress`. File reads and listings never hold
|
||||
it. The operator applies the same rule when `desiredState` is flipped to
|
||||
`Running` by anything other than felis-api: the StatefulSet is not scaled up,
|
||||
and the `Ready` condition reads `MaintenanceInProgress` until the Job ends.
|
||||
|
||||
What holds the world, in order:
|
||||
|
||||
1. An unfinished Job labelled `felis.lolicon.best/server=<name>` with
|
||||
`app.kubernetes.io/managed-by` `felis-restore`, `felis-backup`, or
|
||||
`felis-files` plus `felis.lolicon.best/files-mode=write`:
|
||||
|
||||
```sh
|
||||
kubectl -n minecraft get jobs -l felis.lolicon.best/server=<name>
|
||||
```
|
||||
|
||||
A Job that is genuinely wedged is ended by its own `activeDeadlineSeconds`;
|
||||
deleting it by hand releases the world at once (`kubectl -n minecraft delete
|
||||
job <job>`), at the cost of whatever it was writing.
|
||||
|
||||
2. The admission lock `felis.lolicon.best/maintenance=<kind>@<RFC3339>` on the
|
||||
MinecraftServer. felis-api sets it for the milliseconds between admitting an
|
||||
operation and creating its Job; it holds for at most two minutes if felis-api
|
||||
died in between, and the next wake clears a stale one. To drop it by hand:
|
||||
|
||||
```sh
|
||||
kubectl -n minecraft annotate minecraftserver <name> felis.lolicon.best/maintenance-
|
||||
```
|
||||
|
||||
A restore, backup or file write refused with `409 not_stopped` although the
|
||||
panel shows `Stopped` means the game pod is still terminating (its preStop save
|
||||
can take a while); retry once `kubectl -n minecraft get pods -l
|
||||
felis.lolicon.best/server=<name>` shows nothing.
|
||||
|
||||
[GO-TESTED: `internal/maintenance`, `k8scluster_maintenance_test.go`,
|
||||
`handlers_maintenance_test.go`, operator `maintenance_test.go`.]
|
||||
|
||||
---
|
||||
|
||||
## 4. Routing is disabled even though servers are up (online-mode coupling)
|
||||
@@ -947,7 +991,8 @@ installer built — only hand-built tags need a manual re-mirror.
|
||||
| RCON secret/auth/port errors | §1b, §1c |
|
||||
| Phase `Failed` | §2 |
|
||||
| Players land in lobby / wrong place | §3, §4 |
|
||||
| Wake refused / rate-limited (403/429/503) | §3a |
|
||||
| Wake refused / rate-limited (403/409/429/503) | §3a |
|
||||
| `maintenance_in_progress`; server won't start after a restore | §3b |
|
||||
| Routing disabled, offline-mode | §4 |
|
||||
| Panel 401/403; fails-closed; audience error | §5 |
|
||||
| Local password login rejected | §5c |
|
||||
|
||||
Reference in new issue
Block a user