1package remote
2
3import (
4	"bytes"
5	"encoding/json"
6	"errors"
7	"fmt"
8	"io"
9	"io/ioutil"
10	"net"
11	"net/http"
12	"net/http/httptest"
13	"os"
14	"testing"
15
16	"github.com/docker/docker/pkg/plugins"
17	"github.com/docker/libnetwork/datastore"
18	"github.com/docker/libnetwork/discoverapi"
19	"github.com/docker/libnetwork/driverapi"
20	_ "github.com/docker/libnetwork/testutils"
21	"github.com/docker/libnetwork/types"
22)
23
24func decodeToMap(r *http.Request) (res map[string]interface{}, err error) {
25	err = json.NewDecoder(r.Body).Decode(&res)
26	return
27}
28
29func handle(t *testing.T, mux *http.ServeMux, method string, h func(map[string]interface{}) interface{}) {
30	mux.HandleFunc(fmt.Sprintf("/%s.%s", driverapi.NetworkPluginEndpointType, method), func(w http.ResponseWriter, r *http.Request) {
31		ask, err := decodeToMap(r)
32		if err != nil && err != io.EOF {
33			t.Fatal(err)
34		}
35		answer := h(ask)
36		err = json.NewEncoder(w).Encode(&answer)
37		if err != nil {
38			t.Fatal(err)
39		}
40	})
41}
42
43func setupPlugin(t *testing.T, name string, mux *http.ServeMux) func() {
44	if err := os.MkdirAll("/etc/docker/plugins", 0755); err != nil {
45		t.Fatal(err)
46	}
47
48	server := httptest.NewServer(mux)
49	if server == nil {
50		t.Fatal("Failed to start an HTTP Server")
51	}
52
53	if err := ioutil.WriteFile(fmt.Sprintf("/etc/docker/plugins/%s.spec", name), []byte(server.URL), 0644); err != nil {
54		t.Fatal(err)
55	}
56
57	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
58		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
59		fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType)
60	})
61
62	return func() {
63		if err := os.RemoveAll("/etc/docker/plugins"); err != nil {
64			t.Fatal(err)
65		}
66		server.Close()
67	}
68}
69
70type testEndpoint struct {
71	t                     *testing.T
72	src                   string
73	dst                   string
74	address               string
75	addressIPv6           string
76	macAddress            string
77	gateway               string
78	gatewayIPv6           string
79	resolvConfPath        string
80	hostsPath             string
81	nextHop               string
82	destination           string
83	routeType             int
84	disableGatewayService bool
85}
86
87func (test *testEndpoint) Interface() driverapi.InterfaceInfo {
88	return test
89}
90
91func (test *testEndpoint) Address() *net.IPNet {
92	if test.address == "" {
93		return nil
94	}
95	nw, _ := types.ParseCIDR(test.address)
96	return nw
97}
98
99func (test *testEndpoint) AddressIPv6() *net.IPNet {
100	if test.addressIPv6 == "" {
101		return nil
102	}
103	nw, _ := types.ParseCIDR(test.addressIPv6)
104	return nw
105}
106
107func (test *testEndpoint) MacAddress() net.HardwareAddr {
108	if test.macAddress == "" {
109		return nil
110	}
111	mac, _ := net.ParseMAC(test.macAddress)
112	return mac
113}
114
115func (test *testEndpoint) SetMacAddress(mac net.HardwareAddr) error {
116	if test.macAddress != "" {
117		return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", test.macAddress, mac)
118	}
119	if mac == nil {
120		return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface")
121	}
122	test.macAddress = mac.String()
123	return nil
124}
125
126func (test *testEndpoint) SetIPAddress(address *net.IPNet) error {
127	if address.IP == nil {
128		return types.BadRequestErrorf("tried to set nil IP address to endpoint interface")
129	}
130	if address.IP.To4() == nil {
131		return setAddress(&test.addressIPv6, address)
132	}
133	return setAddress(&test.address, address)
134}
135
136func setAddress(ifaceAddr *string, address *net.IPNet) error {
137	if *ifaceAddr != "" {
138		return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with (%s).", *ifaceAddr, address)
139	}
140	*ifaceAddr = address.String()
141	return nil
142}
143
144func (test *testEndpoint) InterfaceName() driverapi.InterfaceNameInfo {
145	return test
146}
147
148func compareIPs(t *testing.T, kind string, shouldBe string, supplied net.IP) {
149	ip := net.ParseIP(shouldBe)
150	if ip == nil {
151		t.Fatalf(`Invalid IP to test against: "%s"`, shouldBe)
152	}
153	if !ip.Equal(supplied) {
154		t.Fatalf(`%s IPs are not equal: expected "%s", got %v`, kind, shouldBe, supplied)
155	}
156}
157
158func compareIPNets(t *testing.T, kind string, shouldBe string, supplied net.IPNet) {
159	_, net, _ := net.ParseCIDR(shouldBe)
160	if net == nil {
161		t.Fatalf(`Invalid IP network to test against: "%s"`, shouldBe)
162	}
163	if !types.CompareIPNet(net, &supplied) {
164		t.Fatalf(`%s IP networks are not equal: expected "%s", got %v`, kind, shouldBe, supplied)
165	}
166}
167
168func (test *testEndpoint) SetGateway(ipv4 net.IP) error {
169	compareIPs(test.t, "Gateway", test.gateway, ipv4)
170	return nil
171}
172
173func (test *testEndpoint) SetGatewayIPv6(ipv6 net.IP) error {
174	compareIPs(test.t, "GatewayIPv6", test.gatewayIPv6, ipv6)
175	return nil
176}
177
178func (test *testEndpoint) SetNames(src string, dst string) error {
179	if test.src != src {
180		test.t.Fatalf(`Wrong SrcName; expected "%s", got "%s"`, test.src, src)
181	}
182	if test.dst != dst {
183		test.t.Fatalf(`Wrong DstPrefix; expected "%s", got "%s"`, test.dst, dst)
184	}
185	return nil
186}
187
188func (test *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error {
189	compareIPNets(test.t, "Destination", test.destination, *destination)
190	compareIPs(test.t, "NextHop", test.nextHop, nextHop)
191
192	if test.routeType != routeType {
193		test.t.Fatalf(`Wrong RouteType; expected "%d", got "%d"`, test.routeType, routeType)
194	}
195
196	return nil
197}
198
199func (test *testEndpoint) DisableGatewayService() {
200	test.disableGatewayService = true
201}
202
203func (test *testEndpoint) AddTableEntry(tableName string, key string, value []byte) error {
204	return nil
205}
206
207func TestGetEmptyCapabilities(t *testing.T) {
208	var plugin = "test-net-driver-empty-cap"
209
210	mux := http.NewServeMux()
211	defer setupPlugin(t, plugin, mux)()
212
213	handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} {
214		return map[string]interface{}{}
215	})
216
217	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
218	if err != nil {
219		t.Fatal(err)
220	}
221
222	d := newDriver(plugin, p.Client())
223	if d.Type() != plugin {
224		t.Fatal("Driver type does not match that given")
225	}
226
227	_, err = d.(*driver).getCapabilities()
228	if err == nil {
229		t.Fatal("There should be error reported when get empty capability")
230	}
231}
232
233func TestGetExtraCapabilities(t *testing.T) {
234	var plugin = "test-net-driver-extra-cap"
235
236	mux := http.NewServeMux()
237	defer setupPlugin(t, plugin, mux)()
238
239	handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} {
240		return map[string]interface{}{
241			"Scope":             "local",
242			"foo":               "bar",
243			"ConnectivityScope": "global",
244		}
245	})
246
247	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
248	if err != nil {
249		t.Fatal(err)
250	}
251
252	d := newDriver(plugin, p.Client())
253	if d.Type() != plugin {
254		t.Fatal("Driver type does not match that given")
255	}
256
257	c, err := d.(*driver).getCapabilities()
258	if err != nil {
259		t.Fatal(err)
260	} else if c.DataScope != datastore.LocalScope {
261		t.Fatalf("get capability '%s', expecting 'local'", c.DataScope)
262	} else if c.ConnectivityScope != datastore.GlobalScope {
263		t.Fatalf("get capability '%s', expecting %q", c.ConnectivityScope, datastore.GlobalScope)
264	}
265}
266
267func TestGetInvalidCapabilities(t *testing.T) {
268	var plugin = "test-net-driver-invalid-cap"
269
270	mux := http.NewServeMux()
271	defer setupPlugin(t, plugin, mux)()
272
273	handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} {
274		return map[string]interface{}{
275			"Scope": "fake",
276		}
277	})
278
279	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
280	if err != nil {
281		t.Fatal(err)
282	}
283
284	d := newDriver(plugin, p.Client())
285	if d.Type() != plugin {
286		t.Fatal("Driver type does not match that given")
287	}
288
289	_, err = d.(*driver).getCapabilities()
290	if err == nil {
291		t.Fatal("There should be error reported when get invalid capability")
292	}
293}
294
295func TestRemoteDriver(t *testing.T) {
296	var plugin = "test-net-driver"
297
298	ep := &testEndpoint{
299		t:              t,
300		src:            "vethsrc",
301		dst:            "vethdst",
302		address:        "192.168.5.7/16",
303		addressIPv6:    "2001:DB8::5:7/48",
304		macAddress:     "ab:cd:ef:ee:ee:ee",
305		gateway:        "192.168.0.1",
306		gatewayIPv6:    "2001:DB8::1",
307		hostsPath:      "/here/comes/the/host/path",
308		resolvConfPath: "/there/goes/the/resolv/conf",
309		destination:    "10.0.0.0/8",
310		nextHop:        "10.0.0.1",
311		routeType:      1,
312	}
313
314	mux := http.NewServeMux()
315	defer setupPlugin(t, plugin, mux)()
316
317	var networkID string
318
319	handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} {
320		return map[string]interface{}{
321			"Scope": "global",
322		}
323	})
324	handle(t, mux, "CreateNetwork", func(msg map[string]interface{}) interface{} {
325		nid := msg["NetworkID"]
326		var ok bool
327		if networkID, ok = nid.(string); !ok {
328			t.Fatal("RPC did not include network ID string")
329		}
330		return map[string]interface{}{}
331	})
332	handle(t, mux, "DeleteNetwork", func(msg map[string]interface{}) interface{} {
333		if nid, ok := msg["NetworkID"]; !ok || nid != networkID {
334			t.Fatal("Network ID missing or does not match that created")
335		}
336		return map[string]interface{}{}
337	})
338	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
339		iface := map[string]interface{}{
340			"MacAddress":  ep.macAddress,
341			"Address":     ep.address,
342			"AddressIPv6": ep.addressIPv6,
343		}
344		return map[string]interface{}{
345			"Interface": iface,
346		}
347	})
348	handle(t, mux, "Join", func(msg map[string]interface{}) interface{} {
349		options := msg["Options"].(map[string]interface{})
350		foo, ok := options["foo"].(string)
351		if !ok || foo != "fooValue" {
352			t.Fatalf("Did not receive expected foo string in request options: %+v", msg)
353		}
354		return map[string]interface{}{
355			"Gateway":        ep.gateway,
356			"GatewayIPv6":    ep.gatewayIPv6,
357			"HostsPath":      ep.hostsPath,
358			"ResolvConfPath": ep.resolvConfPath,
359			"InterfaceName": map[string]interface{}{
360				"SrcName":   ep.src,
361				"DstPrefix": ep.dst,
362			},
363			"StaticRoutes": []map[string]interface{}{
364				{
365					"Destination": ep.destination,
366					"RouteType":   ep.routeType,
367					"NextHop":     ep.nextHop,
368				},
369			},
370		}
371	})
372	handle(t, mux, "Leave", func(msg map[string]interface{}) interface{} {
373		return map[string]string{}
374	})
375	handle(t, mux, "DeleteEndpoint", func(msg map[string]interface{}) interface{} {
376		return map[string]interface{}{}
377	})
378	handle(t, mux, "EndpointOperInfo", func(msg map[string]interface{}) interface{} {
379		return map[string]interface{}{
380			"Value": map[string]string{
381				"Arbitrary": "key",
382				"Value":     "pairs?",
383			},
384		}
385	})
386	handle(t, mux, "DiscoverNew", func(msg map[string]interface{}) interface{} {
387		return map[string]string{}
388	})
389	handle(t, mux, "DiscoverDelete", func(msg map[string]interface{}) interface{} {
390		return map[string]interface{}{}
391	})
392
393	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
394	if err != nil {
395		t.Fatal(err)
396	}
397
398	d := newDriver(plugin, p.Client())
399	if d.Type() != plugin {
400		t.Fatal("Driver type does not match that given")
401	}
402
403	c, err := d.(*driver).getCapabilities()
404	if err != nil {
405		t.Fatal(err)
406	} else if c.DataScope != datastore.GlobalScope {
407		t.Fatalf("get capability '%s', expecting 'global'", c.DataScope)
408	}
409
410	netID := "dummy-network"
411	err = d.CreateNetwork(netID, map[string]interface{}{}, nil, nil, nil)
412	if err != nil {
413		t.Fatal(err)
414	}
415
416	endID := "dummy-endpoint"
417	ifInfo := &testEndpoint{}
418	err = d.CreateEndpoint(netID, endID, ifInfo, map[string]interface{}{})
419	if err != nil {
420		t.Fatal(err)
421	}
422
423	if !bytes.Equal(ep.MacAddress(), ifInfo.MacAddress()) || !types.CompareIPNet(ep.Address(), ifInfo.Address()) ||
424		!types.CompareIPNet(ep.AddressIPv6(), ifInfo.AddressIPv6()) {
425		t.Fatalf("Unexpected InterfaceInfo data. Expected (%s, %s, %s). Got (%v, %v, %v)",
426			ep.MacAddress(), ep.Address(), ep.AddressIPv6(),
427			ifInfo.MacAddress(), ifInfo.Address(), ifInfo.AddressIPv6())
428	}
429
430	joinOpts := map[string]interface{}{"foo": "fooValue"}
431	err = d.Join(netID, endID, "sandbox-key", ep, joinOpts)
432	if err != nil {
433		t.Fatal(err)
434	}
435	if _, err = d.EndpointOperInfo(netID, endID); err != nil {
436		t.Fatal(err)
437	}
438	if err = d.Leave(netID, endID); err != nil {
439		t.Fatal(err)
440	}
441	if err = d.DeleteEndpoint(netID, endID); err != nil {
442		t.Fatal(err)
443	}
444	if err = d.DeleteNetwork(netID); err != nil {
445		t.Fatal(err)
446	}
447
448	data := discoverapi.NodeDiscoveryData{
449		Address: "192.168.1.1",
450	}
451	if err = d.DiscoverNew(discoverapi.NodeDiscovery, data); err != nil {
452		t.Fatal(err)
453	}
454	if err = d.DiscoverDelete(discoverapi.NodeDiscovery, data); err != nil {
455		t.Fatal(err)
456	}
457}
458
459func TestDriverError(t *testing.T) {
460	var plugin = "test-net-driver-error"
461
462	mux := http.NewServeMux()
463	defer setupPlugin(t, plugin, mux)()
464
465	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
466		return map[string]interface{}{
467			"Err": "this should get raised as an error",
468		}
469	})
470
471	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
472	if err != nil {
473		t.Fatal(err)
474	}
475
476	driver := newDriver(plugin, p.Client())
477
478	if err := driver.CreateEndpoint("dummy", "dummy", &testEndpoint{t: t}, map[string]interface{}{}); err == nil {
479		t.Fatal("Expected error from driver")
480	}
481}
482
483func TestMissingValues(t *testing.T) {
484	var plugin = "test-net-driver-missing"
485
486	mux := http.NewServeMux()
487	defer setupPlugin(t, plugin, mux)()
488
489	ep := &testEndpoint{
490		t: t,
491	}
492
493	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
494		iface := map[string]interface{}{
495			"Address":     ep.address,
496			"AddressIPv6": ep.addressIPv6,
497			"MacAddress":  ep.macAddress,
498		}
499		return map[string]interface{}{
500			"Interface": iface,
501		}
502	})
503
504	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
505	if err != nil {
506		t.Fatal(err)
507	}
508	driver := newDriver(plugin, p.Client())
509
510	if err := driver.CreateEndpoint("dummy", "dummy", ep, map[string]interface{}{}); err != nil {
511		t.Fatal(err)
512	}
513}
514
515type rollbackEndpoint struct {
516}
517
518func (r *rollbackEndpoint) Interface() driverapi.InterfaceInfo {
519	return r
520}
521
522func (r *rollbackEndpoint) MacAddress() net.HardwareAddr {
523	return nil
524}
525
526func (r *rollbackEndpoint) Address() *net.IPNet {
527	return nil
528}
529
530func (r *rollbackEndpoint) AddressIPv6() *net.IPNet {
531	return nil
532}
533
534func (r *rollbackEndpoint) SetMacAddress(mac net.HardwareAddr) error {
535	return errors.New("invalid mac")
536}
537
538func (r *rollbackEndpoint) SetIPAddress(ip *net.IPNet) error {
539	return errors.New("invalid ip")
540}
541
542func TestRollback(t *testing.T) {
543	var plugin = "test-net-driver-rollback"
544
545	mux := http.NewServeMux()
546	defer setupPlugin(t, plugin, mux)()
547
548	rolledback := false
549
550	handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
551		iface := map[string]interface{}{
552			"Address":     "192.168.4.5/16",
553			"AddressIPv6": "",
554			"MacAddress":  "7a:12:34:56:78:90",
555		}
556		return map[string]interface{}{
557			"Interface": interface{}(iface),
558		}
559	})
560	handle(t, mux, "DeleteEndpoint", func(msg map[string]interface{}) interface{} {
561		rolledback = true
562		return map[string]interface{}{}
563	})
564
565	p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
566	if err != nil {
567		t.Fatal(err)
568	}
569	driver := newDriver(plugin, p.Client())
570
571	ep := &rollbackEndpoint{}
572
573	if err := driver.CreateEndpoint("dummy", "dummy", ep.Interface(), map[string]interface{}{}); err == nil {
574		t.Fatal("Expected error from driver")
575	}
576	if !rolledback {
577		t.Fatal("Expected to have had DeleteEndpoint called")
578	}
579}
580