Merge pull request #47191 from vvoland/volume-cifs-resolve-optout-25

[25.0 backport] volume/local: Make host resolution backwards compatible
This commit is contained in:
Paweł Gronowski 2024-01-23 19:13:39 +01:00 committed by GitHub
commit 6eef840b8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 117 additions and 16 deletions

View file

@ -3,6 +3,7 @@
package local // import "github.com/docker/docker/volume/local"
import (
"net"
"os"
"path/filepath"
"strconv"
@ -246,3 +247,84 @@ func TestVolCreateValidation(t *testing.T) {
})
}
}
func TestVolMountOpts(t *testing.T) {
tests := []struct {
name string
opts optsConfig
expectedErr string
expectedDevice, expectedOpts string
}{
{
name: "cifs url with space",
opts: optsConfig{
MountType: "cifs",
MountDevice: "//1.2.3.4/Program Files",
},
expectedDevice: "//1.2.3.4/Program Files",
expectedOpts: "",
},
{
name: "cifs resolve addr",
opts: optsConfig{
MountType: "cifs",
MountDevice: "//example.com/Program Files",
MountOpts: "addr=example.com",
},
expectedDevice: "//example.com/Program Files",
expectedOpts: "addr=1.2.3.4",
},
{
name: "cifs resolve device",
opts: optsConfig{
MountType: "cifs",
MountDevice: "//example.com/Program Files",
},
expectedDevice: "//1.2.3.4/Program Files",
},
{
name: "nfs dont resolve device",
opts: optsConfig{
MountType: "nfs",
MountDevice: "//example.com/Program Files",
},
expectedDevice: "//example.com/Program Files",
},
{
name: "nfs resolve addr",
opts: optsConfig{
MountType: "nfs",
MountDevice: "//example.com/Program Files",
MountOpts: "addr=example.com",
},
expectedDevice: "//example.com/Program Files",
expectedOpts: "addr=1.2.3.4",
},
}
ip1234 := net.ParseIP("1.2.3.4")
resolveIP := func(network, addr string) (*net.IPAddr, error) {
switch addr {
case "example.com":
return &net.IPAddr{IP: ip1234}, nil
}
return nil, &net.DNSError{Err: "no such host", Name: addr, IsNotFound: true}
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
dev, opts, err := getMountOptions(&tc.opts, resolveIP)
if tc.expectedErr != "" {
assert.Check(t, is.ErrorContains(err, tc.expectedErr))
} else {
assert.Check(t, err)
}
assert.Check(t, is.Equal(dev, tc.expectedDevice))
assert.Check(t, is.Equal(opts, tc.expectedOpts))
})
}
}

View file

@ -118,37 +118,56 @@ func (v *localVolume) needsMount() bool {
return false
}
func (v *localVolume) mount() error {
if v.opts.MountDevice == "" {
return fmt.Errorf("missing device in volume options")
func getMountOptions(opts *optsConfig, resolveIP func(string, string) (*net.IPAddr, error)) (mountDevice string, mountOpts string, _ error) {
if opts.MountDevice == "" {
return "", "", fmt.Errorf("missing device in volume options")
}
mountOpts := v.opts.MountOpts
mountDevice := v.opts.MountDevice
mountOpts = opts.MountOpts
mountDevice = opts.MountDevice
switch v.opts.MountType {
case "nfs":
if addrValue := getAddress(v.opts.MountOpts); addrValue != "" && net.ParseIP(addrValue).To4() == nil {
ipAddr, err := net.ResolveIPAddr("ip", addrValue)
switch opts.MountType {
case "nfs", "cifs":
if addrValue := getAddress(opts.MountOpts); addrValue != "" && net.ParseIP(addrValue).To4() == nil {
ipAddr, err := resolveIP("ip", addrValue)
if err != nil {
return errors.Wrapf(err, "error resolving passed in network volume address")
return "", "", errors.Wrap(err, "error resolving passed in network volume address")
}
mountOpts = strings.Replace(mountOpts, "addr="+addrValue, "addr="+ipAddr.String(), 1)
break
}
case "cifs":
deviceURL, err := url.Parse(v.opts.MountDevice)
if opts.MountType != "cifs" {
break
}
deviceURL, err := url.Parse(mountDevice)
if err != nil {
return errors.Wrapf(err, "error parsing mount device url")
return "", "", errors.Wrap(err, "error parsing mount device url")
}
if deviceURL.Host != "" && net.ParseIP(deviceURL.Host) == nil {
ipAddr, err := net.ResolveIPAddr("ip", deviceURL.Host)
ipAddr, err := resolveIP("ip", deviceURL.Host)
if err != nil {
return errors.Wrapf(err, "error resolving passed in network volume address")
return "", "", errors.Wrap(err, "error resolving passed in network volume address")
}
deviceURL.Host = ipAddr.String()
mountDevice = deviceURL.String()
dev, err := url.QueryUnescape(deviceURL.String())
if err != nil {
return "", "", fmt.Errorf("failed to unescape device URL: %q", deviceURL)
}
mountDevice = dev
}
}
return mountDevice, mountOpts, nil
}
func (v *localVolume) mount() error {
mountDevice, mountOpts, err := getMountOptions(v.opts, net.ResolveIPAddr)
if err != nil {
return err
}
if err := mount.Mount(mountDevice, v.path, v.opts.MountType, mountOpts); err != nil {
if password := getPassword(v.opts.MountOpts); password != "" {
err = errors.New(strings.Replace(err.Error(), "password="+password, "password=********", 1))