github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/libnetwork/ipams/remote/remote_test.go (about)

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