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 }