From 0d6a52083ca3bfb37f4eac560c3fec35ccf11f50 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sun, 3 May 2026 13:10:47 -0500 Subject: [PATCH 1/2] fix(run): respect user-specified consistency in workspaceMount Parse the user's workspaceMount string for an existing consistency value. Only apply the default consistency='consistent' on non-Linux if the user hasn't specified one. Also generalize nerdctl consistency stripping to handle all consistency values, not just the hardcoded 'consistent'. --- e2e/tests/up/provider_docker.go | 32 +++++++++++++++ .../.devcontainer.json | 6 +++ pkg/devcontainer/run.go | 21 +++++++++- pkg/devcontainer/run_test.go | 41 ++++++++++++++++++- pkg/driver/docker/docker.go | 14 ++++++- pkg/driver/docker/docker_test.go | 14 +++++++ 6 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 e2e/tests/up/testdata/docker-workspace-mount-consistency/.devcontainer.json diff --git a/e2e/tests/up/provider_docker.go b/e2e/tests/up/provider_docker.go index 1092fa266..55584b8b0 100644 --- a/e2e/tests/up/provider_docker.go +++ b/e2e/tests/up/provider_docker.go @@ -592,6 +592,38 @@ var _ = ginkgo.Describe( gomega.Expect(hasCustomMount).To(gomega.BeTrue()) }, ginkgo.SpecTimeout(framework.TimeoutShort())) + ginkgo.It( + "custom workspace mount with user-specified consistency", + func(ctx context.Context) { + tempDir, err := dtc.setupAndUp( + ctx, + "tests/up/testdata/docker-workspace-mount-consistency", + ) + framework.ExpectNoError(err) + + workspace, err := dtc.f.FindWorkspace(ctx, tempDir) + framework.ExpectNoError(err) + + ids, err := dtc.findWorkspaceContainer(ctx, workspace) + framework.ExpectNoError(err) + gomega.Expect(ids).To(gomega.HaveLen(1)) + + var details []container.InspectResponse + err = dtc.dockerHelper.Inspect(ctx, ids, "container", &details) + framework.ExpectNoError(err) + + hasCustomMount := false + for _, m := range details[0].Mounts { + if m.Destination == "/custom-workspace" { + hasCustomMount = true + break + } + } + gomega.Expect(hasCustomMount).To(gomega.BeTrue()) + }, + ginkgo.SpecTimeout(framework.TimeoutShort()), + ) + ginkgo.It("secrets-file injects env into lifecycle commands", func(ctx context.Context) { tempDir, err := setupWorkspace( "tests/up/testdata/docker-secrets-file", diff --git a/e2e/tests/up/testdata/docker-workspace-mount-consistency/.devcontainer.json b/e2e/tests/up/testdata/docker-workspace-mount-consistency/.devcontainer.json new file mode 100644 index 000000000..0ddfc448c --- /dev/null +++ b/e2e/tests/up/testdata/docker-workspace-mount-consistency/.devcontainer.json @@ -0,0 +1,6 @@ +{ + "name": "Go", + "image": "ghcr.io/devsy-org/test-images/go:1", + "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/custom-workspace,consistency=delegated", + "workspaceFolder": "/custom-workspace" +} diff --git a/pkg/devcontainer/run.go b/pkg/devcontainer/run.go index e75dda2c4..60ba867ed 100644 --- a/pkg/devcontainer/run.go +++ b/pkg/devcontainer/run.go @@ -311,13 +311,30 @@ func (c *initCmdContext) runSingle(name string, cmd []string) error { return nil } +func mountHasConsistency(mount string) bool { + for part := range strings.SplitSeq(mount, ",") { + if strings.HasPrefix(part, "consistency=") { + return true + } + } + return false +} + +func needsDefaultConsistency() bool { + return runtime.GOOS != "linux" +} + func getWorkspace( workspaceFolder, workspaceID string, conf *config.DevContainerConfig, ) (string, string) { if conf.WorkspaceMount != "" { mount := config.ParseMount(conf.WorkspaceMount) - return conf.WorkspaceMount, mount.Target + ws := conf.WorkspaceMount + if needsDefaultConsistency() && !mountHasConsistency(ws) { + ws += ",consistency='consistent'" + } + return ws, mount.Target } containerMountFolder := conf.WorkspaceFolder @@ -326,7 +343,7 @@ func getWorkspace( } consistency := "" - if runtime.GOOS != "linux" { + if needsDefaultConsistency() { consistency = ",consistency='consistent'" } diff --git a/pkg/devcontainer/run_test.go b/pkg/devcontainer/run_test.go index ffe987e8c..13126ea64 100644 --- a/pkg/devcontainer/run_test.go +++ b/pkg/devcontainer/run_test.go @@ -141,8 +141,8 @@ func TestGetWorkspace_CustomWorkspaceMount(t *testing.T) { mount, folder := getWorkspace("/ignored", "ws-id", conf) - if mount != customMount { - t.Fatalf("expected raw workspaceMount string, got %q", mount) + if !contains(mount, customMount) { + t.Fatalf("expected workspaceMount string to contain original, got %q", mount) } if folder != "/custom-ws" { t.Fatalf("expected target /custom-ws, got %q", folder) @@ -198,3 +198,40 @@ func TestGetWorkspace_EmptyWorkspaceMount(t *testing.T) { t.Fatalf("expected default bind mount, got %q", mount) } } + +func TestGetWorkspace_UserConsistencyPreserved(t *testing.T) { + customMount := "type=bind,source=/src,target=/ws,consistency=delegated" + conf := &config.DevContainerConfig{ + NonComposeBase: config.NonComposeBase{ + WorkspaceMount: customMount, + }, + } + + mount, _ := getWorkspace("/ignored", "ws-id", conf) + + if !contains(mount, "consistency=delegated") { + t.Fatalf("expected user consistency=delegated preserved, got %q", mount) + } + if contains(mount, "consistency='consistent'") { + t.Fatalf( + "default consistency should not be appended when user specifies one, got %q", + mount, + ) + } +} + +func TestMountHasConsistency(t *testing.T) { + tests := []struct { + mount string + want bool + }{ + {"type=bind,source=/s,target=/t,consistency=cached", true}, + {"type=bind,source=/s,target=/t,consistency='consistent'", true}, + {"type=bind,source=/s,target=/t", false}, + } + for _, tt := range tests { + if got := mountHasConsistency(tt.mount); got != tt.want { + t.Errorf("mountHasConsistency(%q) = %v, want %v", tt.mount, got, tt.want) + } + } +} diff --git a/pkg/driver/docker/docker.go b/pkg/driver/docker/docker.go index c26e0c060..864cf578e 100644 --- a/pkg/driver/docker/docker.go +++ b/pkg/driver/docker/docker.go @@ -622,14 +622,24 @@ func (d *dockerDriver) addWorkspaceMountArgs( if options.WorkspaceMount != nil { workspacePath := d.EnsurePath(options.WorkspaceMount) mountPath := workspacePath.String() - if helper.IsNerdctl() && strings.Contains(mountPath, ",consistency='consistent'") { - mountPath = strings.Replace(mountPath, ",consistency='consistent'", "", 1) + if helper.IsNerdctl() { + mountPath = stripMountConsistency(mountPath) } args = append(args, "--mount", mountPath) } return args } +func stripMountConsistency(mount string) string { + var parts []string + for part := range strings.SplitSeq(mount, ",") { + if !strings.HasPrefix(part, "consistency=") { + parts = append(parts, part) + } + } + return strings.Join(parts, ",") +} + func (d *dockerDriver) addUserArgs(args []string, options *driver.RunOptions) []string { if options.User != "" { args = append(args, "-u", options.User) diff --git a/pkg/driver/docker/docker_test.go b/pkg/driver/docker/docker_test.go index 37bc0de52..71b2bc66d 100644 --- a/pkg/driver/docker/docker_test.go +++ b/pkg/driver/docker/docker_test.go @@ -178,3 +178,17 @@ func (s *DockerDriverTestSuite) TestAddCapabilityArgs_CapAddAndSecurityOpt() { testSecurityOptFlag, testSeccompUnconfined, }, args) } + +func (s *DockerDriverTestSuite) TestStripMountConsistency() { + tests := []struct { + input string + want string + }{ + {"type=bind,src=/a,dst=/b,consistency='consistent'", "type=bind,src=/a,dst=/b"}, + {"type=bind,src=/a,dst=/b,consistency=delegated", "type=bind,src=/a,dst=/b"}, + {"type=bind,src=/a,dst=/b", "type=bind,src=/a,dst=/b"}, + } + for _, tt := range tests { + s.Equal(tt.want, stripMountConsistency(tt.input)) + } +} From 56daaa86accae9516d1712d9dc65886aef077f84 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sun, 3 May 2026 13:19:33 -0500 Subject: [PATCH 2/2] fix(lint): extract repeated mount string to constant Satisfies goconst linter rule for string with 4+ occurrences. --- pkg/driver/docker/docker_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/driver/docker/docker_test.go b/pkg/driver/docker/docker_test.go index 71b2bc66d..d210d8597 100644 --- a/pkg/driver/docker/docker_test.go +++ b/pkg/driver/docker/docker_test.go @@ -12,6 +12,7 @@ import ( const ( testSeccompUnconfined = "seccomp=unconfined" testSecurityOptFlag = "--security-opt" + testBindMount = "type=bind,src=/a,dst=/b" ) type DockerDriverTestSuite struct { @@ -184,9 +185,9 @@ func (s *DockerDriverTestSuite) TestStripMountConsistency() { input string want string }{ - {"type=bind,src=/a,dst=/b,consistency='consistent'", "type=bind,src=/a,dst=/b"}, - {"type=bind,src=/a,dst=/b,consistency=delegated", "type=bind,src=/a,dst=/b"}, - {"type=bind,src=/a,dst=/b", "type=bind,src=/a,dst=/b"}, + {testBindMount + ",consistency='consistent'", testBindMount}, + {testBindMount + ",consistency=delegated", testBindMount}, + {testBindMount, testBindMount}, } for _, tt := range tests { s.Equal(tt.want, stripMountConsistency(tt.input))