feat: add storage:set, storage:exec, storage:wait, storage:report --global

storage:set is the in-place editor for an existing entry; size grows are passed through to a helm upgrade and Kubernetes-incompatible swaps (access-mode, storage-class) are rejected up front. storage:exec spawns a throwaway container that mounts the entry: docker run on docker-local entries, kubectl run with a Pod-spec override on k3s, falling back to bash then sh when no command is supplied. storage:wait blocks until a k3s entry's PVC reports Bound via the storage-status trigger, with a docker-local fast path that just checks the host directory. storage:report --global enumerates every registered entry and the apps that mount it, in text or JSON form.
This commit is contained in:
Jose Diaz-Gonzalez
2026-04-29 14:51:49 -04:00
parent cfb7da8ec0
commit 1ca961251d
5 changed files with 300 additions and 3 deletions

View File

@@ -21,12 +21,15 @@ Additional commands:`
storage:create <name> [<path>] [flags], Register a named storage entry
storage:destroy <name>, Remove a named storage entry (must be unmounted from every app first)
storage:ensure-directory [--chown option] <directory>, [DEPRECATED] use storage:create instead
storage:exec <name> [-- <cmd>...], Run a command (or shell) in a temporary container that mounts the entry
storage:info <name> [--format text|json], Show details for one storage entry
storage:list <app> [--format text|json], List bind mounts for app's container(s) (host:container)
storage:list-entries [--scheduler s] [--format text|json], List registered storage entries
storage:mount <app> <host-dir:container-dir>, Create a new bind mount
storage:report [<app>] [<flag>], Displays a storage report for one or more apps
storage:unmount <app> <host-dir:container-dir>, Remove an existing bind mount`
storage:set <name> [flags], Update a storage entry in place
storage:unmount <app> <host-dir:container-dir>, Remove an existing bind mount
storage:wait <name>, Wait for a storage entry's PVC to be bound (k3s)`
)
func main() {

View File

@@ -78,6 +78,57 @@ func main() {
args.Parse(os.Args[2:])
appName := args.Arg(0)
err = storage.CommandList(appName, *format)
case "set":
args := flag.NewFlagSet("storage:set", flag.ExitOnError)
size := args.String("size", "", "--size: new PVC size (k3s)")
accessMode := args.String("access-mode", "", "--access-mode: existing access mode (must match)")
storageClass := args.String("storage-class-name", "", "--storage-class-name: existing storage class (must match)")
namespace := args.String("namespace", "", "--namespace: new namespace")
chown := args.String("chown", "", "--chown: chown option")
reclaim := args.String("reclaim-policy", "", "--reclaim-policy: PV reclaim policy")
annotations := args.StringSlice("annotation", nil, "--annotation key=value: PVC annotation (repeatable, replaces all)")
labels := args.StringSlice("label", nil, "--label key=value: PVC label (repeatable, replaces all)")
args.Parse(os.Args[2:])
annotMap, parseErr := parseKVPairs(*annotations)
if parseErr != nil {
err = parseErr
break
}
labelMap, parseErr := parseKVPairs(*labels)
if parseErr != nil {
err = parseErr
break
}
err = storage.CommandSet(storage.CommandSetInput{
Name: args.Arg(0),
Size: *size,
AccessMode: *accessMode,
StorageClass: *storageClass,
Namespace: *namespace,
Chown: *chown,
ReclaimPolicy: *reclaim,
Annotations: annotMap,
Labels: labelMap,
})
case "exec":
args := flag.NewFlagSet("storage:exec", flag.ExitOnError)
image := args.String("image", "", "--image: container image to use (default alpine:3)")
args.Parse(os.Args[2:])
positional := args.Args()
if len(positional) == 0 {
err = fmt.Errorf("storage:exec requires a storage entry name")
break
}
name := positional[0]
var cmd []string
if len(positional) > 1 {
cmd = positional[1:]
}
err = storage.CommandExec(name, *image, cmd)
case "wait":
args := flag.NewFlagSet("storage:wait", flag.ExitOnError)
args.Parse(os.Args[2:])
err = storage.CommandWait(args.Arg(0))
case "list-entries":
args := flag.NewFlagSet("storage:list-entries", flag.ExitOnError)
scheduler := args.String("scheduler", "", "--scheduler: filter to a single scheduler")