github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/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/Prakhar-Agarwal-byte/moby/api/types" 18 "github.com/Prakhar-Agarwal-byte/moby/api/types/versions/v1p20" 19 "github.com/Prakhar-Agarwal-byte/moby/integration-cli/cli" 20 "github.com/Prakhar-Agarwal-byte/moby/integration-cli/daemon" 21 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/driverapi" 22 remoteapi "github.com/Prakhar-Agarwal-byte/moby/libnetwork/drivers/remote/api" 23 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/ipamapi" 24 remoteipam "github.com/Prakhar-Agarwal-byte/moby/libnetwork/ipams/remote/api" 25 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/netlabel" 26 "github.com/Prakhar-Agarwal-byte/moby/pkg/plugins" 27 "github.com/Prakhar-Agarwal-byte/moby/pkg/stringid" 28 "github.com/Prakhar-Agarwal-byte/moby/runconfig" 29 "github.com/Prakhar-Agarwal-byte/moby/testutil" 30 testdaemon "github.com/Prakhar-Agarwal-byte/moby/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 body = getInspectBody(c, "v1.21", id) 1026 var inspect121 types.ContainerJSON 1027 err = json.Unmarshal(body, &inspect121) 1028 assert.NilError(c, err) 1029 assert.Equal(c, len(inspect121.NetworkSettings.Networks), 3) 1030 1031 bridge := inspect121.NetworkSettings.Networks["bridge"] 1032 assert.Equal(c, bridge.IPAddress, versionedIP) 1033 assert.Equal(c, bridge.IPAddress, inspect121.NetworkSettings.IPAddress) 1034 } 1035 1036 func connectContainerToNetworks(c *testing.T, d *daemon.Daemon, cName string, nws []string) { 1037 // Run a container on the default network 1038 out, err := d.Cmd("run", "-d", "--name", cName, "busybox", "top") 1039 assert.NilError(c, err, out) 1040 1041 // Attach the container to other networks 1042 for _, nw := range nws { 1043 out, err = d.Cmd("network", "create", nw) 1044 assert.NilError(c, err, out) 1045 out, err = d.Cmd("network", "connect", nw, cName) 1046 assert.NilError(c, err, out) 1047 } 1048 } 1049 1050 func verifyContainerIsConnectedToNetworks(c *testing.T, d *daemon.Daemon, cName string, nws []string) { 1051 // Verify container is connected to all the networks 1052 for _, nw := range nws { 1053 out, err := d.Cmd("inspect", "-f", fmt.Sprintf("{{.NetworkSettings.Networks.%s}}", nw), cName) 1054 assert.NilError(c, err, out) 1055 assert.Assert(c, out != "<no value>\n") 1056 } 1057 } 1058 1059 func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksGracefulDaemonRestart(c *testing.T) { 1060 testRequires(c, testEnv.IsLocalDaemon) 1061 ctx := testutil.GetContext(c) 1062 cName := "bb" 1063 nwList := []string{"nw1", "nw2", "nw3"} 1064 1065 s.d.StartWithBusybox(ctx, c) 1066 1067 connectContainerToNetworks(c, s.d, cName, nwList) 1068 verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList) 1069 1070 // Reload daemon 1071 s.d.Restart(c) 1072 1073 _, err := s.d.Cmd("start", cName) 1074 assert.NilError(c, err) 1075 1076 verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList) 1077 } 1078 1079 func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksUngracefulDaemonRestart(c *testing.T) { 1080 testRequires(c, testEnv.IsLocalDaemon) 1081 ctx := testutil.GetContext(c) 1082 cName := "cc" 1083 nwList := []string{"nw1", "nw2", "nw3"} 1084 1085 s.d.StartWithBusybox(ctx, c) 1086 1087 connectContainerToNetworks(c, s.d, cName, nwList) 1088 verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList) 1089 1090 // Kill daemon and restart 1091 assert.Assert(c, s.d.Kill() == nil) 1092 s.d.Restart(c) 1093 1094 // Restart container 1095 _, err := s.d.Cmd("start", cName) 1096 assert.NilError(c, err) 1097 1098 verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList) 1099 } 1100 1101 func (s *DockerNetworkSuite) TestDockerNetworkRunNetByID(c *testing.T) { 1102 out := cli.DockerCmd(c, "network", "create", "one").Stdout() 1103 containerOut, _, err := dockerCmdWithError("run", "-d", "--net", strings.TrimSpace(out), "busybox", "top") 1104 assert.Assert(c, err == nil, containerOut) 1105 } 1106 1107 func (s *DockerNetworkSuite) TestDockerNetworkHostModeUngracefulDaemonRestart(c *testing.T) { 1108 testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon) 1109 ctx := testutil.GetContext(c) 1110 s.d.StartWithBusybox(ctx, c) 1111 1112 // Run a few containers on host network 1113 for i := 0; i < 10; i++ { 1114 cName := fmt.Sprintf("hostc-%d", i) 1115 out, err := s.d.Cmd("run", "-d", "--name", cName, "--net=host", "--restart=always", "busybox", "top") 1116 assert.NilError(c, err, out) 1117 1118 // verify container has finished starting before killing daemon 1119 err = s.d.WaitRun(cName) 1120 assert.NilError(c, err) 1121 } 1122 1123 // Kill daemon ungracefully and restart 1124 assert.Assert(c, s.d.Kill() == nil) 1125 s.d.Restart(c) 1126 1127 // make sure all the containers are up and running 1128 for i := 0; i < 10; i++ { 1129 err := s.d.WaitRun(fmt.Sprintf("hostc-%d", i)) 1130 assert.NilError(c, err) 1131 } 1132 } 1133 1134 func (s *DockerNetworkSuite) TestDockerNetworkConnectToHostFromOtherNetwork(c *testing.T) { 1135 cli.DockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top") 1136 cli.WaitRun(c, "container1") 1137 cli.DockerCmd(c, "network", "disconnect", "bridge", "container1") 1138 out, _, err := dockerCmdWithError("network", "connect", "host", "container1") 1139 assert.ErrorContains(c, err, "", out) 1140 assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetwork.Error())) 1141 } 1142 1143 func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromHost(c *testing.T) { 1144 cli.DockerCmd(c, "run", "-d", "--name", "container1", "--net=host", "busybox", "top") 1145 cli.WaitRun(c, "container1") 1146 out, _, err := dockerCmdWithError("network", "disconnect", "host", "container1") 1147 assert.Assert(c, err != nil, "Should err out disconnect from host") 1148 assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetwork.Error())) 1149 } 1150 1151 func (s *DockerNetworkSuite) TestDockerNetworkConnectWithPortMapping(c *testing.T) { 1152 cli.DockerCmd(c, "network", "create", "test1") 1153 cli.DockerCmd(c, "run", "-d", "--name", "c1", "-p", "5000:5000", "busybox", "top") 1154 cli.WaitRun(c, "c1") 1155 cli.DockerCmd(c, "network", "connect", "test1", "c1") 1156 } 1157 1158 func verifyPortMap(c *testing.T, container, port, originalMapping string, mustBeEqual bool) { 1159 currentMapping := cli.DockerCmd(c, "port", container, port).Stdout() 1160 if mustBeEqual { 1161 assert.Equal(c, currentMapping, originalMapping) 1162 } else { 1163 assert.Assert(c, currentMapping != originalMapping) 1164 } 1165 } 1166 1167 func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectWithPortMapping(c *testing.T) { 1168 // Connect and disconnect a container with explicit and non-explicit 1169 // host port mapping to/from networks which do cause and do not cause 1170 // the container default gateway to change, and verify docker port cmd 1171 // returns congruent information 1172 cnt := "c1" 1173 cli.DockerCmd(c, "network", "create", "aaa") 1174 cli.DockerCmd(c, "network", "create", "ccc") 1175 1176 cli.DockerCmd(c, "run", "-d", "--name", cnt, "-p", "9000:90", "-p", "70", "busybox", "top") 1177 cli.WaitRun(c, cnt) 1178 curPortMap := cli.DockerCmd(c, "port", cnt, "70").Stdout() 1179 curExplPortMap := cli.DockerCmd(c, "port", cnt, "90").Stdout() 1180 1181 // Connect to a network which causes the container's default gw switch 1182 cli.DockerCmd(c, "network", "connect", "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 // Disconnect from a network which causes the container's default gw switch 1190 cli.DockerCmd(c, "network", "disconnect", "aaa", cnt) 1191 verifyPortMap(c, cnt, "70", curPortMap, false) 1192 verifyPortMap(c, cnt, "90", curExplPortMap, true) 1193 1194 // Read current mapping 1195 curPortMap = cli.DockerCmd(c, "port", cnt, "70").Stdout() 1196 1197 // Connect to a network which does not cause the container's default gw switch 1198 cli.DockerCmd(c, "network", "connect", "ccc", cnt) 1199 verifyPortMap(c, cnt, "70", curPortMap, true) 1200 verifyPortMap(c, cnt, "90", curExplPortMap, true) 1201 } 1202 1203 func (s *DockerNetworkSuite) TestDockerNetworkConnectWithMac(c *testing.T) { 1204 macAddress := "02:42:ac:11:00:02" 1205 cli.DockerCmd(c, "network", "create", "mynetwork") 1206 cli.DockerCmd(c, "run", "--name=test", "-d", "--mac-address", macAddress, "busybox", "top") 1207 cli.WaitRun(c, "test") 1208 mac1 := inspectField(c, "test", "NetworkSettings.Networks.bridge.MacAddress") 1209 assert.Equal(c, strings.TrimSpace(mac1), macAddress) 1210 cli.DockerCmd(c, "network", "connect", "mynetwork", "test") 1211 mac2 := inspectField(c, "test", "NetworkSettings.Networks.mynetwork.MacAddress") 1212 assert.Assert(c, strings.TrimSpace(mac2) != strings.TrimSpace(mac1)) 1213 } 1214 1215 func (s *DockerNetworkSuite) TestDockerNetworkInspectCreatedContainer(c *testing.T) { 1216 cli.DockerCmd(c, "create", "--name", "test", "busybox") 1217 networks := inspectField(c, "test", "NetworkSettings.Networks") 1218 assert.Assert(c, strings.Contains(networks, "bridge"), "Should return 'bridge' network") 1219 } 1220 1221 func (s *DockerNetworkSuite) TestDockerNetworkRestartWithMultipleNetworks(c *testing.T) { 1222 cli.DockerCmd(c, "network", "create", "test") 1223 cli.DockerCmd(c, "run", "--name=foo", "-d", "busybox", "top") 1224 cli.WaitRun(c, "foo") 1225 cli.DockerCmd(c, "network", "connect", "test", "foo") 1226 cli.DockerCmd(c, "restart", "foo") 1227 networks := inspectField(c, "foo", "NetworkSettings.Networks") 1228 assert.Assert(c, strings.Contains(networks, "bridge"), "Should contain 'bridge' network") 1229 assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network") 1230 } 1231 1232 func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectToStoppedContainer(c *testing.T) { 1233 testRequires(c, testEnv.IsLocalDaemon) 1234 cli.DockerCmd(c, "network", "create", "test") 1235 cli.DockerCmd(c, "create", "--name=foo", "busybox", "top") 1236 cli.DockerCmd(c, "network", "connect", "test", "foo") 1237 networks := inspectField(c, "foo", "NetworkSettings.Networks") 1238 assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network") 1239 // Restart docker daemon to test the config has persisted to disk 1240 s.d.Restart(c) 1241 networks = inspectField(c, "foo", "NetworkSettings.Networks") 1242 assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network") 1243 // start the container and test if we can ping it from another container in the same network 1244 cli.DockerCmd(c, "start", "foo") 1245 cli.WaitRun(c, "foo") 1246 ip := inspectField(c, "foo", "NetworkSettings.Networks.test.IPAddress") 1247 ip = strings.TrimSpace(ip) 1248 cli.DockerCmd(c, "run", "--net=test", "busybox", "sh", "-c", fmt.Sprintf("ping -c 1 %s", ip)) 1249 1250 cli.DockerCmd(c, "stop", "foo") 1251 1252 // Test disconnect 1253 cli.DockerCmd(c, "network", "disconnect", "test", "foo") 1254 networks = inspectField(c, "foo", "NetworkSettings.Networks") 1255 assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network") 1256 // Restart docker daemon to test the config has persisted to disk 1257 s.d.Restart(c) 1258 networks = inspectField(c, "foo", "NetworkSettings.Networks") 1259 assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network") 1260 } 1261 1262 func (s *DockerNetworkSuite) TestDockerNetworkDisconnectContainerNonexistingNetwork(c *testing.T) { 1263 cli.DockerCmd(c, "network", "create", "test") 1264 cli.DockerCmd(c, "run", "--net=test", "-d", "--name=foo", "busybox", "top") 1265 networks := inspectField(c, "foo", "NetworkSettings.Networks") 1266 assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network") 1267 // Stop container and remove network 1268 cli.DockerCmd(c, "stop", "foo") 1269 cli.DockerCmd(c, "network", "rm", "test") 1270 1271 // Test disconnecting stopped container from nonexisting network 1272 cli.DockerCmd(c, "network", "disconnect", "-f", "test", "foo") 1273 networks = inspectField(c, "foo", "NetworkSettings.Networks") 1274 assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network") 1275 } 1276 1277 func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *testing.T) { 1278 // create two networks 1279 cli.DockerCmd(c, "network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "n0") 1280 assertNwIsAvailable(c, "n0") 1281 1282 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") 1283 assertNwIsAvailable(c, "n1") 1284 1285 // run a container on first network specifying the ip addresses 1286 cli.DockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top") 1287 cli.WaitRun(c, "c0") 1288 verifyIPAddressConfig(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988") 1289 verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988") 1290 1291 // connect the container to the second network specifying an ip addresses 1292 cli.DockerCmd(c, "network", "connect", "--ip", "172.30.5.44", "--ip6", "2001:db8:abcd::5544", "n1", "c0") 1293 verifyIPAddressConfig(c, "c0", "n1", "172.30.5.44", "2001:db8:abcd::5544") 1294 verifyIPAddresses(c, "c0", "n1", "172.30.5.44", "2001:db8:abcd::5544") 1295 1296 // Stop and restart the container 1297 cli.DockerCmd(c, "stop", "c0") 1298 cli.DockerCmd(c, "start", "c0") 1299 1300 // verify requested addresses are applied and configs are still there 1301 verifyIPAddressConfig(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988") 1302 verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988") 1303 verifyIPAddressConfig(c, "c0", "n1", "172.30.5.44", "2001:db8:abcd::5544") 1304 verifyIPAddresses(c, "c0", "n1", "172.30.5.44", "2001:db8:abcd::5544") 1305 1306 // Still it should fail to connect to the default network with a specified IP (whatever ip) 1307 out, _, err := dockerCmdWithError("network", "connect", "--ip", "172.21.55.44", "bridge", "c0") 1308 assert.Assert(c, err != nil, "out: %s", out) 1309 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndIP.Error())) 1310 } 1311 1312 func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIPStoppedContainer(c *testing.T) { 1313 // create a container 1314 cli.DockerCmd(c, "create", "--name", "c0", "busybox", "top") 1315 1316 // create a network 1317 cli.DockerCmd(c, "network", "create", "--ipv6", "--subnet=172.30.0.0/16", "--subnet=2001:db8:abcd::/64", "n0") 1318 assertNwIsAvailable(c, "n0") 1319 1320 // connect the container to the network specifying an ip addresses 1321 cli.DockerCmd(c, "network", "connect", "--ip", "172.30.55.44", "--ip6", "2001:db8:abcd::5544", "n0", "c0") 1322 verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544") 1323 1324 // start the container, verify config has not changed and ip addresses are assigned 1325 cli.DockerCmd(c, "start", "c0") 1326 cli.WaitRun(c, "c0") 1327 verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544") 1328 verifyIPAddresses(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544") 1329 1330 // stop the container and check ip config has not changed 1331 cli.DockerCmd(c, "stop", "c0") 1332 verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544") 1333 } 1334 1335 func (s *DockerNetworkSuite) TestDockerNetworkUnsupportedRequiredIP(c *testing.T) { 1336 // requested IP is not supported on predefined networks 1337 for _, mode := range []string{"none", "host", "bridge", "default"} { 1338 checkUnsupportedNetworkAndIP(c, mode) 1339 } 1340 1341 // requested IP is not supported on networks with no user defined subnets 1342 cli.DockerCmd(c, "network", "create", "n0") 1343 assertNwIsAvailable(c, "n0") 1344 1345 out, _, err := dockerCmdWithError("run", "-d", "--ip", "172.28.99.88", "--net", "n0", "busybox", "top") 1346 assert.Assert(c, err != nil, "out: %s", out) 1347 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error())) 1348 out, _, err = dockerCmdWithError("run", "-d", "--ip6", "2001:db8:1234::9988", "--net", "n0", "busybox", "top") 1349 assert.Assert(c, err != nil, "out: %s", out) 1350 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error())) 1351 cli.DockerCmd(c, "network", "rm", "n0") 1352 assertNwNotAvailable(c, "n0") 1353 } 1354 1355 func checkUnsupportedNetworkAndIP(c *testing.T, nwMode string) { 1356 out, _, err := dockerCmdWithError("run", "-d", "--net", nwMode, "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top") 1357 assert.Assert(c, err != nil, "out: %s", out) 1358 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndIP.Error())) 1359 } 1360 1361 func verifyIPAddressConfig(c *testing.T, cName, nwname, ipv4, ipv6 string) { 1362 if ipv4 != "" { 1363 out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAMConfig.IPv4Address", nwname)) 1364 assert.Equal(c, strings.TrimSpace(out), ipv4) 1365 } 1366 1367 if ipv6 != "" { 1368 out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAMConfig.IPv6Address", nwname)) 1369 assert.Equal(c, strings.TrimSpace(out), ipv6) 1370 } 1371 } 1372 1373 func verifyIPAddresses(c *testing.T, cName, nwname, ipv4, ipv6 string) { 1374 out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAddress", nwname)) 1375 assert.Equal(c, strings.TrimSpace(out), ipv4) 1376 1377 out = inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.GlobalIPv6Address", nwname)) 1378 assert.Equal(c, strings.TrimSpace(out), ipv6) 1379 } 1380 1381 func (s *DockerNetworkSuite) TestDockerNetworkConnectLinkLocalIP(c *testing.T) { 1382 // create one test network 1383 cli.DockerCmd(c, "network", "create", "--ipv6", "--subnet=2001:db8:1234::/64", "n0") 1384 assertNwIsAvailable(c, "n0") 1385 1386 // run a container with incorrect link-local address 1387 _, _, err := dockerCmdWithError("run", "--link-local-ip", "169.253.5.5", "busybox", "true") 1388 assert.ErrorContains(c, err, "") 1389 _, _, err = dockerCmdWithError("run", "--link-local-ip", "2001:db8::89", "busybox", "true") 1390 assert.ErrorContains(c, err, "") 1391 1392 // run two containers with link-local ip on the test network 1393 cli.DockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--link-local-ip", "169.254.7.7", "--link-local-ip", "fe80::254:77", "busybox", "top") 1394 cli.WaitRun(c, "c0") 1395 cli.DockerCmd(c, "run", "-d", "--name", "c1", "--net=n0", "--link-local-ip", "169.254.8.8", "--link-local-ip", "fe80::254:88", "busybox", "top") 1396 cli.WaitRun(c, "c1") 1397 1398 // run a container on the default network and connect it to the test network specifying a link-local address 1399 cli.DockerCmd(c, "run", "-d", "--name", "c2", "busybox", "top") 1400 cli.WaitRun(c, "c2") 1401 cli.DockerCmd(c, "network", "connect", "--link-local-ip", "169.254.9.9", "n0", "c2") 1402 1403 // verify the three containers can ping each other via the link-local addresses 1404 _, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8") 1405 assert.NilError(c, err) 1406 _, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9") 1407 assert.NilError(c, err) 1408 _, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7") 1409 assert.NilError(c, err) 1410 1411 // Stop and restart the three containers 1412 cli.DockerCmd(c, "stop", "c0") 1413 cli.DockerCmd(c, "stop", "c1") 1414 cli.DockerCmd(c, "stop", "c2") 1415 cli.DockerCmd(c, "start", "c0") 1416 cli.DockerCmd(c, "start", "c1") 1417 cli.DockerCmd(c, "start", "c2") 1418 1419 // verify the ping again 1420 _, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8") 1421 assert.NilError(c, err) 1422 _, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9") 1423 assert.NilError(c, err) 1424 _, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7") 1425 assert.NilError(c, err) 1426 } 1427 1428 func (s *DockerCLINetworkSuite) TestUserDefinedNetworkConnectDisconnectLink(c *testing.T) { 1429 testRequires(c, DaemonIsLinux, NotUserNamespace) 1430 cli.DockerCmd(c, "network", "create", "-d", "bridge", "foo1") 1431 cli.DockerCmd(c, "network", "create", "-d", "bridge", "foo2") 1432 1433 cli.DockerCmd(c, "run", "-d", "--net=foo1", "--name=first", "busybox", "top") 1434 cli.WaitRun(c, "first") 1435 1436 // run a container in a user-defined network with a link for an existing container 1437 // and a link for a container that doesn't exist 1438 cli.DockerCmd(c, "run", "-d", "--net=foo1", "--name=second", "--link=first:FirstInFoo1", "--link=third:bar", "busybox", "top") 1439 cli.WaitRun(c, "second") 1440 1441 // ping to first and its alias FirstInFoo1 must succeed 1442 _, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") 1443 assert.NilError(c, err) 1444 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1") 1445 assert.NilError(c, err) 1446 1447 // connect first container to foo2 network 1448 cli.DockerCmd(c, "network", "connect", "foo2", "first") 1449 // connect second container to foo2 network with a different alias for first container 1450 cli.DockerCmd(c, "network", "connect", "--link=first:FirstInFoo2", "foo2", "second") 1451 1452 // ping the new alias in network foo2 1453 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2") 1454 assert.NilError(c, err) 1455 1456 // disconnect first container from foo1 network 1457 cli.DockerCmd(c, "network", "disconnect", "foo1", "first") 1458 1459 // link in foo1 network must fail 1460 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1") 1461 assert.ErrorContains(c, err, "") 1462 1463 // link in foo2 network must succeed 1464 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2") 1465 assert.NilError(c, err) 1466 } 1467 1468 func (s *DockerNetworkSuite) TestDockerNetworkDisconnectDefault(c *testing.T) { 1469 netWorkName1 := "test1" 1470 netWorkName2 := "test2" 1471 containerName := "foo" 1472 1473 cli.DockerCmd(c, "network", "create", netWorkName1) 1474 cli.DockerCmd(c, "network", "create", netWorkName2) 1475 cli.DockerCmd(c, "create", "--name", containerName, "busybox", "top") 1476 cli.DockerCmd(c, "network", "connect", netWorkName1, containerName) 1477 cli.DockerCmd(c, "network", "connect", netWorkName2, containerName) 1478 cli.DockerCmd(c, "network", "disconnect", "bridge", containerName) 1479 1480 cli.DockerCmd(c, "start", containerName) 1481 cli.WaitRun(c, containerName) 1482 networks := inspectField(c, containerName, "NetworkSettings.Networks") 1483 assert.Assert(c, strings.Contains(networks, netWorkName1), fmt.Sprintf("Should contain '%s' network", netWorkName1)) 1484 assert.Assert(c, strings.Contains(networks, netWorkName2), fmt.Sprintf("Should contain '%s' network", netWorkName2)) 1485 assert.Assert(c, !strings.Contains(networks, "bridge"), "Should not contain 'bridge' network") 1486 } 1487 1488 func (s *DockerNetworkSuite) TestDockerNetworkConnectWithAliasOnDefaultNetworks(c *testing.T) { 1489 testRequires(c, DaemonIsLinux, NotUserNamespace) 1490 1491 defaults := []string{"bridge", "host", "none"} 1492 out := cli.DockerCmd(c, "run", "-d", "--net=none", "busybox", "top").Stdout() 1493 containerID := strings.TrimSpace(out) 1494 for _, nw := range defaults { 1495 res, _, err := dockerCmdWithError("network", "connect", "--alias", "alias"+nw, nw, containerID) 1496 assert.ErrorContains(c, err, "") 1497 assert.Assert(c, strings.Contains(res, runconfig.ErrUnsupportedNetworkAndAlias.Error())) 1498 } 1499 } 1500 1501 func (s *DockerCLINetworkSuite) TestUserDefinedNetworkConnectDisconnectAlias(c *testing.T) { 1502 testRequires(c, DaemonIsLinux, NotUserNamespace) 1503 cli.DockerCmd(c, "network", "create", "-d", "bridge", "net1") 1504 cli.DockerCmd(c, "network", "create", "-d", "bridge", "net2") 1505 1506 cid := cli.DockerCmd(c, "run", "-d", "--net=net1", "--name=first", "--net-alias=foo", "busybox:glibc", "top").Stdout() 1507 cli.WaitRun(c, "first") 1508 1509 cli.DockerCmd(c, "run", "-d", "--net=net1", "--name=second", "busybox:glibc", "top") 1510 cli.WaitRun(c, "second") 1511 1512 // ping first container and its alias 1513 _, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") 1514 assert.NilError(c, err) 1515 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo") 1516 assert.NilError(c, err) 1517 1518 // ping first container's short-id alias 1519 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", stringid.TruncateID(cid)) 1520 assert.NilError(c, err) 1521 1522 // connect first container to net2 network 1523 cli.DockerCmd(c, "network", "connect", "--alias=bar", "net2", "first") 1524 // connect second container to foo2 network with a different alias for first container 1525 cli.DockerCmd(c, "network", "connect", "net2", "second") 1526 1527 // ping the new alias in network foo2 1528 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar") 1529 assert.NilError(c, err) 1530 1531 // disconnect first container from net1 network 1532 cli.DockerCmd(c, "network", "disconnect", "net1", "first") 1533 1534 // ping to net1 scoped alias "foo" must fail 1535 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo") 1536 assert.ErrorContains(c, err, "") 1537 1538 // ping to net2 scoped alias "bar" must still succeed 1539 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar") 1540 assert.NilError(c, err) 1541 // ping to net2 scoped alias short-id must still succeed 1542 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", stringid.TruncateID(cid)) 1543 assert.NilError(c, err) 1544 1545 // verify the alias option is rejected when running on predefined network 1546 out, _, err := dockerCmdWithError("run", "--rm", "--name=any", "--net-alias=any", "busybox:glibc", "true") 1547 assert.Assert(c, err != nil, "out: %s", out) 1548 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndAlias.Error())) 1549 // verify the alias option is rejected when connecting to predefined network 1550 out, _, err = dockerCmdWithError("network", "connect", "--alias=any", "bridge", "first") 1551 assert.Assert(c, err != nil, "out: %s", out) 1552 assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndAlias.Error())) 1553 } 1554 1555 func (s *DockerCLINetworkSuite) TestUserDefinedNetworkConnectivity(c *testing.T) { 1556 testRequires(c, DaemonIsLinux, NotUserNamespace) 1557 cli.DockerCmd(c, "network", "create", "-d", "bridge", "br.net1") 1558 1559 cli.DockerCmd(c, "run", "-d", "--net=br.net1", "--name=c1.net1", "busybox:glibc", "top") 1560 cli.WaitRun(c, "c1.net1") 1561 1562 cli.DockerCmd(c, "run", "-d", "--net=br.net1", "--name=c2.net1", "busybox:glibc", "top") 1563 cli.WaitRun(c, "c2.net1") 1564 1565 // ping first container by its unqualified name 1566 _, _, err := dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1") 1567 assert.NilError(c, err) 1568 1569 // ping first container by its qualified name 1570 _, _, err = dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1.br.net1") 1571 assert.NilError(c, err) 1572 1573 // ping with first qualified name masked by an additional domain. should fail 1574 _, _, err = dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1.br.net1.google.com") 1575 assert.ErrorContains(c, err, "") 1576 } 1577 1578 func (s *DockerCLINetworkSuite) TestEmbeddedDNSInvalidInput(c *testing.T) { 1579 testRequires(c, DaemonIsLinux, NotUserNamespace) 1580 cli.DockerCmd(c, "network", "create", "-d", "bridge", "nw1") 1581 1582 // Sending garbage to embedded DNS shouldn't crash the daemon 1583 cli.DockerCmd(c, "run", "-i", "--net=nw1", "--name=c1", "debian:bullseye-slim", "bash", "-c", "echo InvalidQuery > /dev/udp/127.0.0.11/53") 1584 } 1585 1586 func (s *DockerCLINetworkSuite) TestDockerNetworkConnectFailsNoInspectChange(c *testing.T) { 1587 cli.DockerCmd(c, "run", "-d", "--name=bb", "busybox", "top") 1588 cli.WaitRun(c, "bb") 1589 defer cli.DockerCmd(c, "stop", "bb") 1590 1591 ns0 := inspectField(c, "bb", "NetworkSettings.Networks.bridge") 1592 1593 // A failing redundant network connect should not alter current container's endpoint settings 1594 _, _, err := dockerCmdWithError("network", "connect", "bridge", "bb") 1595 assert.ErrorContains(c, err, "") 1596 1597 ns1 := inspectField(c, "bb", "NetworkSettings.Networks.bridge") 1598 assert.Equal(c, ns1, ns0) 1599 } 1600 1601 func (s *DockerCLINetworkSuite) TestDockerNetworkInternalMode(c *testing.T) { 1602 cli.DockerCmd(c, "network", "create", "--driver=bridge", "--internal", "internal") 1603 assertNwIsAvailable(c, "internal") 1604 nr := getNetworkResource(c, "internal") 1605 assert.Assert(c, nr.Internal) 1606 1607 cli.DockerCmd(c, "run", "-d", "--net=internal", "--name=first", "busybox:glibc", "top") 1608 cli.WaitRun(c, "first") 1609 cli.DockerCmd(c, "run", "-d", "--net=internal", "--name=second", "busybox:glibc", "top") 1610 cli.WaitRun(c, "second") 1611 out, _, err := dockerCmdWithError("exec", "first", "ping", "-W", "4", "-c", "1", "8.8.8.8") 1612 assert.ErrorContains(c, err, "") 1613 assert.Assert(c, is.Contains(out, "Network is unreachable")) 1614 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") 1615 assert.NilError(c, err) 1616 } 1617 1618 // Test for #21401 1619 func (s *DockerNetworkSuite) TestDockerNetworkCreateDeleteSpecialCharacters(c *testing.T) { 1620 cli.DockerCmd(c, "network", "create", "test@#$") 1621 assertNwIsAvailable(c, "test@#$") 1622 cli.DockerCmd(c, "network", "rm", "test@#$") 1623 assertNwNotAvailable(c, "test@#$") 1624 1625 cli.DockerCmd(c, "network", "create", "kiwl$%^") 1626 assertNwIsAvailable(c, "kiwl$%^") 1627 cli.DockerCmd(c, "network", "rm", "kiwl$%^") 1628 assertNwNotAvailable(c, "kiwl$%^") 1629 } 1630 1631 func (s *DockerDaemonSuite) TestDaemonRestartRestoreBridgeNetwork(t *testing.T) { 1632 ctx := testutil.GetContext(t) 1633 s.d.StartWithBusybox(ctx, t, "--live-restore") 1634 defer s.d.Stop(t) 1635 oldCon := "old" 1636 1637 _, err := s.d.Cmd("run", "-d", "--name", oldCon, "-p", "80:80", "busybox", "top") 1638 if err != nil { 1639 t.Fatal(err) 1640 } 1641 oldContainerIP, err := s.d.Cmd("inspect", "-f", "{{ .NetworkSettings.Networks.bridge.IPAddress }}", oldCon) 1642 if err != nil { 1643 t.Fatal(err) 1644 } 1645 // Kill the daemon 1646 if err := s.d.Kill(); err != nil { 1647 t.Fatal(err) 1648 } 1649 1650 // restart the daemon 1651 s.d.Start(t, "--live-restore") 1652 1653 // start a new container, the new container's ip should not be the same with 1654 // old running container. 1655 newCon := "new" 1656 _, err = s.d.Cmd("run", "-d", "--name", newCon, "busybox", "top") 1657 if err != nil { 1658 t.Fatal(err) 1659 } 1660 newContainerIP, err := s.d.Cmd("inspect", "-f", "{{ .NetworkSettings.Networks.bridge.IPAddress }}", newCon) 1661 if err != nil { 1662 t.Fatal(err) 1663 } 1664 if strings.Compare(strings.TrimSpace(oldContainerIP), strings.TrimSpace(newContainerIP)) == 0 { 1665 t.Fatalf("new container ip should not equal to old running container ip") 1666 } 1667 1668 // start a new container, the new container should ping old running container 1669 _, err = s.d.Cmd("run", "-t", "busybox", "ping", "-c", "1", oldContainerIP) 1670 if err != nil { 1671 t.Fatal(err) 1672 } 1673 1674 // start a new container, trying to publish port 80:80 should fail 1675 out, err := s.d.Cmd("run", "-p", "80:80", "-d", "busybox", "top") 1676 if err == nil || !strings.Contains(out, "Bind for 0.0.0.0:80 failed: port is already allocated") { 1677 t.Fatalf("80 port is allocated to old running container, it should failed on allocating to new container") 1678 } 1679 1680 // kill old running container and try to allocate again 1681 _, err = s.d.Cmd("kill", oldCon) 1682 if err != nil { 1683 t.Fatal(err) 1684 } 1685 id, err := s.d.Cmd("run", "-p", "80:80", "-d", "busybox", "top") 1686 if err != nil { 1687 t.Fatal(err) 1688 } 1689 1690 // Cleanup because these containers will not be shut down by daemon 1691 out, err = s.d.Cmd("stop", newCon) 1692 if err != nil { 1693 t.Fatalf("err: %v %v", err, out) 1694 } 1695 _, err = s.d.Cmd("stop", strings.TrimSpace(id)) 1696 if err != nil { 1697 t.Fatal(err) 1698 } 1699 } 1700 1701 func (s *DockerNetworkSuite) TestDockerNetworkFlagAlias(c *testing.T) { 1702 cli.DockerCmd(c, "network", "create", "user") 1703 result := cli.DockerCmd(c, "run", "--rm", "--network=user", "--network-alias=foo", "busybox", "true") 1704 assert.Equal(c, result.ExitCode, 0, fmt.Sprintf("unexpected status code %d (%s)", result.ExitCode, result.Combined())) 1705 1706 output, status, _ := dockerCmdWithError("run", "--rm", "--network=user", "--net-alias=foo", "--network-alias=bar", "busybox", "true") 1707 assert.Equal(c, status, 0, fmt.Sprintf("unexpected status code %d (%s)", status, output)) 1708 } 1709 1710 func (s *DockerNetworkSuite) TestDockerNetworkValidateIP(c *testing.T) { 1711 _, _, err := dockerCmdWithError("network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "mynet") 1712 assert.NilError(c, err) 1713 assertNwIsAvailable(c, "mynet") 1714 1715 _, _, err = dockerCmdWithError("run", "-d", "--name", "mynet0", "--net=mynet", "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top") 1716 assert.NilError(c, err) 1717 cli.WaitRun(c, "mynet0") 1718 verifyIPAddressConfig(c, "mynet0", "mynet", "172.28.99.88", "2001:db8:1234::9988") 1719 verifyIPAddresses(c, "mynet0", "mynet", "172.28.99.88", "2001:db8:1234::9988") 1720 1721 _, _, err = dockerCmdWithError("run", "--net=mynet", "--ip", "mynet_ip", "--ip6", "2001:db8:1234::9999", "busybox", "top") 1722 assert.ErrorContains(c, err, "invalid IPv4 address") 1723 _, _, err = dockerCmdWithError("run", "--net=mynet", "--ip", "172.28.99.99", "--ip6", "mynet_ip6", "busybox", "top") 1724 assert.ErrorContains(c, err, "invalid IPv6 address") 1725 1726 // This is a case of IPv4 address to `--ip6` 1727 _, _, err = dockerCmdWithError("run", "--net=mynet", "--ip6", "172.28.99.99", "busybox", "top") 1728 assert.ErrorContains(c, err, "invalid IPv6 address") 1729 // This is a special case of an IPv4-mapped IPv6 address 1730 _, _, err = dockerCmdWithError("run", "--net=mynet", "--ip6", "::ffff:172.28.99.99", "busybox", "top") 1731 assert.ErrorContains(c, err, "invalid IPv6 address") 1732 } 1733 1734 // Test case for 26220 1735 func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromBridge(c *testing.T) { 1736 out := cli.DockerCmd(c, "network", "inspect", "--format", "{{.Id}}", "bridge").Stdout() 1737 network := strings.TrimSpace(out) 1738 1739 name := "test" 1740 cli.DockerCmd(c, "create", "--name", name, "busybox", "top") 1741 1742 _, _, err := dockerCmdWithError("network", "disconnect", network, name) 1743 assert.NilError(c, err) 1744 } 1745 1746 // TestConntrackFlowsLeak covers the failure scenario of ticket: https://github.com/Prakhar-Agarwal-byte/moby/issues/8795 1747 // Validates that conntrack is correctly cleaned once a container is destroyed 1748 func (s *DockerNetworkSuite) TestConntrackFlowsLeak(c *testing.T) { 1749 testRequires(c, IsAmd64, DaemonIsLinux, Network, testEnv.IsLocalDaemon) 1750 1751 // Create a new network 1752 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") 1753 assertNwIsAvailable(c, "testbind") 1754 1755 // Launch the server, this will remain listening on an exposed port and reply to any request in a ping/pong fashion 1756 cmd := "while true; do echo hello | nc -w 1 -l -u -p 8080; done" 1757 cli.DockerCmd(c, "run", "-d", "--name", "server", "--net", "testbind", "-p", "8080:8080/udp", "busybox", "sh", "-c", cmd) 1758 1759 // Launch a container client, here the objective is to create a flow that is natted in order to expose the bug 1760 cmd = "echo world | nc -w 1 -u 192.168.10.1 8080" 1761 cli.DockerCmd(c, "run", "-d", "--name", "client", "--net=host", "busybox", "sh", "-c", cmd) 1762 1763 // Get all the flows using netlink 1764 flows, err := netlink.ConntrackTableList(netlink.ConntrackTable, unix.AF_INET) 1765 assert.NilError(c, err) 1766 var flowMatch int 1767 for _, flow := range flows { 1768 // count only the flows that we are interested in, skipping others that can be laying around the host 1769 if flow.Forward.Protocol == unix.IPPROTO_UDP && 1770 flow.Forward.DstIP.Equal(net.ParseIP("192.168.10.1")) && 1771 flow.Forward.DstPort == 8080 { 1772 flowMatch++ 1773 } 1774 } 1775 // The client should have created only 1 flow 1776 assert.Equal(c, flowMatch, 1) 1777 1778 // Now delete the server, this will trigger the conntrack cleanup 1779 cli.DockerCmd(c, "rm", "-fv", "server") 1780 1781 // Fetch again all the flows and validate that there is no server flow in the conntrack laying around 1782 flows, err = netlink.ConntrackTableList(netlink.ConntrackTable, unix.AF_INET) 1783 assert.NilError(c, err) 1784 flowMatch = 0 1785 for _, flow := range flows { 1786 if flow.Forward.Protocol == unix.IPPROTO_UDP && 1787 flow.Forward.DstIP.Equal(net.ParseIP("192.168.10.1")) && 1788 flow.Forward.DstPort == 8080 { 1789 flowMatch++ 1790 } 1791 } 1792 // All the flows have to be gone 1793 assert.Equal(c, flowMatch, 0) 1794 }