From 66b2de4b35cdac862c8e9cfa779943db2812d7bd Mon Sep 17 00:00:00 2001 From: "chip.selden" Date: Tue, 31 Mar 2026 11:57:14 -0700 Subject: [PATCH 1/2] feat: add bind mount option support to docker and podman runtimes --- runtime/docker/docker_test.go | 25 +++++++++++++++++++++++++ runtime/docker/tcontainer.go | 20 ++++++++++++++------ runtime/podman/podman.go | 21 +++++++++++++++++---- runtime/podman/podman_test.go | 31 +++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 10 deletions(-) diff --git a/runtime/docker/docker_test.go b/runtime/docker/docker_test.go index bf48aaa5..964648e7 100644 --- a/runtime/docker/docker_test.go +++ b/runtime/docker/docker_test.go @@ -348,6 +348,31 @@ func TestRunTaskWithBind(t *testing.T) { assert.NoError(t, err) } +func TestRunTaskWithBindAndPropagation(t *testing.T) { + mm := runtime.NewMultiMounter() + vm, err := NewVolumeMounter() + assert.NoError(t, err) + mm.RegisterMounter("bind", NewBindMounter(BindConfig{Allowed: true})) + mm.RegisterMounter("volume", vm) + rt, err := NewDockerRuntime(WithMounter(mm)) + assert.NoError(t, err) + ctx := context.Background() + dir := path.Join(os.TempDir(), uuid.NewUUID()) + t1 := &tork.Task{ + ID: uuid.NewUUID(), + Image: "busybox:stable", + Run: "echo hello world > /xyz/thing", + Mounts: []*tork.Mount{{ + Type: tork.MountTypeBind, + Target: "/xyz", + Source: dir, + Opts: map[string]string{"propagation": "rslave"}, + }}, + } + err = rt.Run(ctx, t1) + assert.NoError(t, err) +} + func TestRunTaskWithTempfs(t *testing.T) { rt, err := NewDockerRuntime( WithMounter(NewTmpfsMounter()), diff --git a/runtime/docker/tcontainer.go b/runtime/docker/tcontainer.go index eb9dacf5..4b408f7f 100644 --- a/runtime/docker/tcontainer.go +++ b/runtime/docker/tcontainer.go @@ -77,13 +77,21 @@ func createTaskContainer(ctx context.Context, rt *DockerRuntime, t *tork.Task, l default: return nil, errors.Errorf("unknown mount type: %s", m.Type) } - mount := mount.Mount{ - Type: mt, - Source: m.Source, - Target: m.Target, + mn := mount.Mount{ + Type: mt, + Source: m.Source, + Target: m.Target, + ReadOnly: m.Opts["readonly"] == "true", } - log.Debug().Msgf("Mounting %s -> %s", mount.Source, mount.Target) - mounts = append(mounts, mount) + if mt == mount.TypeBind { + if prop, ok := m.Opts["propagation"]; ok { + mn.BindOptions = &mount.BindOptions{ + Propagation: mount.Propagation(prop), + } + } + } + log.Debug().Msgf("Mounting %s -> %s", mn.Source, mn.Target) + mounts = append(mounts, mn) } torkdir := &tork.Mount{ diff --git a/runtime/podman/podman.go b/runtime/podman/podman.go index c7eea32f..622abc27 100644 --- a/runtime/podman/podman.go +++ b/runtime/podman/podman.go @@ -247,10 +247,8 @@ func (d *PodmanRuntime) doRun(ctx context.Context, t *tork.Task, logger io.Write // add mounts to the container for _, mount := range t.Mounts { switch mount.Type { - case tork.MountTypeVolume: - createCmd.Args = append(createCmd.Args, "-v", fmt.Sprintf("%s:%s", mount.Source, mount.Target)) - case tork.MountTypeBind: - createCmd.Args = append(createCmd.Args, "-v", fmt.Sprintf("%s:%s", mount.Source, mount.Target)) + case tork.MountTypeVolume, tork.MountTypeBind: + createCmd.Args = append(createCmd.Args, "-v", formatVolumeSpec(mount)) default: return fmt.Errorf("unknown mount type: %s", mount.Type) } @@ -383,6 +381,21 @@ func (d *PodmanRuntime) stop(ctx context.Context, t *tork.Task) error { return nil } +func formatVolumeSpec(m *tork.Mount) string { + spec := fmt.Sprintf("%s:%s", m.Source, m.Target) + var opts []string + if m.Opts["readonly"] == "true" { + opts = append(opts, "ro") + } + if prop, ok := m.Opts["propagation"]; ok { + opts = append(opts, prop) + } + if len(opts) > 0 { + spec += ":" + strings.Join(opts, ",") + } + return spec +} + func (d *PodmanRuntime) HealthCheck(ctx context.Context) error { cmd := exec.CommandContext(ctx, "podman", "version") if err := cmd.Run(); err != nil { diff --git a/runtime/podman/podman_test.go b/runtime/podman/podman_test.go index 2f3c965f..171e94d9 100644 --- a/runtime/podman/podman_test.go +++ b/runtime/podman/podman_test.go @@ -407,6 +407,37 @@ func TestRunTaskWithCustomMounter(t *testing.T) { assert.NoError(t, err) } +func TestFormatVolumeSpec(t *testing.T) { + assert.Equal(t, "/src:/dst", formatVolumeSpec(&tork.Mount{Source: "/src", Target: "/dst"})) + assert.Equal(t, "/src:/dst:rslave", formatVolumeSpec(&tork.Mount{Source: "/src", Target: "/dst", Opts: map[string]string{"propagation": "rslave"}})) + assert.Equal(t, "/src:/dst:ro", formatVolumeSpec(&tork.Mount{Source: "/src", Target: "/dst", Opts: map[string]string{"readonly": "true"}})) + assert.Equal(t, "/src:/dst:ro,rslave", formatVolumeSpec(&tork.Mount{Source: "/src", Target: "/dst", Opts: map[string]string{"readonly": "true", "propagation": "rslave"}})) +} + +func TestPodmanRunTaskWithBindAndPropagation(t *testing.T) { + mm := runtime.NewMultiMounter() + vm := NewVolumeMounter() + mm.RegisterMounter("bind", docker.NewBindMounter(docker.BindConfig{Allowed: true})) + mm.RegisterMounter("volume", vm) + rt := NewPodmanRuntime(WithMounter(mm)) + ctx := context.Background() + dir := path.Join(os.TempDir(), uuid.NewUUID()) + t1 := &tork.Task{ + ID: uuid.NewUUID(), + Name: "Some task", + Image: "busybox:stable", + Run: "echo hello world > /xyz/thing", + Mounts: []*tork.Mount{{ + Type: tork.MountTypeBind, + Target: "/xyz", + Source: dir, + Opts: map[string]string{"propagation": "rslave"}, + }}, + } + err := rt.Run(ctx, t1) + assert.NoError(t, err) +} + func Test_imagePull(t *testing.T) { ctx := context.Background() From b849aea377a2f7b194dd5d3bc7d0ee207405fd87 Mon Sep 17 00:00:00 2001 From: "chip.selden" Date: Tue, 31 Mar 2026 12:07:30 -0700 Subject: [PATCH 2/2] do not parse mount ops for podman volume mounts --- runtime/podman/podman.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/podman/podman.go b/runtime/podman/podman.go index 622abc27..4715817d 100644 --- a/runtime/podman/podman.go +++ b/runtime/podman/podman.go @@ -247,7 +247,9 @@ func (d *PodmanRuntime) doRun(ctx context.Context, t *tork.Task, logger io.Write // add mounts to the container for _, mount := range t.Mounts { switch mount.Type { - case tork.MountTypeVolume, tork.MountTypeBind: + case tork.MountTypeVolume: + createCmd.Args = append(createCmd.Args, "-v", fmt.Sprintf("%s:%s", mount.Source, mount.Target)) + case tork.MountTypeBind: createCmd.Args = append(createCmd.Args, "-v", formatVolumeSpec(mount)) default: return fmt.Errorf("unknown mount type: %s", mount.Type)