Browse code

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 authored on 2015/05/16 10:14:36
Showing 4 changed files
... ...
@@ -48,6 +48,7 @@ package libnetwork
48 48
 import (
49 49
 	"sync"
50 50
 
51
+	"github.com/docker/docker/pkg/plugins"
51 52
 	"github.com/docker/docker/pkg/stringid"
52 53
 	"github.com/docker/libnetwork/driverapi"
53 54
 	"github.com/docker/libnetwork/sandbox"
... ...
@@ -140,7 +141,11 @@ func (c *controller) NewNetwork(networkType, name string, options ...NetworkOpti
140 140
 	d, ok := c.drivers[networkType]
141 141
 	c.Unlock()
142 142
 	if !ok {
143
-		return nil, ErrInvalidNetworkDriver
143
+		var err error
144
+		d, err = c.loadDriver(networkType)
145
+		if err != nil {
146
+			return nil, err
147
+		}
144 148
 	}
145 149
 
146 150
 	// Check if a network already exists with the specified network name
... ...
@@ -275,3 +280,19 @@ func (c *controller) sandboxGet(key string) sandbox.Sandbox {
275 275
 
276 276
 	return sData.sandbox
277 277
 }
278
+
279
+func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) {
280
+	// Plugins pkg performs lazy loading of plugins that acts as remote drivers.
281
+	// As per the design, this Get call will result in remote driver discovery if there is a corresponding plugin available.
282
+	_, err := plugins.Get(networkType, driverapi.NetworkPluginEndpointType)
283
+	if err != nil {
284
+		return nil, err
285
+	}
286
+	c.Lock()
287
+	defer c.Unlock()
288
+	d, ok := c.drivers[networkType]
289
+	if !ok {
290
+		return nil, ErrInvalidNetworkDriver
291
+	}
292
+	return d, nil
293
+}
... ...
@@ -19,6 +19,9 @@ var (
19 19
 	ErrNotImplemented = errors.New("The API is not implemented yet")
20 20
 )
21 21
 
22
+// NetworkPluginEndpointType represents the Endpoint Type used by Plugin system
23
+const NetworkPluginEndpointType = "NetworkDriver"
24
+
22 25
 // Driver is an interface that every plugin driver needs to implement.
23 26
 type Driver interface {
24 27
 	// Push driver specific config to the driver
... ...
@@ -3,6 +3,8 @@ package remote
3 3
 import (
4 4
 	"errors"
5 5
 
6
+	log "github.com/Sirupsen/logrus"
7
+	"github.com/docker/docker/pkg/plugins"
6 8
 	"github.com/docker/libnetwork/driverapi"
7 9
 	"github.com/docker/libnetwork/sandbox"
8 10
 	"github.com/docker/libnetwork/types"
... ...
@@ -10,13 +12,22 @@ import (
10 10
 
11 11
 var errNoCallback = errors.New("No Callback handler registered with Driver")
12 12
 
13
-const remoteNetworkType = "remote"
14
-
15 13
 type driver struct {
14
+	endpoint    *plugins.Client
15
+	networkType string
16 16
 }
17 17
 
18 18
 // Init does the necessary work to register remote drivers
19 19
 func Init(dc driverapi.DriverCallback) error {
20
+	plugins.Handle(driverapi.NetworkPluginEndpointType, func(name string, client *plugins.Client) {
21
+
22
+		// TODO : Handhake with the Remote Plugin goes here
23
+
24
+		newDriver := &driver{networkType: name, endpoint: client}
25
+		if err := dc.RegisterDriver(name, newDriver); err != nil {
26
+			log.Errorf("Error registering Driver for %s due to %v", name, err)
27
+		}
28
+	})
20 29
 	return nil
21 30
 }
22 31
 
... ...
@@ -55,5 +66,5 @@ func (d *driver) Leave(nid, eid types.UUID, options map[string]interface{}) erro
55 55
 }
56 56
 
57 57
 func (d *driver) Type() string {
58
-	return remoteNetworkType
58
+	return d.networkType
59 59
 }
... ...
@@ -6,6 +6,8 @@ import (
6 6
 	"fmt"
7 7
 	"io/ioutil"
8 8
 	"net"
9
+	"net/http"
10
+	"net/http/httptest"
9 11
 	"os"
10 12
 	"runtime"
11 13
 	"strconv"
... ...
@@ -13,8 +15,10 @@ import (
13 13
 	"testing"
14 14
 
15 15
 	log "github.com/Sirupsen/logrus"
16
+	"github.com/docker/docker/pkg/plugins"
16 17
 	"github.com/docker/docker/pkg/reexec"
17 18
 	"github.com/docker/libnetwork"
19
+	"github.com/docker/libnetwork/driverapi"
18 20
 	"github.com/docker/libnetwork/netlabel"
19 21
 	"github.com/docker/libnetwork/netutils"
20 22
 	"github.com/docker/libnetwork/options"
... ...
@@ -226,7 +230,7 @@ func TestUnknownDriver(t *testing.T) {
226 226
 	}
227 227
 }
228 228
 
229
-func TestNilDriver(t *testing.T) {
229
+func TestNilRemoteDriver(t *testing.T) {
230 230
 	controller, err := libnetwork.New()
231 231
 	if err != nil {
232 232
 		t.Fatal(err)
... ...
@@ -238,24 +242,7 @@ func TestNilDriver(t *testing.T) {
238 238
 		t.Fatal("Expected to fail. But instead succeeded")
239 239
 	}
240 240
 
241
-	if err != libnetwork.ErrInvalidNetworkDriver {
242
-		t.Fatalf("Did not fail with expected error. Actual error: %v", err)
243
-	}
244
-}
245
-
246
-func TestNoInitDriver(t *testing.T) {
247
-	controller, err := libnetwork.New()
248
-	if err != nil {
249
-		t.Fatal(err)
250
-	}
251
-
252
-	_, err = controller.NewNetwork("ppp", "dummy",
253
-		libnetwork.NetworkOptionGeneric(getEmptyGenericOption()))
254
-	if err == nil {
255
-		t.Fatal("Expected to fail. But instead succeeded")
256
-	}
257
-
258
-	if err != libnetwork.ErrInvalidNetworkDriver {
241
+	if err != plugins.ErrNotFound {
259 242
 		t.Fatalf("Did not fail with expected error. Actual error: %v", err)
260 243
 	}
261 244
 }
... ...
@@ -1134,6 +1121,102 @@ func TestResolvConf(t *testing.T) {
1134 1134
 	}
1135 1135
 }
1136 1136
 
1137
+func TestInvalidRemoteDriver(t *testing.T) {
1138
+	if !netutils.IsRunningInContainer() {
1139
+		t.Skip("Skipping test when not running inside a Container")
1140
+	}
1141
+
1142
+	mux := http.NewServeMux()
1143
+	server := httptest.NewServer(mux)
1144
+	if server == nil {
1145
+		t.Fatal("Failed to start a HTTP Server")
1146
+	}
1147
+	defer server.Close()
1148
+
1149
+	type pluginRequest struct {
1150
+		name string
1151
+	}
1152
+
1153
+	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
1154
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
1155
+		fmt.Fprintln(w, `{"Implements": ["InvalidDriver"]}`)
1156
+	})
1157
+
1158
+	if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil {
1159
+		t.Fatal(err)
1160
+	}
1161
+	defer func() {
1162
+		if err := os.RemoveAll("/usr/share/docker/plugins"); err != nil {
1163
+			t.Fatal(err)
1164
+		}
1165
+	}()
1166
+
1167
+	if err := ioutil.WriteFile("/usr/share/docker/plugins/invalid-network-driver.spec", []byte(server.URL), 0644); err != nil {
1168
+		t.Fatal(err)
1169
+	}
1170
+
1171
+	controller, err := libnetwork.New()
1172
+	if err != nil {
1173
+		t.Fatal(err)
1174
+	}
1175
+
1176
+	_, err = controller.NewNetwork("invalid-network-driver", "dummy",
1177
+		libnetwork.NetworkOptionGeneric(getEmptyGenericOption()))
1178
+	if err == nil {
1179
+		t.Fatal("Expected to fail. But instead succeeded")
1180
+	}
1181
+
1182
+	if err != plugins.ErrNotImplements {
1183
+		t.Fatalf("Did not fail with expected error. Actual error: %v", err)
1184
+	}
1185
+}
1186
+
1187
+func TestValidRemoteDriver(t *testing.T) {
1188
+	if !netutils.IsRunningInContainer() {
1189
+		t.Skip("Skipping test when not running inside a Container")
1190
+	}
1191
+
1192
+	mux := http.NewServeMux()
1193
+	server := httptest.NewServer(mux)
1194
+	if server == nil {
1195
+		t.Fatal("Failed to start a HTTP Server")
1196
+	}
1197
+	defer server.Close()
1198
+
1199
+	type pluginRequest struct {
1200
+		name string
1201
+	}
1202
+
1203
+	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
1204
+		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
1205
+		fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType)
1206
+	})
1207
+
1208
+	if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil {
1209
+		t.Fatal(err)
1210
+	}
1211
+	defer func() {
1212
+		if err := os.RemoveAll("/usr/share/docker/plugins"); err != nil {
1213
+			t.Fatal(err)
1214
+		}
1215
+	}()
1216
+
1217
+	if err := ioutil.WriteFile("/usr/share/docker/plugins/valid-network-driver.spec", []byte(server.URL), 0644); err != nil {
1218
+		t.Fatal(err)
1219
+	}
1220
+
1221
+	controller, err := libnetwork.New()
1222
+	if err != nil {
1223
+		t.Fatal(err)
1224
+	}
1225
+
1226
+	_, err = controller.NewNetwork("valid-network-driver", "dummy",
1227
+		libnetwork.NetworkOptionGeneric(getEmptyGenericOption()))
1228
+	if err != nil && err != driverapi.ErrNotImplemented {
1229
+		t.Fatal(err)
1230
+	}
1231
+}
1232
+
1137 1233
 var (
1138 1234
 	once   sync.Once
1139 1235
 	ctrlr  libnetwork.NetworkController