feat(services): volume mounts + admin volume inventory (C1)

services.volumes JSONB [{type: volume|bind, source, target, read_only}]
mapped into every replica's container config at deploy and reconcile
time, hashed into the runtime spec so mount changes trigger
reconciliation. Mounted via existing docker.HostConfig.Mounts.

- RuntimeSpec.Volumes + specHash + mount.Mount mapping in createReplica
- Create/update service accepts volumes; PUT replaces the whole list
- Replica-count reconcile now goes through the deploy queue — the last
  raw `go` reconcile left that could race a build
- GET /admin/volumes inventories node volumes with in_use flags;
  DELETE /admin/volumes/:name removes unused ones (409 on in-use)
- CLI: services update --volume/--bind/--clear-volumes, volumes
  list|delete; MCP: services_update + volumes_list/delete
- Settings UI: Volumes section with mount editor
- Migration normalizes the nullable baseline column to NOT NULL [].

Verified live: deploy applies mounts to the container, docker
auto-creates named volumes, in-use deletion is refused.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-10-08 16:38:46 +02:00
1 parent 747b714a02
commit 2f8f595d6e
21 files changed
+863 -93

No files matched your search

+1 -2
View File
@@ -326,6 +326,7 @@ func (de *DeploymentEngine) deployService(ctx context.Context, deployment *Deplo
RestartPolicy: cfg.RestartPolicy,
MemoryBytes: cfg.Resources.MemoryBytes,
NanoCPUs: cfg.Resources.CPUQuota,
Volumes: cfg.VolumeMounts,
}
for _, pm := range cfg.PortMappings {
if spec.Port == 0 {
@@ -336,8 +337,6 @@ func (de *DeploymentEngine) deployService(ctx context.Context, deployment *Deplo
spec.RestartPolicy = "unless-stopped"
}
// Volume mounts aren't reconciled replicas-aware yet; still applied on the
// container config of every replica via spec extension later if needed.
state, err := de.ReconcileService(ctx, spec)
if err != nil {
return err