github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/integration-cli/docker_api_network_test.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net" 7 "net/http" 8 "net/url" 9 "strings" 10 "testing" 11 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/filters" 14 "github.com/docker/docker/api/types/network" 15 "github.com/docker/docker/integration-cli/cli" 16 "github.com/docker/docker/testutil" 17 "github.com/docker/docker/testutil/request" 18 "gotest.tools/v3/assert" 19 ) 20 21 func (s *DockerAPISuite) TestAPINetworkGetDefaults(c *testing.T) { 22 testRequires(c, DaemonIsLinux) 23 // By default docker daemon creates 3 networks. check if they are present 24 defaults := []string{"bridge", "host", "none"} 25 for _, nn := range defaults { 26 assert.Assert(c, isNetworkAvailable(c, nn)) 27 } 28 } 29 30 func (s *DockerAPISuite) TestAPINetworkFilter(c *testing.T) { 31 testRequires(c, DaemonIsLinux) 32 nr := getNetworkResource(c, getNetworkIDByName(c, "bridge")) 33 assert.Equal(c, nr.Name, "bridge") 34 } 35 36 func (s *DockerAPISuite) TestAPINetworkInspectBridge(c *testing.T) { 37 testRequires(c, DaemonIsLinux) 38 // Inspect default bridge network 39 nr := getNetworkResource(c, "bridge") 40 assert.Equal(c, nr.Name, "bridge") 41 42 // run a container and attach it to the default bridge network 43 out := cli.DockerCmd(c, "run", "-d", "--name", "test", "busybox", "top").Stdout() 44 containerID := strings.TrimSpace(out) 45 containerIP := findContainerIP(c, "test", "bridge") 46 47 // inspect default bridge network again and make sure the container is connected 48 nr = getNetworkResource(c, nr.ID) 49 assert.Equal(c, nr.Driver, "bridge") 50 assert.Equal(c, nr.Scope, "local") 51 assert.Equal(c, nr.Internal, false) 52 assert.Equal(c, nr.EnableIPv6, false) 53 assert.Equal(c, nr.IPAM.Driver, "default") 54 _, ok := nr.Containers[containerID] 55 assert.Assert(c, ok) 56 57 ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) 58 assert.NilError(c, err) 59 assert.Equal(c, ip.String(), containerIP) 60 } 61 62 func (s *DockerAPISuite) TestAPINetworkInspectUserDefinedNetwork(c *testing.T) { 63 testRequires(c, DaemonIsLinux) 64 // IPAM configuration inspect 65 ipam := &network.IPAM{ 66 Driver: "default", 67 Config: []network.IPAMConfig{{Subnet: "172.28.0.0/16", IPRange: "172.28.5.0/24", Gateway: "172.28.5.254"}}, 68 } 69 config := types.NetworkCreateRequest{ 70 Name: "br0", 71 NetworkCreate: types.NetworkCreate{ 72 Driver: "bridge", 73 IPAM: ipam, 74 Options: map[string]string{"foo": "bar", "opts": "dopts"}, 75 }, 76 } 77 id0 := createNetwork(c, config, http.StatusCreated) 78 assert.Assert(c, isNetworkAvailable(c, "br0")) 79 80 nr := getNetworkResource(c, id0) 81 assert.Equal(c, len(nr.IPAM.Config), 1) 82 assert.Equal(c, nr.IPAM.Config[0].Subnet, "172.28.0.0/16") 83 assert.Equal(c, nr.IPAM.Config[0].IPRange, "172.28.5.0/24") 84 assert.Equal(c, nr.IPAM.Config[0].Gateway, "172.28.5.254") 85 assert.Equal(c, nr.Options["foo"], "bar") 86 assert.Equal(c, nr.Options["opts"], "dopts") 87 88 // delete the network and make sure it is deleted 89 deleteNetwork(c, id0, true) 90 assert.Assert(c, !isNetworkAvailable(c, "br0")) 91 } 92 93 func (s *DockerAPISuite) TestAPINetworkConnectDisconnect(c *testing.T) { 94 testRequires(c, DaemonIsLinux) 95 // Create test network 96 name := "testnetwork" 97 config := types.NetworkCreateRequest{ 98 Name: name, 99 } 100 id := createNetwork(c, config, http.StatusCreated) 101 nr := getNetworkResource(c, id) 102 assert.Equal(c, nr.Name, name) 103 assert.Equal(c, nr.ID, id) 104 assert.Equal(c, len(nr.Containers), 0) 105 106 // run a container 107 out := cli.DockerCmd(c, "run", "-d", "--name", "test", "busybox", "top").Stdout() 108 containerID := strings.TrimSpace(out) 109 110 // connect the container to the test network 111 connectNetwork(c, nr.ID, containerID) 112 113 // inspect the network to make sure container is connected 114 nr = getNetworkResource(c, nr.ID) 115 assert.Equal(c, len(nr.Containers), 1) 116 _, ok := nr.Containers[containerID] 117 assert.Assert(c, ok) 118 119 // check if container IP matches network inspect 120 ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) 121 assert.NilError(c, err) 122 containerIP := findContainerIP(c, "test", "testnetwork") 123 assert.Equal(c, ip.String(), containerIP) 124 125 // disconnect container from the network 126 disconnectNetwork(c, nr.ID, containerID) 127 nr = getNetworkResource(c, nr.ID) 128 assert.Equal(c, nr.Name, name) 129 assert.Equal(c, len(nr.Containers), 0) 130 131 // delete the network 132 deleteNetwork(c, nr.ID, true) 133 } 134 135 func (s *DockerAPISuite) TestAPINetworkIPAMMultipleBridgeNetworks(c *testing.T) { 136 testRequires(c, DaemonIsLinux) 137 // test0 bridge network 138 ipam0 := &network.IPAM{ 139 Driver: "default", 140 Config: []network.IPAMConfig{{Subnet: "192.178.0.0/16", IPRange: "192.178.128.0/17", Gateway: "192.178.138.100"}}, 141 } 142 config0 := types.NetworkCreateRequest{ 143 Name: "test0", 144 NetworkCreate: types.NetworkCreate{ 145 Driver: "bridge", 146 IPAM: ipam0, 147 }, 148 } 149 id0 := createNetwork(c, config0, http.StatusCreated) 150 assert.Assert(c, isNetworkAvailable(c, "test0")) 151 152 ipam1 := &network.IPAM{ 153 Driver: "default", 154 Config: []network.IPAMConfig{{Subnet: "192.178.128.0/17", Gateway: "192.178.128.1"}}, 155 } 156 // test1 bridge network overlaps with test0 157 config1 := types.NetworkCreateRequest{ 158 Name: "test1", 159 NetworkCreate: types.NetworkCreate{ 160 Driver: "bridge", 161 IPAM: ipam1, 162 }, 163 } 164 createNetwork(c, config1, http.StatusForbidden) 165 assert.Assert(c, !isNetworkAvailable(c, "test1")) 166 167 ipam2 := &network.IPAM{ 168 Driver: "default", 169 Config: []network.IPAMConfig{{Subnet: "192.169.0.0/16", Gateway: "192.169.100.100"}}, 170 } 171 // test2 bridge network does not overlap 172 config2 := types.NetworkCreateRequest{ 173 Name: "test2", 174 NetworkCreate: types.NetworkCreate{ 175 Driver: "bridge", 176 IPAM: ipam2, 177 }, 178 } 179 createNetwork(c, config2, http.StatusCreated) 180 assert.Assert(c, isNetworkAvailable(c, "test2")) 181 182 // remove test0 and retry to create test1 183 deleteNetwork(c, id0, true) 184 createNetwork(c, config1, http.StatusCreated) 185 assert.Assert(c, isNetworkAvailable(c, "test1")) 186 187 // for networks w/o ipam specified, docker will choose proper non-overlapping subnets 188 createNetwork(c, types.NetworkCreateRequest{Name: "test3"}, http.StatusCreated) 189 assert.Assert(c, isNetworkAvailable(c, "test3")) 190 createNetwork(c, types.NetworkCreateRequest{Name: "test4"}, http.StatusCreated) 191 assert.Assert(c, isNetworkAvailable(c, "test4")) 192 createNetwork(c, types.NetworkCreateRequest{Name: "test5"}, http.StatusCreated) 193 assert.Assert(c, isNetworkAvailable(c, "test5")) 194 195 for i := 1; i < 6; i++ { 196 deleteNetwork(c, fmt.Sprintf("test%d", i), true) 197 } 198 } 199 200 func (s *DockerAPISuite) TestAPICreateDeletePredefinedNetworks(c *testing.T) { 201 testRequires(c, DaemonIsLinux, SwarmInactive) 202 createDeletePredefinedNetwork(c, "bridge") 203 createDeletePredefinedNetwork(c, "none") 204 createDeletePredefinedNetwork(c, "host") 205 } 206 207 func createDeletePredefinedNetwork(c *testing.T, name string) { 208 // Create pre-defined network 209 config := types.NetworkCreateRequest{Name: name} 210 expectedStatus := http.StatusForbidden 211 createNetwork(c, config, expectedStatus) 212 deleteNetwork(c, name, false) 213 } 214 215 func isNetworkAvailable(c *testing.T, name string) bool { 216 resp, body, err := request.Get(testutil.GetContext(c), "/networks") 217 assert.NilError(c, err) 218 defer resp.Body.Close() 219 assert.Equal(c, resp.StatusCode, http.StatusOK) 220 221 var nJSON []types.NetworkResource 222 err = json.NewDecoder(body).Decode(&nJSON) 223 assert.NilError(c, err) 224 225 for _, n := range nJSON { 226 if n.Name == name { 227 return true 228 } 229 } 230 return false 231 } 232 233 func getNetworkIDByName(c *testing.T, name string) string { 234 filterJSON, err := filters.ToJSON(filters.NewArgs(filters.Arg("name", name))) 235 assert.NilError(c, err) 236 v := url.Values{} 237 v.Set("filters", filterJSON) 238 239 resp, body, err := request.Get(testutil.GetContext(c), "/networks?"+v.Encode()) 240 assert.Equal(c, resp.StatusCode, http.StatusOK) 241 assert.NilError(c, err) 242 243 var nJSON []types.NetworkResource 244 err = json.NewDecoder(body).Decode(&nJSON) 245 assert.NilError(c, err) 246 var res string 247 for _, n := range nJSON { 248 // Find exact match 249 if n.Name == name { 250 res = n.ID 251 } 252 } 253 assert.Assert(c, res != "") 254 255 return res 256 } 257 258 func getNetworkResource(c *testing.T, id string) *types.NetworkResource { 259 _, obj, err := request.Get(testutil.GetContext(c), "/networks/"+id) 260 assert.NilError(c, err) 261 262 nr := types.NetworkResource{} 263 err = json.NewDecoder(obj).Decode(&nr) 264 assert.NilError(c, err) 265 266 return &nr 267 } 268 269 func createNetwork(c *testing.T, config types.NetworkCreateRequest, expectedStatusCode int) string { 270 resp, body, err := request.Post(testutil.GetContext(c), "/networks/create", request.JSONBody(config)) 271 assert.NilError(c, err) 272 defer resp.Body.Close() 273 274 if expectedStatusCode >= 0 { 275 assert.Equal(c, resp.StatusCode, expectedStatusCode) 276 } else { 277 assert.Assert(c, resp.StatusCode != -expectedStatusCode) 278 } 279 280 if expectedStatusCode == http.StatusCreated || expectedStatusCode < 0 { 281 var nr types.NetworkCreateResponse 282 err = json.NewDecoder(body).Decode(&nr) 283 assert.NilError(c, err) 284 285 return nr.ID 286 } 287 return "" 288 } 289 290 func connectNetwork(c *testing.T, nid, cid string) { 291 config := types.NetworkConnect{ 292 Container: cid, 293 } 294 295 resp, _, err := request.Post(testutil.GetContext(c), "/networks/"+nid+"/connect", request.JSONBody(config)) 296 assert.Equal(c, resp.StatusCode, http.StatusOK) 297 assert.NilError(c, err) 298 } 299 300 func disconnectNetwork(c *testing.T, nid, cid string) { 301 config := types.NetworkConnect{ 302 Container: cid, 303 } 304 305 resp, _, err := request.Post(testutil.GetContext(c), "/networks/"+nid+"/disconnect", request.JSONBody(config)) 306 assert.Equal(c, resp.StatusCode, http.StatusOK) 307 assert.NilError(c, err) 308 } 309 310 func deleteNetwork(c *testing.T, id string, shouldSucceed bool) { 311 resp, _, err := request.Delete(testutil.GetContext(c), "/networks/"+id) 312 assert.NilError(c, err) 313 defer resp.Body.Close() 314 if !shouldSucceed { 315 assert.Assert(c, resp.StatusCode != http.StatusOK) 316 return 317 } 318 assert.Equal(c, resp.StatusCode, http.StatusNoContent) 319 }