From 919f351b4b4fa58afdf7cd9d94234392084714b4 Mon Sep 17 00:00:00 2001 From: Ulysses Souza Date: Tue, 31 May 2022 14:59:18 +0200 Subject: [PATCH] Fix bind mounts when in project volumes definition Signed-off-by: Ulysses Souza --- pkg/compose/create.go | 25 ++++++++++++---- .../docker-compose.yml | 14 +++++++++ pkg/e2e/framework.go | 13 ++++++++ pkg/e2e/volumes_test.go | 30 +++++++++++++++++++ 4 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 pkg/e2e/fixtures/project-volume-bind-test/docker-compose.yml diff --git a/pkg/compose/create.go b/pkg/compose/create.go index e9589cf75..780df8d43 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -718,9 +718,15 @@ MOUNTS: // so `Bind` API is used here with raw volume string // see https://github.com/moby/moby/issues/43483 for _, v := range service.Volumes { - if v.Target == m.Target && v.Bind != nil && v.Bind.CreateHostPath { - binds = append(binds, v.String()) - continue MOUNTS + if v.Target == m.Target { + switch { + case string(m.Type) != v.Type: + v.Source = m.Source + fallthrough + case v.Bind != nil && v.Bind.CreateHostPath: + binds = append(binds, v.String()) + continue MOUNTS + } } } } @@ -911,10 +917,14 @@ func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount. } } - bind, vol, tmpfs := buildMountOptions(volume) + bind, vol, tmpfs := buildMountOptions(project, volume) volume.Target = path.Clean(volume.Target) + if bind != nil { + volume.Type = types.VolumeTypeBind + } + return mount.Mount{ Type: mount.Type(volume.Type), Source: source, @@ -927,7 +937,7 @@ func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount. }, nil } -func buildMountOptions(volume types.ServiceVolumeConfig) (*mount.BindOptions, *mount.VolumeOptions, *mount.TmpfsOptions) { +func buildMountOptions(project types.Project, volume types.ServiceVolumeConfig) (*mount.BindOptions, *mount.VolumeOptions, *mount.TmpfsOptions) { switch volume.Type { case "bind": if volume.Volume != nil { @@ -944,6 +954,11 @@ func buildMountOptions(volume types.ServiceVolumeConfig) (*mount.BindOptions, *m if volume.Tmpfs != nil { logrus.Warnf("mount of type `volume` should not define `tmpfs` option") } + if v, ok := project.Volumes[volume.Source]; ok && v.DriverOpts["o"] == types.VolumeTypeBind { + return buildBindOption(&types.ServiceVolumeBind{ + CreateHostPath: true, + }), nil, nil + } return nil, buildVolumeOptions(volume.Volume), nil case "tmpfs": if volume.Bind != nil { diff --git a/pkg/e2e/fixtures/project-volume-bind-test/docker-compose.yml b/pkg/e2e/fixtures/project-volume-bind-test/docker-compose.yml new file mode 100644 index 000000000..aba0bf47e --- /dev/null +++ b/pkg/e2e/fixtures/project-volume-bind-test/docker-compose.yml @@ -0,0 +1,14 @@ +services: + frontend: + image: nginx + container_name: frontend + volumes: + - project_data:/data + +volumes: + project_data: + driver: local + driver_opts: + type: none + o: bind + device: "${TEST_DIR}" diff --git a/pkg/e2e/framework.go b/pkg/e2e/framework.go index fe2e17cb4..040422856 100644 --- a/pkg/e2e/framework.go +++ b/pkg/e2e/framework.go @@ -167,6 +167,19 @@ func (c *E2eCLI) NewCmd(command string, args ...string) icmd.Cmd { } } +// NewCmdWithEnv creates a cmd object configured with the test environment set with additional env vars +func (c *E2eCLI) NewCmdWithEnv(envvars []string, command string, args ...string) icmd.Cmd { + env := append(os.Environ(), + append(envvars, + "DOCKER_CONFIG="+c.ConfigDir, + "KUBECONFIG=invalid")..., + ) + return icmd.Cmd{ + Command: append([]string{command}, args...), + Env: env, + } +} + // MetricsSocket get the path where test metrics will be sent func (c *E2eCLI) MetricsSocket() string { return filepath.Join(c.ConfigDir, "./docker-cli.sock") diff --git a/pkg/e2e/volumes_test.go b/pkg/e2e/volumes_test.go index 571744902..220380641 100644 --- a/pkg/e2e/volumes_test.go +++ b/pkg/e2e/volumes_test.go @@ -18,11 +18,14 @@ package e2e import ( "net/http" + "os" + "path/filepath" "strings" "testing" "time" "gotest.tools/v3/assert" + "gotest.tools/v3/icmd" ) func TestLocalComposeVolume(t *testing.T) { @@ -88,3 +91,30 @@ func TestLocalComposeVolume(t *testing.T) { assert.Assert(t, !strings.Contains(ls, "myvolume")) }) } + +func TestProjectVolumeBind(t *testing.T) { + if composeStandaloneMode { + t.Skip() + } + c := NewParallelE2eCLI(t, binDir) + const projectName = "compose-e2e-project-volume-bind" + + t.Run("up on project volume with bind specification", func(t *testing.T) { + tmpDir, err := os.MkdirTemp("", projectName) + assert.NilError(t, err) + defer os.RemoveAll(tmpDir) // nolint + + c.RunDockerComposeCmd("--project-name", projectName, "down") + + c.RunDockerOrExitError("volume", "rm", "-f", projectName+"_project_data").Assert(t, icmd.Success) + cmd := c.NewCmdWithEnv([]string{"TEST_DIR=" + tmpDir}, + "docker", "compose", "--project-directory", "fixtures/project-volume-bind-test", "--project-name", projectName, "up", "-d") + icmd.RunCmd(cmd).Assert(t, icmd.Success) + defer c.RunDockerComposeCmd("--project-name", projectName, "down") + + c.RunCmd("sh", "-c", "echo SUCCESS > "+filepath.Join(tmpDir, "resultfile")).Assert(t, icmd.Success) + + ret := c.RunDockerOrExitError("exec", "frontend", "bash", "-c", "cat /data/resultfile").Assert(t, icmd.Success) + assert.Assert(t, strings.Contains(ret.Stdout(), "SUCCESS")) + }) +}