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 }