Handle relative source mounts

With this change it is now possible to give a relative path to the --volume and
--mount flags.

$ docker run --mount type=bind,source=./,target=/test ...

$ docker run -v .:/test ...

Fixes #1203

Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
This commit is contained in:
Djordje Lukic 2022-03-10 16:40:49 +01:00
parent 2b52f62e96
commit ab35e3fac3
3 changed files with 59 additions and 1 deletions

View File

@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"reflect"
"regexp"
"strconv"
@ -15,6 +16,7 @@ import (
"github.com/docker/cli/cli/compose/loader"
"github.com/docker/cli/opts"
"github.com/docker/docker/api/types/container"
mounttypes "github.com/docker/docker/api/types/mount"
networktypes "github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/api/types/versions"
@ -348,10 +350,25 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
// add any bind targets to the list of container volumes
for bind := range copts.volumes.GetMap() {
parsed, _ := loader.ParseVolume(bind)
if parsed.Source != "" {
toBind := bind
if parsed.Type == string(mounttypes.TypeBind) {
if arr := strings.SplitN(bind, ":", 2); len(arr) == 2 {
hostPart := arr[0]
if strings.HasPrefix(hostPart, "."+string(filepath.Separator)) || hostPart == "." {
if absHostPart, err := filepath.Abs(hostPart); err == nil {
hostPart = absHostPart
}
}
toBind = hostPart + ":" + arr[1]
}
}
// after creating the bind mount we want to delete it from the copts.volumes values because
// we do not want bind mounts being committed to image configs
binds = append(binds, bind)
binds = append(binds, toBind)
// We should delete from the map (`volumes`) here, as deleting from copts.volumes will not work if
// there are duplicates entries.
delete(volumes, bind)

View File

@ -4,6 +4,7 @@ import (
"encoding/csv"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
@ -92,6 +93,11 @@ func (m *MountOpt) Set(value string) error {
mount.Type = mounttypes.Type(strings.ToLower(value))
case "source", "src":
mount.Source = value
if strings.HasPrefix(value, "."+string(filepath.Separator)) || value == "." {
if abs, err := filepath.Abs(value); err == nil {
mount.Source = abs
}
}
case "target", "dst", "destination":
mount.Target = value
case "readonly", "ro":

View File

@ -2,6 +2,7 @@ package opts
import (
"os"
"path/filepath"
"testing"
mounttypes "github.com/docker/docker/api/types/mount"
@ -28,6 +29,40 @@ func TestMountOptString(t *testing.T) {
assert.Check(t, is.Equal(expected, mount.String()))
}
func TestMountRelative(t *testing.T) {
for _, testcase := range []struct {
name string
path string
bind string
}{
{
name: "Current path",
path: ".",
bind: "type=bind,source=.,target=/target",
}, {
name: "Current path with slash",
path: "./",
bind: "type=bind,source=./,target=/target",
},
} {
t.Run(testcase.name, func(t *testing.T) {
var mount MountOpt
assert.NilError(t, mount.Set(testcase.bind))
mounts := mount.Value()
assert.Assert(t, is.Len(mounts, 1))
abs, err := filepath.Abs(testcase.path)
assert.NilError(t, err)
assert.Check(t, is.DeepEqual(mounttypes.Mount{
Type: mounttypes.TypeBind,
Source: abs,
Target: "/target",
}, mounts[0]))
})
}
}
func TestMountOptSetBindNoErrorBind(t *testing.T) {
for _, testcase := range []string{
// tests several aliases that should have same result.