From e4ed2b02d7601bb10d85329873f37598a9077848 Mon Sep 17 00:00:00 2001
From: Guillaume Tardif <guillaume.tardif@gmail.com>
Date: Tue, 8 Dec 2020 17:43:10 +0100
Subject: [PATCH] Fix container attaching to wrong volume name for compose
 non-external volumes. Took the opportunity to validate R/O volumes, but not
 related to this fix

Signed-off-by: Guillaume Tardif <guillaume.tardif@gmail.com>
---
 local/compose/compose_test.go                 | 23 ++++++++++++++++++-
 local/compose/create.go                       | 16 +++++++++----
 tests/compose-e2e/compose_test.go             | 10 ++++++++
 .../volume-test/docker-compose.yml            |  2 +-
 4 files changed, 44 insertions(+), 7 deletions(-)

diff --git a/local/compose/compose_test.go b/local/compose/compose_test.go
index 94d8460b2..6aa295558 100644
--- a/local/compose/compose_test.go
+++ b/local/compose/compose_test.go
@@ -110,13 +110,34 @@ func TestStacksMixedStatus(t *testing.T) {
 }
 
 func TestBuildBindMount(t *testing.T) {
+	project := composetypes.Project{}
 	volume := composetypes.ServiceVolumeConfig{
 		Type:   composetypes.VolumeTypeBind,
 		Source: "e2e/volume-test",
 		Target: "/data",
 	}
-	mount, err := buildMount(volume)
+	mount, err := buildMount(project, volume)
 	assert.NilError(t, err)
 	assert.Assert(t, filepath.IsAbs(mount.Source))
 	assert.Equal(t, mount.Type, mountTypes.TypeBind)
 }
+
+func TestBuildVolumeMount(t *testing.T) {
+	project := composetypes.Project{
+		Name: "myProject",
+		Volumes: composetypes.Volumes(map[string]composetypes.VolumeConfig{
+			"myVolume": {
+				Name: "myProject_myVolume",
+			},
+		}),
+	}
+	volume := composetypes.ServiceVolumeConfig{
+		Type:   composetypes.VolumeTypeVolume,
+		Source: "myVolume",
+		Target: "/data",
+	}
+	mount, err := buildMount(project, volume)
+	assert.NilError(t, err)
+	assert.Equal(t, mount.Source, "myProject_myVolume")
+	assert.Equal(t, mount.Type, mountTypes.TypeVolume)
+}
diff --git a/local/compose/create.go b/local/compose/create.go
index 94bae858c..2eafbf2b7 100644
--- a/local/compose/create.go
+++ b/local/compose/create.go
@@ -144,7 +144,7 @@ func getContainerCreateOptions(p *types.Project, s types.ServiceConfig, number i
 		StopTimeout: convert.ToSeconds(s.StopGracePeriod),
 	}
 
-	mountOptions, err := buildContainerMountOptions(s, inherit)
+	mountOptions, err := buildContainerMountOptions(*p, s, inherit)
 	if err != nil {
 		return nil, nil, nil, err
 	}
@@ -191,7 +191,7 @@ func buildContainerBindingOptions(s types.ServiceConfig) nat.PortMap {
 	return bindings
 }
 
-func buildContainerMountOptions(s types.ServiceConfig, inherit *moby.Container) ([]mount.Mount, error) {
+func buildContainerMountOptions(p types.Project, s types.ServiceConfig, inherit *moby.Container) ([]mount.Mount, error) {
 	mounts := []mount.Mount{}
 	var inherited []string
 	if inherit != nil {
@@ -217,7 +217,7 @@ func buildContainerMountOptions(s types.ServiceConfig, inherit *moby.Container)
 		if contains(inherited, v.Target) {
 			continue
 		}
-		mount, err := buildMount(v)
+		mount, err := buildMount(p, v)
 		if err != nil {
 			return nil, err
 		}
@@ -226,9 +226,9 @@ func buildContainerMountOptions(s types.ServiceConfig, inherit *moby.Container)
 	return mounts, nil
 }
 
-func buildMount(volume types.ServiceVolumeConfig) (mount.Mount, error) {
+func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.Mount, error) {
 	source := volume.Source
-	if volume.Type == "bind" && !filepath.IsAbs(source) {
+	if volume.Type == types.VolumeTypeBind && !filepath.IsAbs(source) {
 		// volume source has already been prefixed with workdir if required, by compose-go project loader
 		var err error
 		source, err = filepath.Abs(source)
@@ -236,6 +236,12 @@ func buildMount(volume types.ServiceVolumeConfig) (mount.Mount, error) {
 			return mount.Mount{}, err
 		}
 	}
+	if volume.Type == types.VolumeTypeVolume {
+		pVolume, ok := project.Volumes[volume.Source]
+		if ok {
+			source = pVolume.Name
+		}
+	}
 
 	return mount.Mount{
 		Type:          mount.Type(volume.Type),
diff --git a/tests/compose-e2e/compose_test.go b/tests/compose-e2e/compose_test.go
index f9e5f4d46..a36a67e3e 100644
--- a/tests/compose-e2e/compose_test.go
+++ b/tests/compose-e2e/compose_test.go
@@ -120,10 +120,20 @@ func TestLocalComposeVolume(t *testing.T) {
 		c.RunDockerOrExitError("rmi", "compose-e2e-volume_nginx")
 		c.RunDockerOrExitError("volume", "rm", projectName+"_staticVol")
 		c.RunDockerCmd("compose", "up", "-d", "--workdir", "volume-test", "--project-name", projectName)
+	})
 
+	t.Run("access bind mount data", func(t *testing.T) {
 		output := HTTPGetWithRetry(t, "http://localhost:8090", http.StatusOK, 2*time.Second, 20*time.Second)
 		assert.Assert(t, strings.Contains(output, "Hello from Nginx container"))
+	})
 
+	t.Run("check container volume specs", func(t *testing.T) {
+		res := c.RunDockerCmd("inspect", "compose-e2e-volume_nginx2_1", "--format", "{{ json .HostConfig.Mounts }}")
+		//nolint
+		res.Assert(t, icmd.Expected{Out: `[{"Type":"volume","Source":"compose-e2e-volume_staticVol","Target":"/usr/share/nginx/html","ReadOnly":true},{"Type":"volume","Target":"/usr/src/app/node_modules"}]`})
+	})
+
+	t.Run("cleanup volume project", func(t *testing.T) {
 		_ = c.RunDockerCmd("compose", "down", "--project-name", projectName)
 		_ = c.RunDockerCmd("volume", "rm", projectName+"_staticVol")
 	})
diff --git a/tests/compose-e2e/volume-test/docker-compose.yml b/tests/compose-e2e/volume-test/docker-compose.yml
index 1e027f862..57ec42fd0 100644
--- a/tests/compose-e2e/volume-test/docker-compose.yml
+++ b/tests/compose-e2e/volume-test/docker-compose.yml
@@ -9,7 +9,7 @@ services:
   nginx2:
     build: nginx-build
     volumes:
-      - staticVol:/usr/share/nginx/html
+      - staticVol:/usr/share/nginx/html:ro
       - /usr/src/app/node_modules
     ports:
       - 9090:80