mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Change in remote IPAM API payload
- Avoid net.IP and net.IPNet types to ease marshalling/unmarshalling at client and server side Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
parent
c454b1084d
commit
8d56508190
3 changed files with 263 additions and 12 deletions
|
@ -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
|
||||
|
|
|
@ -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
libnetwork/ipams/remote/remote_test.go
Normal file
244
libnetwork/ipams/remote/remote_test.go
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue