github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/libnetwork/ipams/remote/remote_test.go (about)

     1  package remote
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"os"
    12  	"path/filepath"
    13  	"runtime"
    14  	"testing"
    15  
    16  	"github.com/docker/docker/libnetwork/ipamapi"
    17  	_ "github.com/docker/docker/libnetwork/testutils"
    18  	"github.com/docker/docker/pkg/plugins"
    19  )
    20  
    21  func decodeToMap(r *http.Request) (res map[string]interface{}, err error) {
    22  	err = json.NewDecoder(r.Body).Decode(&res)
    23  	return
    24  }
    25  
    26  func handle(t *testing.T, mux *http.ServeMux, method string, h func(map[string]interface{}) interface{}) {
    27  	mux.HandleFunc(fmt.Sprintf("/%s.%s", ipamapi.PluginEndpointType, method), func(w http.ResponseWriter, r *http.Request) {
    28  		ask, err := decodeToMap(r)
    29  		if err != nil && err != io.EOF {
    30  			t.Fatal(err)
    31  		}
    32  		answer := h(ask)
    33  		err = json.NewEncoder(w).Encode(&answer)
    34  		if err != nil {
    35  			t.Fatal(err)
    36  		}
    37  	})
    38  }
    39  
    40  func setupPlugin(t *testing.T, name string, mux *http.ServeMux) func() {
    41  	specPath := "/etc/docker/plugins"
    42  	if runtime.GOOS == "windows" {
    43  		specPath = filepath.Join(os.Getenv("programdata"), "docker", "plugins")
    44  	}
    45  
    46  	if err := os.MkdirAll(specPath, 0755); err != nil {
    47  		t.Fatal(err)
    48  	}
    49  
    50  	defer func() {
    51  		if t.Failed() {
    52  			os.RemoveAll(specPath)
    53  		}
    54  	}()
    55  
    56  	server := httptest.NewServer(mux)
    57  	if server == nil {
    58  		t.Fatal("Failed to start an HTTP Server")
    59  	}
    60  
    61  	if err := ioutil.WriteFile(filepath.Join(specPath, name+".spec"), []byte(server.URL), 0644); err != nil {
    62  		t.Fatal(err)
    63  	}
    64  
    65  	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
    66  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
    67  		fmt.Fprintf(w, `{"Implements": ["%s"]}`, ipamapi.PluginEndpointType)
    68  	})
    69  
    70  	return func() {
    71  		if err := os.RemoveAll(specPath); err != nil {
    72  			t.Fatal(err)
    73  		}
    74  		server.Close()
    75  	}
    76  }
    77  
    78  func TestGetCapabilities(t *testing.T) {
    79  	var plugin = "test-ipam-driver-capabilities"
    80  
    81  	mux := http.NewServeMux()
    82  	defer setupPlugin(t, plugin, mux)()
    83  
    84  	handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} {
    85  		return map[string]interface{}{
    86  			"RequiresMACAddress": true,
    87  		}
    88  	})
    89  
    90  	p, err := plugins.Get(plugin, ipamapi.PluginEndpointType)
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	client, err := getPluginClient(p)
    96  	if err != nil {
    97  		t.Fatal(err)
    98  	}
    99  	d := newAllocator(plugin, client)
   100  
   101  	caps, err := d.(*allocator).getCapabilities()
   102  	if err != nil {
   103  		t.Fatal(err)
   104  	}
   105  
   106  	if !caps.RequiresMACAddress || caps.RequiresRequestReplay {
   107  		t.Fatalf("Unexpected capability: %v", caps)
   108  	}
   109  }
   110  
   111  func TestGetCapabilitiesFromLegacyDriver(t *testing.T) {
   112  	var plugin = "test-ipam-legacy-driver"
   113  
   114  	mux := http.NewServeMux()
   115  	defer setupPlugin(t, plugin, mux)()
   116  
   117  	p, err := plugins.Get(plugin, ipamapi.PluginEndpointType)
   118  	if err != nil {
   119  		t.Fatal(err)
   120  	}
   121  
   122  	client, err := getPluginClient(p)
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  
   127  	d := newAllocator(plugin, client)
   128  
   129  	if _, err := d.(*allocator).getCapabilities(); err == nil {
   130  		t.Fatalf("Expected error, but got Success %v", err)
   131  	}
   132  }
   133  
   134  func TestGetDefaultAddressSpaces(t *testing.T) {
   135  	var plugin = "test-ipam-driver-addr-spaces"
   136  
   137  	mux := http.NewServeMux()
   138  	defer setupPlugin(t, plugin, mux)()
   139  
   140  	handle(t, mux, "GetDefaultAddressSpaces", func(msg map[string]interface{}) interface{} {
   141  		return map[string]interface{}{
   142  			"LocalDefaultAddressSpace":  "white",
   143  			"GlobalDefaultAddressSpace": "blue",
   144  		}
   145  	})
   146  
   147  	p, err := plugins.Get(plugin, ipamapi.PluginEndpointType)
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  
   152  	client, err := getPluginClient(p)
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  	d := newAllocator(plugin, client)
   157  
   158  	l, g, err := d.(*allocator).GetDefaultAddressSpaces()
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  
   163  	if l != "white" || g != "blue" {
   164  		t.Fatalf("Unexpected default local and global address spaces: %s, %s", l, g)
   165  	}
   166  }
   167  
   168  func TestRemoteDriver(t *testing.T) {
   169  	var plugin = "test-ipam-driver"
   170  
   171  	mux := http.NewServeMux()
   172  	defer setupPlugin(t, plugin, mux)()
   173  
   174  	handle(t, mux, "GetDefaultAddressSpaces", func(msg map[string]interface{}) interface{} {
   175  		return map[string]interface{}{
   176  			"LocalDefaultAddressSpace":  "white",
   177  			"GlobalDefaultAddressSpace": "blue",
   178  		}
   179  	})
   180  
   181  	handle(t, mux, "RequestPool", func(msg map[string]interface{}) interface{} {
   182  		as := "white"
   183  		if v, ok := msg["AddressSpace"]; ok && v.(string) != "" {
   184  			as = v.(string)
   185  		}
   186  
   187  		pl := "172.18.0.0/16"
   188  		sp := ""
   189  		if v, ok := msg["Pool"]; ok && v.(string) != "" {
   190  			pl = v.(string)
   191  		}
   192  		if v, ok := msg["SubPool"]; ok && v.(string) != "" {
   193  			sp = v.(string)
   194  		}
   195  		pid := fmt.Sprintf("%s/%s", as, pl)
   196  		if sp != "" {
   197  			pid = fmt.Sprintf("%s/%s", pid, sp)
   198  		}
   199  		return map[string]interface{}{
   200  			"PoolID": pid,
   201  			"Pool":   pl,
   202  			"Data":   map[string]string{"DNS": "8.8.8.8"},
   203  		}
   204  	})
   205  
   206  	handle(t, mux, "ReleasePool", func(msg map[string]interface{}) interface{} {
   207  		if _, ok := msg["PoolID"]; !ok {
   208  			t.Fatal("Missing PoolID in Release request")
   209  		}
   210  		return map[string]interface{}{}
   211  	})
   212  
   213  	handle(t, mux, "RequestAddress", func(msg map[string]interface{}) interface{} {
   214  		if _, ok := msg["PoolID"]; !ok {
   215  			t.Fatal("Missing PoolID in address request")
   216  		}
   217  		prefAddr := ""
   218  		if v, ok := msg["Address"]; ok {
   219  			prefAddr = v.(string)
   220  		}
   221  		ip := prefAddr
   222  		if ip == "" {
   223  			ip = "172.20.0.34"
   224  		}
   225  		ip = fmt.Sprintf("%s/16", ip)
   226  		return map[string]interface{}{
   227  			"Address": ip,
   228  		}
   229  	})
   230  
   231  	handle(t, mux, "ReleaseAddress", func(msg map[string]interface{}) interface{} {
   232  		if _, ok := msg["PoolID"]; !ok {
   233  			t.Fatal("Missing PoolID in address request")
   234  		}
   235  		if _, ok := msg["Address"]; !ok {
   236  			t.Fatal("Missing Address in release address request")
   237  		}
   238  		return map[string]interface{}{}
   239  	})
   240  
   241  	p, err := plugins.Get(plugin, ipamapi.PluginEndpointType)
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  
   246  	client, err := getPluginClient(p)
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  	d := newAllocator(plugin, client)
   251  
   252  	l, g, err := d.(*allocator).GetDefaultAddressSpaces()
   253  	if err != nil {
   254  		t.Fatal(err)
   255  	}
   256  	if l != "white" || g != "blue" {
   257  		t.Fatalf("Unexpected default local/global address spaces: %s, %s", l, g)
   258  	}
   259  
   260  	// Request any pool
   261  	poolID, pool, _, err := d.RequestPool("white", "", "", nil, false)
   262  	if err != nil {
   263  		t.Fatal(err)
   264  	}
   265  	if poolID != "white/172.18.0.0/16" {
   266  		t.Fatalf("Unexpected pool id: %s", poolID)
   267  	}
   268  	if pool == nil || pool.String() != "172.18.0.0/16" {
   269  		t.Fatalf("Unexpected pool: %s", pool)
   270  	}
   271  
   272  	// Request specific pool
   273  	poolID2, pool2, ops, err := d.RequestPool("white", "172.20.0.0/16", "", nil, false)
   274  	if err != nil {
   275  		t.Fatal(err)
   276  	}
   277  	if poolID2 != "white/172.20.0.0/16" {
   278  		t.Fatalf("Unexpected pool id: %s", poolID2)
   279  	}
   280  	if pool2 == nil || pool2.String() != "172.20.0.0/16" {
   281  		t.Fatalf("Unexpected pool: %s", pool2)
   282  	}
   283  	if dns, ok := ops["DNS"]; !ok || dns != "8.8.8.8" {
   284  		t.Fatal("Missing options")
   285  	}
   286  
   287  	// Request specific pool and subpool
   288  	poolID3, pool3, _, err := d.RequestPool("white", "172.20.0.0/16", "172.20.3.0/24" /*nil*/, map[string]string{"culo": "yes"}, false)
   289  	if err != nil {
   290  		t.Fatal(err)
   291  	}
   292  	if poolID3 != "white/172.20.0.0/16/172.20.3.0/24" {
   293  		t.Fatalf("Unexpected pool id: %s", poolID3)
   294  	}
   295  	if pool3 == nil || pool3.String() != "172.20.0.0/16" {
   296  		t.Fatalf("Unexpected pool: %s", pool3)
   297  	}
   298  
   299  	// Request any address
   300  	addr, _, err := d.RequestAddress(poolID2, nil, nil)
   301  	if err != nil {
   302  		t.Fatal(err)
   303  	}
   304  	if addr == nil || addr.String() != "172.20.0.34/16" {
   305  		t.Fatalf("Unexpected address: %s", addr)
   306  	}
   307  
   308  	// Request specific address
   309  	addr2, _, err := d.RequestAddress(poolID2, net.ParseIP("172.20.1.45"), nil)
   310  	if err != nil {
   311  		t.Fatal(err)
   312  	}
   313  	if addr2 == nil || addr2.String() != "172.20.1.45/16" {
   314  		t.Fatalf("Unexpected address: %s", addr2)
   315  	}
   316  
   317  	// Release address
   318  	err = d.ReleaseAddress(poolID, net.ParseIP("172.18.1.45"))
   319  	if err != nil {
   320  		t.Fatal(err)
   321  	}
   322  }