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