Переглянути джерело

Merge pull request #36711 from cpuguy83/plugin_mounts_sorting

Don't sort plugin mounts slice
Anusha Ragunathan 7 роки тому
батько
коміт
18d1688530

+ 32 - 0
integration-cli/fixtures/plugin/plugin.go

@@ -155,6 +155,38 @@ func makePluginBundle(inPath string, opts ...CreateOpt) (io.ReadCloser, error) {
 	if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(p.Entrypoint[0])), 0755); err != nil {
 	if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(p.Entrypoint[0])), 0755); err != nil {
 		return nil, errors.Wrap(err, "error creating plugin rootfs dir")
 		return nil, errors.Wrap(err, "error creating plugin rootfs dir")
 	}
 	}
+
+	// Ensure the mount target paths exist
+	for _, m := range p.Mounts {
+		var stat os.FileInfo
+		if m.Source != nil {
+			stat, err = os.Stat(*m.Source)
+			if err != nil && !os.IsNotExist(err) {
+				return nil, err
+			}
+		}
+
+		if stat == nil || stat.IsDir() {
+			var mode os.FileMode = 0755
+			if stat != nil {
+				mode = stat.Mode()
+			}
+			if err := os.MkdirAll(filepath.Join(inPath, "rootfs", m.Destination), mode); err != nil {
+				return nil, errors.Wrap(err, "error preparing plugin mount destination path")
+			}
+		} else {
+			if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(m.Destination)), 0755); err != nil {
+				return nil, errors.Wrap(err, "error preparing plugin mount destination dir")
+			}
+			f, err := os.Create(filepath.Join(inPath, "rootfs", m.Destination))
+			if err != nil && !os.IsExist(err) {
+				return nil, errors.Wrap(err, "error preparing plugin mount destination file")
+			}
+			if f != nil {
+				f.Close()
+			}
+		}
+	}
 	if err := archive.NewDefaultArchiver().CopyFileWithTar(cfg.binPath, filepath.Join(inPath, "rootfs", p.Entrypoint[0])); err != nil {
 	if err := archive.NewDefaultArchiver().CopyFileWithTar(cfg.binPath, filepath.Join(inPath, "rootfs", p.Entrypoint[0])); err != nil {
 		return nil, errors.Wrap(err, "error copying plugin binary to rootfs path")
 		return nil, errors.Wrap(err, "error copying plugin binary to rootfs path")
 	}
 	}

+ 1 - 0
integration/plugin/volumes/cmd/cmd_test.go

@@ -0,0 +1 @@
+package cmd

+ 19 - 0
integration/plugin/volumes/cmd/dummy/main.go

@@ -0,0 +1,19 @@
+package main
+
+import (
+	"net"
+	"net/http"
+)
+
+func main() {
+	l, err := net.Listen("unix", "/run/docker/plugins/plugin.sock")
+	if err != nil {
+		panic(err)
+	}
+
+	server := http.Server{
+		Addr:    l.Addr().String(),
+		Handler: http.NewServeMux(),
+	}
+	server.Serve(l)
+}

+ 1 - 0
integration/plugin/volumes/cmd/dummy/main_test.go

@@ -0,0 +1 @@
+package main

+ 73 - 0
integration/plugin/volumes/helpers_test.go

