network_test.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. package network // import "github.com/docker/docker/integration/network"
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "net/http"
  8. "os/exec"
  9. "strings"
  10. "testing"
  11. "github.com/docker/docker/api/types"
  12. ntypes "github.com/docker/docker/api/types/network"
  13. "github.com/docker/docker/integration/internal/container"
  14. "github.com/docker/docker/integration/internal/network"
  15. "github.com/docker/docker/testutil/daemon"
  16. "github.com/docker/docker/testutil/request"
  17. "gotest.tools/v3/assert"
  18. is "gotest.tools/v3/assert/cmp"
  19. "gotest.tools/v3/icmd"
  20. "gotest.tools/v3/skip"
  21. )
  22. func TestRunContainerWithBridgeNone(t *testing.T) {
  23. skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
  24. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  25. skip.If(t, testEnv.IsUserNamespace)
  26. skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
  27. d := daemon.New(t)
  28. d.StartWithBusybox(t, "-b", "none")
  29. defer d.Stop(t)
  30. c := d.NewClientT(t)
  31. ctx := context.Background()
  32. id1 := container.Run(ctx, t, c)
  33. defer c.ContainerRemove(ctx, id1, types.ContainerRemoveOptions{Force: true})
  34. result, err := container.Exec(ctx, c, id1, []string{"ip", "l"})
  35. assert.NilError(t, err)
  36. assert.Check(t, is.Equal(false, strings.Contains(result.Combined(), "eth0")), "There shouldn't be eth0 in container in default(bridge) mode when bridge network is disabled")
  37. id2 := container.Run(ctx, t, c, container.WithNetworkMode("bridge"))
  38. defer c.ContainerRemove(ctx, id2, types.ContainerRemoveOptions{Force: true})
  39. result, err = container.Exec(ctx, c, id2, []string{"ip", "l"})
  40. assert.NilError(t, err)
  41. assert.Check(t, is.Equal(false, strings.Contains(result.Combined(), "eth0")), "There shouldn't be eth0 in container in bridge mode when bridge network is disabled")
  42. nsCommand := "ls -l /proc/self/ns/net | awk -F '->' '{print $2}'"
  43. cmd := exec.Command("sh", "-c", nsCommand)
  44. stdout := bytes.NewBuffer(nil)
  45. cmd.Stdout = stdout
  46. err = cmd.Run()
  47. assert.NilError(t, err, "Failed to get current process network namespace: %+v", err)
  48. id3 := container.Run(ctx, t, c, container.WithNetworkMode("host"))
  49. defer c.ContainerRemove(ctx, id3, types.ContainerRemoveOptions{Force: true})
  50. result, err = container.Exec(ctx, c, id3, []string{"sh", "-c", nsCommand})
  51. assert.NilError(t, err)
  52. assert.Check(t, is.Equal(stdout.String(), result.Combined()), "The network namespace of container should be the same with host when --net=host and bridge network is disabled")
  53. }
  54. // TestNetworkInvalidJSON tests that POST endpoints that expect a body return
  55. // the correct error when sending invalid JSON requests.
  56. func TestNetworkInvalidJSON(t *testing.T) {
  57. t.Cleanup(setupTest(t))
  58. // POST endpoints that accept / expect a JSON body;
  59. endpoints := []string{
  60. "/networks/create",
  61. "/networks/bridge/connect",
  62. "/networks/bridge/disconnect",
  63. }
  64. for _, ep := range endpoints {
  65. ep := ep
  66. t.Run(ep[1:], func(t *testing.T) {
  67. t.Parallel()
  68. t.Run("invalid content type", func(t *testing.T) {
  69. res, body, err := request.Post(ep, request.RawString("{}"), request.ContentType("text/plain"))
  70. assert.NilError(t, err)
  71. assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
  72. buf, err := request.ReadBody(body)
  73. assert.NilError(t, err)
  74. assert.Check(t, is.Contains(string(buf), "unsupported Content-Type header (text/plain): must be 'application/json'"))
  75. })
  76. t.Run("invalid JSON", func(t *testing.T) {
  77. res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
  78. assert.NilError(t, err)
  79. assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
  80. buf, err := request.ReadBody(body)
  81. assert.NilError(t, err)
  82. assert.Check(t, is.Contains(string(buf), "invalid JSON: invalid character 'i' looking for beginning of object key string"))
  83. })
  84. t.Run("extra content after JSON", func(t *testing.T) {
  85. res, body, err := request.Post(ep, request.RawString(`{} trailing content`), request.JSON)
  86. assert.NilError(t, err)
  87. assert.Check(t, is.Equal(res.StatusCode, http.StatusBadRequest))
  88. buf, err := request.ReadBody(body)
  89. assert.NilError(t, err)
  90. assert.Check(t, is.Contains(string(buf), "unexpected content after JSON"))
  91. })
  92. t.Run("empty body", func(t *testing.T) {
  93. // empty body should not produce an 500 internal server error, or
  94. // any 5XX error (this is assuming the request does not produce
  95. // an internal server error for another reason, but it shouldn't)
  96. res, _, err := request.Post(ep, request.RawString(``), request.JSON)
  97. assert.NilError(t, err)
  98. assert.Check(t, res.StatusCode < http.StatusInternalServerError)
  99. })
  100. })
  101. }
  102. }
  103. // TestNetworkList verifies that /networks returns a list of networks either
  104. // with, or without a trailing slash (/networks/). Regression test for https://github.com/moby/moby/issues/24595
  105. func TestNetworkList(t *testing.T) {
  106. t.Cleanup(setupTest(t))
  107. endpoints := []string{
  108. "/networks",
  109. "/networks/",
  110. }
  111. for _, ep := range endpoints {
  112. ep := ep
  113. t.Run(ep, func(t *testing.T) {
  114. t.Parallel()
  115. res, body, err := request.Get(ep, request.JSON)
  116. assert.NilError(t, err)
  117. assert.Equal(t, res.StatusCode, http.StatusOK)
  118. buf, err := request.ReadBody(body)
  119. assert.NilError(t, err)
  120. var nws []types.NetworkResource
  121. err = json.Unmarshal(buf, &nws)
  122. assert.NilError(t, err)
  123. assert.Assert(t, len(nws) > 0)
  124. })
  125. }
  126. }
  127. func TestHostIPv4BridgeLabel(t *testing.T) {
  128. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  129. skip.If(t, testEnv.IsRemoteDaemon)
  130. skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
  131. d := daemon.New(t)
  132. d.Start(t)
  133. defer d.Stop(t)
  134. c := d.NewClientT(t)
  135. defer c.Close()
  136. ctx := context.Background()
  137. ipv4SNATAddr := "172.0.0.172"
  138. // Create a bridge network with --opt com.docker.network.host_ipv4=172.0.0.172
  139. bridgeName := "hostIPv4Bridge"
  140. network.CreateNoError(ctx, t, c, bridgeName,
  141. network.WithDriver("bridge"),
  142. network.WithOption("com.docker.network.host_ipv4", ipv4SNATAddr),
  143. network.WithOption("com.docker.network.bridge.name", bridgeName),
  144. )
  145. out, err := c.NetworkInspect(ctx, bridgeName, types.NetworkInspectOptions{Verbose: true})
  146. assert.NilError(t, err)
  147. assert.Assert(t, len(out.IPAM.Config) > 0)
  148. // Make sure the SNAT rule exists
  149. icmd.RunCommand("iptables", "-t", "nat", "-C", "POSTROUTING", "-s", out.IPAM.Config[0].Subnet, "!", "-o", bridgeName, "-j", "SNAT", "--to-source", ipv4SNATAddr).Assert(t, icmd.Success)
  150. }
  151. func TestDefaultNetworkOpts(t *testing.T) {
  152. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  153. skip.If(t, testEnv.IsRemoteDaemon)
  154. skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
  155. tests := []struct {
  156. name string
  157. mtu int
  158. configFrom bool
  159. args []string
  160. }{
  161. {
  162. name: "default value",
  163. mtu: 1500,
  164. args: []string{},
  165. },
  166. {
  167. name: "cmdline value",
  168. mtu: 1234,
  169. args: []string{"--default-network-opt", "bridge=com.docker.network.driver.mtu=1234"},
  170. },
  171. {
  172. name: "config-from value",
  173. configFrom: true,
  174. mtu: 1233,
  175. args: []string{"--default-network-opt", "bridge=com.docker.network.driver.mtu=1234"},
  176. },
  177. }
  178. for _, tc := range tests {
  179. tc := tc
  180. t.Run(tc.name, func(t *testing.T) {
  181. d := daemon.New(t)
  182. d.StartWithBusybox(t, tc.args...)
  183. defer d.Stop(t)
  184. c := d.NewClientT(t)
  185. defer c.Close()
  186. ctx := context.Background()
  187. if tc.configFrom {
  188. // Create a new network config
  189. network.CreateNoError(ctx, t, c, "from-net", func(create *types.NetworkCreate) {
  190. create.ConfigOnly = true
  191. create.Options = map[string]string{
  192. "com.docker.network.driver.mtu": fmt.Sprint(tc.mtu),
  193. }
  194. })
  195. defer c.NetworkRemove(ctx, "from-net")
  196. }
  197. // Create a new network
  198. networkName := "testnet"
  199. network.CreateNoError(ctx, t, c, networkName, func(create *types.NetworkCreate) {
  200. if tc.configFrom {
  201. create.ConfigFrom = &ntypes.ConfigReference{
  202. Network: "from-net",
  203. }
  204. }
  205. })
  206. defer c.NetworkRemove(ctx, networkName)
  207. // Start a container to inspect the MTU of its network interface
  208. id1 := container.Run(ctx, t, c, container.WithNetworkMode(networkName))
  209. defer c.ContainerRemove(ctx, id1, types.ContainerRemoveOptions{Force: true})
  210. result, err := container.Exec(ctx, c, id1, []string{"ip", "l", "show", "eth0"})
  211. assert.NilError(t, err)
  212. assert.Check(t, is.Contains(result.Combined(), fmt.Sprintf(" mtu %d ", tc.mtu)), "Network MTU should have been set to %d", tc.mtu)
  213. })
  214. }
  215. }