docs: migrate worker implementation plans
Все проверки выполнены успешно
CI / test (push) Successful in 30s
Docker / Build and publish worker image (push) Successful in 10m49s
Все проверки выполнены успешно
CI / test (push) Successful in 30s
Docker / Build and publish worker image (push) Successful in 10m49s
Этот коммит содержится в:
156
docs/control-plane-protocol.md
Обычный файл
156
docs/control-plane-protocol.md
Обычный файл
@@ -0,0 +1,156 @@
|
||||
# Control-Plane Protocol
|
||||
|
||||
## Authority
|
||||
|
||||
`internal/wire/types.go` is the executable schema. New fields must be optional
|
||||
during rollout, and new branches require decode tests against both old and new
|
||||
frames. Version strings are informational; explicit capabilities select
|
||||
protocol features.
|
||||
|
||||
The worker connects to:
|
||||
|
||||
```text
|
||||
GET /worker?token=<RSMON_TOKEN>
|
||||
```
|
||||
|
||||
`/api/worker` and the HTTP jobs/results APIs remain compatibility paths. New
|
||||
workers use WebSocket task envelopes.
|
||||
|
||||
## Frame Model
|
||||
|
||||
Every frame is a `wire.WorkerMessage` with `kind` and one active content
|
||||
branch.
|
||||
|
||||
| Direction | `kind` | Active branch | State |
|
||||
| --- | --- | --- | --- |
|
||||
| Server to worker | `init` or `config` | `init` | Implemented |
|
||||
| Server to worker | `task` | `task_envelope` | Implemented |
|
||||
| Worker to server | `result` | `result` | Implemented |
|
||||
| Worker to server | `result` | `notification_result` | Implemented |
|
||||
| Worker to server | `result` | `server_metric` | Implemented |
|
||||
| Worker to server | `heartbeat` | `heartbeat` | Implemented |
|
||||
| Either | `error` | `error` | Implemented |
|
||||
| Worker to server | `result` | inventory report | Planned |
|
||||
| Server to worker | cluster config/witness | dedicated typed branch | Planned |
|
||||
| Server to worker | diagnostic task | new task-envelope variant | Planned |
|
||||
|
||||
The legacy top-level `task` and `notification_task` branches are accepted for
|
||||
rollout compatibility. If a frame includes both a valid current
|
||||
`task_envelope` and a legacy branch, the worker executes only
|
||||
`task_envelope`. The current runner ignores malformed or unsupported task
|
||||
branches and does not yet compare the envelope job ID with the inner job ID;
|
||||
strict rejection and reporting are P1 work below.
|
||||
|
||||
## Initialization And Refresh
|
||||
|
||||
`wire.WorkerInit` supplies runtime values owned by the control plane:
|
||||
|
||||
- worker ID, region, advertised URL, capabilities, and concurrency;
|
||||
- allowed notification methods and account IDs;
|
||||
- optional linked server ID for host metrics;
|
||||
- LLM endpoints;
|
||||
- scoped notification credentials and system contacts;
|
||||
- peer workers for selfcheck/cluster-adjacent behavior.
|
||||
|
||||
The worker clamps supplied concurrency to its local maximum. Credentials are
|
||||
replaced atomically in memory on refresh. Removed credentials must become
|
||||
unavailable immediately after the refresh is applied.
|
||||
|
||||
Private-worker hardening will add an immutable worker account ID, config
|
||||
version, expiry, and signature. Until then the executable trusts the
|
||||
authenticated control plane to send a correctly scoped config; server-side
|
||||
selection remains the primary isolation boundary.
|
||||
|
||||
## Normal Task Envelope
|
||||
|
||||
`wire.TaskEnvelope` is a tagged union:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "task",
|
||||
"task_envelope": {
|
||||
"type": "check",
|
||||
"job_id": "uuid",
|
||||
"check": {
|
||||
"job_id": "uuid",
|
||||
"lease_token": "per-lease-secret",
|
||||
"check_id": 123,
|
||||
"monitor_id": 456,
|
||||
"kind": "http",
|
||||
"host": "example.com",
|
||||
"url": "https://example.com",
|
||||
"interval": 60,
|
||||
"settings": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`type=notification` activates `notification` instead. Job IDs in the envelope
|
||||
and active branch must match once P1 validation lands. Today a task without a
|
||||
recognized populated branch is ignored without execution.
|
||||
|
||||
## Result Invariants
|
||||
|
||||
- Echo `job_id` and the exact `lease_token` from the task.
|
||||
- Send one terminal result per execution attempt.
|
||||
- Never retry a result by executing the task again. Result transport retries
|
||||
resend the same terminal report.
|
||||
- Treat duplicate terminal acknowledgements as success.
|
||||
- Do not infer task acceptance from a WebSocket write alone; durable ownership
|
||||
remains on the control plane until it validates the result.
|
||||
- Bound error strings and provider responses before transmission.
|
||||
|
||||
## Host Metrics
|
||||
|
||||
`wire.ServerMetricReport` is sent only after `WorkerInit.ServerID` is present.
|
||||
The worker collects locally and sends bounded snapshots. The control plane
|
||||
validates worker/server/account ownership and persists both the latest cache
|
||||
and VictoriaMetrics points. The worker does not have TSDB credentials.
|
||||
|
||||
## Protocol Work Packages
|
||||
|
||||
### P1: Conformance Tests
|
||||
|
||||
Files:
|
||||
|
||||
- `internal/wire/types_test.go`
|
||||
- `internal/distworker/runner_protocol_test.go`
|
||||
|
||||
Tests:
|
||||
|
||||
- decode every current frame branch;
|
||||
- prefer the current envelope over duplicate legacy branches;
|
||||
- reject mismatched envelope and inner job IDs;
|
||||
- preserve unknown optional fields during compatible rollout;
|
||||
- reject missing lease tokens before execution result submission;
|
||||
- atomically replace config and credentials.
|
||||
|
||||
### P2: Safe Token Rotation
|
||||
|
||||
Current `Runner.RotateToken` closes the runner stop channel. Replace this with a
|
||||
connection-scoped cancellation path that stores the new token, closes only the
|
||||
active WebSocket, and reconnects without terminating worker services.
|
||||
|
||||
Acceptance: rotating from the web console produces a reconnect using the new
|
||||
token while the HTTP listener, collectors, and optional cluster stay running.
|
||||
|
||||
### P3: Signed Private-Worker Config
|
||||
|
||||
Add to the init/config branch:
|
||||
|
||||
- `account_id` for private workers;
|
||||
- monotonic `config_version`;
|
||||
- `issued_at` and `expires_at`;
|
||||
- signature key ID and Ed25519 signature over canonical payload bytes.
|
||||
|
||||
Reject regressions, invalid signatures, expired config, and account changes.
|
||||
Keep the last valid config only until its expiry; do not silently accept an
|
||||
invalid replacement.
|
||||
|
||||
### P4: New Typed Branches
|
||||
|
||||
Inventory, diagnostic, and critical-cluster messages each receive a dedicated
|
||||
wire type. Do not tunnel them through `event` or arbitrary `json.RawMessage`.
|
||||
Each branch must define payload limits, account/target validation ownership,
|
||||
idempotency, and compatibility behavior before implementation.
|
||||
Ссылка в новой задаче
Block a user