瀏覽代碼

Merge pull request #637 from aboch/rm

Change in remote IPAM API payload
Jana Radhakrishnan 9 年之前
父節點
當前提交
09453df618
共有 3 個文件被更改,包括 263 次插入12 次删除
  1. 4 8
      libnetwork/ipams/remote/api/api.go
  2. 15 4
      libnetwork/ipams/remote/remote.go
  3. 244 0
      libnetwork/ipams/remote/remote_test.go

+ 4 - 8
libnetwork/ipams/remote/api/api.go

@@ -2,10 +2,6 @@
 // messages between libnetwork and the remote ipam plugin
 package api
 
-import (
-	"net"
-)
-
 // Response is the basic response structure used in all responses
 type Response struct {
 	Error string
@@ -41,7 +37,7 @@ type RequestPoolRequest struct {
 type RequestPoolResponse struct {
 	Response
 	PoolID string
-	Pool   *net.IPNet
+	Pool   string // CIDR format
 	Data   map[string]string
 }
 
@@ -58,21 +54,21 @@ type ReleasePoolResponse struct {
 // RequestAddressRequest represents the expected data in a ``request address`` request message
 type RequestAddressRequest struct {
 	PoolID  string
-	Address net.IP
+	Address string
 	Options map[string]string
 }
 
 // RequestAddressResponse represents the expected data in the response message to a ``request address`` request
 type RequestAddressResponse struct {
 	Response
-	Address *net.IPNet
+	Address string // in CIDR format
 	Data    map[string]string
 }
 
 // ReleaseAddressRequest represents the expected data in a ``release address`` request message
 type ReleaseAddressRequest struct {
 	PoolID  string
-	Address net.IP
+	Address string
 }
 
 // ReleaseAddressResponse represents the response message to a ``release address`` request

+ 15 - 4
libnetwork/ipams/remote/remote.go

@@ -8,6 +8,7 @@ import (
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/libnetwork/ipamapi"
 	"github.com/docker/libnetwork/ipams/remote/api"
+	"github.com/docker/libnetwork/types"
 )
 
 type allocator struct {
@@ -64,7 +65,8 @@ func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[
 	if err := a.call("RequestPool", req, res); err != nil {
 		return "", nil, nil, err
 	}
-	return res.PoolID, res.Pool, res.Data, nil
+	retPool, err := types.ParseCIDR(res.Pool)
+	return res.PoolID, retPool, res.Data, err
 }
 
 // ReleasePool removes an address pool from the specified address space
@@ -76,17 +78,26 @@ func (a *allocator) ReleasePool(poolID string) error {
 
 // RequestAddress requests an address from the address pool
 func (a *allocator) RequestAddress(poolID string, address net.IP, options map[string]string) (*net.IPNet, map[string]string, error) {
-	req := &api.RequestAddressRequest{PoolID: poolID, Address: address, Options: options}
+	var prefAddress string
+	if address != nil {
+		prefAddress = address.String()
+	}
+	req := &api.RequestAddressRequest{PoolID: poolID, Address: prefAddress, Options: options}
 	res := &api.RequestAddressResponse{}
 	if err := a.call("RequestAddress", req, res); err != nil {
 		return nil, nil, err
 	}
-	return res.Address, res.Data, nil
+	retAddress, err := types.ParseCIDR(res.Address)
+	return retAddress, res.Data, err
 }
 
 // ReleaseAddress releases the address from the specified address pool
 func (a *allocator) ReleaseAddress(poolID string, address net.IP) error {
-	req := &api.ReleaseAddressRequest{PoolID: poolID, Address: address}
+	var relAddress string
+	if address != nil {
+		relAddress = address.String()
+	}
+	req := &api.ReleaseAddressRequest{PoolID: poolID, Address: relAddress}
 	res := &api.ReleaseAddressResponse{}
 	return a.call("ReleaseAddress", req, res)
 }

+ 244 - 0
libnetwork/ipams/remote/remote_test.go

@@ -0,0 +1,244 @@
+package remote
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"testing"
+
+	"github.com/docker/docker/pkg/plugins"
+	"github.com/docker/libnetwork/ipamapi"
+	_ "github.com/docker/libnetwork/testutils"
+)
+
+func decodeToMap(r *http.Request) (res map[string]interface{}, err error) {
+	err = json.NewDecoder(r.Body).Decode(&res)
+	return
+}
+
+func handle(t *testing.T, mux *http.ServeMux, method string, h func(map[string]interface{}) interface{}) {
+	mux.HandleFunc(fmt.Sprintf("/%s.%s", ipamapi.PluginEndpointType, method), func(w http.ResponseWriter, r *http.Request) {
+		ask, err := decodeToMap(r)
+		if err != nil {
+			t.Fatal(err)
+		}
+		answer := h(ask)
+		err = json.NewEncoder(w).Encode(&answer)
+		if err != nil {
+			t.Fatal(err)
+		}
+	})
+}
+
+func setupPlugin(t *testing.T, name string, mux *http.ServeMux) func() {
+	if err := os.MkdirAll("/etc/docker/plugins", 0755); err != nil {
+		t.Fatal(err)
+	}
+
+	server := httptest.NewServer(mux)
+	if server == nil {
+		t.Fatal("Failed to start a HTTP Server")
+	}
+
+	if err := ioutil.WriteFile(fmt.Sprintf("/etc/docker/plugins/%s.spec", name), []byte(server.URL), 0644); err != nil {
+		t.Fatal(err)
+	}
+
+	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"]}`, ipamapi.PluginEndpointType)
+	})
+
+	return func() {
+		if err := os.RemoveAll("/etc/docker/plugins"); err != nil {
+			t.Fatal(err)
+		}
+		server.Close()
+	}
+}
+
+func TestGetDefaultAddressSpaces(t *testing.T) {
+	var plugin = "test-ipam-driver-addr-spaces"
+
+	mux := http.NewServeMux()
+	defer setupPlugin(t, plugin, mux)()
+
+	handle(t, mux, "GetDefaultAddressSpaces", func(msg map[string]interface{}) interface{} {
+		return map[string]interface{}{
+			"LocalDefaultAddressSpace":  "white",
+			"GlobalDefaultAddressSpace": "blue",
+		}
+	})
+
+	p, err := plugins.Get(plugin, ipamapi.PluginEndpointType)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	d := newAllocator(plugin, p.Client)
+
+	l, g, err := d.(*allocator).GetDefaultAddressSpaces()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if l != "white" || g != "blue" {
+		t.Fatalf("Unexpected default local and global address spaces: %s, %s", l, g)
+	}
+}
+
+func TestRemoteDriver(t *testing.T) {
+	var plugin = "test-ipam-driver"
+
+	mux := http.NewServeMux()
+	defer setupPlugin(t, plugin, mux)()
+
+	handle(t, mux, "GetDefaultAddressSpaces", func(msg map[string]interface{}) interface{} {
+		return map[string]interface{}{
+			"LocalDefaultAddressSpace":  "white",
+			"GlobalDefaultAddressSpace": "blue",
+		}
+	})
+
+	handle(t, mux, "RequestPool", func(msg map[string]interface{}) interface{} {
+		as := "white"
+		if v, ok := msg["AddressSpace"]; ok && v.(string) != "" {
+			as = v.(string)
+		}
+
+		pl := "172.18.0.0/16"
+		sp := ""
+		if v, ok := msg["Pool"]; ok && v.(string) != "" {
+			pl = v.(string)
+		}
+		if v, ok := msg["SubPool"]; ok && v.(string) != "" {
+			sp = v.(string)
+		}
+		pid := fmt.Sprintf("%s/%s", as, pl)
+		if sp != "" {
+			pid = fmt.Sprintf("%s/%s", pid, sp)
+		}
+		return map[string]interface{}{
+			"PoolID": pid,
+			"Pool":   pl,
+			"Data":   map[string]string{"DNS": "8.8.8.8"},
+		}
+	})
+
+	handle(t, mux, "ReleasePool", func(msg map[string]interface{}) interface{} {
+		if _, ok := msg["PoolID"]; !ok {
+			t.Fatalf("Missing PoolID in Release request")
+		}
+		return map[string]interface{}{}
+	})
+
+	handle(t, mux, "RequestAddress", func(msg map[string]interface{}) interface{} {
+		if _, ok := msg["PoolID"]; !ok {
+			t.Fatalf("Missing PoolID in address request")
+		}
+		prefAddr := ""
+		if v, ok := msg["Address"]; ok {
+			prefAddr = v.(string)
+		}
+		ip := prefAddr
+		if ip == "" {
+			ip = "172.20.0.34"
+		}
+		ip = fmt.Sprintf("%s/16", ip)
+		return map[string]interface{}{
+			"Address": ip,
+		}
+	})
+
+	handle(t, mux, "ReleaseAddress", func(msg map[string]interface{}) interface{} {
+		if _, ok := msg["PoolID"]; !ok {
+			t.Fatalf("Missing PoolID in address request")
+		}
+		if _, ok := msg["Address"]; !ok {
+			t.Fatalf("Missing Address in release address request")
+		}
+		return map[string]interface{}{}
+	})
+
+	p, err := plugins.Get(plugin, ipamapi.PluginEndpointType)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	d := newAllocator(plugin, p.Client)
+
+	l, g, err := d.(*allocator).GetDefaultAddressSpaces()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if l != "white" || g != "blue" {
+		t.Fatalf("Unexpected default local/global address spaces: %s, %s", l, g)
+	}
+
+	// Request any pool
+	poolID, pool, _, err := d.RequestPool("white", "", "", nil, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if poolID != "white/172.18.0.0/16" {
+		t.Fatalf("Unexpected pool id: %s", poolID)
+	}
+	if pool == nil || pool.String() != "172.18.0.0/16" {
+		t.Fatalf("Unexpected pool: %s", pool)
+	}
+
+	// Request specific pool
+	poolID2, pool2, ops, err := d.RequestPool("white", "172.20.0.0/16", "", nil, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if poolID2 != "white/172.20.0.0/16" {
+		t.Fatalf("Unexpected pool id: %s", poolID2)
+	}
+	if pool2 == nil || pool2.String() != "172.20.0.0/16" {
+		t.Fatalf("Unexpected pool: %s", pool2)
+	}
+	if dns, ok := ops["DNS"]; !ok || dns != "8.8.8.8" {
+		t.Fatalf("Missing options")
+	}
+
+	// Request specific pool and subpool
+	poolID3, pool3, _, err := d.RequestPool("white", "172.20.0.0/16", "172.20.3.0/24" /*nil*/, map[string]string{"culo": "yes"}, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if poolID3 != "white/172.20.0.0/16/172.20.3.0/24" {
+		t.Fatalf("Unexpected pool id: %s", poolID3)
+	}
+	if pool3 == nil || pool3.String() != "172.20.0.0/16" {
+		t.Fatalf("Unexpected pool: %s", pool3)
+	}
+
+	// Request any address
+	addr, _, err := d.RequestAddress(poolID2, nil, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if addr == nil || addr.String() != "172.20.0.34/16" {
+		t.Fatalf("Unexpected address: %s", addr)
+	}
+
+	// Request specific address
+	addr2, _, err := d.RequestAddress(poolID2, net.ParseIP("172.20.1.45"), nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if addr2 == nil || addr2.String() != "172.20.1.45/16" {
+		t.Fatalf("Unexpected address: %s", addr2)
+	}
+
+	// Release address
+	err = d.ReleaseAddress(poolID, net.ParseIP("172.18.1.45"))
+	if err != nil {
+		t.Fatal(err)
+	}
+}