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