@@ -0,0 +1,73 @@
+package volumes
+
+import (
+	"context"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/integration-cli/fixtures/plugin"
+	"github.com/docker/docker/pkg/locker"
+	"github.com/pkg/errors"
+)
+
+var pluginBuildLock = locker.New()
+
+// ensurePlugin makes the that a plugin binary has been installed on the system.
+// Plugins that have not been installed are built from `cmd/<name>`.
+func ensurePlugin(t *testing.T, name string) string {
+	pluginBuildLock.Lock(name)
+	defer pluginBuildLock.Unlock(name)
+
+	goPath := os.Getenv("GOPATH")
+	if goPath == "" {
+		goPath = "/go"
+	}
+	installPath := filepath.Join(goPath, "bin", name)
+	if _, err := os.Stat(installPath); err == nil {
+		return installPath
+	}
+
+	goBin, err := exec.LookPath("go")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	cmd := exec.Command(goBin, "build", "-o", installPath, "./"+filepath.Join("cmd", name))
+	cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
+	if out, err := cmd.CombinedOutput(); err != nil {
+		t.Fatal(errors.Wrapf(err, "error building basic plugin bin: %s", string(out)))
+	}
+
+	return installPath
+}
+
+func withSockPath(name string) func(*plugin.Config) {
+	return func(cfg *plugin.Config) {
+		cfg.Interface.Socket = name
+	}
+}
+
+func createPlugin(t *testing.T, client plugin.CreateClient, alias, bin string, opts ...plugin.CreateOpt) {
+	pluginBin := ensurePlugin(t, bin)
+
+	opts = append(opts, withSockPath("plugin.sock"))
+	opts = append(opts, plugin.WithBinary(pluginBin))
+
+	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
+	err := plugin.Create(ctx, client, alias, opts...)
+	cancel()
+
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func asVolumeDriver(cfg *plugin.Config) {
+	cfg.Interface.Types = []types.PluginInterfaceType{
+		{Capability: "volumedriver", Prefix: "docker", Version: "1.0"},
+	}
+}

+ 34 - 0
integration/plugin/volumes/main_test.go

@@ -0,0 +1,34 @@
+package volumes // import "github.com/docker/docker/integration/plugin/volumes"
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/docker/docker/internal/test/environment"
+)
+
+var (
+	testEnv *environment.Execution
+)
+
+const dockerdBinary = "dockerd"
+
+func TestMain(m *testing.M) {
+	var err error
+	testEnv, err = environment.New()
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+	if testEnv.OSType != "linux" {
+		os.Exit(0)
+	}
+	err = environment.EnsureFrozenImagesLinux(testEnv)
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+	testEnv.Print()
+	os.Exit(m.Run())
+}

+ 56 - 0
integration/plugin/volumes/mounts_test.go

@@ -0,0 +1,56 @@
+package volumes
+
+import (
+	"context"
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/integration-cli/daemon"
+	"github.com/docker/docker/integration-cli/fixtures/plugin"
+	"github.com/gotestyourself/gotestyourself/assert"
+)
+
+// TestPluginWithDevMounts tests very specific regression caused by mounts ordering
+// (sorted in the daemon). See #36698
+func TestPluginWithDevMounts(t *testing.T) {
+	t.Parallel()
+
+	d := daemon.New(t, "", dockerdBinary, daemon.Config{})
+	d.Start(t, "--iptables=false")
+	defer d.Stop(t)
+
+	client, err := d.NewClient()
+	assert.Assert(t, err)
+	ctx := context.Background()
+
+	testDir, err := ioutil.TempDir("", "test-dir")
+	assert.Assert(t, err)
+	defer os.RemoveAll(testDir)
+
+	createPlugin(t, client, "test", "dummy", asVolumeDriver, func(c *plugin.Config) {
+		root := "/"
+		dev := "/dev"
+		mounts := []types.PluginMount{
+			{Type: "bind", Source: &root, Destination: "/host", Options: []string{"rbind"}},
+			{Type: "bind", Source: &dev, Destination: "/dev", Options: []string{"rbind"}},
+			{Type: "bind", Source: &testDir, Destination: "/etc/foo", Options: []string{"rbind"}},
+		}
+		c.PluginConfig.Mounts = append(c.PluginConfig.Mounts, mounts...)
+		c.PropagatedMount = "/propagated"
+		c.Network = types.PluginConfigNetwork{Type: "host"}
+		c.IpcHost = true
+	})
+
+	err = client.PluginEnable(ctx, "test", types.PluginEnableOptions{Timeout: 30})
+	assert.Assert(t, err)
+	defer func() {
+		err := client.PluginRemove(ctx, "test", types.PluginRemoveOptions{Force: true})
+		assert.Check(t, err)
+	}()
+
+	p, _, err := client.PluginInspectWithRaw(ctx, "test")
+	assert.Assert(t, err)
+	assert.Assert(t, p.Enabled)
+}

+ 0 - 5
plugin/v2/plugin_linux.go

@@ -4,7 +4,6 @@ import (
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"runtime"
 	"runtime"
-	"sort"
 	"strings"
 	"strings"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
@@ -138,9 +137,5 @@ func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
 		p.modifyRuntimeSpec(&s)
 		p.modifyRuntimeSpec(&s)
 	}
 	}
 
 
-	sort.Slice(s.Mounts, func(i, j int) bool {
-		return s.Mounts[i].Destination < s.Mounts[j].Destination
-	})
-
 	return &s, nil
 	return &s, nil
 }
 }