Преглед изворни кода

Remote Driver integration with Plugin Framework

This commit brings in Remote driver integrated with the newly introduced
Plugin framework as a Docker Package.

The Plugin framework is designed as a Package and has no runtime
dependancy on Docker platform. It stands on its own and is a good
candidate for getting the remote drivers hooked to libnetwork

Signed-off-by: Madhu Venugopal <madhu@docker.com>
Madhu Venugopal пре 10 година
родитељ
комит
80ca3c2330

+ 22 - 1
libnetwork/controller.go

@@ -48,6 +48,7 @@ package libnetwork
 import (
 	"sync"
 
+	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/sandbox"
@@ -140,7 +141,11 @@ func (c *controller) NewNetwork(networkType, name string, options ...NetworkOpti
 	d, ok := c.drivers[networkType]
 	c.Unlock()
 	if !ok {
-		return nil, ErrInvalidNetworkDriver
+		var err error
+		d, err = c.loadDriver(networkType)
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	// Check if a network already exists with the specified network name
@@ -275,3 +280,19 @@ func (c *controller) sandboxGet(key string) sandbox.Sandbox {
 
 	return sData.sandbox
 }
+
+func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) {
+	// Plugins pkg performs lazy loading of plugins that acts as remote drivers.
+	// As per the design, this Get call will result in remote driver discovery if there is a corresponding plugin available.
+	_, err := plugins.Get(networkType, driverapi.NetworkPluginEndpointType)
+	if err != nil {
+		return nil, err
+	}
+	c.Lock()
+	defer c.Unlock()
+	d, ok := c.drivers[networkType]
+	if !ok {
+		return nil, ErrInvalidNetworkDriver
+	}
+	return d, nil
+}

+ 3 - 0
libnetwork/driverapi/driverapi.go

@@ -19,6 +19,9 @@ var (
 	ErrNotImplemented = errors.New("The API is not implemented yet")
 )
 
+// NetworkPluginEndpointType represents the Endpoint Type used by Plugin system
+const NetworkPluginEndpointType = "NetworkDriver"
+
 // Driver is an interface that every plugin driver needs to implement.
 type Driver interface {
 	// Push driver specific config to the driver

+ 14 - 3
libnetwork/drivers/remote/driver.go

@@ -3,6 +3,8 @@ package remote
 import (
 	"errors"
 
+	log "github.com/Sirupsen/logrus"
+	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/types"
@@ -10,13 +12,22 @@ import (
 
 var errNoCallback = errors.New("No Callback handler registered with Driver")
 
-const remoteNetworkType = "remote"
-
 type driver struct {
+	endpoint    *plugins.Client
+	networkType string
 }
 
 // Init does the necessary work to register remote drivers
 func Init(dc driverapi.DriverCallback) error {
+	plugins.Handle(driverapi.NetworkPluginEndpointType, func(name string, client *plugins.Client) {
+
+		// TODO : Handhake with the Remote Plugin goes here
+
+		newDriver := &driver{networkType: name, endpoint: client}
+		if err := dc.RegisterDriver(name, newDriver); err != nil {
+			log.Errorf("Error registering Driver for %s due to %v", name, err)
+		}
+	})
 	return nil
 }
 
@@ -55,5 +66,5 @@ func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) erro
 }
 
 func (d *driver) Type() string {
-	return remoteNetworkType
+	return d.networkType
 }

+ 102 - 19
libnetwork/libnetwork_test.go

@@ -6,6 +6,8 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net"
+	"net/http"
+	"net/http/httptest"
 	"os"
 	"runtime"
 	"strconv"
@@ -13,8 +15,10 @@ import (
 	"testing"
 
 	log "github.com/Sirupsen/logrus"
+	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/netutils"
 	"github.com/docker/libnetwork/options"
@@ -226,7 +230,7 @@ func TestUnknownDriver(t *testing.T) {
 	}
 }
 
-func TestNilDriver(t *testing.T) {
+func TestNilRemoteDriver(t *testing.T) {
 	controller, err := libnetwork.New()
 	if err != nil {
 		t.Fatal(err)
@@ -238,24 +242,7 @@ func TestNilDriver(t *testing.T) {
 		t.Fatal("Expected to fail. But instead succeeded")
 	}
 
-	if err != libnetwork.ErrInvalidNetworkDriver {
-		t.Fatalf("Did not fail with expected error. Actual error: %v", err)
-	}
-}
-
-func TestNoInitDriver(t *testing.T) {
-	controller, err := libnetwork.New()
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	_, err = controller.NewNetwork("ppp", "dummy",
-		libnetwork.NetworkOptionGeneric(getEmptyGenericOption()))
-	if err == nil {
-		t.Fatal("Expected to fail. But instead succeeded")
-	}
-
-	if err != libnetwork.ErrInvalidNetworkDriver {
+	if err != plugins.ErrNotFound {
 		t.Fatalf("Did not fail with expected error. Actual error: %v", err)
 	}
 }
@@ -1134,6 +1121,102 @@ func TestResolvConf(t *testing.T) {
 	}
 }
 
+func TestInvalidRemoteDriver(t *testing.T) {
+	if !netutils.IsRunningInContainer() {
+		t.Skip("Skipping test when not running inside a Container")
+	}
+
+	mux := http.NewServeMux()
+	server := httptest.NewServer(mux)
+	if server == nil {
+		t.Fatal("Failed to start a HTTP Server")
+	}
+	defer server.Close()
+
+	type pluginRequest struct {
+		name string
+	}
+
+	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
+		fmt.Fprintln(w, `{"Implements": ["InvalidDriver"]}`)
+	})
+
+	if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		if err := os.RemoveAll("/usr/share/docker/plugins"); err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	if err := ioutil.WriteFile("/usr/share/docker/plugins/invalid-network-driver.spec", []byte(server.URL), 0644); err != nil {
+		t.Fatal(err)
+	}
+
+	controller, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	_, err = controller.NewNetwork("invalid-network-driver", "dummy",
+		libnetwork.NetworkOptionGeneric(getEmptyGenericOption()))
+	if err == nil {
+		t.Fatal("Expected to fail. But instead succeeded")
+	}
+
+	if err != plugins.ErrNotImplements {
+		t.Fatalf("Did not fail with expected error. Actual error: %v", err)
+	}
+}
+
+func TestValidRemoteDriver(t *testing.T) {
+	if !netutils.IsRunningInContainer() {
+		t.Skip("Skipping test when not running inside a Container")
+	}
+
+	mux := http.NewServeMux()
+	server := httptest.NewServer(mux)
+	if server == nil {
+		t.Fatal("Failed to start a HTTP Server")
+	}
+	defer server.Close()
+
+	type pluginRequest struct {
+		name string
+	}
+
+	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
+		fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType)
+	})
+
+	if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		if err := os.RemoveAll("/usr/share/docker/plugins"); err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	if err := ioutil.WriteFile("/usr/share/docker/plugins/valid-network-driver.spec", []byte(server.URL), 0644); err != nil {
+		t.Fatal(err)
+	}
+
+	controller, err := libnetwork.New()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	_, err = controller.NewNetwork("valid-network-driver", "dummy",
+		libnetwork.NetworkOptionGeneric(getEmptyGenericOption()))
+	if err != nil && err != driverapi.ErrNotImplemented {
+		t.Fatal(err)
+	}
+}
+
 var (
 	once   sync.Once
 	ctrlr  libnetwork.NetworkController