| package remote |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "io" |
| "net" |
| "net/http" |
| "net/http/httptest" |
| "os" |
| "path/filepath" |
| "runtime" |
| "testing" |
| |
| "github.com/docker/docker/libnetwork/ipamapi" |
| "github.com/docker/docker/pkg/plugins" |
| ) |
| |
| 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 && err != io.EOF { |
| 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() { |
| specPath := "/etc/docker/plugins" |
| if runtime.GOOS == "windows" { |
| specPath = filepath.Join(os.Getenv("programdata"), "docker", "plugins") |
| } |
| |
| if err := os.MkdirAll(specPath, 0755); err != nil { |
| t.Fatal(err) |
| } |
| |
| defer func() { |
| if t.Failed() { |
| os.RemoveAll(specPath) |
| } |
| }() |
| |
| server := httptest.NewServer(mux) |
| if server == nil { |
| t.Fatal("Failed to start an HTTP Server") |
| } |
| |
| if err := os.WriteFile(filepath.Join(specPath, name+".spec"), []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(specPath); err != nil { |
| t.Fatal(err) |
| } |
| server.Close() |
| } |
| } |
| |
| func TestGetCapabilities(t *testing.T) { |
| var plugin = "test-ipam-driver-capabilities" |
| |
| mux := http.NewServeMux() |
| defer setupPlugin(t, plugin, mux)() |
| |
| handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} { |
| return map[string]interface{}{ |
| "RequiresMACAddress": true, |
| } |
| }) |
| |
| p, err := plugins.Get(plugin, ipamapi.PluginEndpointType) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| client, err := getPluginClient(p) |
| if err != nil { |
| t.Fatal(err) |
| } |
| d := newAllocator(plugin, client) |
| |
| caps, err := d.(*allocator).getCapabilities() |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if !caps.RequiresMACAddress || caps.RequiresRequestReplay { |
| t.Fatalf("Unexpected capability: %v", caps) |
| } |
| } |
| |
| func TestGetCapabilitiesFromLegacyDriver(t *testing.T) { |
| var plugin = "test-ipam-legacy-driver" |
| |
| mux := http.NewServeMux() |
| defer setupPlugin(t, plugin, mux)() |
| |
| p, err := plugins.Get(plugin, ipamapi.PluginEndpointType) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| client, err := getPluginClient(p) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| d := newAllocator(plugin, client) |
| |
| if _, err := d.(*allocator).getCapabilities(); err == nil { |
| t.Fatalf("Expected error, but got Success %v", err) |
| } |
| } |
| |
| 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) |
| } |
| |
| client, err := getPluginClient(p) |
| if err != nil { |
| t.Fatal(err) |
| } |
| d := newAllocator(plugin, 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.Fatal("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.Fatal("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.Fatal("Missing PoolID in address request") |
| } |
| if _, ok := msg["Address"]; !ok { |
| t.Fatal("Missing Address in release address request") |
| } |
| return map[string]interface{}{} |
| }) |
| |
| p, err := plugins.Get(plugin, ipamapi.PluginEndpointType) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| client, err := getPluginClient(p) |
| if err != nil { |
| t.Fatal(err) |
| } |
| d := newAllocator(plugin, 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.Fatal("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) |
| } |
| } |