feat: add --volume-options flag to storage:mount

The named-entry form of `storage:mount` had no way to set the attachment's `volume_options`, even though the legacy `host:container:opts` colon form parses options into the same field and every downstream consumer (the docker-args trigger and the storage:list/storage:report display path) already renders them. Tooling that declaratively re-applies attachments silently dropped options on every re-apply.
This commit is contained in:
Jose Diaz-Gonzalez
2026-05-30 02:28:14 -04:00
parent 411cb88796
commit f4d27be7a6
5 changed files with 133 additions and 8 deletions

View File

@@ -129,6 +129,48 @@ func TestListAppMountEntriesK3sUsesEntryName(t *testing.T) {
Expect(formatStorageListEntry(rows[0])).To(Equal("demo-pvc:/data"))
}
func TestListAppMountEntriesVolumeOptions(t *testing.T) {
RegisterTestingT(t)
root := withTempLibRoot(t)
Expect(SaveEntry(&Entry{
Name: "demo-data",
Scheduler: SchedulerDockerLocal,
HostPath: "/var/lib/dokku/data/storage/demo-data",
})).To(Succeed())
writeAttachmentsFile(t, root, "demo", []*Attachment{
{
EntryName: "demo-data",
ContainerPath: "/data",
Phases: []string{PhaseDeploy, PhaseRun},
VolumeOptions: "Z",
},
{
EntryName: "demo-data",
ContainerPath: "/ro",
Phases: []string{PhaseDeploy, PhaseRun},
Readonly: true,
VolumeOptions: "noexec,nosuid",
},
})
rows, err := ListAppMountEntries("demo", PhaseDeploy)
Expect(err).NotTo(HaveOccurred())
Expect(rows).To(HaveLen(2))
byPath := map[string]StorageListEntry{}
for _, row := range rows {
byPath[row.ContainerPath] = row
}
Expect(byPath["/data"].VolumeOptions).To(Equal("Z"))
Expect(formatStorageListEntry(byPath["/data"])).To(Equal("/var/lib/dokku/data/storage/demo-data:/data:Z"))
Expect(byPath["/ro"].VolumeOptions).To(Equal("ro,noexec,nosuid"))
Expect(formatStorageListEntry(byPath["/ro"])).To(Equal("/var/lib/dokku/data/storage/demo-data:/ro:ro,noexec,nosuid"))
}
func TestListAppMountEntriesPhaseFilter(t *testing.T) {
RegisterTestingT(t)
root := withTempLibRoot(t)

View File

@@ -155,16 +155,18 @@ func main() {
subpath := args.String("volume-subpath", "", "--volume-subpath: subpath within the entry")
readonly := args.Bool("volume-readonly", false, "--volume-readonly: mount the volume read-only")
volumeChown := args.String("volume-chown", "", "--volume-chown: chown option applied at mount time")
volumeOptions := args.String("volume-options", "", "--volume-options: comma-separated mount options (e.g. Z, noexec,nosuid)")
args.Parse(os.Args[2:])
err = storage.CommandMount(storage.CommandMountInput{
AppName: args.Arg(0),
NameOrPath: args.Arg(1),
ContainerDir: *containerDir,
Phases: *phases,
ProcessType: *processType,
Subpath: *subpath,
Readonly: *readonly,
VolumeChown: *volumeChown,
AppName: args.Arg(0),
NameOrPath: args.Arg(1),
ContainerDir: *containerDir,
Phases: *phases,
ProcessType: *processType,
Subpath: *subpath,
Readonly: *readonly,
VolumeChown: *volumeChown,
VolumeOptions: *volumeOptions,
})
case "report":
args := flag.NewFlagSet("storage:report", flag.ExitOnError)

View File

@@ -38,3 +38,25 @@ func TestRepairRegistryOwnership(t *testing.T) {
Expect(os.RemoveAll(RegistryDirectory())).To(Succeed())
Expect(repairRegistryOwnership()).To(Succeed())
}
// TestBuildDockerVFlagVolumeOptions covers the rendering rules in
// buildDockerVFlag for the various combinations of Readonly and
// VolumeOptions. This is the docker-args path that the running
// container actually receives.
func TestBuildDockerVFlagVolumeOptions(t *testing.T) {
RegisterTestingT(t)
entry := &Entry{HostPath: "/host"}
plain := buildDockerVFlag(entry, &Attachment{ContainerPath: "/container"})
Expect(plain).To(Equal("-v /host:/container"))
opts := buildDockerVFlag(entry, &Attachment{ContainerPath: "/container", VolumeOptions: "Z"})
Expect(opts).To(Equal("-v /host:/container:Z"))
ro := buildDockerVFlag(entry, &Attachment{ContainerPath: "/container", Readonly: true})
Expect(ro).To(Equal("-v /host:/container:ro"))
roOpts := buildDockerVFlag(entry, &Attachment{ContainerPath: "/container", Readonly: true, VolumeOptions: "noexec,nosuid"})
Expect(roOpts).To(Equal("-v /host:/container:ro,noexec,nosuid"))
}