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