瀏覽代碼

Upgrading Godep to the Latest Docker Pacakages that brings in the Plugins infra

Signed-off-by: Madhu Venugopal <madhu@docker.com>
Madhu Venugopal 10 年之前
父節點
當前提交
cb8bbd3ded
共有 16 個文件被更改,包括 783 次插入21 次删除
  1. 20 15
      libnetwork/Godeps/Godeps.json
  2. 125 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/readers_test.go
  3. 47 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/writeflusher.go
  4. 24 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/writers_test.go
  5. 1 1
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/flag.go
  6. 2 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go
  7. 65 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go
  8. 100 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/client.go
  9. 63 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/client_test.go
  10. 78 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery.go
  11. 108 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery_test.go
  12. 100 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/plugins.go
  13. 1 1
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unsupported.go
  14. 14 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_windows.go
  15. 13 3
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/stringid/stringid.go
  16. 22 1
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/stringid/stringid_test.go

+ 20 - 15
libnetwork/Godeps/Godeps.json

@@ -1,6 +1,6 @@
 {
 	"ImportPath": "github.com/docker/libnetwork",
-	"GoVersion": "go1.4.2",
+	"GoVersion": "go1.4.1",
 	"Packages": [
 		"./..."
 	],
@@ -12,38 +12,43 @@
 		},
 		{
 			"ImportPath": "github.com/docker/docker/pkg/homedir",
-			"Comment": "v1.4.1-3152-g3e85803",
-			"Rev": "3e85803f311c3883a9b395ad046c894ea255e9be"
+			"Comment": "v1.4.1-3479-ga9172f5",
+			"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
 		},
 		{
 			"ImportPath": "github.com/docker/docker/pkg/ioutils",
-			"Comment": "v1.4.1-3152-g3e85803",
-			"Rev": "3e85803f311c3883a9b395ad046c894ea255e9be"
+			"Comment": "v1.4.1-3479-ga9172f5",
+			"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
 		},
 		{
 			"ImportPath": "github.com/docker/docker/pkg/mflag",
-			"Comment": "v1.4.1-3152-g3e85803",
-			"Rev": "3e85803f311c3883a9b395ad046c894ea255e9be"
+			"Comment": "v1.4.1-3479-ga9172f5",
+			"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
 		},
 		{
 			"ImportPath": "github.com/docker/docker/pkg/parsers/kernel",
-			"Comment": "v1.4.1-3152-g3e85803",
-			"Rev": "3e85803f311c3883a9b395ad046c894ea255e9be"
+			"Comment": "v1.4.1-3479-ga9172f5",
+			"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
+		},
+		{
+			"ImportPath": "github.com/docker/docker/pkg/plugins",
+			"Comment": "v1.4.1-3479-ga9172f5",
+			"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
 		},
 		{
 			"ImportPath": "github.com/docker/docker/pkg/proxy",
-			"Comment": "v1.4.1-3152-g3e85803",
-			"Rev": "3e85803f311c3883a9b395ad046c894ea255e9be"
+			"Comment": "v1.4.1-3479-ga9172f5",
+			"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
 		},
 		{
 			"ImportPath": "github.com/docker/docker/pkg/reexec",
-			"Comment": "v1.4.1-3152-g3e85803",
-			"Rev": "3e85803f311c3883a9b395ad046c894ea255e9be"
+			"Comment": "v1.4.1-3479-ga9172f5",
+			"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
 		},
 		{
 			"ImportPath": "github.com/docker/docker/pkg/stringid",
-			"Comment": "v1.4.1-3152-g3e85803",
-			"Rev": "3e85803f311c3883a9b395ad046c894ea255e9be"
+			"Comment": "v1.4.1-3479-ga9172f5",
+			"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
 		},
 		{
 			"ImportPath": "github.com/docker/libcontainer/user",

+ 125 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/readers_test.go

@@ -2,11 +2,92 @@ package ioutils
 
 import (
 	"bytes"
+	"fmt"
 	"io"
 	"io/ioutil"
+	"strings"
 	"testing"
 )
 
+// Implement io.Reader
+type errorReader struct{}
+
+func (r *errorReader) Read(p []byte) (int, error) {
+	return 0, fmt.Errorf("Error reader always fail.")
+}
+
+func TestReadCloserWrapperClose(t *testing.T) {
+	reader := strings.NewReader("A string reader")
+	wrapper := NewReadCloserWrapper(reader, func() error {
+		return fmt.Errorf("This will be called when closing")
+	})
+	err := wrapper.Close()
+	if err == nil || !strings.Contains(err.Error(), "This will be called when closing") {
+		t.Fatalf("readCloserWrapper should have call the anonymous func and thus, fail.")
+	}
+}
+
+func TestReaderErrWrapperReadOnError(t *testing.T) {
+	called := false
+	reader := &errorReader{}
+	wrapper := NewReaderErrWrapper(reader, func() {
+		called = true
+	})
+	_, err := wrapper.Read([]byte{})
+	if err == nil || !strings.Contains(err.Error(), "Error reader always fail.") {
+		t.Fatalf("readErrWrapper should returned an error")
+	}
+	if !called {
+		t.Fatalf("readErrWrapper should have call the anonymous function on failure")
+	}
+}
+
+func TestReaderErrWrapperRead(t *testing.T) {
+	called := false
+	reader := strings.NewReader("a string reader.")
+	wrapper := NewReaderErrWrapper(reader, func() {
+		called = true // Should not be called
+	})
+	// Read 20 byte (should be ok with the string above)
+	num, err := wrapper.Read(make([]byte, 20))
+	if err != nil {
+		t.Fatal(err)
+	}
+	if num != 16 {
+		t.Fatalf("readerErrWrapper should have read 16 byte, but read %d", num)
+	}
+}
+
+func TestNewBufReaderWithDrainbufAndBuffer(t *testing.T) {
+	reader, writer := io.Pipe()
+
+	drainBuffer := make([]byte, 1024)
+	buffer := bytes.Buffer{}
+	bufreader := NewBufReaderWithDrainbufAndBuffer(reader, drainBuffer, &buffer)
+
+	// Write everything down to a Pipe
+	// Usually, a pipe should block but because of the buffered reader,
+	// the writes will go through
+	done := make(chan bool)
+	go func() {
+		writer.Write([]byte("hello world"))
+		writer.Close()
+		done <- true
+	}()
+
+	// Drain the reader *after* everything has been written, just to verify
+	// it is indeed buffering
+	<-done
+
+	output, err := ioutil.ReadAll(bufreader)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !bytes.Equal(output, []byte("hello world")) {
+		t.Error(string(output))
+	}
+}
+
 func TestBufReader(t *testing.T) {
 	reader, writer := io.Pipe()
 	bufreader := NewBufReader(reader)
@@ -33,6 +114,50 @@ func TestBufReader(t *testing.T) {
 	}
 }
 
+func TestBufReaderCloseWithNonReaderCloser(t *testing.T) {
+	reader := strings.NewReader("buffer")
+	bufreader := NewBufReader(reader)
+
+	if err := bufreader.Close(); err != nil {
+		t.Fatal(err)
+	}
+
+}
+
+// implements io.ReadCloser
+type simpleReaderCloser struct{}
+
+func (r *simpleReaderCloser) Read(p []byte) (n int, err error) {
+	return 0, nil
+}
+
+func (r *simpleReaderCloser) Close() error {
+	return nil
+}
+
+func TestBufReaderCloseWithReaderCloser(t *testing.T) {
+	reader := &simpleReaderCloser{}
+	bufreader := NewBufReader(reader)
+
+	err := bufreader.Close()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+}
+
+func TestHashData(t *testing.T) {
+	reader := strings.NewReader("hash-me")
+	actual, err := HashData(reader)
+	if err != nil {
+		t.Fatal(err)
+	}
+	expected := "sha256:4d11186aed035cc624d553e10db358492c84a7cd6b9670d92123c144930450aa"
+	if actual != expected {
+		t.Fatalf("Expecting %s, got %s", expected, actual)
+	}
+}
+
 type repeatedReader struct {
 	readCount int
 	maxReads  int

+ 47 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/writeflusher.go

@@ -0,0 +1,47 @@
+package ioutils
+
+import (
+	"io"
+	"net/http"
+	"sync"
+)
+
+type WriteFlusher struct {
+	sync.Mutex
+	w       io.Writer
+	flusher http.Flusher
+	flushed bool
+}
+
+func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
+	wf.Lock()
+	defer wf.Unlock()
+	n, err = wf.w.Write(b)
+	wf.flushed = true
+	wf.flusher.Flush()
+	return n, err
+}
+
+// Flush the stream immediately.
+func (wf *WriteFlusher) Flush() {
+	wf.Lock()
+	defer wf.Unlock()
+	wf.flushed = true
+	wf.flusher.Flush()
+}
+
+func (wf *WriteFlusher) Flushed() bool {
+	wf.Lock()
+	defer wf.Unlock()
+	return wf.flushed
+}
+
+func NewWriteFlusher(w io.Writer) *WriteFlusher {
+	var flusher http.Flusher
+	if f, ok := w.(http.Flusher); ok {
+		flusher = f
+	} else {
+		flusher = &NopFlusher{}
+	}
+	return &WriteFlusher{w: w, flusher: flusher}
+}

+ 24 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/writers_test.go

@@ -6,6 +6,30 @@ import (
 	"testing"
 )
 
+func TestWriteCloserWrapperClose(t *testing.T) {
+	called := false
+	writer := bytes.NewBuffer([]byte{})
+	wrapper := NewWriteCloserWrapper(writer, func() error {
+		called = true
+		return nil
+	})
+	if err := wrapper.Close(); err != nil {
+		t.Fatal(err)
+	}
+	if !called {
+		t.Fatalf("writeCloserWrapper should have call the anonymous function.")
+	}
+}
+
+func TestNopWriteCloser(t *testing.T) {
+	writer := bytes.NewBuffer([]byte{})
+	wrapper := NopWriteCloser(writer)
+	if err := wrapper.Close(); err != nil {
+		t.Fatal("NopWriteCloser always return nil on Close.")
+	}
+
+}
+
 func TestNopWriter(t *testing.T) {
 	nw := &NopWriter{}
 	l, err := nw.Write([]byte{'c'})

+ 1 - 1
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/flag.go

@@ -1085,7 +1085,7 @@ func (cmd *FlagSet) ReportError(str string, withHelp bool) {
 			str += ". See '" + os.Args[0] + " " + cmd.Name() + " --help'"
 		}
 	}
-	fmt.Fprintf(cmd.Out(), "docker: %s.\n", str)
+	fmt.Fprintf(cmd.Out(), "docker: %s\n", str)
 	os.Exit(1)
 }
 

