mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-29 08:04:53 +00:00
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:
1 parent
747b714a02
commit
2f8f595d6e
21 files changed
+863
-93
No files matched your search
@@ -0,0 +1,71 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// VolumesCmd manages docker volumes on the node (admin).
|
||||
var VolumesCmd = &cobra.Command{
|
||||
Use: "volumes",
|
||||
Aliases: []string{"volume"},
|
||||
Short: "Manage docker volumes (admin)",
|
||||
}
|
||||
|
||||
var volumesListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List docker volumes with in-use flags",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
c, err := client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := c.Do("GET", "/admin/volumes", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items, err := unwrapList(data, "volumes")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows := make([][]string, 0, len(items))
|
||||
for _, v := range items {
|
||||
inUse := "no"
|
||||
if b, ok := v["in_use"].(bool); ok && b {
|
||||
inUse = "yes"
|
||||
}
|
||||
rows = append(rows, []string{str(v, "name"), str(v, "driver"), inUse})
|
||||
}
|
||||
printRows(data, []string{"NAME", "DRIVER", "IN USE"}, rows)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var volumesDeleteCmd = &cobra.Command{
|
||||
Use: "delete <name>",
|
||||
Short: "Delete an unused docker volume",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
c, err := client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !Confirm(fmt.Sprintf("Delete volume %s?", args[0])) {
|
||||
return &APIError{Message: "aborted (pass --yes to skip confirmation)", ExitCode: ExitError}
|
||||
}
|
||||
if _, err := c.Do("DELETE", "/admin/volumes/"+args[0], nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if JSONMode() {
|
||||
return PrintJSON(map[string]string{"status": "deleted", "name": args[0]})
|
||||
}
|
||||
fmt.Printf("Deleted volume %s\n", args[0])
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
VolumesCmd.AddCommand(volumesListCmd, volumesDeleteCmd)
|
||||
}
|
||||
Reference in new issue
Block a user