github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/integration-cli/docker_cli_network_unix_test.go (about) 1 // +build !windows 2 3 package main 4 5 import ( 6 "encoding/json" 7 "fmt" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "net/http/httptest" 12 "os" 13 "strings" 14 "testing" 15 "time" 16 17 "github.com/docker/docker/api/types" 18 "github.com/docker/docker/api/types/versions/v1p20" 19 "github.com/docker/docker/integration-cli/cli" 20 "github.com/docker/docker/integration-cli/daemon" 21 "github.com/docker/docker/libnetwork/driverapi" 22 remoteapi "github.com/docker/docker/libnetwork/drivers/remote/api" 23 "github.com/docker/docker/libnetwork/ipamapi" 24 remoteipam "github.com/docker/docker/libnetwork/ipams/remote/api" 25 "github.com/docker/docker/libnetwork/netlabel" 26 "github.com/docker/docker/pkg/stringid" 27 "github.com/docker/docker/runconfig" 28 testdaemon "github.com/docker/docker/testutil/daemon" 29 "github.com/vishvananda/netlink" 30 "golang.org/x/sys/unix" 31 "gotest.tools/v3/assert" 32 "gotest.tools/v3/icmd" 33 ) 34 35 const dummyNetworkDriver = "dummy-network-driver" 36 const dummyIPAMDriver = "dummy-ipam-driver" 37 38 var remoteDriverNetworkRequest remoteapi.CreateNetworkRequest 39 40 func (s *DockerNetworkSuite) SetUpTest(c *testing.T) { 41 s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution)) 42 } 43 44 func (s *DockerNetworkSuite) TearDownTest(c *testing.T) { 45 if s.d != nil { 46 s.d.Stop(c) 47 s.ds.TearDownTest(c) 48 } 49 } 50 51 func (s *DockerNetworkSuite) SetUpSuite(c *testing.T) { 52 mux := http.NewServeMux() 53 s.server = httptest.NewServer(mux) 54 assert.Assert(c, s.server != nil, "Failed to start an HTTP Server") 55 setupRemoteNetworkDrivers(c, mux, s.server.URL, dummyNetworkDriver, dummyIPAMDriver) 56 } 57 58 func setupRemoteNetworkDrivers(c *testing.T, mux *http.ServeMux, url, netDrv, ipamDrv string) { 59 60 mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { 61 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 62 fmt.Fprintf(w, `{"Implements": ["%s", "%s"]}`, driverapi.NetworkPluginEndpointType, ipamapi.PluginEndpointType) 63 }) 64 65 // Network driver implementation 66 mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 67 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 68 fmt.Fprintf(w, `{"Scope":"local"}`) 69 }) 70 71 mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 72 err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest) 73 if err != nil { 74 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 75 return 76 } 77 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 78 fmt.Fprintf(w, "null") 79 }) 80 81 mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 82 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 83 fmt.Fprintf(w, "null") 84 }) 85 86 mux.HandleFunc(fmt.Sprintf("/%s.CreateEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 87 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 88 fmt.Fprintf(w, `{"Interface":{"MacAddress":"a0:b1:c2:d3:e4:f5"}}`) 89 }) 90 91 mux.HandleFunc(fmt.Sprintf("/%s.Join", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 92 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 93 94 veth := &netlink.Veth{ 95 LinkAttrs: netlink.LinkAttrs{Name: "randomIfName", TxQLen: 0}, PeerName: "cnt0"} 96 if err := netlink.LinkAdd(veth); err != nil { 97 fmt.Fprintf(w, `{"Error":"failed to add veth pair: `+err.Error()+`"}`) 98 } else { 99 fmt.Fprintf(w, `{"InterfaceName":{ "SrcName":"cnt0", "DstPrefix":"veth"}}`) 100 } 101 }) 102 103 mux.HandleFunc(fmt.Sprintf("/%s.Leave", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 104 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 105 fmt.Fprintf(w, "null") 106 }) 107 108 mux.HandleFunc(fmt.Sprintf("/%s.DeleteEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 109 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 110 if link, err := netlink.LinkByName("cnt0"); err == nil { 111 netlink.LinkDel(link) 112 } 113 fmt.Fprintf(w, "null") 114 }) 115 116 // IPAM Driver implementation 117 var ( 118 poolRequest remoteipam.RequestPoolRequest 119 poolReleaseReq remoteipam.ReleasePoolRequest 120 addressRequest remoteipam.RequestAddressRequest 121 addressReleaseReq remoteipam.ReleaseAddressRequest 122 lAS = "localAS" 123 gAS = "globalAS" 124 pool = "172.28.0.0/16" 125 poolID = lAS + "/" + pool 126 gw = "172.28.255.254/16" 127 ) 128 129 mux.HandleFunc(fmt.Sprintf("/%s.GetDefaultAddressSpaces", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 130 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 131 fmt.Fprintf(w, `{"LocalDefaultAddressSpace":"`+lAS+`", "GlobalDefaultAddressSpace": "`+gAS+`"}`) 132 }) 133 134 mux.HandleFunc(fmt.Sprintf("/%s.RequestPool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 135 err := json.NewDecoder(r.Body).Decode(&poolRequest) 136 if err != nil { 137 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 138 return 139 } 140 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 141 if poolRequest.AddressSpace != lAS && poolRequest.AddressSpace != gAS { 142 fmt.Fprintf(w, `{"Error":"Unknown address space in pool request: `+poolRequest.AddressSpace+`"}`) 143 } else if poolRequest.Pool != "" && poolRequest.Pool != pool { 144 fmt.Fprintf(w, `{"Error":"Cannot handle explicit pool requests yet"}`) 145 } else { 146 fmt.Fprintf(w, `{"PoolID":"`+poolID+`", "Pool":"`+pool+`"}`) 147 } 148 }) 149 150 mux.HandleFunc(fmt.Sprintf("/%s.RequestAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 151 err := json.NewDecoder(r.Body).Decode(&addressRequest) 152 if err != nil { 153 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 154 return 155 } 156 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 157 // make sure libnetwork is now querying on the expected pool id 158 if addressRequest.PoolID != poolID { 159 fmt.Fprintf(w, `{"Error":"unknown pool id"}`) 160 } else if addressRequest.Address != "" { 161 fmt.Fprintf(w, `{"Error":"Cannot handle explicit address requests yet"}`) 162 } else { 163 fmt.Fprintf(w, `{"Address":"`+gw+`"}`) 164 } 165 }) 166 167 mux.HandleFunc(fmt.Sprintf("/%s.ReleaseAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 168 err := json.NewDecoder(r.Body).Decode(&addressReleaseReq) 169 if err != nil { 170 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 171 return 172 } 173 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 174 // make sure libnetwork is now asking to release the expected address from the expected poolid 175 if addressRequest.PoolID != poolID { 176 fmt.Fprintf(w, `{"Error":"unknown pool id"}`) 177 } else if addressReleaseReq.Address != gw { 178 fmt.Fprintf(w, `{"Error":"unknown address"}`) 179 } else { 180 fmt.Fprintf(w, "null") 181 } 182 }) 183 184 mux.HandleFunc(fmt.Sprintf("/%s.ReleasePool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 185 err := json.NewDecoder(r.Body).Decode(&poolReleaseReq) 186 if err != nil { 187 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 188 return 189 } 190 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 191 // make sure libnetwork is now asking to release the expected poolid 192 if addressRequest.PoolID != poolID { 193 fmt.Fprintf(w, `{"Error":"unknown pool id"}`) 194 } else { 195 fmt.Fprintf(w, "null") 196 } 197 }) 198 199 err := os.MkdirAll("/etc/docker/plugins", 0755) 200 assert.NilError(c, err) 201 202 fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv) 203 err = ioutil.WriteFile(fileName, []byte(url), 0644) 204 assert.NilError(c, err) 205 206 ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv) 207 err = ioutil.WriteFile(ipamFileName, []byte(url), 0644) 208 assert.NilError(c, err) 209 } 210 211 func (s *DockerNetworkSuite) TearDownSuite(c *testing.T) { 212 if s.server == nil { 213 return 214 } 215 216 s.server.Close() 217 218 err := os.RemoveAll("/etc/docker/plugins") 219 assert.NilError(c, err) 220 } 221 222 func assertNwIsAvailable(c *testing.T, name string) { 223 if !isNwPresent(c, name) { 224 c.Fatalf("Network %s not found in network ls o/p", name) 225 } 226 } 227 228 func assertNwNotAvailable(c *testing.T, name string) { 229 if isNwPresent(c, name) { 230 c.Fatalf("Found network %s in network ls o/p", name) 231 } 232 } 233 234 func isNwPresent(c *testing.T, name string) bool { 235 out, _ := dockerCmd(c, "network", "ls") 236 lines := strings.Split(out, "\n") 237 for i := 1; i < len(lines)-1; i++ { 238 netFields := strings.Fields(lines[i]) 239 if netFields[1] == name { 240 return true 241 } 242 } 243 return false 244 } 245 246 // assertNwList checks network list retrieved with ls command 247 // equals to expected network list 248 // note: out should be `network ls [option]` result 249 func assertNwList(c *testing.T, out string, expectNws []string) { 250 lines := strings.Split(out, "\n") 251 var nwList []string 252 for _, line := range lines[1 : len(lines)-1] { 253 netFields := strings.Fields(line) 254 // wrap all network name in nwList 255 nwList = append(nwList, netFields[1]) 256 } 257 258 // network ls should contains all expected networks 259 assert.DeepEqual(c, nwList, expectNws) 260 } 261 262 func getNwResource(c *testing.T, name string) *types.NetworkResource { 263 out, _ := dockerCmd(c, "network", "inspect", name) 264 var nr []types.NetworkResource 265 err := json.Unmarshal([]byte(out), &nr) 266 assert.NilError(c, err) 267 return &nr[0] 268 } 269 270 func (s *DockerNetworkSuite) TestDockerNetworkLsDefault(c *testing.T) { 271 defaults := []string{"bridge", "host", "none"} 272 for _, nn := range defaults { 273 assertNwIsAvailable(c, nn) 274 } 275 } 276 277 func (s *DockerNetworkSuite) TestDockerNetworkCreatePredefined(c *testing.T) { 278 predefined := []string{"bridge", "host", "none", "default"} 279 for _, net := range predefined { 280 // predefined networks can't be created again 281 out, _, err := dockerCmdWithError("network", "create", net) 282 assert.ErrorContains(c, err, "", out) 283 } 284 } 285 286 func (s *DockerNetworkSuite) TestDockerNetworkCreateHostBind(c *testing.T) { 287 dockerCmd(c, "network", "create", "--subnet=192.168.10.0/24", "--gateway=192.168.10.1", "-o", "com.docker.network.bridge.host_binding_ipv4=192.168.10.1", "testbind") 288 assertNwIsAvailable(c, "testbind") 289 290 out := runSleepingContainer(c, "--net=testbind", "-p", "5000:5000") 291 id := strings.TrimSpace(out) 292 assert.NilError(c, waitRun(id)) 293 out, _ = dockerCmd(c, "ps") 294 assert.Assert(c, strings.Contains(out, "192.168.10.1:5000->5000/tcp")) 295 } 296 297 func (s *DockerNetworkSuite) TestDockerNetworkRmPredefined(c *testing.T) { 298 predefined := []string{"bridge", "host", "none", "default"} 299 for _, net := range predefined { 300 // predefined networks can't be removed 301 out, _, err := dockerCmdWithError("network", "rm", net) 302 assert.ErrorContains(c, err, "", out) 303 } 304 } 305 306 func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *testing.T) { 307 testRequires(c, OnlyDefaultNetworks) 308 testNet := "testnet1" 309 testLabel := "foo" 310 testValue := "bar" 311 out, _ := dockerCmd(c, "network", "create", "dev") 312 defer func() { 313 dockerCmd(c, "network", "rm", "dev") 314 dockerCmd(c, "network", "rm", testNet) 315 }() 316 networkID := strings.TrimSpace(out) 317 318 // filter with partial ID 319 // only show 'dev' network 320 out, _ = dockerCmd(c, "network", "ls", "-f", "id="+networkID[0:5]) 321 assertNwList(c, out, []string{"dev"}) 322 323 out, _ = dockerCmd(c, "network", "ls", "-f", "name=dge") 324 assertNwList(c, out, []string{"bridge"}) 325 326 // only show built-in network (bridge, none, host) 327 out, _ = dockerCmd(c, "network", "ls", "-f", "type=builtin") 328 assertNwList(c, out, []string{"bridge", "host", "none"}) 329 330 // only show custom networks (dev) 331 out, _ = dockerCmd(c, "network", "ls", "-f", "type=custom") 332 assertNwList(c, out, []string{"dev"}) 333 334 // show all networks with filter 335 // it should be equivalent of ls without option 336 out, _ = dockerCmd(c, "network", "ls", "-f", "type=custom", "-f", "type=builtin") 337 assertNwList(c, out, []string{"bridge", "dev", "host", "none"}) 338 339 dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet) 340 assertNwIsAvailable(c, testNet) 341 342 out, _ = dockerCmd(c, "network", "ls", "-f", "label="+testLabel) 343 assertNwList(c, out, []string{testNet}) 344 345 out, _ = dockerCmd(c, "network", "ls", "-f", "label="+testLabel+"="+testValue) 346 assertNwList(c, out, []string{testNet}) 347 348 out, _ = dockerCmd(c, "network", "ls", "-f", "label=nonexistent") 349 outArr := strings.Split(strings.TrimSpace(out), "\n") 350 assert.Equal(c, len(outArr), 1, fmt.Sprintf("%s\n", out)) 351 352 out, _ = dockerCmd(c, "network", "ls", "-f", "driver=null") 353 assertNwList(c, out, []string{"none"}) 354 355 out, _ = dockerCmd(c, "network", "ls", "-f", "driver=host") 356 assertNwList(c, out, []string{"host"}) 357 358 out, _ = dockerCmd(c, "network", "ls", "-f", "driver=bridge") 359 assertNwList(c, out, []string{"bridge", "dev", testNet}) 360 } 361 362 func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *testing.T) { 363 dockerCmd(c, "network", "create", "test") 364 assertNwIsAvailable(c, "test") 365 366 dockerCmd(c, "network", "rm", "test") 367 assertNwNotAvailable(c, "test") 368 } 369 370 func (s *DockerNetworkSuite) TestDockerNetworkCreateLabel(c *testing.T) { 371 testNet := "testnetcreatelabel" 372 testLabel := "foo" 373 testValue := "bar" 374 375 dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet) 376 assertNwIsAvailable(c, testNet) 377 378 out, _, err := dockerCmdWithError("network", "inspect", "--format={{ .Labels."+testLabel+" }}", testNet) 379 assert.NilError(c, err) 380 assert.Equal(c, strings.TrimSpace(out), testValue) 381 382 dockerCmd(c, "network", "rm", testNet) 383 assertNwNotAvailable(c, testNet) 384 } 385 386 func (s *DockerSuite) TestDockerNetworkDeleteNotExists(c *testing.T) { 387 out, _, err := dockerCmdWithError("network", "rm", "test") 388 assert.ErrorContains(c, err, "", out) 389 } 390 391 func (s *DockerSuite) TestDockerNetworkDeleteMultiple(c *testing.T) { 392 dockerCmd(c, "network", "create", "testDelMulti0") 393 assertNwIsAvailable(c, "testDelMulti0") 394 dockerCmd(c, "network", "create", "testDelMulti1") 395 assertNwIsAvailable(c, "testDelMulti1") 396 dockerCmd(c, "network", "create", "testDelMulti2") 397 assertNwIsAvailable(c, "testDelMulti2") 398 out, _ := dockerCmd(c, "run", "-d", "--net", "testDelMulti2", "busybox", "top") 399 containerID := strings.TrimSpace(out) 400 waitRun(containerID) 401 402 // delete three networks at the same time, since testDelMulti2 403 // contains active container, its deletion should fail. 404 out, _, err := dockerCmdWithError("network", "rm", "testDelMulti0", "testDelMulti1", "testDelMulti2") 405 // err should not be nil due to deleting testDelMulti2 failed. 406 assert.Assert(c, err != nil, "out: %s", out) 407 // testDelMulti2 should fail due to network has active endpoints 408 assert.Assert(c, strings.Contains(out, "has active endpoints")) 409 assertNwNotAvailable(c, "testDelMulti0") 410 assertNwNotAvailable(c, "testDelMulti1") 411 // testDelMulti2 can't be deleted, so it should exist 412 assertNwIsAvailable(c, "testDelMulti2") 413 } 414 415 func (s *DockerSuite) TestDockerNetworkInspect(c *testing.T) { 416 out, _ := dockerCmd(c, "network", "inspect", "host") 417 var networkResources []types.NetworkResource 418 err := json.Unmarshal([]byte(out), &networkResources) 419 assert.NilError(c, err) 420 assert.Equal(c, len(networkResources), 1) 421 422 out, _ = dockerCmd(c, "network", "inspect", "--format={{ .Name }}", "host") 423 assert.Equal(c, strings.TrimSpace(out), "host") 424 } 425 426 func (s *DockerSuite) TestDockerNetworkInspectWithID(c *testing.T) { 427 out, _ := dockerCmd(c, "network", "create", "test2") 428 networkID := strings.TrimSpace(out) 429 assertNwIsAvailable(c, "test2") 430 out, _ = dockerCmd(c, "network", "inspect", "--format={{ .Id }}", "test2") 431 assert.Equal(c, strings.TrimSpace(out), networkID) 432 433 out, _ = dockerCmd(c, "network", "inspect", "--format={{ .ID }}", "test2") 434 assert.Equal(c, strings.TrimSpace(out), networkID) 435 } 436 437 func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *testing.T) { 438 result := dockerCmdWithResult("network", "inspect", "host", "none") 439 result.Assert(c, icmd.Success) 440 441 var networkResources []types.NetworkResource 442 err := json.Unmarshal([]byte(result.Stdout()), &networkResources) 443 assert.NilError(c, err) 444 assert.Equal(c, len(networkResources), 2) 445 } 446 447 func (s *DockerSuite) TestDockerInspectMultipleNetworksIncludingNonexistent(c *testing.T) { 448 // non-existent network was not at the beginning of the inspect list 449 // This should print an error, return an exitCode 1 and print the host network 450 result := dockerCmdWithResult("network", "inspect", "host", "nonexistent") 451 result.Assert(c, icmd.Expected{ 452 ExitCode: 1, 453 Err: "Error: No such network: nonexistent", 454 Out: "host", 455 }) 456 457 var networkResources []types.NetworkResource 458 err := json.Unmarshal([]byte(result.Stdout()), &networkResources) 459 assert.NilError(c, err) 460 assert.Equal(c, len(networkResources), 1) 461 462 // Only one non-existent network to inspect 463 // Should print an error and return an exitCode, nothing else 464 result = dockerCmdWithResult("network", "inspect", "nonexistent") 465 result.Assert(c, icmd.Expected{ 466 ExitCode: 1, 467 Err: "Error: No such network: nonexistent", 468 Out: "[]", 469 }) 470 471 // non-existent network was at the beginning of the inspect list 472 // Should not fail fast, and still print host network but print an error 473 result = dockerCmdWithResult("network", "inspect", "nonexistent", "host") 474 result.Assert(c, icmd.Expected{ 475 ExitCode: 1, 476 Err: "Error: No such network: nonexistent", 477 Out: "host", 478 }) 479 480 networkResources = []types.NetworkResource{} 481 err = json.Unmarshal([]byte(result.Stdout()), &networkResources) 482 assert.NilError(c, err) 483 assert.Equal(c, len(networkResources), 1) 484 } 485 486 func (s *DockerSuite) TestDockerInspectNetworkWithContainerName(c *testing.T) { 487 dockerCmd(c, "network", "create", "brNetForInspect") 488 assertNwIsAvailable(c, "brNetForInspect") 489 defer func() { 490 dockerCmd(c, "network", "rm", "brNetForInspect") 491 assertNwNotAvailable(c, "brNetForInspect") 492 }() 493 494 out, _ := dockerCmd(c, "run", "-d", "--name", "testNetInspect1", "--net", "brNetForInspect", "busybox", "top") 495 assert.Assert(c, waitRun("testNetInspect1") == nil) 496 containerID := strings.TrimSpace(out) 497 defer func() { 498 // we don't stop container by name, because we'll rename it later 499 dockerCmd(c, "stop", containerID) 500 }() 501 502 out, _ = dockerCmd(c, "network", "inspect", "brNetForInspect") 503 var networkResources []types.NetworkResource 504 err := json.Unmarshal([]byte(out), &networkResources) 505 assert.NilError(c, err) 506 assert.Equal(c, len(networkResources), 1) 507 container, ok := networkResources[0].Containers[containerID] 508 assert.Assert(c, ok) 509 assert.Equal(c, container.Name, "testNetInspect1") 510 511 // rename container and check docker inspect output update 512 newName := "HappyNewName" 513 dockerCmd(c, "rename", "testNetInspect1", newName) 514 515 // check whether network inspect works properly 516 out, _ = dockerCmd(c, "network", "inspect", "brNetForInspect") 517 var newNetRes []types.NetworkResource 518 err = json.Unmarshal([]byte(out), &newNetRes) 519 assert.NilError(c, err) 520 assert.Equal(c, len(newNetRes), 1) 521 container1, ok := newNetRes[0].Containers[containerID] 522 assert.Assert(c, ok) 523 assert.Equal(c, container1.Name, newName) 524 } 525 526 func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnect(c *testing.T) { 527 dockerCmd(c, "network", "create", "test") 528 assertNwIsAvailable(c, "test") 529 nr := getNwResource(c, "test") 530 531 assert.Equal(c, nr.Name, "test") 532 assert.Equal(c, len(nr.Containers), 0) 533 534 // run a container 535 out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top") 536 assert.Assert(c, waitRun("test") == nil) 537 containerID := strings.TrimSpace(out) 538 539 // connect the container to the test network 540 dockerCmd(c, "network", "connect", "test", containerID) 541 542 // inspect the network to make sure container is connected 543 nr = getNetworkResource(c, nr.ID) 544 assert.Equal(c, len(nr.Containers), 1) 545 546 // check if container IP matches network inspect 547 ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) 548 assert.NilError(c, err) 549 containerIP := findContainerIP(c, "test", "test") 550 assert.Equal(c, ip.String(), containerIP) 551 552 // disconnect container from the network 553 dockerCmd(c, "network", "disconnect", "test", containerID) 554 nr = getNwResource(c, "test") 555 assert.Equal(c, nr.Name, "test") 556 assert.Equal(c, len(nr.Containers), 0) 557 558 // run another container 559 out, _ = dockerCmd(c, "run", "-d", "--net", "test", "--name", "test2", "busybox", "top") 560 assert.Assert(c, waitRun("test2") == nil) 561 containerID = strings.TrimSpace(out) 562 563 nr = getNwResource(c, "test") 564 assert.Equal(c, nr.Name, "test") 565 assert.Equal(c, len(nr.Containers), 1) 566 567 // force disconnect the container to the test network 568 dockerCmd(c, "network", "disconnect", "-f", "test", containerID) 569 570 nr = getNwResource(c, "test") 571 assert.Equal(c, nr.Name, "test") 572 assert.Equal(c, len(nr.Containers), 0) 573 574 dockerCmd(c, "network", "rm", "test") 575 assertNwNotAvailable(c, "test") 576 } 577 578 func (s *DockerNetworkSuite) TestDockerNetworkIPAMMultipleNetworks(c *testing.T) { 579 testRequires(c, testEnv.IsLocalDaemon) 580 // test0 bridge network 581 dockerCmd(c, "network", "create", "--subnet=192.168.0.0/16", "test1") 582 assertNwIsAvailable(c, "test1") 583 584 // test2 bridge network does not overlap 585 dockerCmd(c, "network", "create", "--subnet=192.169.0.0/16", "test2") 586 assertNwIsAvailable(c, "test2") 587 588 // for networks w/o ipam specified, docker will choose proper non-overlapping subnets 589 dockerCmd(c, "network", "create", "test3") 590 assertNwIsAvailable(c, "test3") 591 dockerCmd(c, "network", "create", "test4") 592 assertNwIsAvailable(c, "test4") 593 dockerCmd(c, "network", "create", "test5") 594 assertNwIsAvailable(c, "test5") 595 596 // test network with multiple subnets 597 // bridge network doesn't support multiple subnets. hence, use a dummy driver that supports 598 599 dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, "--subnet=192.170.0.0/16", "--subnet=192.171.0.0/16", "test6") 600 assertNwIsAvailable(c, "test6") 601 602 // test network with multiple subnets with valid ipam combinations 603 // also check same subnet across networks when the driver supports it. 604 dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, 605 "--subnet=192.172.0.0/16", "--subnet=192.173.0.0/16", 606 "--gateway=192.172.0.100", "--gateway=192.173.0.100", 607 "--ip-range=192.172.1.0/24", 608 "--aux-address", "a=192.172.1.5", "--aux-address", "b=192.172.1.6", 609 "--aux-address", "c=192.173.1.5", "--aux-address", "d=192.173.1.6", 610 "test7") 611 assertNwIsAvailable(c, "test7") 612 613 // cleanup 614 for i := 1; i < 8; i++ { 615 dockerCmd(c, "network", "rm", fmt.Sprintf("test%d", i)) 616 } 617 } 618 619 func (s *DockerNetworkSuite) TestDockerNetworkCustomIPAM(c *testing.T) { 620 testRequires(c, testEnv.IsLocalDaemon) 621 // Create a bridge network using custom ipam driver 622 dockerCmd(c, "network", "create", "--ipam-driver", dummyIPAMDriver, "br0") 623 assertNwIsAvailable(c, "br0") 624 625 // Verify expected network ipam fields are there 626 nr := getNetworkResource(c, "br0") 627 assert.Equal(c, nr.Driver, "bridge") 628 assert.Equal(c, nr.IPAM.Driver, dummyIPAMDriver) 629 630 // remove network and exercise remote ipam driver 631 dockerCmd(c, "network", "rm", "br0") 632 assertNwNotAvailable(c, "br0") 633 } 634 635 func (s *DockerNetworkSuite) TestDockerNetworkIPAMOptions(c *testing.T) { 636 testRequires(c, testEnv.IsLocalDaemon) 637 // Create a bridge network using custom ipam driver and options 638 dockerCmd(c, "network", "create", "--ipam-driver", dummyIPAMDriver, "--ipam-opt", "opt1=drv1", "--ipam-opt", "opt2=drv2", "br0") 639 assertNwIsAvailable(c, "br0") 640 641 // Verify expected network ipam options 642 nr := getNetworkResource(c, "br0") 643 opts := nr.IPAM.Options 644 assert.Equal(c, opts["opt1"], "drv1") 645 assert.Equal(c, opts["opt2"], "drv2") 646 } 647 648 func (s *DockerNetworkSuite) TestDockerNetworkNullIPAMDriver(c *testing.T) { 649 testRequires(c, testEnv.IsLocalDaemon) 650 // Create a network with null ipam driver 651 _, _, err := dockerCmdWithError("network", "create", "-d", dummyNetworkDriver, "--ipam-driver", "null", "test000") 652 assert.NilError(c, err) 653 assertNwIsAvailable(c, "test000") 654 655 // Verify the inspect data contains the default subnet provided by the null 656 // ipam driver and no gateway, as the null ipam driver does not provide one 657 nr := getNetworkResource(c, "test000") 658 assert.Equal(c, nr.IPAM.Driver, "null") 659 assert.Equal(c, len(nr.IPAM.Config), 1) 660 assert.Equal(c, nr.IPAM.Config[0].Subnet, "0.0.0.0/0") 661 assert.Equal(c, nr.IPAM.Config[0].Gateway, "") 662 } 663 664 func (s *DockerNetworkSuite) TestDockerNetworkInspectDefault(c *testing.T) { 665 nr := getNetworkResource(c, "none") 666 assert.Equal(c, nr.Driver, "null") 667 assert.Equal(c, nr.Scope, "local") 668 assert.Equal(c, nr.Internal, false) 669 assert.Equal(c, nr.EnableIPv6, false) 670 assert.Equal(c, nr.IPAM.Driver, "default") 671 assert.Equal(c, len(nr.IPAM.Config), 0) 672 673 nr = getNetworkResource(c, "host") 674 assert.Equal(c, nr.Driver, "host") 675 assert.Equal(c, nr.Scope, "local") 676 assert.Equal(c, nr.Internal, false) 677 assert.Equal(c, nr.EnableIPv6, false) 678 assert.Equal(c, nr.IPAM.Driver, "default") 679 assert.Equal(c, len(nr.IPAM.Config), 0) 680 681 nr = getNetworkResource(c, "bridge") 682 assert.Equal(c, nr.Driver, "bridge") 683 assert.Equal(c, nr.Scope, "local") 684 assert.Equal(c, nr.Internal, false) 685 assert.Equal(c, nr.EnableIPv6, false) 686 assert.Equal(c, nr.IPAM.Driver, "default") 687 assert.Equal(c, len(nr.IPAM.Config), 1) 688 } 689 690 func (s *DockerNetworkSuite) TestDockerNetworkInspectCustomUnspecified(c *testing.T) { 691 // if unspecified, network subnet will be selected from inside preferred pool 692 dockerCmd(c, "network", "create", "test01") 693 assertNwIsAvailable(c, "test01") 694 695 nr := getNetworkResource(c, "test01") 696 assert.Equal(c, nr.Driver, "bridge") 697 assert.Equal(c, nr.Scope, "local") 698 assert.Equal(c, nr.Internal, false) 699 assert.Equal(c, nr.EnableIPv6, false) 700 assert.Equal(c, nr.IPAM.Driver, "default") 701 assert.Equal(c, len(nr.IPAM.Config), 1) 702 703 dockerCmd(c, "network", "rm", "test01") 704 assertNwNotAvailable(c, "test01") 705 } 706 707 func (s *DockerNetworkSuite) TestDockerNetworkInspectCustomSpecified(c *testing.T) { 708 dockerCmd(c, "network", "create", "--driver=bridge", "--ipv6", "--subnet=fd80:24e2:f998:72d6::/64", "--subnet=172.28.0.0/16", "--ip-range=172.28.5.0/24", "--gateway=172.28.5.254", "br0") 709 assertNwIsAvailable(c, "br0") 710 711 nr := getNetworkResource(c, "br0") 712 assert.Equal(c, nr.Driver, "bridge") 713 assert.Equal(c, nr.Scope, "local") 714 assert.Equal(c, nr.Internal, false) 715 assert.Equal(c, nr.EnableIPv6, true) 716 assert.Equal(c, nr.IPAM.Driver, "default") 717 assert.Equal(c, len(nr.IPAM.Config), 2) 718 assert.Equal(c, nr.IPAM.Config[0].Subnet, "172.28.0.0/16") 719 assert.Equal(c, nr.IPAM.Config[0].IPRange, "172.28.5.0/24") 720 assert.Equal(c, nr.IPAM.Config[0].Gateway, "172.28.5.254") 721 assert.Equal(c, nr.Internal, false) 722 dockerCmd(c, "network", "rm", "br0") 723 assertNwNotAvailable(c, "br0") 724 } 725 726 func (s *DockerNetworkSuite) TestDockerNetworkIPAMInvalidCombinations(c *testing.T) { 727 // network with ip-range out of subnet range 728 _, _, err := dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--ip-range=192.170.0.0/16", "test") 729 assert.ErrorContains(c, err, "") 730 731 // network with multiple gateways for a single subnet 732 _, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--gateway=192.168.0.1", "--gateway=192.168.0.2", "test") 733 assert.ErrorContains(c, err, "") 734 735 // Multiple overlapping subnets in the same network must fail 736 _, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--subnet=192.168.1.0/16", "test") 737 assert.ErrorContains(c, err, "") 738 739 // overlapping subnets across networks must fail 740 // create a valid test0 network 741 dockerCmd(c, "network", "create", "--subnet=192.168.0.0/16", "test0") 742 assertNwIsAvailable(c, "test0") 743 // create an overlapping test1 network 744 _, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.128.0/17", "test1") 745 assert.ErrorContains(c, err, "") 746 dockerCmd(c, "network", "rm", "test0") 747 assertNwNotAvailable(c, "test0") 748 } 749 750 func (s *DockerNetworkSuite) TestDockerNetworkDriverOptions(c *testing.T) { 751 testRequires(c, testEnv.IsLocalDaemon) 752 dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, "-o", "opt1=drv1", "-o", "opt2=drv2", "testopt") 753 assertNwIsAvailable(c, "testopt") 754 gopts := remoteDriverNetworkRequest.Options[netlabel.GenericData] 755 assert.Assert(c, gopts != nil) 756 opts, ok := gopts.(map[string]interface{}) 757 assert.Equal(c, ok, true) 758 assert.Equal(c, opts["opt1"], "drv1") 759 assert.Equal(c, opts["opt2"], "drv2") 760 dockerCmd(c, "network", "rm", "testopt") 761 assertNwNotAvailable(c, "testopt") 762 763 } 764 765 func (s *DockerNetworkSuite) TestDockerPluginV2NetworkDriver(c *testing.T) { 766 testRequires(c, DaemonIsLinux, IsAmd64, Network) 767 768 var ( 769 npName = "tiborvass/test-docker-netplugin" 770 npTag = "latest" 771 npNameWithTag = npName + ":" + npTag 772 ) 773 _, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", npNameWithTag) 774 assert.NilError(c, err) 775 776 out, _, err := dockerCmdWithError("plugin", "ls") 777 assert.NilError(c, err) 778 assert.Assert(c, strings.Contains(out, npName)) 779 assert.Assert(c, strings.Contains(out, npTag)) 780 assert.Assert(c, strings.Contains(out, "true")) 781 dockerCmd(c, "network", "create", "-d", npNameWithTag, "v2net") 782 assertNwIsAvailable(c, "v2net") 783 dockerCmd(c, "network", "rm", "v2net") 784 assertNwNotAvailable(c, "v2net") 785 786 } 787 788 func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c *testing.T) { 789 // On default bridge network built-in service discovery should not happen 790 hostsFile := "/etc/hosts" 791 bridgeName := "external-bridge" 792 bridgeIP := "192.169.255.254/24" 793 createInterface(c, "bridge", bridgeName, bridgeIP) 794 defer deleteInterface(c, bridgeName) 795 796 s.d.StartWithBusybox(c, "--bridge", bridgeName) 797 defer s.d.Restart(c) 798 799 // run two containers and store first container's etc/hosts content 800 out, err := s.d.Cmd("run", "-d", "busybox", "top") 801 assert.NilError(c, err) 802 cid1 := strings.TrimSpace(out) 803 defer s.d.Cmd("stop", cid1) 804 805 hosts, err := s.d.Cmd("exec", cid1, "cat", hostsFile) 806 assert.NilError(c, err) 807 808 out, err = s.d.Cmd("run", "-d", "--name", "container2", "busybox", "top") 809 assert.NilError(c, err) 810 cid2 := strings.TrimSpace(out) 811 812 // verify first container's etc/hosts file has not changed after spawning the second named container 813 hostsPost, err := s.d.Cmd("exec", cid1, "cat", hostsFile) 814 assert.NilError(c, err) 815 assert.Equal(c, hosts, hostsPost, fmt.Sprintf("Unexpected %s change on second container creation", hostsFile)) 816 // stop container 2 and verify first container's etc/hosts has not changed 817 _, err = s.d.Cmd("stop", cid2) 818 assert.NilError(c, err) 819 820 hostsPost, err = s.d.Cmd("exec", cid1, "cat", hostsFile) 821 assert.NilError(c, err) 822 assert.Equal(c, hosts, hostsPost, fmt.Sprintf("Unexpected %s change on second container creation", hostsFile)) 823 // but discovery is on when connecting to non default bridge network 824 network := "anotherbridge" 825 out, err = s.d.Cmd("network", "create", network) 826 assert.NilError(c, err, out) 827 defer s.d.Cmd("network", "rm", network) 828 829 out, err = s.d.Cmd("network", "connect", network, cid1) 830 assert.NilError(c, err, out) 831 832 hosts, err = s.d.Cmd("exec", cid1, "cat", hostsFile) 833 assert.NilError(c, err) 834 835 hostsPost, err = s.d.Cmd("exec", cid1, "cat", hostsFile) 836 assert.NilError(c, err) 837 assert.Equal(c, hosts, hostsPost, fmt.Sprintf("Unexpected %s change on second network connection", hostsFile)) 838 } 839 840 func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *testing.T) { 841 testRequires(c, NotArm) 842 hostsFile := "/etc/hosts" 843 cstmBridgeNw := "custom-bridge-nw" 844 cstmBridgeNw1 := "custom-bridge-nw1" 845 846 dockerCmd(c, "network", "create", "-d", "bridge", cstmBridgeNw) 847 assertNwIsAvailable(c, cstmBridgeNw) 848 849 // run two anonymous containers and store their etc/hosts content 850 out, _ := dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "busybox", "top") 851 cid1 := strings.TrimSpace(out) 852 853 hosts1 := readContainerFileWithExec(c, cid1, hostsFile) 854 855 out, _ = dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "busybox", "top") 856 cid2 := strings.TrimSpace(out) 857 858 // verify first container etc/hosts file has not changed 859 hosts1post := readContainerFileWithExec(c, cid1, hostsFile) 860 assert.Equal(c, string(hosts1), string(hosts1post), fmt.Sprintf("Unexpected %s change on anonymous container creation", hostsFile)) 861 // Connect the 2nd container to a new network and verify the 862 // first container /etc/hosts file still hasn't changed. 863 dockerCmd(c, "network", "create", "-d", "bridge", cstmBridgeNw1) 864 assertNwIsAvailable(c, cstmBridgeNw1) 865 866 dockerCmd(c, "network", "connect", cstmBridgeNw1, cid2) 867 868 hosts2 := readContainerFileWithExec(c, cid2, hostsFile) 869 hosts1post = readContainerFileWithExec(c, cid1, hostsFile) 870 assert.Equal(c, string(hosts1), string(hosts1post), fmt.Sprintf("Unexpected %s change on container connect", hostsFile)) 871 // start a named container 872 cName := "AnyName" 873 out, _ = dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "--name", cName, "busybox", "top") 874 cid3 := strings.TrimSpace(out) 875 876 // verify that container 1 and 2 can ping the named container 877 dockerCmd(c, "exec", cid1, "ping", "-c", "1", cName) 878 dockerCmd(c, "exec", cid2, "ping", "-c", "1", cName) 879 880 // Stop named container and verify first two containers' etc/hosts file hasn't changed 881 dockerCmd(c, "stop", cid3) 882 hosts1post = readContainerFileWithExec(c, cid1, hostsFile) 883 assert.Equal(c, string(hosts1), string(hosts1post), fmt.Sprintf("Unexpected %s change on name container creation", hostsFile)) 884 hosts2post := readContainerFileWithExec(c, cid2, hostsFile) 885 assert.Equal(c, string(hosts2), string(hosts2post), fmt.Sprintf("Unexpected %s change on name container creation", hostsFile)) 886 // verify that container 1 and 2 can't ping the named container now 887 _, _, err := dockerCmdWithError("exec", cid1, "ping", "-c", "1", cName) 888 assert.ErrorContains(c, err, "") 889 _, _, err = dockerCmdWithError("exec", cid2, "ping", "-c", "1", cName) 890 assert.ErrorContains(c, err, "") 891 } 892 893 func (s *DockerNetworkSuite) TestDockerNetworkLinkOnDefaultNetworkOnly(c *testing.T) { 894 // Legacy Link feature must work only on default network, and not across networks 895 cnt1 := "container1" 896 cnt2 := "container2" 897 network := "anotherbridge" 898 899 // Run first container on default network 900 dockerCmd(c, "run", "-d", "--name", cnt1, "busybox", "top") 901 902 // Create another network and run the second container on it 903 dockerCmd(c, "network", "create", network) 904 assertNwIsAvailable(c, network) 905 dockerCmd(c, "run", "-d", "--net", network, "--name", cnt2, "busybox", "top") 906 907 // Try launching a container on default network, linking to the first container. Must succeed 908 dockerCmd(c, "run", "-d", "--link", fmt.Sprintf("%s:%s", cnt1, cnt1), "busybox", "top") 909 910 // Try launching a container on default network, linking to the second container. Must fail 911 _, _, err := dockerCmdWithError("run", "-d", "--link", fmt.Sprintf("%s:%s", cnt2, cnt2), "busybox", "top") 912 assert.ErrorContains(c, err, "") 913 914 // Connect second container to default network. Now a container on default network can link to it 915 dockerCmd(c, "network", "connect", "bridge", cnt2) 916 dockerCmd(c, "run", "-d", "--link", fmt.Sprintf("%s:%s", cnt2, cnt2), "busybox", "top") 917 } 918 919 func (s *DockerNetworkSuite) TestDockerNetworkOverlayPortMapping(c *testing.T) { 920 testRequires(c, testEnv.IsLocalDaemon) 921 // Verify exposed ports are present in ps output when running a container on 922 // a network managed by a driver which does not provide the default gateway 923 // for the container 924 nwn := "ov" 925 ctn := "bb" 926 port1 := 80 927 port2 := 443 928 expose1 := fmt.Sprintf("--expose=%d", port1) 929 expose2 := fmt.Sprintf("--expose=%d", port2) 930 931 dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, nwn) 932 assertNwIsAvailable(c, nwn) 933 934 dockerCmd(c, "run", "-d", "--net", nwn, "--name", ctn, expose1, expose2, "busybox", "top") 935 936 // Check docker ps o/p for last created container reports the unpublished ports 937 unpPort1 := fmt.Sprintf("%d/tcp", port1) 938 unpPort2 := fmt.Sprintf("%d/tcp", port2) 939 out, _ := dockerCmd(c, "ps", "-n=1") 940 // Missing unpublished ports in docker ps output 941 assert.Assert(c, strings.Contains(out, unpPort1)) 942 // Missing unpublished ports in docker ps output 943 assert.Assert(c, strings.Contains(out, unpPort2)) 944 } 945 946 func (s *DockerNetworkSuite) TestDockerNetworkDriverUngracefulRestart(c *testing.T) { 947 testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon) 948 dnd := "dnd" 949 did := "did" 950 951 mux := http.NewServeMux() 952 server := httptest.NewServer(mux) 953 setupRemoteNetworkDrivers(c, mux, server.URL, dnd, did) 954 955 s.d.StartWithBusybox(c) 956 _, err := s.d.Cmd("network", "create", "-d", dnd, "--subnet", "1.1.1.0/24", "net1") 957 assert.NilError(c, err) 958 959 _, err = s.d.Cmd("run", "-d", "--net", "net1", "--name", "foo", "--ip", "1.1.1.10", "busybox", "top") 960 assert.NilError(c, err) 961 962 // Kill daemon and restart 963 assert.Assert(c, s.d.Kill() == nil) 964 965 server.Close() 966 967 startTime := time.Now().Unix() 968 s.d.Restart(c) 969 lapse := time.Now().Unix() - startTime 970 if lapse > 60 { 971 // In normal scenarios, daemon restart takes ~1 second. 972 // Plugin retry mechanism can delay the daemon start. systemd may not like it. 973 // Avoid accessing plugins during daemon bootup 974 c.Logf("daemon restart took too long : %d seconds", lapse) 975 } 976 977 // Restart the custom dummy plugin 978 mux = http.NewServeMux() 979 server = httptest.NewServer(mux) 980 setupRemoteNetworkDrivers(c, mux, server.URL, dnd, did) 981 982 // trying to reuse the same ip must succeed 983 _, err = s.d.Cmd("run", "-d", "--net", "net1", "--name", "bar", "--ip", "1.1.1.10", "busybox", "top") 984 assert.NilError(c, err) 985 } 986 987 func (s *DockerNetworkSuite) TestDockerNetworkMacInspect(c *testing.T) { 988 testRequires(c, testEnv.IsLocalDaemon) 989 // Verify endpoint MAC address is correctly populated in container's network settings 990 nwn := "ov" 991 ctn := "bb" 992 993 dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, nwn) 994 assertNwIsAvailable(c, nwn) 995 996 dockerCmd(c, "run", "-d", "--net", nwn, "--name", ctn, "busybox", "top") 997 998 mac := inspectField(c, ctn, "NetworkSettings.Networks."+nwn+".MacAddress") 999 assert.Equal(c, mac, "a0:b1:c2:d3:e4:f5") 1000 } 1001 1002 func (s *DockerSuite) TestInspectAPIMultipleNetworks(c *testing.T) { 1003 dockerCmd(c, "network", "create", "mybridge1") 1004 dockerCmd(c, "network", "create", "mybridge2") 1005 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 1006 id := strings.TrimSpace(out) 1007 assert.NilError(c, waitRun(id)) 1008 1009 dockerCmd(c, "network", "connect", "mybridge1", id) 1010 dockerCmd(c, "network", "connect", "mybridge2", id) 1011 1012 body := getInspectBody(c, "v1.20", id) 1013 var inspect120 v1p20.ContainerJSON 1014 err := json.Unmarshal(body, &inspect120) 1015 assert.NilError(c, err) 1016 1017 versionedIP := inspect120.NetworkSettings.IPAddress 1018 1019 body = getInspectBody(c, "v1.21", id) 1020 var inspect121 types.ContainerJSON 1021 err = json.Unmarshal(body, &inspect121) 1022 assert.NilError(c, err) 1023 assert.Equal(c, len(inspect121.NetworkSettings.Networks), 3) 1024 1025 bridge := inspect121.NetworkSettings.Networks["bridge"] 1026 assert.Equal(c, bridge.IPAddress, versionedIP) 1027 assert.Equal(c, bridge.IPAddress, inspect121.NetworkSettings.IPAddress) 1028 } 1029 1030 func connectContainerToNetworks(c *testing.T, d *daemon.Daemon, cName string, nws []string) { 1031 // Run a container on the default network 1032 out, err := d.Cmd("run", "-d", "--name", cName, "busybox", "top") 1033 assert.NilError(c, err, out) 1034 1035 // Attach the container to other networks 1036 for _, nw := range nws { 1037 out, err = d.Cmd("network", "create", nw) 1038 assert.NilError(c, err, out) 1039 out, err = d.Cmd("network", "connect", nw, cName) 1040 assert.NilError(c, err, out) 1041 } 1042 } 1043 1044 func verifyContainerIsConnectedToNetworks(c *testing.T, d *daemon.Daemon, cName string, nws []string) { 1045 // Verify container is connected to all the networks 1046 for _, nw := range nws { 1047 out, err := d.Cmd("inspect", "-f", fmt.Sprintf("{{.NetworkSettings.Networks.%s}}", nw), cName) 1048 assert.NilError(c, err, out) 1049 assert.Assert(c, out != "<no value>\n") 1050 } 1051 } 1052 1053 func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksGracefulDaemonRestart(c *testing.T) { 1054 testRequires(c, testEnv.IsLocalDaemon) 1055 cName := "bb" 1056 nwList := []string{"nw1", "nw2", "nw3"} 1057 1058 s.d.StartWithBusybox(c) 1059 1060 connectContainerToNetworks(c, s.d, cName, nwList) 1061 verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList) 1062 1063 // Reload daemon 1064 s.d.Restart(c) 1065 1066 _, err := s.d.Cmd("start", cName) 1067 assert.NilError(c, err) 1068 1069 verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList) 1070 } 1071 1072 func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksUngracefulDaemonRestart(c *testing.T) { 1073 testRequires(c, testEnv.IsLocalDaemon) 1074 cName := "cc" 1075 nwList := []string{"nw1", "nw2", "nw3"} 1076 1077 s.d.StartWithBusybox(c) 1078 1079 connectContainerToNetworks(c, s.d, cName, nwList) 1080 verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList) 1081 1082 // Kill daemon and restart 1083 assert.Assert(c, s.d.Kill() == nil) 1084 s.d.Restart(c) 1085 1086 // Restart container 1087 _, err := s.d.Cmd("start", cName) 1088 assert.NilError(c, err) 1089 1090 verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList) 1091 } 1092 1093 func (s *DockerNetworkSuite) TestDockerNetworkRunNetByID(c *testing.T) { 1094 out, _ := dockerCmd(c, "network", "create", "one") 1095 containerOut, _, err := dockerCmdWithError("run", "-d", "--net", strings.TrimSpace(out), "busybox", "top") 1096 assert.Assert(c, err == nil, containerOut) 1097 } 1098 1099 func (s *DockerNetworkSuite) TestDockerNetworkHostModeUngracefulDaemonRestart(c *testing.T) { 1100 testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon) 1101 s.d.StartWithBusybox(c) 1102 1103 // Run a few containers on host network 1104 for i := 0; i < 10; i++ { 1105 cName := fmt.Sprintf("hostc-%d", i) 1106 out, err := s.d.Cmd("run", "-d", "--name", cName, "--net=host", "--restart=always", "busybox", "top") 1107 assert.NilError(c, err, out) 1108 1109 // verify container has finished starting before killing daemon 1110 err = s.d.WaitRun(cName) 1111 assert.NilError(c, err) 1112 } 1113 1114 // Kill daemon ungracefully and restart 1115 assert.Assert(c, s.d.Kill() == nil) 1116 s.d.Restart(c) 1117 1118 // make sure all the containers are up and running 1119 for i := 0; i < 10; i++ { 1120 err := s.d.WaitRun(fmt.Sprintf("hostc-%d", i)) 1121 assert.NilError(c, err) 1122 } 1123 } 1124 1125 func (s *DockerNetworkSuite) TestDockerNetworkConnectToHostFromOtherNetwork(c *testing.T) { 1126 dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top") 1127 assert.Assert(c, waitRun("container1") == nil) 1128 dockerCmd(c, "network", "disconnect", "bridge", "container1") 1129 out, _, err := dockerCmdWithError("network", "connect", "host", "container1") 1130 assert.ErrorContains(c, err, "", out) 1131 assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetwork.Error())) 1132 } 1133 1134 func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromHost(c *testing.T) { 1135 dockerCmd(c, "run", "-d", "--name", "container1", "--net=host", "busybox", "top") 1136 assert.Assert(c, waitRun("container1") == nil) 1137 out, _, err := dockerCmdWithError("network", "disconnect", "host", "container1") 1138 assert.Assert(c, err != nil, "Should err out disconnect from host") 1139 assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetwork.Error())) 1140 } 1141 1142 func (s *DockerNetworkSuite) TestDockerNetworkConnectWithPortMapping(c *testing.T) { 1143 testRequires(c, NotArm) 1144 dockerCmd(c, "network", "create", "test1") 1145 dockerCmd(c, "run", "-d", "--name", "c1", "-p", "5000:5000", "busybox", "top") 1146 assert.Assert(c, waitRun("c1") == nil) 1147 dockerCmd(c, "network", "connect", "test1", "c1") 1148 } 1149 1150 func verifyPortMap(c *testing.T, container, port, originalMapping string, mustBeEqual bool) { 1151 currentMapping, _ := dockerCmd(c, "port", container, port) 1152 if mustBeEqual { 1153 assert.Equal(c, currentMapping, originalMapping) 1154 } else { 1155 assert.Assert(c, currentMapping != originalMapping) 1156 } 1157 } 1158 1159 func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectWithPortMapping(c *testing.T) { 1160 // Connect and disconnect a container with explicit and non-explicit 1161 // host port mapping to/from networks which do cause and do not cause 1162 // the container default gateway to change, and verify docker port cmd 1163 // returns congruent information 1164 testRequires(c, NotArm) 1165 cnt := "c1" 1166 dockerCmd(c, "network", "create", "aaa") 1167 dockerCmd(c, "network", "create", "ccc") 1168 1169 dockerCmd(c, "run", "-d", "--name", cnt, "-p", "9000:90", "-p", "70", "busybox", "top") 1170 assert.Assert(c, waitRun(cnt) == nil) 1171 curPortMap, _ := dockerCmd(c, "port", cnt, "70") 1172 curExplPortMap, _ := dockerCmd(c, "port", cnt, "90") 1173 1174 // Connect to a network which causes the container's default gw switch 1175 dockerCmd(c, "network", "connect", "aaa", cnt) 1176 verifyPortMap(c, cnt, "70", curPortMap, false) 1177 verifyPortMap(c, cnt, "90", curExplPortMap, true) 1178 1179 // Read current mapping 1180 curPortMap, _ = dockerCmd(c, "port", cnt, "70") 1181 1182 // Disconnect from a network which causes the container's default gw switch 1183 dockerCmd(c, "network", "disconnect", "aaa", cnt) 1184 verifyPortMap(c, cnt, "70", curPortMap, false) 1185 verifyPortMap(c, cnt, "90", curExplPortMap, true) 1186 1187 // Read current mapping 1188 curPortMap, _ = dockerCmd(c, "port", cnt, "70") 1189 1190 // Connect to a network which does not cause the container's default gw switch 1191 dockerCmd(c, "network", "connect", "ccc", cnt) 1192 verifyPortMap(c, cnt, "70", curPortMap, true) 1193 verifyPortMap(c, cnt, "90", curExplPortMap, true) 1194 } 1195 1196 func (s *DockerNetworkSuite) TestDockerNetworkConnectWithMac(c *testing.T) { 1197 macAddress := "02:42:ac:11:00:02" 1198 dockerCmd(c, "network", "create", "mynetwork") 1199 dockerCmd(c, "run", "--name=test", "-d", "--mac-address", macAddress, "busybox", "top") 1200 assert.Assert(c, waitRun("test") == nil) 1201 mac1 := inspectField(c, "test", "NetworkSettings.Networks.bridge.MacAddress") 1202 assert.Equal(c, strings.TrimSpace(mac1), macAddress) 1203 dockerCmd(c, "network", "connect", "mynetwork", "test") 1204 mac2 := inspectField(c, "test", "NetworkSettings.Networks.mynetwork.MacAddress") 1205 assert.Assert(c, strings.TrimSpace(mac2) != strings.TrimSpace(mac1)) 1206 } 1207 1208 func (s *DockerNetworkSuite) TestDockerNetworkInspectCreatedContainer(c *testing.T) { 1209 dockerCmd(c, "create", "--name", "test", "busybox") 1210 networks := inspectField(c, "test", "NetworkSettings.Networks") 1211 assert.Assert(c, strings.Contains(networks, "bridge"), "Should return 'bridge' network") 1212 } 1213 1214 func (s *DockerNetworkSuite) TestDockerNetworkRestartWithMultipleNetworks(c *testing.T) { 1215 dockerCmd(c, "network", "create", "test") 1216 dockerCmd(c, "run", "--name=foo", "-d", "busybox", "top") 1217 assert.Assert(c, waitRun("foo") == nil) 1218 dockerCmd(c, "network", "connect", "test", "foo") 1219 dockerCmd(c, "restart", "foo") 1220 networks := inspectField(c, "foo", "NetworkSettings.Networks") 1221 assert.Assert(c, strings.Contains(networks, "bridge"), "Should contain 'bridge' network") 1222 assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network") 1223 } 1224 1225 func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectToStoppedContainer(c *testing.T) { 1226 testRequires(c, testEnv.IsLocalDaemon) 1227 dockerCmd(c, "network", "create", "test") 1228 dockerCmd(c, "create", "--name=foo", "busybox", "top") 1229 dockerCmd(c, "network", "connect", "test", "foo") 1230 networks := inspectField(c, "foo", "NetworkSettings.Networks") 1231 assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network") 1232 // Restart docker daemon to test the config has persisted to disk 1233 s.d.Restart(c) 1234 networks = inspectField(c, "foo", "NetworkSettings.Networks") 1235 assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network") 1236 // start the container and test if we can ping it from another container in the same network 1237 dockerCmd(c, "start", "foo") 1238 assert.Assert(c, waitRun("foo") == nil) 1239 ip := inspectField(c, "foo", "NetworkSettings.Networks.test.IPAddress") 1240 ip = strings.TrimSpace(ip) 1241 dockerCmd(c, "run", "--net=test", "busybox", "sh", "-c", fmt.Sprintf("ping -c 1 %s", ip)) 1242 1243 dockerCmd(c, "stop", "foo") 1244 1245 // Test disconnect 1246 dockerCmd(c, "network", "disconnect", "test", "foo") 1247 networks = inspectField(c, "foo", "NetworkSettings.Networks") 1248 assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network") 1249 // Restart docker daemon to test the config has persisted to disk 1250 s.d.Restart(c) 1251 networks = inspectField(c, "foo", "NetworkSettings.Networks") 1252 assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network") 1253 } 1254 1255 func (s *DockerNetworkSuite) TestDockerNetworkDisconnectContainerNonexistingNetwork(c *testing.T) { 1256 dockerCmd(c, "network", "create", "test") 1257 dockerCmd(c, "run", "--net=test", "-d", "--name=foo", "busybox", "top") 1258 networks := inspectField(c, "foo", "NetworkSettings.Networks") 1259 assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network") 1260 // Stop container and remove network 1261 dockerCmd(c, "stop", "foo") 1262 dockerCmd(c, "network", "rm", "test") 1263 1264 // Test disconnecting stopped container from nonexisting network 1265 dockerCmd(c, "network", "disconnect", "-f", "test", "foo") 1266 networks = inspectField(c, "foo", "NetworkSettings.Networks") 1267 assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network") 1268 } 1269 1270 func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *testing.T) { 1271 // create two networks 1272 dockerCmd(c, "network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "n0") 1273 assertNwIsAvailable(c, "n0") 1274 1275 dockerCmd(c, "network", "create", "--ipv6", "--subnet=172.30.0.0/16", "--ip-range=172.30.5.0/24", "--subnet=2001:db8:abcd::/64", "--ip-range=2001:db8:abcd::/80", "n1") 1276 assertNwIsAvailable(c, "n1") 1277 1278 // run a container on first network specifying the ip addresses 1279 dockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top") 1280 assert.Assert(c, waitRun("c0") == nil) 1281 verifyIPAddressConfig(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988") 1282 verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988") 1283 1284 // connect the container to the second network specifying an ip addresses 1285 dockerCmd(c, "network", "connect", "--ip", "172.30.55.44", "--ip6", "2001:db8:abcd::5544", "n1", "c0") 1286 verifyIPAddressConfig(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544") 1287 verifyIPAddresses(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544") 1288 1289 // Stop and restart the container 1290 dockerCmd(c, "stop", "c0") 1291 dockerCmd(c, "start", "c0") 1292 1293 // verify requested addresses are applied and configs are still there 1294 verifyIPAddressConfig(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988") 1295 verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988") 1296 verifyIPAddressConfig(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544") 1297 verifyIPAddresses(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544") 1298 1299 // Still it should fail to connect to the default network with a specified IP (whatever ip) 1300 out, _, err := dockerCmdWithError("network", "connect", "--ip", "172.21.55.44", "bridge", "c0") 1301 assert.Assert(c, err != nil, "out: %s", out) 1302 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndIP.Error())) 1303 } 1304 1305 func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIPStoppedContainer(c *testing.T) { 1306 // create a container 1307 dockerCmd(c, "create", "--name", "c0", "busybox", "top") 1308 1309 // create a network 1310 dockerCmd(c, "network", "create", "--ipv6", "--subnet=172.30.0.0/16", "--subnet=2001:db8:abcd::/64", "n0") 1311 assertNwIsAvailable(c, "n0") 1312 1313 // connect the container to the network specifying an ip addresses 1314 dockerCmd(c, "network", "connect", "--ip", "172.30.55.44", "--ip6", "2001:db8:abcd::5544", "n0", "c0") 1315 verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544") 1316 1317 // start the container, verify config has not changed and ip addresses are assigned 1318 dockerCmd(c, "start", "c0") 1319 assert.Assert(c, waitRun("c0") == nil) 1320 verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544") 1321 verifyIPAddresses(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544") 1322 1323 // stop the container and check ip config has not changed 1324 dockerCmd(c, "stop", "c0") 1325 verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544") 1326 } 1327 1328 func (s *DockerNetworkSuite) TestDockerNetworkUnsupportedRequiredIP(c *testing.T) { 1329 // requested IP is not supported on predefined networks 1330 for _, mode := range []string{"none", "host", "bridge", "default"} { 1331 checkUnsupportedNetworkAndIP(c, mode) 1332 } 1333 1334 // requested IP is not supported on networks with no user defined subnets 1335 dockerCmd(c, "network", "create", "n0") 1336 assertNwIsAvailable(c, "n0") 1337 1338 out, _, err := dockerCmdWithError("run", "-d", "--ip", "172.28.99.88", "--net", "n0", "busybox", "top") 1339 assert.Assert(c, err != nil, "out: %s", out) 1340 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error())) 1341 out, _, err = dockerCmdWithError("run", "-d", "--ip6", "2001:db8:1234::9988", "--net", "n0", "busybox", "top") 1342 assert.Assert(c, err != nil, "out: %s", out) 1343 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error())) 1344 dockerCmd(c, "network", "rm", "n0") 1345 assertNwNotAvailable(c, "n0") 1346 } 1347 1348 func checkUnsupportedNetworkAndIP(c *testing.T, nwMode string) { 1349 out, _, err := dockerCmdWithError("run", "-d", "--net", nwMode, "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top") 1350 assert.Assert(c, err != nil, "out: %s", out) 1351 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndIP.Error())) 1352 } 1353 1354 func verifyIPAddressConfig(c *testing.T, cName, nwname, ipv4, ipv6 string) { 1355 if ipv4 != "" { 1356 out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAMConfig.IPv4Address", nwname)) 1357 assert.Equal(c, strings.TrimSpace(out), ipv4) 1358 } 1359 1360 if ipv6 != "" { 1361 out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAMConfig.IPv6Address", nwname)) 1362 assert.Equal(c, strings.TrimSpace(out), ipv6) 1363 } 1364 } 1365 1366 func verifyIPAddresses(c *testing.T, cName, nwname, ipv4, ipv6 string) { 1367 out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAddress", nwname)) 1368 assert.Equal(c, strings.TrimSpace(out), ipv4) 1369 1370 out = inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.GlobalIPv6Address", nwname)) 1371 assert.Equal(c, strings.TrimSpace(out), ipv6) 1372 } 1373 1374 func (s *DockerNetworkSuite) TestDockerNetworkConnectLinkLocalIP(c *testing.T) { 1375 // create one test network 1376 dockerCmd(c, "network", "create", "--ipv6", "--subnet=2001:db8:1234::/64", "n0") 1377 assertNwIsAvailable(c, "n0") 1378 1379 // run a container with incorrect link-local address 1380 _, _, err := dockerCmdWithError("run", "--link-local-ip", "169.253.5.5", "busybox", "true") 1381 assert.ErrorContains(c, err, "") 1382 _, _, err = dockerCmdWithError("run", "--link-local-ip", "2001:db8::89", "busybox", "true") 1383 assert.ErrorContains(c, err, "") 1384 1385 // run two containers with link-local ip on the test network 1386 dockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--link-local-ip", "169.254.7.7", "--link-local-ip", "fe80::254:77", "busybox", "top") 1387 assert.Assert(c, waitRun("c0") == nil) 1388 dockerCmd(c, "run", "-d", "--name", "c1", "--net=n0", "--link-local-ip", "169.254.8.8", "--link-local-ip", "fe80::254:88", "busybox", "top") 1389 assert.Assert(c, waitRun("c1") == nil) 1390 1391 // run a container on the default network and connect it to the test network specifying a link-local address 1392 dockerCmd(c, "run", "-d", "--name", "c2", "busybox", "top") 1393 assert.Assert(c, waitRun("c2") == nil) 1394 dockerCmd(c, "network", "connect", "--link-local-ip", "169.254.9.9", "n0", "c2") 1395 1396 // verify the three containers can ping each other via the link-local addresses 1397 _, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8") 1398 assert.NilError(c, err) 1399 _, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9") 1400 assert.NilError(c, err) 1401 _, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7") 1402 assert.NilError(c, err) 1403 1404 // Stop and restart the three containers 1405 dockerCmd(c, "stop", "c0") 1406 dockerCmd(c, "stop", "c1") 1407 dockerCmd(c, "stop", "c2") 1408 dockerCmd(c, "start", "c0") 1409 dockerCmd(c, "start", "c1") 1410 dockerCmd(c, "start", "c2") 1411 1412 // verify the ping again 1413 _, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8") 1414 assert.NilError(c, err) 1415 _, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9") 1416 assert.NilError(c, err) 1417 _, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7") 1418 assert.NilError(c, err) 1419 } 1420 1421 func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectLink(c *testing.T) { 1422 testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm) 1423 dockerCmd(c, "network", "create", "-d", "bridge", "foo1") 1424 dockerCmd(c, "network", "create", "-d", "bridge", "foo2") 1425 1426 dockerCmd(c, "run", "-d", "--net=foo1", "--name=first", "busybox", "top") 1427 assert.Assert(c, waitRun("first") == nil) 1428 1429 // run a container in a user-defined network with a link for an existing container 1430 // and a link for a container that doesn't exist 1431 dockerCmd(c, "run", "-d", "--net=foo1", "--name=second", "--link=first:FirstInFoo1", 1432 "--link=third:bar", "busybox", "top") 1433 assert.Assert(c, waitRun("second") == nil) 1434 1435 // ping to first and its alias FirstInFoo1 must succeed 1436 _, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") 1437 assert.NilError(c, err) 1438 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1") 1439 assert.NilError(c, err) 1440 1441 // connect first container to foo2 network 1442 dockerCmd(c, "network", "connect", "foo2", "first") 1443 // connect second container to foo2 network with a different alias for first container 1444 dockerCmd(c, "network", "connect", "--link=first:FirstInFoo2", "foo2", "second") 1445 1446 // ping the new alias in network foo2 1447 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2") 1448 assert.NilError(c, err) 1449 1450 // disconnect first container from foo1 network 1451 dockerCmd(c, "network", "disconnect", "foo1", "first") 1452 1453 // link in foo1 network must fail 1454 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1") 1455 assert.ErrorContains(c, err, "") 1456 1457 // link in foo2 network must succeed 1458 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2") 1459 assert.NilError(c, err) 1460 } 1461 1462 func (s *DockerNetworkSuite) TestDockerNetworkDisconnectDefault(c *testing.T) { 1463 netWorkName1 := "test1" 1464 netWorkName2 := "test2" 1465 containerName := "foo" 1466 1467 dockerCmd(c, "network", "create", netWorkName1) 1468 dockerCmd(c, "network", "create", netWorkName2) 1469 dockerCmd(c, "create", "--name", containerName, "busybox", "top") 1470 dockerCmd(c, "network", "connect", netWorkName1, containerName) 1471 dockerCmd(c, "network", "connect", netWorkName2, containerName) 1472 dockerCmd(c, "network", "disconnect", "bridge", containerName) 1473 1474 dockerCmd(c, "start", containerName) 1475 assert.Assert(c, waitRun(containerName) == nil) 1476 networks := inspectField(c, containerName, "NetworkSettings.Networks") 1477 assert.Assert(c, strings.Contains(networks, netWorkName1), fmt.Sprintf("Should contain '%s' network", netWorkName1)) 1478 assert.Assert(c, strings.Contains(networks, netWorkName2), fmt.Sprintf("Should contain '%s' network", netWorkName2)) 1479 assert.Assert(c, !strings.Contains(networks, "bridge"), "Should not contain 'bridge' network") 1480 } 1481 1482 func (s *DockerNetworkSuite) TestDockerNetworkConnectWithAliasOnDefaultNetworks(c *testing.T) { 1483 testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm) 1484 1485 defaults := []string{"bridge", "host", "none"} 1486 out, _ := dockerCmd(c, "run", "-d", "--net=none", "busybox", "top") 1487 containerID := strings.TrimSpace(out) 1488 for _, net := range defaults { 1489 res, _, err := dockerCmdWithError("network", "connect", "--alias", "alias"+net, net, containerID) 1490 assert.ErrorContains(c, err, "") 1491 assert.Assert(c, strings.Contains(res, runconfig.ErrUnsupportedNetworkAndAlias.Error())) 1492 } 1493 } 1494 1495 func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectAlias(c *testing.T) { 1496 testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm) 1497 dockerCmd(c, "network", "create", "-d", "bridge", "net1") 1498 dockerCmd(c, "network", "create", "-d", "bridge", "net2") 1499 1500 cid, _ := dockerCmd(c, "run", "-d", "--net=net1", "--name=first", "--net-alias=foo", "busybox:glibc", "top") 1501 assert.Assert(c, waitRun("first") == nil) 1502 1503 dockerCmd(c, "run", "-d", "--net=net1", "--name=second", "busybox:glibc", "top") 1504 assert.Assert(c, waitRun("second") == nil) 1505 1506 // ping first container and its alias 1507 _, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") 1508 assert.NilError(c, err) 1509 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo") 1510 assert.NilError(c, err) 1511 1512 // ping first container's short-id alias 1513 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", stringid.TruncateID(cid)) 1514 assert.NilError(c, err) 1515 1516 // connect first container to net2 network 1517 dockerCmd(c, "network", "connect", "--alias=bar", "net2", "first") 1518 // connect second container to foo2 network with a different alias for first container 1519 dockerCmd(c, "network", "connect", "net2", "second") 1520 1521 // ping the new alias in network foo2 1522 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar") 1523 assert.NilError(c, err) 1524 1525 // disconnect first container from net1 network 1526 dockerCmd(c, "network", "disconnect", "net1", "first") 1527 1528 // ping to net1 scoped alias "foo" must fail 1529 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo") 1530 assert.ErrorContains(c, err, "") 1531 1532 // ping to net2 scoped alias "bar" must still succeed 1533 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar") 1534 assert.NilError(c, err) 1535 // ping to net2 scoped alias short-id must still succeed 1536 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", stringid.TruncateID(cid)) 1537 assert.NilError(c, err) 1538 1539 // verify the alias option is rejected when running on predefined network 1540 out, _, err := dockerCmdWithError("run", "--rm", "--name=any", "--net-alias=any", "busybox:glibc", "true") 1541 assert.Assert(c, err != nil, "out: %s", out) 1542 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndAlias.Error())) 1543 // verify the alias option is rejected when connecting to predefined network 1544 out, _, err = dockerCmdWithError("network", "connect", "--alias=any", "bridge", "first") 1545 assert.Assert(c, err != nil, "out: %s", out) 1546 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndAlias.Error())) 1547 } 1548 1549 func (s *DockerSuite) TestUserDefinedNetworkConnectivity(c *testing.T) { 1550 testRequires(c, DaemonIsLinux, NotUserNamespace) 1551 dockerCmd(c, "network", "create", "-d", "bridge", "br.net1") 1552 1553 dockerCmd(c, "run", "-d", "--net=br.net1", "--name=c1.net1", "busybox:glibc", "top") 1554 assert.Assert(c, waitRun("c1.net1") == nil) 1555 1556 dockerCmd(c, "run", "-d", "--net=br.net1", "--name=c2.net1", "busybox:glibc", "top") 1557 assert.Assert(c, waitRun("c2.net1") == nil) 1558 1559 // ping first container by its unqualified name 1560 _, _, err := dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1") 1561 assert.NilError(c, err) 1562 1563 // ping first container by its qualified name 1564 _, _, err = dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1.br.net1") 1565 assert.NilError(c, err) 1566 1567 // ping with first qualified name masked by an additional domain. should fail 1568 _, _, err = dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1.br.net1.google.com") 1569 assert.ErrorContains(c, err, "") 1570 } 1571 1572 func (s *DockerSuite) TestEmbeddedDNSInvalidInput(c *testing.T) { 1573 testRequires(c, DaemonIsLinux, NotUserNamespace) 1574 dockerCmd(c, "network", "create", "-d", "bridge", "nw1") 1575 1576 // Sending garbage to embedded DNS shouldn't crash the daemon 1577 dockerCmd(c, "run", "-i", "--net=nw1", "--name=c1", "debian:bullseye", "bash", "-c", "echo InvalidQuery > /dev/udp/127.0.0.11/53") 1578 } 1579 1580 func (s *DockerSuite) TestDockerNetworkConnectFailsNoInspectChange(c *testing.T) { 1581 dockerCmd(c, "run", "-d", "--name=bb", "busybox", "top") 1582 assert.Assert(c, waitRun("bb") == nil) 1583 defer dockerCmd(c, "stop", "bb") 1584 1585 ns0 := inspectField(c, "bb", "NetworkSettings.Networks.bridge") 1586 1587 // A failing redundant network connect should not alter current container's endpoint settings 1588 _, _, err := dockerCmdWithError("network", "connect", "bridge", "bb") 1589 assert.ErrorContains(c, err, "") 1590 1591 ns1 := inspectField(c, "bb", "NetworkSettings.Networks.bridge") 1592 assert.Equal(c, ns1, ns0) 1593 } 1594 1595 func (s *DockerSuite) TestDockerNetworkInternalMode(c *testing.T) { 1596 dockerCmd(c, "network", "create", "--driver=bridge", "--internal", "internal") 1597 assertNwIsAvailable(c, "internal") 1598 nr := getNetworkResource(c, "internal") 1599 assert.Assert(c, nr.Internal) 1600 1601 dockerCmd(c, "run", "-d", "--net=internal", "--name=first", "busybox:glibc", "top") 1602 assert.Assert(c, waitRun("first") == nil) 1603 dockerCmd(c, "run", "-d", "--net=internal", "--name=second", "busybox:glibc", "top") 1604 assert.Assert(c, waitRun("second") == nil) 1605 out, _, err := dockerCmdWithError("exec", "first", "ping", "-W", "4", "-c", "1", "8.8.8.8") 1606 assert.ErrorContains(c, err, "") 1607 assert.Assert(c, strings.Contains(out, "100% packet loss")) 1608 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") 1609 assert.NilError(c, err) 1610 } 1611 1612 // Test for #21401 1613 func (s *DockerNetworkSuite) TestDockerNetworkCreateDeleteSpecialCharacters(c *testing.T) { 1614 dockerCmd(c, "network", "create", "test@#$") 1615 assertNwIsAvailable(c, "test@#$") 1616 dockerCmd(c, "network", "rm", "test@#$") 1617 assertNwNotAvailable(c, "test@#$") 1618 1619 dockerCmd(c, "network", "create", "kiwl$%^") 1620 assertNwIsAvailable(c, "kiwl$%^") 1621 dockerCmd(c, "network", "rm", "kiwl$%^") 1622 assertNwNotAvailable(c, "kiwl$%^") 1623 } 1624 1625 func (s *DockerDaemonSuite) TestDaemonRestartRestoreBridgeNetwork(t *testing.T) { 1626 s.d.StartWithBusybox(t, "--live-restore") 1627 defer s.d.Stop(t) 1628 oldCon := "old" 1629 1630 _, err := s.d.Cmd("run", "-d", "--name", oldCon, "-p", "80:80", "busybox", "top") 1631 if err != nil { 1632 t.Fatal(err) 1633 } 1634 oldContainerIP, err := s.d.Cmd("inspect", "-f", "{{ .NetworkSettings.Networks.bridge.IPAddress }}", oldCon) 1635 if err != nil { 1636 t.Fatal(err) 1637 } 1638 // Kill the daemon 1639 if err := s.d.Kill(); err != nil { 1640 t.Fatal(err) 1641 } 1642 1643 // restart the daemon 1644 s.d.Start(t, "--live-restore") 1645 1646 // start a new container, the new container's ip should not be the same with 1647 // old running container. 1648 newCon := "new" 1649 _, err = s.d.Cmd("run", "-d", "--name", newCon, "busybox", "top") 1650 if err != nil { 1651 t.Fatal(err) 1652 } 1653 newContainerIP, err := s.d.Cmd("inspect", "-f", "{{ .NetworkSettings.Networks.bridge.IPAddress }}", newCon) 1654 if err != nil { 1655 t.Fatal(err) 1656 } 1657 if strings.Compare(strings.TrimSpace(oldContainerIP), strings.TrimSpace(newContainerIP)) == 0 { 1658 t.Fatalf("new container ip should not equal to old running container ip") 1659 } 1660 1661 // start a new container, the new container should ping old running container 1662 _, err = s.d.Cmd("run", "-t", "busybox", "ping", "-c", "1", oldContainerIP) 1663 if err != nil { 1664 t.Fatal(err) 1665 } 1666 1667 // start a new container, trying to publish port 80:80 should fail 1668 out, err := s.d.Cmd("run", "-p", "80:80", "-d", "busybox", "top") 1669 if err == nil || !strings.Contains(out, "Bind for 0.0.0.0:80 failed: port is already allocated") { 1670 t.Fatalf("80 port is allocated to old running container, it should failed on allocating to new container") 1671 } 1672 1673 // kill old running container and try to allocate again 1674 _, err = s.d.Cmd("kill", oldCon) 1675 if err != nil { 1676 t.Fatal(err) 1677 } 1678 id, err := s.d.Cmd("run", "-p", "80:80", "-d", "busybox", "top") 1679 if err != nil { 1680 t.Fatal(err) 1681 } 1682 1683 // Cleanup because these containers will not be shut down by daemon 1684 out, err = s.d.Cmd("stop", newCon) 1685 if err != nil { 1686 t.Fatalf("err: %v %v", err, out) 1687 } 1688 _, err = s.d.Cmd("stop", strings.TrimSpace(id)) 1689 if err != nil { 1690 t.Fatal(err) 1691 } 1692 } 1693 1694 func (s *DockerNetworkSuite) TestDockerNetworkFlagAlias(c *testing.T) { 1695 dockerCmd(c, "network", "create", "user") 1696 output, status := dockerCmd(c, "run", "--rm", "--network=user", "--network-alias=foo", "busybox", "true") 1697 assert.Equal(c, status, 0, fmt.Sprintf("unexpected status code %d (%s)", status, output)) 1698 1699 output, status, _ = dockerCmdWithError("run", "--rm", "--network=user", "--net-alias=foo", "--network-alias=bar", "busybox", "true") 1700 assert.Equal(c, status, 0, fmt.Sprintf("unexpected status code %d (%s)", status, output)) 1701 } 1702 1703 func (s *DockerNetworkSuite) TestDockerNetworkValidateIP(c *testing.T) { 1704 _, _, err := dockerCmdWithError("network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "mynet") 1705 assert.NilError(c, err) 1706 assertNwIsAvailable(c, "mynet") 1707 1708 _, _, err = dockerCmdWithError("run", "-d", "--name", "mynet0", "--net=mynet", "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top") 1709 assert.NilError(c, err) 1710 assert.Assert(c, waitRun("mynet0") == nil) 1711 verifyIPAddressConfig(c, "mynet0", "mynet", "172.28.99.88", "2001:db8:1234::9988") 1712 verifyIPAddresses(c, "mynet0", "mynet", "172.28.99.88", "2001:db8:1234::9988") 1713 1714 _, _, err = dockerCmdWithError("run", "--net=mynet", "--ip", "mynet_ip", "--ip6", "2001:db8:1234::9999", "busybox", "top") 1715 assert.ErrorContains(c, err, "invalid IPv4 address") 1716 _, _, err = dockerCmdWithError("run", "--net=mynet", "--ip", "172.28.99.99", "--ip6", "mynet_ip6", "busybox", "top") 1717 assert.ErrorContains(c, err, "invalid IPv6 address") 1718 1719 // This is a case of IPv4 address to `--ip6` 1720 _, _, err = dockerCmdWithError("run", "--net=mynet", "--ip6", "172.28.99.99", "busybox", "top") 1721 assert.ErrorContains(c, err, "invalid IPv6 address") 1722 // This is a special case of an IPv4-mapped IPv6 address 1723 _, _, err = dockerCmdWithError("run", "--net=mynet", "--ip6", "::ffff:172.28.99.99", "busybox", "top") 1724 assert.ErrorContains(c, err, "invalid IPv6 address") 1725 } 1726 1727 // Test case for 26220 1728 func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromBridge(c *testing.T) { 1729 out, _ := dockerCmd(c, "network", "inspect", "--format", "{{.Id}}", "bridge") 1730 1731 network := strings.TrimSpace(out) 1732 1733 name := "test" 1734 dockerCmd(c, "create", "--name", name, "busybox", "top") 1735 1736 _, _, err := dockerCmdWithError("network", "disconnect", network, name) 1737 assert.NilError(c, err) 1738 } 1739 1740 // TestConntrackFlowsLeak covers the failure scenario of ticket: https://github.com/docker/docker/issues/8795 1741 // Validates that conntrack is correctly cleaned once a container is destroyed 1742 func (s *DockerNetworkSuite) TestConntrackFlowsLeak(c *testing.T) { 1743 testRequires(c, IsAmd64, DaemonIsLinux, Network, testEnv.IsLocalDaemon) 1744 1745 // Create a new network 1746 cli.DockerCmd(c, "network", "create", "--subnet=192.168.10.0/24", "--gateway=192.168.10.1", "-o", "com.docker.network.bridge.host_binding_ipv4=192.168.10.1", "testbind") 1747 assertNwIsAvailable(c, "testbind") 1748 1749 // Launch the server, this will remain listening on an exposed port and reply to any request in a ping/pong fashion 1750 cmd := "while true; do echo hello | nc -w 1 -lu 8080; done" 1751 cli.DockerCmd(c, "run", "-d", "--name", "server", "--net", "testbind", "-p", "8080:8080/udp", "appropriate/nc", "sh", "-c", cmd) 1752 1753 // Launch a container client, here the objective is to create a flow that is natted in order to expose the bug 1754 cmd = "echo world | nc -q 1 -u 192.168.10.1 8080" 1755 cli.DockerCmd(c, "run", "-d", "--name", "client", "--net=host", "appropriate/nc", "sh", "-c", cmd) 1756 1757 // Get all the flows using netlink 1758 flows, err := netlink.ConntrackTableList(netlink.ConntrackTable, unix.AF_INET) 1759 assert.NilError(c, err) 1760 var flowMatch int 1761 for _, flow := range flows { 1762 // count only the flows that we are interested in, skipping others that can be laying around the host 1763 if flow.Forward.Protocol == unix.IPPROTO_UDP && 1764 flow.Forward.DstIP.Equal(net.ParseIP("192.168.10.1")) && 1765 flow.Forward.DstPort == 8080 { 1766 flowMatch++ 1767 } 1768 } 1769 // The client should have created only 1 flow 1770 assert.Equal(c, flowMatch, 1) 1771 1772 // Now delete the server, this will trigger the conntrack cleanup 1773 cli.DockerCmd(c, "rm", "-fv", "server") 1774 1775 // Fetch again all the flows and validate that there is no server flow in the conntrack laying around 1776 flows, err = netlink.ConntrackTableList(netlink.ConntrackTable, unix.AF_INET) 1777 assert.NilError(c, err) 1778 flowMatch = 0 1779 for _, flow := range flows { 1780 if flow.Forward.Protocol == unix.IPPROTO_UDP && 1781 flow.Forward.DstIP.Equal(net.ParseIP("192.168.10.1")) && 1782 flow.Forward.DstPort == 8080 { 1783 flowMatch++ 1784 } 1785 } 1786 // All the flows have to be gone 1787 assert.Equal(c, flowMatch, 0) 1788 }