+ 2 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go

@@ -1,3 +1,5 @@
+// +build !windows
+
 package kernel
 
 import (

+ 65 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go

@@ -0,0 +1,65 @@
+package kernel
+
+import (
+	"fmt"
+	"syscall"
+	"unsafe"
+)
+
+type KernelVersionInfo struct {
+	kvi   string
+	major int
+	minor int
+	build int
+}
+
+func (k *KernelVersionInfo) String() string {
+	return fmt.Sprintf("%d.%d %d (%s)", k.major, k.minor, k.build, k.kvi)
+}
+
+func GetKernelVersion() (*KernelVersionInfo, error) {
+
+	var (
+		h         syscall.Handle
+		dwVersion uint32
+		err       error
+	)
+
+	KVI := &KernelVersionInfo{"Unknown", 0, 0, 0}
+
+	if err = syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE,
+		syscall.StringToUTF16Ptr(`SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\`),
+		0,
+		syscall.KEY_READ,
+		&h); err != nil {
+		return KVI, err
+	}
+	defer syscall.RegCloseKey(h)
+
+	var buf [1 << 10]uint16
+	var typ uint32
+	n := uint32(len(buf) * 2) // api expects array of bytes, not uint16
+
+	if err = syscall.RegQueryValueEx(h,
+		syscall.StringToUTF16Ptr("BuildLabEx"),
+		nil,
+		&typ,
+		(*byte)(unsafe.Pointer(&buf[0])),
+		&n); err != nil {
+		return KVI, err
+	}
+
+	KVI.kvi = syscall.UTF16ToString(buf[:])
+
+	// Important - docker.exe MUST be manifested for this API to return
+	// the correct information.
+	if dwVersion, err = syscall.GetVersion(); err != nil {
+		return KVI, err
+	}
+
+	KVI.major = int(dwVersion & 0xFF)
+	KVI.minor = int((dwVersion & 0XFF00) >> 8)
+	KVI.build = int((dwVersion & 0xFFFF0000) >> 16)
+
+	return KVI, nil
+}

+ 100 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/client.go

@@ -0,0 +1,100 @@
+package plugins
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/Sirupsen/logrus"
+)
+
+const (
+	versionMimetype = "application/vnd.docker.plugins.v1+json"
+	defaultTimeOut  = 30
+)
+
+func NewClient(addr string) *Client {
+	tr := &http.Transport{}
+	protoAndAddr := strings.Split(addr, "://")
+	configureTCPTransport(tr, protoAndAddr[0], protoAndAddr[1])
+	return &Client{&http.Client{Transport: tr}, protoAndAddr[1]}
+}
+
+type Client struct {
+	http *http.Client
+	addr string
+}
+
+func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error {
+	var buf bytes.Buffer
+	if err := json.NewEncoder(&buf).Encode(args); err != nil {
+		return err
+	}
+
+	req, err := http.NewRequest("POST", "/"+serviceMethod, &buf)
+	if err != nil {
+		return err
+	}
+	req.Header.Add("Accept", versionMimetype)
+	req.URL.Scheme = "http"
+	req.URL.Host = c.addr
+
+	var retries int
+	start := time.Now()
+
+	for {
+		resp, err := c.http.Do(req)
+		if err != nil {
+			timeOff := backoff(retries)
+			if timeOff+time.Since(start) > defaultTimeOut {
+				return err
+			}
+			retries++
+			logrus.Warn("Unable to connect to plugin: %s, retrying in %ds\n", c.addr, timeOff)
+			time.Sleep(timeOff)
+			continue
+		}
+
+		if resp.StatusCode != http.StatusOK {
+			remoteErr, err := ioutil.ReadAll(resp.Body)
+			if err != nil {
+				return nil
+			}
+			return fmt.Errorf("Plugin Error: %s", remoteErr)
+		}
+
+		return json.NewDecoder(resp.Body).Decode(&ret)
+	}
+}
+
+func backoff(retries int) time.Duration {
+	b, max := float64(1), float64(defaultTimeOut)
+	for b < max && retries > 0 {
+		b *= 2
+		retries--
+	}
+	if b > max {
+		b = max
+	}
+	return time.Duration(b)
+}
+
+func configureTCPTransport(tr *http.Transport, proto, addr string) {
+	// Why 32? See https://github.com/docker/docker/pull/8035.
+	timeout := 32 * time.Second
+	if proto == "unix" {
+		// No need for compression in local communications.
+		tr.DisableCompression = true
+		tr.Dial = func(_, _ string) (net.Conn, error) {
+			return net.DialTimeout(proto, addr, timeout)
+		}
+	} else {
+		tr.Proxy = http.ProxyFromEnvironment
+		tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
+	}
+}

+ 63 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/client_test.go

@@ -0,0 +1,63 @@
+package plugins
+
+import (
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"reflect"
+	"testing"
+)
+
+var (
+	mux    *http.ServeMux
+	server *httptest.Server
+)
+
+func setupRemotePluginServer() string {
+	mux = http.NewServeMux()
+	server = httptest.NewServer(mux)
+	return server.URL
+}
+
+func teardownRemotePluginServer() {
+	if server != nil {
+		server.Close()
+	}
+}
+
+func TestFailedConnection(t *testing.T) {
+	c := NewClient("tcp://127.0.0.1:1")
+	err := c.Call("Service.Method", nil, nil)
+	if err == nil {
+		t.Fatal("Unexpected successful connection")
+	}
+}
+
+func TestEchoInputOutput(t *testing.T) {
+	addr := setupRemotePluginServer()
+	defer teardownRemotePluginServer()
+
+	m := Manifest{[]string{"VolumeDriver", "NetworkDriver"}}
+
+	mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) {
+		if r.Method != "POST" {
+			t.Fatalf("Expected POST, got %s\n", r.Method)
+		}
+
+		header := w.Header()
+		header.Set("Content-Type", versionMimetype)
+
+		io.Copy(w, r.Body)
+	})
+
+	c := NewClient(addr)
+	var output Manifest
+	err := c.Call("Test.Echo", m, &output)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !reflect.DeepEqual(output, m) {
+		t.Fatalf("Expected %v, was %v\n", m, output)
+	}
+}

+ 78 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery.go

@@ -0,0 +1,78 @@
+package plugins
+
+import (
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/url"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+const defaultLocalRegistry = "/usr/share/docker/plugins"
+
+var (
+	ErrNotFound = errors.New("Plugin not found")
+)
+
+type Registry interface {
+	Plugins() ([]*Plugin, error)
+	Plugin(name string) (*Plugin, error)
+}
+
+type LocalRegistry struct {
+	path string
+}
+
+func newLocalRegistry(path string) *LocalRegistry {
+	if len(path) == 0 {
+		path = defaultLocalRegistry
+	}
+
+	return &LocalRegistry{path}
+}
+
+func (l *LocalRegistry) Plugin(name string) (*Plugin, error) {
+	filepath := filepath.Join(l.path, name)
+	specpath := filepath + ".spec"
+	if fi, err := os.Stat(specpath); err == nil {
+		return readPluginInfo(specpath, fi)
+	}
+	socketpath := filepath + ".sock"
+	if fi, err := os.Stat(socketpath); err == nil {
+		return readPluginInfo(socketpath, fi)
+	}
+	return nil, ErrNotFound
+}
+
+func readPluginInfo(path string, fi os.FileInfo) (*Plugin, error) {
+	name := strings.Split(fi.Name(), ".")[0]
+
+	if fi.Mode()&os.ModeSocket != 0 {
+		return &Plugin{
+			Name: name,
+			Addr: "unix://" + path,
+		}, nil
+	}
+
+	content, err := ioutil.ReadFile(path)
+	if err != nil {
+		return nil, err
+	}
+	addr := strings.TrimSpace(string(content))
+
+	u, err := url.Parse(addr)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(u.Scheme) == 0 {
+		return nil, fmt.Errorf("Unknown protocol")
+	}
+
+	return &Plugin{
+		Name: name,
+		Addr: addr,
+	}, nil
+}

+ 108 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery_test.go

@@ -0,0 +1,108 @@
+package plugins
+
+import (
+	"fmt"
+	"io/ioutil"
+	"net"
+	"os"
+	"path"
+	"path/filepath"
+	"reflect"
+	"testing"
+)
+
+func TestUnknownLocalPath(t *testing.T) {
+	tmpdir, err := ioutil.TempDir("", "docker-test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tmpdir)
+
+	l := newLocalRegistry(filepath.Join(tmpdir, "unknown"))
+	_, err = l.Plugin("foo")
+	if err == nil || err != ErrNotFound {
+		t.Fatalf("Expected error for unknown directory")
+	}
+}
+
+func TestLocalSocket(t *testing.T) {
+	tmpdir, err := ioutil.TempDir("", "docker-test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tmpdir)
+	l, err := net.Listen("unix", filepath.Join(tmpdir, "echo.sock"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer l.Close()
+
+	r := newLocalRegistry(tmpdir)
+	p, err := r.Plugin("echo")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	pp, err := r.Plugin("echo")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !reflect.DeepEqual(p, pp) {
+		t.Fatalf("Expected %v, was %v\n", p, pp)
+	}
+
+	if p.Name != "echo" {
+		t.Fatalf("Expected plugin `echo`, got %s\n", p.Name)
+	}
+
+	addr := fmt.Sprintf("unix://%s/echo.sock", tmpdir)
+	if p.Addr != addr {
+		t.Fatalf("Expected plugin addr `%s`, got %s\n", addr, p.Addr)
+	}
+}
+
+func TestFileSpecPlugin(t *testing.T) {
+	tmpdir, err := ioutil.TempDir("", "docker-test")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	cases := []struct {
+		path string
+		name string
+		addr string
+		fail bool
+	}{
+		{filepath.Join(tmpdir, "echo.spec"), "echo", "unix://var/lib/docker/plugins/echo.sock", false},
+		{filepath.Join(tmpdir, "foo.spec"), "foo", "tcp://localhost:8080", false},
+		{filepath.Join(tmpdir, "bar.spec"), "bar", "localhost:8080", true}, // unknown transport
+	}
+
+	for _, c := range cases {
+		if err = os.MkdirAll(path.Dir(c.path), 0755); err != nil {
+			t.Fatal(err)
+		}
+		if err = ioutil.WriteFile(c.path, []byte(c.addr), 0644); err != nil {
+			t.Fatal(err)
+		}
+
+		r := newLocalRegistry(tmpdir)
+		p, err := r.Plugin(c.name)
+		if c.fail && err == nil {
+			continue
+		}
+
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		if p.Name != c.name {
+			t.Fatalf("Expected plugin `%s`, got %s\n", c.name, p.Name)
+		}
+
+		if p.Addr != c.addr {
+			t.Fatalf("Expected plugin addr `%s`, got %s\n", c.addr, p.Addr)
+		}
+		os.Remove(c.path)
+	}
+}

+ 100 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/plugins.go

@@ -0,0 +1,100 @@
+package plugins
+
+import (
+	"errors"
+	"sync"
+
+	"github.com/Sirupsen/logrus"
+)
+
+var (
+	ErrNotImplements = errors.New("Plugin does not implement the requested driver")
+)
+
+type plugins struct {
+	sync.Mutex
+	plugins map[string]*Plugin
+}
+
+var (
+	storage          = plugins{plugins: make(map[string]*Plugin)}
+	extpointHandlers = make(map[string]func(string, *Client))
+)
+
+type Manifest struct {
+	Implements []string
+}
+
+type Plugin struct {
+	Name     string
+	Addr     string
+	Client   *Client
+	Manifest *Manifest
+}
+
+func (p *Plugin) activate() error {
+	m := new(Manifest)
+	p.Client = NewClient(p.Addr)
+	err := p.Client.Call("Plugin.Activate", nil, m)
+	if err != nil {
+		return err
+	}
+
+	logrus.Debugf("%s's manifest: %v", p.Name, m)
+	p.Manifest = m
+	for _, iface := range m.Implements {
+		handler, handled := extpointHandlers[iface]
+		if !handled {
+			continue
+		}
+		handler(p.Name, p.Client)
+	}
+	return nil
+}
+
+func load(name string) (*Plugin, error) {
+	registry := newLocalRegistry("")
+	pl, err := registry.Plugin(name)
+	if err != nil {
+		return nil, err
+	}
+	if err := pl.activate(); err != nil {
+		return nil, err
+	}
+	return pl, nil
+}
+
+func get(name string) (*Plugin, error) {
+	storage.Lock()
+	defer storage.Unlock()
+	pl, ok := storage.plugins[name]
+	if ok {
+		return pl, nil
+	}
+	pl, err := load(name)
+	if err != nil {
+		return nil, err
+	}
+
+	logrus.Debugf("Plugin: %v", pl)
+	storage.plugins[name] = pl
+	return pl, nil
+}
+
+func Get(name, imp string) (*Plugin, error) {
+	pl, err := get(name)
+	if err != nil {
+		return nil, err
+	}
+	for _, driver := range pl.Manifest.Implements {
+		logrus.Debugf("%s implements: %s", name, driver)
+		if driver == imp {
+			return pl, nil
+		}
+	}
+	return nil, ErrNotImplements
+}
+
+func Handle(iface string, fn func(string, *Client)) {
+	extpointHandlers[iface] = fn
+}

+ 1 - 1
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unsupported.go

@@ -1,4 +1,4 @@
-// +build !linux
+// +build !linux,!windows
 
 package reexec
 

+ 14 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_windows.go

@@ -0,0 +1,14 @@
+// +build windows
+
+package reexec
+
+import (
+	"os/exec"
+)
+
+func Command(args ...string) *exec.Cmd {
+	return &exec.Cmd{
+		Path: Self(),
+		Args: args,
+	}
+}

+ 13 - 3
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/stringid/stringid.go

@@ -4,19 +4,29 @@ import (
 	"crypto/rand"
 	"encoding/hex"
 	"io"
+	"regexp"
 	"strconv"
 )
 
+const shortLen = 12
+
+var validShortID = regexp.MustCompile("^[a-z0-9]{12}$")
+
+// Determine if an arbitrary string *looks like* a short ID.
+func IsShortID(id string) bool {
+	return validShortID.MatchString(id)
+}
+
 // TruncateID returns a shorthand version of a string identifier for convenience.
 // A collision with other shorthands is very unlikely, but possible.
 // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
 // will need to use a langer prefix, or the full-length Id.
 func TruncateID(id string) string {
-	shortLen := 12
+	trimTo := shortLen
 	if len(id) < shortLen {
-		shortLen = len(id)
+		trimTo = len(id)
 	}
-	return id[:shortLen]
+	return id[:trimTo]
 }
 
 // GenerateRandomID returns an unique id

+ 22 - 1
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/stringid/stringid_test.go

@@ -1,6 +1,9 @@
 package stringid
 
-import "testing"
+import (
+	"strings"
+	"testing"
+)
 
 func TestGenerateRandomID(t *testing.T) {
 	id := GenerateRandomID()
@@ -33,3 +36,21 @@ func TestShortenIdInvalid(t *testing.T) {
 		t.Fatalf("Id returned is incorrect: truncate on %s returned %s", id, truncID)
 	}
 }
+
+func TestIsShortIDNonHex(t *testing.T) {
+	id := "some non-hex value"
+	if IsShortID(id) {
+		t.Fatalf("%s is not a short ID", id)
+	}
+}
+
+func TestIsShortIDNotCorrectSize(t *testing.T) {
+	id := strings.Repeat("a", shortLen+1)
+	if IsShortID(id) {
+		t.Fatalf("%s is not a short ID", id)
+	}
+	id = strings.Repeat("a", shortLen-1)
+	if IsShortID(id) {
+		t.Fatalf("%s is not a short ID", id)
+	}
+}