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