From 814536c0bd2099ca31632b2d1c5ae9bc50481c8d Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Fri, 4 Dec 2020 10:28:43 +0100 Subject: [PATCH 1/3] Fixing bind mount with relative path when specifying relative working dir. Added first local compose volume e2e test Signed-off-by: Guillaume Tardif --- local/compose.go | 17 ++++++++++++----- local/e2e/compose_test.go | 19 ++++++++++++++++++- local/e2e/volume-test/docker-compose.yml | 7 +++++++ local/e2e/volume-test/static/index.html | 10 ++++++++++ 4 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 local/e2e/volume-test/docker-compose.yml create mode 100644 local/e2e/volume-test/static/index.html diff --git a/local/compose.go b/local/compose.go index a6653e33..e73c32ad 100644 --- a/local/compose.go +++ b/local/compose.go @@ -800,7 +800,10 @@ func getContainerCreateOptions(p *types.Project, s types.ServiceConfig, number i StopTimeout: toSeconds(s.StopGracePeriod), } - mountOptions := buildContainerMountOptions(p, s, inherit) + mountOptions, err := buildContainerMountOptions(p, s, inherit) + if err != nil { + return nil, nil, nil, err + } bindings := buildContainerBindingOptions(s) networkMode := getNetworkMode(p, s) @@ -844,7 +847,7 @@ func buildContainerBindingOptions(s types.ServiceConfig) nat.PortMap { return bindings } -func buildContainerMountOptions(p *types.Project, s types.ServiceConfig, inherit *moby.Container) []mount.Mount { +func buildContainerMountOptions(p *types.Project, s types.ServiceConfig, inherit *moby.Container) ([]mount.Mount, error) { mounts := []mount.Mount{} var inherited []string if inherit != nil { @@ -872,8 +875,12 @@ func buildContainerMountOptions(p *types.Project, s types.ServiceConfig, inherit } source := v.Source if v.Type == "bind" && !filepath.IsAbs(source) { - // FIXME handle ~/ - source = filepath.Join(p.WorkingDir, source) + // volume source has already been prefixed with workdir if required, by compose-go project loader + var err error + source, err = filepath.Abs(source) + if err != nil { + return nil, err + } } mounts = append(mounts, mount.Mount{ @@ -887,7 +894,7 @@ func buildContainerMountOptions(p *types.Project, s types.ServiceConfig, inherit TmpfsOptions: buildTmpfsOptions(v.Tmpfs), }) } - return mounts + return mounts, nil } func buildBindOption(bind *types.ServiceVolumeBind) *mount.BindOptions { diff --git a/local/e2e/compose_test.go b/local/e2e/compose_test.go index 9e5ea535..a9042f35 100644 --- a/local/e2e/compose_test.go +++ b/local/e2e/compose_test.go @@ -28,7 +28,7 @@ import ( . "github.com/docker/compose-cli/tests/framework" ) -func TestLocalBackendComposeUp(t *testing.T) { +func TestLocalComposeUp(t *testing.T) { c := NewParallelE2eCLI(t, binDir) c.RunDockerCmd("context", "create", "local", "test-context").Assert(t, icmd.Success) c.RunDockerCmd("context", "use", "test-context").Assert(t, icmd.Success) @@ -86,3 +86,20 @@ func TestLocalBackendComposeUp(t *testing.T) { assert.Equal(t, networkList.Stdout(), networksAfterDown.Stdout()) }) } + +func TestLocalComposeVolume(t *testing.T) { + c := NewParallelE2eCLI(t, binDir) + c.RunDockerCmd("context", "create", "local", "test-context").Assert(t, icmd.Success) + c.RunDockerCmd("context", "use", "test-context").Assert(t, icmd.Success) + + const projectName = "compose-e2e-volume" + + t.Run("up with volume", func(t *testing.T) { + c.RunDockerCmd("compose", "up", "--workdir", "volume-test", "--project-name", projectName) + + output := HTTPGetWithRetry(t, "http://localhost:8090", http.StatusOK, 2*time.Second, 20*time.Second) + assert.Assert(t, strings.Contains(output, "Hello from Nginx container")) + + _ = c.RunDockerCmd("compose", "down", "--project-name", projectName) + }) +} diff --git a/local/e2e/volume-test/docker-compose.yml b/local/e2e/volume-test/docker-compose.yml new file mode 100644 index 00000000..09afab38 --- /dev/null +++ b/local/e2e/volume-test/docker-compose.yml @@ -0,0 +1,7 @@ +services: + nginx: + image: nginx + volumes: + - ./static:/usr/share/nginx/html + ports: + - 8090:80 diff --git a/local/e2e/volume-test/static/index.html b/local/e2e/volume-test/static/index.html new file mode 100644 index 00000000..0c7642bc --- /dev/null +++ b/local/e2e/volume-test/static/index.html @@ -0,0 +1,10 @@ + + + + + Docker Nginx + + +

Hello from Nginx container

+ + \ No newline at end of file From 0b2eaede8cd50131689a05309e178f3b8d60cd58 Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Fri, 4 Dec 2020 10:55:37 +0100 Subject: [PATCH 2/3] Adding unit test for bind mount creation Signed-off-by: Guillaume Tardif --- local/compose.go | 46 +++++++++++++++++++++++++------------------ local/compose_test.go | 18 +++++++++++++++++ 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/local/compose.go b/local/compose.go index e73c32ad..2fb7cc5e 100644 --- a/local/compose.go +++ b/local/compose.go @@ -873,30 +873,38 @@ func buildContainerMountOptions(p *types.Project, s types.ServiceConfig, inherit if contains(inherited, v.Target) { continue } - source := v.Source - if v.Type == "bind" && !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) - if err != nil { - return nil, err - } + mount, err := buildMount(v) + if err != nil { + return nil, err } - - mounts = append(mounts, mount.Mount{ - Type: mount.Type(v.Type), - Source: source, - Target: v.Target, - ReadOnly: v.ReadOnly, - Consistency: mount.Consistency(v.Consistency), - BindOptions: buildBindOption(v.Bind), - VolumeOptions: buildVolumeOptions(v.Volume), - TmpfsOptions: buildTmpfsOptions(v.Tmpfs), - }) + mounts = append(mounts, mount) } return mounts, nil } +func buildMount(volume types.ServiceVolumeConfig) (mount.Mount, error) { + source := volume.Source + if volume.Type == "bind" && !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) + if err != nil { + return mount.Mount{}, err + } + } + + return mount.Mount{ + Type: mount.Type(volume.Type), + Source: source, + Target: volume.Target, + ReadOnly: volume.ReadOnly, + Consistency: mount.Consistency(volume.Consistency), + BindOptions: buildBindOption(volume.Bind), + VolumeOptions: buildVolumeOptions(volume.Volume), + TmpfsOptions: buildTmpfsOptions(volume.Tmpfs), + }, nil +} + func buildBindOption(bind *types.ServiceVolumeBind) *mount.BindOptions { if bind == nil { return nil diff --git a/local/compose_test.go b/local/compose_test.go index 74de0745..fa406709 100644 --- a/local/compose_test.go +++ b/local/compose_test.go @@ -19,9 +19,13 @@ package local import ( + "os" + "path/filepath" "testing" + composetypes "github.com/compose-spec/compose-go/types" "github.com/docker/docker/api/types" + mountTypes "github.com/docker/docker/api/types/mount" "gotest.tools/v3/assert" "github.com/docker/compose-cli/api/compose" @@ -107,3 +111,17 @@ func TestStacksMixedStatus(t *testing.T) { assert.Equal(t, combinedStatus([]string{"running", "running", "running"}), "running(3)") assert.Equal(t, combinedStatus([]string{"running", "exited", "running"}), "exited(1), running(2)") } + +func TestBuildBindMount(t *testing.T) { + volume := composetypes.ServiceVolumeConfig{ + Type: composetypes.VolumeTypeBind, + Source: "e2e/volume-test", + Target: "/data", + } + mount, err := buildMount(volume) + assert.NilError(t, err) + assert.Assert(t, filepath.IsAbs(mount.Source)) + _, err = os.Stat(mount.Source) + assert.NilError(t, err) + assert.Equal(t, mount.Type, mountTypes.TypeBind) +} From aacd881358265c53f8af3c2c91a24061e5c296d3 Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Fri, 4 Dec 2020 15:19:12 +0100 Subject: [PATCH 3/3] Add -d flag in e2e tests now that `docker compose up` attaches to logs by default Signed-off-by: Guillaume Tardif --- local/e2e/compose_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/local/e2e/compose_test.go b/local/e2e/compose_test.go index a9042f35..9a2211ec 100644 --- a/local/e2e/compose_test.go +++ b/local/e2e/compose_test.go @@ -45,7 +45,7 @@ func TestLocalComposeUp(t *testing.T) { }) t.Run("up", func(t *testing.T) { - c.RunDockerCmd("compose", "up", "-f", "../../tests/composefiles/demo_multi_port.yaml", "--project-name", projectName, "-d") + c.RunDockerCmd("compose", "up", "-d", "-f", "../../tests/composefiles/demo_multi_port.yaml", "--project-name", projectName, "-d") }) t.Run("check running project", func(t *testing.T) { @@ -95,7 +95,7 @@ func TestLocalComposeVolume(t *testing.T) { const projectName = "compose-e2e-volume" t.Run("up with volume", func(t *testing.T) { - c.RunDockerCmd("compose", "up", "--workdir", "volume-test", "--project-name", projectName) + c.RunDockerCmd("compose", "up", "-d", "--workdir", "volume-test", "--project-name", projectName) output := HTTPGetWithRetry(t, "http://localhost:8090", http.StatusOK, 2*time.Second, 20*time.Second) assert.Assert(t, strings.Contains(output, "Hello from Nginx container"))