github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/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 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/api/types/filters" 13 "github.com/docker/docker/api/types/network" 14 "github.com/docker/docker/api/types/versions" 15 "github.com/docker/docker/integration-cli/checker" 16 "github.com/docker/docker/internal/test/request" 17 "github.com/go-check/check" 18 ) 19 20 func (s *DockerSuite) TestAPINetworkGetDefaults(c *check.C) { 21 testRequires(c, DaemonIsLinux) 22 // By default docker daemon creates 3 networks. check if they are present 23 defaults := []string{"bridge", "host", "none"} 24 for _, nn := range defaults { 25 c.Assert(isNetworkAvailable(c, nn), checker.Equals, true) 26 } 27 } 28 29 func (s *DockerSuite) TestAPINetworkCreateCheckDuplicate(c *check.C) { 30 testRequires(c, DaemonIsLinux) 31 name := "testcheckduplicate" 32 configOnCheck := types.NetworkCreateRequest{ 33 Name: name, 34 NetworkCreate: types.NetworkCreate{ 35 CheckDuplicate: true, 36 }, 37 } 38 configNotCheck := types.NetworkCreateRequest{ 39 Name: name, 40 NetworkCreate: types.NetworkCreate{ 41 CheckDuplicate: false, 42 }, 43 } 44 45 // Creating a new network first 46 createNetwork(c, configOnCheck, http.StatusCreated) 47 c.Assert(isNetworkAvailable(c, name), checker.Equals, true) 48 49 // Creating another network with same name and CheckDuplicate must fail 50 isOlderAPI := versions.LessThan(testEnv.DaemonAPIVersion(), "1.34") 51 expectedStatus := http.StatusConflict 52 if isOlderAPI { 53 // In the early test code it uses bool value to represent 54 // whether createNetwork() is expected to fail or not. 55 // Therefore, we use negation to handle the same logic after 56 // the code was changed in https://github.com/moby/moby/pull/35030 57 // -http.StatusCreated will also be checked as NOT equal to 58 // http.StatusCreated in createNetwork() function. 59 expectedStatus = -http.StatusCreated 60 } 61 createNetwork(c, configOnCheck, expectedStatus) 62 63 // Creating another network with same name and not CheckDuplicate must succeed 64 createNetwork(c, configNotCheck, http.StatusCreated) 65 } 66 67 func (s *DockerSuite) TestAPINetworkFilter(c *check.C) { 68 testRequires(c, DaemonIsLinux) 69 nr := getNetworkResource(c, getNetworkIDByName(c, "bridge")) 70 c.Assert(nr.Name, checker.Equals, "bridge") 71 } 72 73 func (s *DockerSuite) TestAPINetworkInspectBridge(c *check.C) { 74 testRequires(c, DaemonIsLinux) 75 // Inspect default bridge network 76 nr := getNetworkResource(c, "bridge") 77 c.Assert(nr.Name, checker.Equals, "bridge") 78 79 // run a container and attach it to the default bridge network 80 out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top") 81 containerID := strings.TrimSpace(out) 82 containerIP := findContainerIP(c, "test", "bridge") 83 84 // inspect default bridge network again and make sure the container is connected 85 nr = getNetworkResource(c, nr.ID) 86 c.Assert(nr.Driver, checker.Equals, "bridge") 87 c.Assert(nr.Scope, checker.Equals, "local") 88 c.Assert(nr.Internal, checker.Equals, false) 89 c.Assert(nr.EnableIPv6, checker.Equals, false) 90 c.Assert(nr.IPAM.Driver, checker.Equals, "default") 91 c.Assert(nr.Containers[containerID], checker.NotNil) 92 93 ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) 94 c.Assert(err, checker.IsNil) 95 c.Assert(ip.String(), checker.Equals, containerIP) 96 } 97 98 func (s *DockerSuite) TestAPINetworkInspectUserDefinedNetwork(c *check.C) { 99 testRequires(c, DaemonIsLinux) 100 // IPAM configuration inspect 101 ipam := &network.IPAM{ 102 Driver: "default", 103 Config: []network.IPAMConfig{{Subnet: "172.28.0.0/16", IPRange: "172.28.5.0/24", Gateway: "172.28.5.254"}}, 104 } 105 config := types.NetworkCreateRequest{ 106 Name: "br0", 107 NetworkCreate: types.NetworkCreate{ 108 Driver: "bridge", 109 IPAM: ipam, 110 Options: map[string]string{"foo": "bar", "opts": "dopts"}, 111 }, 112 } 113 id0 := createNetwork(c, config, http.StatusCreated) 114 c.Assert(isNetworkAvailable(c, "br0"), checker.Equals, true) 115 116 nr := getNetworkResource(c, id0) 117 c.Assert(len(nr.IPAM.Config), checker.Equals, 1) 118 c.Assert(nr.IPAM.Config[0].Subnet, checker.Equals, "172.28.0.0/16") 119 c.Assert(nr.IPAM.Config[0].IPRange, checker.Equals, "172.28.5.0/24") 120 c.Assert(nr.IPAM.Config[0].Gateway, checker.Equals, "172.28.5.254") 121 c.Assert(nr.Options["foo"], checker.Equals, "bar") 122 c.Assert(nr.Options["opts"], checker.Equals, "dopts") 123 124 // delete the network and make sure it is deleted 125 deleteNetwork(c, id0, true) 126 c.Assert(isNetworkAvailable(c, "br0"), checker.Equals, false) 127 } 128 129 func (s *DockerSuite) TestAPINetworkConnectDisconnect(c *check.C) { 130 testRequires(c, DaemonIsLinux) 131 // Create test network 132 name := "testnetwork" 133 config := types.NetworkCreateRequest{ 134 Name: name, 135 } 136 id := createNetwork(c, config, http.StatusCreated) 137 nr := getNetworkResource(c, id) 138 c.Assert(nr.Name, checker.Equals, name) 139 c.Assert(nr.ID, checker.Equals, id) 140 c.Assert(len(nr.Containers), checker.Equals, 0) 141 142 // run a container 143 out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top") 144 containerID := strings.TrimSpace(out) 145 146 // connect the container to the test network 147 connectNetwork(c, nr.ID, containerID) 148 149 // inspect the network to make sure container is connected 150 nr = getNetworkResource(c, nr.ID) 151 c.Assert(len(nr.Containers), checker.Equals, 1) 152 c.Assert(nr.Containers[containerID], checker.NotNil) 153 154 // check if container IP matches network inspect 155 ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) 156 c.Assert(err, checker.IsNil) 157 containerIP := findContainerIP(c, "test", "testnetwork") 158 c.Assert(ip.String(), checker.Equals, containerIP) 159 160 // disconnect container from the network 161 disconnectNetwork(c, nr.ID, containerID) 162 nr = getNetworkResource(c, nr.ID) 163 c.Assert(nr.Name, checker.Equals, name) 164 c.Assert(len(nr.Containers), checker.Equals, 0) 165 166 // delete the network 167 deleteNetwork(c, nr.ID, true) 168 } 169 170 func (s *DockerSuite) TestAPINetworkIPAMMultipleBridgeNetworks(c *check.C) { 171 testRequires(c, DaemonIsLinux) 172 // test0 bridge network 173 ipam0 := &network.IPAM{ 174 Driver: "default", 175 Config: []network.IPAMConfig{{Subnet: "192.178.0.0/16", IPRange: "192.178.128.0/17", Gateway: "192.178.138.100"}}, 176 } 177 config0 := types.NetworkCreateRequest{ 178 Name: "test0", 179 NetworkCreate: types.NetworkCreate{ 180 Driver: "bridge", 181 IPAM: ipam0, 182 }, 183 } 184 id0 := createNetwork(c, config0, http.StatusCreated) 185 c.Assert(isNetworkAvailable(c, "test0"), checker.Equals, true) 186 187 ipam1 := &network.IPAM{ 188 Driver: "default", 189 Config: []network.IPAMConfig{{Subnet: "192.178.128.0/17", Gateway: "192.178.128.1"}}, 190 } 191 // test1 bridge network overlaps with test0 192 config1 := types.NetworkCreateRequest{ 193 Name: "test1", 194 NetworkCreate: types.NetworkCreate{ 195 Driver: "bridge", 196 IPAM: ipam1, 197 }, 198 } 199 if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") { 200 createNetwork(c, config1, http.StatusInternalServerError) 201 } else { 202 createNetwork(c, config1, http.StatusForbidden) 203 } 204 c.Assert(isNetworkAvailable(c, "test1"), checker.Equals, false) 205 206 ipam2 := &network.IPAM{ 207 Driver: "default", 208 Config: []network.IPAMConfig{{Subnet: "192.169.0.0/16", Gateway: "192.169.100.100"}}, 209 } 210 // test2 bridge network does not overlap 211 config2 := types.NetworkCreateRequest{ 212 Name: "test2", 213 NetworkCreate: types.NetworkCreate{ 214 Driver: "bridge", 215 IPAM: ipam2, 216 }, 217 } 218 createNetwork(c, config2, http.StatusCreated) 219 c.Assert(isNetworkAvailable(c, "test2"), checker.Equals, true) 220 221 // remove test0 and retry to create test1 222 deleteNetwork(c, id0, true) 223 createNetwork(c, config1, http.StatusCreated) 224 c.Assert(isNetworkAvailable(c, "test1"), checker.Equals, true) 225 226 // for networks w/o ipam specified, docker will choose proper non-overlapping subnets 227 createNetwork(c, types.NetworkCreateRequest{Name: "test3"}, http.StatusCreated) 228 c.Assert(isNetworkAvailable(c, "test3"), checker.Equals, true) 229 createNetwork(c, types.NetworkCreateRequest{Name: "test4"}, http.StatusCreated) 230 c.Assert(isNetworkAvailable(c, "test4"), checker.Equals, true) 231 createNetwork(c, types.NetworkCreateRequest{Name: "test5"}, http.StatusCreated) 232 c.Assert(isNetworkAvailable(c, "test5"), checker.Equals, true) 233 234 for i := 1; i < 6; i++ { 235 deleteNetwork(c, fmt.Sprintf("test%d", i), true) 236 } 237 } 238 239 func (s *DockerSuite) TestAPICreateDeletePredefinedNetworks(c *check.C) { 240 testRequires(c, DaemonIsLinux, SwarmInactive) 241 createDeletePredefinedNetwork(c, "bridge") 242 createDeletePredefinedNetwork(c, "none") 243 createDeletePredefinedNetwork(c, "host") 244 } 245 246 func createDeletePredefinedNetwork(c *check.C, name string) { 247 // Create pre-defined network 248 config := types.NetworkCreateRequest{ 249 Name: name, 250 NetworkCreate: types.NetworkCreate{ 251 CheckDuplicate: true, 252 }, 253 } 254 expectedStatus := http.StatusForbidden 255 if versions.LessThan(testEnv.DaemonAPIVersion(), "1.34") { 256 // In the early test code it uses bool value to represent 257 // whether createNetwork() is expected to fail or not. 258 // Therefore, we use negation to handle the same logic after 259 // the code was changed in https://github.com/moby/moby/pull/35030 260 // -http.StatusCreated will also be checked as NOT equal to 261 // http.StatusCreated in createNetwork() function. 262 expectedStatus = -http.StatusCreated 263 } 264 createNetwork(c, config, expectedStatus) 265 deleteNetwork(c, name, false) 266 } 267 268 func isNetworkAvailable(c *check.C, name string) bool { 269 resp, body, err := request.Get("/networks") 270 c.Assert(err, checker.IsNil) 271 defer resp.Body.Close() 272 c.Assert(resp.StatusCode, checker.Equals, http.StatusOK) 273 274 var nJSON []types.NetworkResource 275 err = json.NewDecoder(body).Decode(&nJSON) 276 c.Assert(err, checker.IsNil) 277 278 for _, n := range nJSON { 279 if n.Name == name { 280 return true 281 } 282 } 283 return false 284 } 285 286 func getNetworkIDByName(c *check.C, name string) string { 287 var ( 288 v = url.Values{} 289 filterArgs = filters.NewArgs() 290 ) 291 filterArgs.Add("name", name) 292 filterJSON, err := filters.ToJSON(filterArgs) 293 c.Assert(err, checker.IsNil) 294 v.Set("filters", filterJSON) 295 296 resp, body, err := request.Get("/networks?" + v.Encode()) 297 c.Assert(resp.StatusCode, checker.Equals, http.StatusOK) 298 c.Assert(err, checker.IsNil) 299 300 var nJSON []types.NetworkResource 301 err = json.NewDecoder(body).Decode(&nJSON) 302 c.Assert(err, checker.IsNil) 303 var res string 304 for _, n := range nJSON { 305 // Find exact match 306 if n.Name == name { 307 res = n.ID 308 } 309 } 310 c.Assert(res, checker.Not(checker.Equals), "") 311 312 return res 313 } 314 315 func getNetworkResource(c *check.C, id string) *types.NetworkResource { 316 _, obj, err := request.Get("/networks/" + id) 317 c.Assert(err, checker.IsNil) 318 319 nr := types.NetworkResource{} 320 err = json.NewDecoder(obj).Decode(&nr) 321 c.Assert(err, checker.IsNil) 322 323 return &nr 324 } 325 326 func createNetwork(c *check.C, config types.NetworkCreateRequest, expectedStatusCode int) string { 327 resp, body, err := request.Post("/networks/create", request.JSONBody(config)) 328 c.Assert(err, checker.IsNil) 329 defer resp.Body.Close() 330 331 if expectedStatusCode >= 0 { 332 c.Assert(resp.StatusCode, checker.Equals, expectedStatusCode) 333 } else { 334 c.Assert(resp.StatusCode, checker.Not(checker.Equals), -expectedStatusCode) 335 } 336 337 if expectedStatusCode == http.StatusCreated || expectedStatusCode < 0 { 338 var nr types.NetworkCreateResponse 339 err = json.NewDecoder(body).Decode(&nr) 340 c.Assert(err, checker.IsNil) 341 342 return nr.ID 343 } 344 return "" 345 } 346 347 func connectNetwork(c *check.C, nid, cid string) { 348 config := types.NetworkConnect{ 349 Container: cid, 350 } 351 352 resp, _, err := request.Post("/networks/"+nid+"/connect", request.JSONBody(config)) 353 c.Assert(resp.StatusCode, checker.Equals, http.StatusOK) 354 c.Assert(err, checker.IsNil) 355 } 356 357 func disconnectNetwork(c *check.C, nid, cid string) { 358 config := types.NetworkConnect{ 359 Container: cid, 360 } 361 362 resp, _, err := request.Post("/networks/"+nid+"/disconnect", request.JSONBody(config)) 363 c.Assert(resp.StatusCode, checker.Equals, http.StatusOK) 364 c.Assert(err, checker.IsNil) 365 } 366 367 func deleteNetwork(c *check.C, id string, shouldSucceed bool) { 368 resp, _, err := request.Delete("/networks/" + id) 369 c.Assert(err, checker.IsNil) 370 defer resp.Body.Close() 371 if !shouldSucceed { 372 c.Assert(resp.StatusCode, checker.Not(checker.Equals), http.StatusOK) 373 return 374 } 375 c.Assert(resp.StatusCode, checker.Equals, http.StatusNoContent) 376 }