mirror of https://github.com/docker/cli.git
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:
parent
2b52f62e96
commit
ab35e3fac3
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -15,6 +16,7 @@ import (
|
||||||
"github.com/docker/cli/cli/compose/loader"
|
"github.com/docker/cli/cli/compose/loader"
|
||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
mounttypes "github.com/docker/docker/api/types/mount"
|
||||||
networktypes "github.com/docker/docker/api/types/network"
|
networktypes "github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/api/types/strslice"
|
"github.com/docker/docker/api/types/strslice"
|
||||||
"github.com/docker/docker/api/types/versions"
|
"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
|
// add any bind targets to the list of container volumes
|
||||||
for bind := range copts.volumes.GetMap() {
|
for bind := range copts.volumes.GetMap() {
|
||||||
parsed, _ := loader.ParseVolume(bind)
|
parsed, _ := loader.ParseVolume(bind)
|
||||||
|
|
||||||
if parsed.Source != "" {
|
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
|
// 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
|
// 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
|
// We should delete from the map (`volumes`) here, as deleting from copts.volumes will not work if
|
||||||
// there are duplicates entries.
|
// there are duplicates entries.
|
||||||
delete(volumes, bind)
|
delete(volumes, bind)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -92,6 +93,11 @@ func (m *MountOpt) Set(value string) error {
|
||||||
mount.Type = mounttypes.Type(strings.ToLower(value))
|
mount.Type = mounttypes.Type(strings.ToLower(value))
|
||||||
case "source", "src":
|
case "source", "src":
|
||||||
mount.Source = value
|
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":
|
case "target", "dst", "destination":
|
||||||
mount.Target = value
|
mount.Target = value
|
||||||
case "readonly", "ro":
|
case "readonly", "ro":
|
||||||
|
|
|
@ -2,6 +2,7 @@ package opts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
mounttypes "github.com/docker/docker/api/types/mount"
|
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()))
|
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) {
|
func TestMountOptSetBindNoErrorBind(t *testing.T) {
|
||||||
for _, testcase := range []string{
|
for _, testcase := range []string{
|
||||||
// tests several aliases that should have same result.
|
// tests several aliases that should have same result.
|
||||||
|
|
Loading…
Reference in New Issue