github.com/cookieai-jar/moby@v17.12.1-ce-rc2+incompatible/api/server/router/network/network_routes.go (about)

     1  package network
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"golang.org/x/net/context"
    10  
    11  	"github.com/docker/docker/api/server/httputils"
    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/libnetwork"
    17  	netconst "github.com/docker/libnetwork/datastore"
    18  	"github.com/docker/libnetwork/networkdb"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  var (
    23  	// acceptedNetworkFilters is a list of acceptable filters
    24  	acceptedNetworkFilters = map[string]bool{
    25  		"driver": true,
    26  		"type":   true,
    27  		"name":   true,
    28  		"id":     true,
    29  		"label":  true,
    30  		"scope":  true,
    31  	}
    32  )
    33  
    34  func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    35  	if err := httputils.ParseForm(r); err != nil {
    36  		return err
    37  	}
    38  
    39  	filter := r.Form.Get("filters")
    40  	netFilters, err := filters.FromJSON(filter)
    41  	if err != nil {
    42  		return err
    43  	}
    44  
    45  	if err := netFilters.Validate(acceptedNetworkFilters); err != nil {
    46  		return err
    47  	}
    48  
    49  	list := []types.NetworkResource{}
    50  
    51  	if nr, err := n.cluster.GetNetworks(); err == nil {
    52  		list = append(list, nr...)
    53  	}
    54  
    55  	// Combine the network list returned by Docker daemon if it is not already
    56  	// returned by the cluster manager
    57  SKIP:
    58  	for _, nw := range n.backend.GetNetworks() {
    59  		for _, nl := range list {
    60  			if nl.ID == nw.ID() {
    61  				continue SKIP
    62  			}
    63  		}
    64  
    65  		var nr *types.NetworkResource
    66  		// Versions < 1.28 fetches all the containers attached to a network
    67  		// in a network list api call. It is a heavy weight operation when
    68  		// run across all the networks. Starting API version 1.28, this detailed
    69  		// info is available for network specific GET API (equivalent to inspect)
    70  		if versions.LessThan(httputils.VersionFromContext(ctx), "1.28") {
    71  			nr = n.buildDetailedNetworkResources(nw, false)
    72  		} else {
    73  			nr = n.buildNetworkResource(nw)
    74  		}
    75  		list = append(list, *nr)
    76  	}
    77  
    78  	list, err = filterNetworks(list, netFilters)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	return httputils.WriteJSON(w, http.StatusOK, list)
    83  }
    84  
    85  type invalidRequestError struct {
    86  	cause error
    87  }
    88  
    89  func (e invalidRequestError) Error() string {
    90  	return e.cause.Error()
    91  }
    92  
    93  func (e invalidRequestError) InvalidParameter() {}
    94  
    95  type ambigousResultsError string
    96  
    97  func (e ambigousResultsError) Error() string {
    98  	return "network " + string(e) + " is ambiguous"
    99  }
   100  
   101  func (ambigousResultsError) InvalidParameter() {}
   102  
   103  type conflictError struct {
   104  	cause error
   105  }
   106  
   107  func (e conflictError) Error() string {
   108  	return e.cause.Error()
   109  }
   110  
   111  func (e conflictError) Cause() error {
   112  	return e.cause
   113  }
   114  
   115  func (e conflictError) Conflict() {}
   116  
   117  func nameConflict(name string) error {
   118  	return conflictError{libnetwork.NetworkNameError(name)}
   119  }
   120  
   121  func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   122  	if err := httputils.ParseForm(r); err != nil {
   123  		return err
   124  	}
   125  
   126  	term := vars["id"]
   127  	var (
   128  		verbose bool
   129  		err     error
   130  	)
   131  	if v := r.URL.Query().Get("verbose"); v != "" {
   132  		if verbose, err = strconv.ParseBool(v); err != nil {
   133  			return errors.Wrapf(invalidRequestError{err}, "invalid value for verbose: %s", v)
   134  		}
   135  	}
   136  	scope := r.URL.Query().Get("scope")
   137  
   138  	isMatchingScope := func(scope, term string) bool {
   139  		if term != "" {
   140  			return scope == term
   141  		}
   142  		return true
   143  	}
   144  
   145  	// In case multiple networks have duplicate names, return error.
   146  	// TODO (yongtang): should we wrap with version here for backward compatibility?
   147  
   148  	// First find based on full ID, return immediately once one is found.
   149  	// If a network appears both in swarm and local, assume it is in local first
   150  
   151  	// For full name and partial ID, save the result first, and process later
   152  	// in case multiple records was found based on the same term
   153  	listByFullName := map[string]types.NetworkResource{}
   154  	listByPartialID := map[string]types.NetworkResource{}
   155  
   156  	nw := n.backend.GetNetworks()
   157  	for _, network := range nw {
   158  		if network.ID() == term && isMatchingScope(network.Info().Scope(), scope) {
   159  			return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network, verbose))
   160  		}
   161  		if network.Name() == term && isMatchingScope(network.Info().Scope(), scope) {
   162  			// No need to check the ID collision here as we are still in
   163  			// local scope and the network ID is unique in this scope.
   164  			listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
   165  		}
   166  		if strings.HasPrefix(network.ID(), term) && isMatchingScope(network.Info().Scope(), scope) {
   167  			// No need to check the ID collision here as we are still in
   168  			// local scope and the network ID is unique in this scope.
   169  			listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
   170  		}
   171  	}
   172  
   173  	nwk, err := n.cluster.GetNetwork(term)
   174  	if err == nil {
   175  		// If the get network is passed with a specific network ID / partial network ID
   176  		// or if the get network was passed with a network name and scope as swarm
   177  		// return the network. Skipped using isMatchingScope because it is true if the scope
   178  		// is not set which would be case if the client API v1.30
   179  		if strings.HasPrefix(nwk.ID, term) || (netconst.SwarmScope == scope) {
   180  			// If we have a previous match "backend", return it, we need verbose when enabled
   181  			// ex: overlay/partial_ID or name/swarm_scope
   182  			if nwv, ok := listByPartialID[nwk.ID]; ok {
   183  				nwk = nwv
   184  			} else if nwv, ok := listByFullName[nwk.ID]; ok {
   185  				nwk = nwv
   186  			}
   187  			return httputils.WriteJSON(w, http.StatusOK, nwk)
   188  		}
   189  	}
   190  
   191  	nr, _ := n.cluster.GetNetworks()
   192  	for _, network := range nr {
   193  		if network.ID == term && isMatchingScope(network.Scope, scope) {
   194  			return httputils.WriteJSON(w, http.StatusOK, network)
   195  		}
   196  		if network.Name == term && isMatchingScope(network.Scope, scope) {
   197  			// Check the ID collision as we are in swarm scope here, and
   198  			// the map (of the listByFullName) may have already had a
   199  			// network with the same ID (from local scope previously)
   200  			if _, ok := listByFullName[network.ID]; !ok {
   201  				listByFullName[network.ID] = network
   202  			}
   203  		}
   204  		if strings.HasPrefix(network.ID, term) && isMatchingScope(network.Scope, scope) {
   205  			// Check the ID collision as we are in swarm scope here, and
   206  			// the map (of the listByPartialID) may have already had a
   207  			// network with the same ID (from local scope previously)
   208  			if _, ok := listByPartialID[network.ID]; !ok {
   209  				listByPartialID[network.ID] = network
   210  			}
   211  		}
   212  	}
   213  
   214  	// Find based on full name, returns true only if no duplicates
   215  	if len(listByFullName) == 1 {
   216  		for _, v := range listByFullName {
   217  			return httputils.WriteJSON(w, http.StatusOK, v)
   218  		}
   219  	}
   220  	if len(listByFullName) > 1 {
   221  		return errors.Wrapf(ambigousResultsError(term), "%d matches found based on name", len(listByFullName))
   222  	}
   223  
   224  	// Find based on partial ID, returns true only if no duplicates
   225  	if len(listByPartialID) == 1 {
   226  		for _, v := range listByPartialID {
   227  			return httputils.WriteJSON(w, http.StatusOK, v)
   228  		}
   229  	}
   230  	if len(listByPartialID) > 1 {
   231  		return errors.Wrapf(ambigousResultsError(term), "%d matches found based on ID prefix", len(listByPartialID))
   232  	}
   233  
   234  	return libnetwork.ErrNoSuchNetwork(term)
   235  }
   236  
   237  func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   238  	var create types.NetworkCreateRequest
   239  
   240  	if err := httputils.ParseForm(r); err != nil {
   241  		return err
   242  	}
   243  
   244  	if err := httputils.CheckForJSON(r); err != nil {
   245  		return err
   246  	}
   247  
   248  	if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
   249  		return err
   250  	}
   251  
   252  	if nws, err := n.cluster.GetNetworksByName(create.Name); err == nil && len(nws) > 0 {
   253  		return nameConflict(create.Name)
   254  	}
   255  
   256  	nw, err := n.backend.CreateNetwork(create)
   257  	if err != nil {
   258  		var warning string
   259  		if _, ok := err.(libnetwork.NetworkNameError); ok {
   260  			// check if user defined CheckDuplicate, if set true, return err
   261  			// otherwise prepare a warning message
   262  			if create.CheckDuplicate {
   263  				return nameConflict(create.Name)
   264  			}
   265  			warning = libnetwork.NetworkNameError(create.Name).Error()
   266  		}
   267  
   268  		if _, ok := err.(libnetwork.ManagerRedirectError); !ok {
   269  			return err
   270  		}
   271  		id, err := n.cluster.CreateNetwork(create)
   272  		if err != nil {
   273  			return err
   274  		}
   275  		nw = &types.NetworkCreateResponse{
   276  			ID:      id,
   277  			Warning: warning,
   278  		}
   279  	}
   280  
   281  	return httputils.WriteJSON(w, http.StatusCreated, nw)
   282  }
   283  
   284  func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   285  	var connect types.NetworkConnect
   286  	if err := httputils.ParseForm(r); err != nil {
   287  		return err
   288  	}
   289  
   290  	if err := httputils.CheckForJSON(r); err != nil {
   291  		return err
   292  	}
   293  
   294  	if err := json.NewDecoder(r.Body).Decode(&connect); err != nil {
   295  		return err
   296  	}
   297  
   298  	return n.backend.ConnectContainerToNetwork(connect.Container, vars["id"], connect.EndpointConfig)
   299  }
   300  
   301  func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   302  	var disconnect types.NetworkDisconnect
   303  	if err := httputils.ParseForm(r); err != nil {
   304  		return err
   305  	}
   306  
   307  	if err := httputils.CheckForJSON(r); err != nil {
   308  		return err
   309  	}
   310  
   311  	if err := json.NewDecoder(r.Body).Decode(&disconnect); err != nil {
   312  		return err
   313  	}
   314  
   315  	return n.backend.DisconnectContainerFromNetwork(disconnect.Container, vars["id"], disconnect.Force)
   316  }
   317  
   318  func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   319  	if err := httputils.ParseForm(r); err != nil {
   320  		return err
   321  	}
   322  	if _, err := n.cluster.GetNetwork(vars["id"]); err == nil {
   323  		if err = n.cluster.RemoveNetwork(vars["id"]); err != nil {
   324  			return err
   325  		}
   326  		w.WriteHeader(http.StatusNoContent)
   327  		return nil
   328  	}
   329  	if err := n.backend.DeleteNetwork(vars["id"]); err != nil {
   330  		return err
   331  	}
   332  	w.WriteHeader(http.StatusNoContent)
   333  	return nil
   334  }
   335  
   336  func (n *networkRouter) buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
   337  	r := &types.NetworkResource{}
   338  	if nw == nil {
   339  		return r
   340  	}
   341  
   342  	info := nw.Info()
   343  	r.Name = nw.Name()
   344  	r.ID = nw.ID()
   345  	r.Created = info.Created()
   346  	r.Scope = info.Scope()
   347  	r.Driver = nw.Type()
   348  	r.EnableIPv6 = info.IPv6Enabled()
   349  	r.Internal = info.Internal()
   350  	r.Attachable = info.Attachable()
   351  	r.Ingress = info.Ingress()
   352  	r.Options = info.DriverOptions()
   353  	r.Containers = make(map[string]types.EndpointResource)
   354  	buildIpamResources(r, info)
   355  	r.Labels = info.Labels()
   356  	r.ConfigOnly = info.ConfigOnly()
   357  
   358  	if cn := info.ConfigFrom(); cn != "" {
   359  		r.ConfigFrom = network.ConfigReference{Network: cn}
   360  	}
   361  
   362  	peers := info.Peers()
   363  	if len(peers) != 0 {
   364  		r.Peers = buildPeerInfoResources(peers)
   365  	}
   366  
   367  	return r
   368  }
   369  
   370  func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network, verbose bool) *types.NetworkResource {
   371  	if nw == nil {
   372  		return &types.NetworkResource{}
   373  	}
   374  
   375  	r := n.buildNetworkResource(nw)
   376  	epl := nw.Endpoints()
   377  	for _, e := range epl {
   378  		ei := e.Info()
   379  		if ei == nil {
   380  			continue
   381  		}
   382  		sb := ei.Sandbox()
   383  		tmpID := e.ID()
   384  		key := "ep-" + tmpID
   385  		if sb != nil {
   386  			key = sb.ContainerID()
   387  		}
   388  
   389  		r.Containers[key] = buildEndpointResource(tmpID, e.Name(), ei)
   390  	}
   391  	if !verbose {
   392  		return r
   393  	}
   394  	services := nw.Info().Services()
   395  	r.Services = make(map[string]network.ServiceInfo)
   396  	for name, service := range services {
   397  		tasks := []network.Task{}
   398  		for _, t := range service.Tasks {
   399  			tasks = append(tasks, network.Task{
   400  				Name:       t.Name,
   401  				EndpointID: t.EndpointID,
   402  				EndpointIP: t.EndpointIP,
   403  				Info:       t.Info,
   404  			})
   405  		}
   406  		r.Services[name] = network.ServiceInfo{
   407  			VIP:          service.VIP,
   408  			Ports:        service.Ports,
   409  			Tasks:        tasks,
   410  			LocalLBIndex: service.LocalLBIndex,
   411  		}
   412  	}
   413  	return r
   414  }
   415  
   416  func buildPeerInfoResources(peers []networkdb.PeerInfo) []network.PeerInfo {
   417  	peerInfo := make([]network.PeerInfo, 0, len(peers))
   418  	for _, peer := range peers {
   419  		peerInfo = append(peerInfo, network.PeerInfo{
   420  			Name: peer.Name,
   421  			IP:   peer.IP,
   422  		})
   423  	}
   424  	return peerInfo
   425  }
   426  
   427  func buildIpamResources(r *types.NetworkResource, nwInfo libnetwork.NetworkInfo) {
   428  	id, opts, ipv4conf, ipv6conf := nwInfo.IpamConfig()
   429  
   430  	ipv4Info, ipv6Info := nwInfo.IpamInfo()
   431  
   432  	r.IPAM.Driver = id
   433  
   434  	r.IPAM.Options = opts
   435  
   436  	r.IPAM.Config = []network.IPAMConfig{}
   437  	for _, ip4 := range ipv4conf {
   438  		if ip4.PreferredPool == "" {
   439  			continue
   440  		}
   441  		iData := network.IPAMConfig{}
   442  		iData.Subnet = ip4.PreferredPool
   443  		iData.IPRange = ip4.SubPool
   444  		iData.Gateway = ip4.Gateway
   445  		iData.AuxAddress = ip4.AuxAddresses
   446  		r.IPAM.Config = append(r.IPAM.Config, iData)
   447  	}
   448  
   449  	if len(r.IPAM.Config) == 0 {
   450  		for _, ip4Info := range ipv4Info {
   451  			iData := network.IPAMConfig{}
   452  			iData.Subnet = ip4Info.IPAMData.Pool.String()
   453  			if ip4Info.IPAMData.Gateway != nil {
   454  				iData.Gateway = ip4Info.IPAMData.Gateway.IP.String()
   455  			}
   456  			r.IPAM.Config = append(r.IPAM.Config, iData)
   457  		}
   458  	}
   459  
   460  	hasIpv6Conf := false
   461  	for _, ip6 := range ipv6conf {
   462  		if ip6.PreferredPool == "" {
   463  			continue
   464  		}
   465  		hasIpv6Conf = true
   466  		iData := network.IPAMConfig{}
   467  		iData.Subnet = ip6.PreferredPool
   468  		iData.IPRange = ip6.SubPool
   469  		iData.Gateway = ip6.Gateway
   470  		iData.AuxAddress = ip6.AuxAddresses
   471  		r.IPAM.Config = append(r.IPAM.Config, iData)
   472  	}
   473  
   474  	if !hasIpv6Conf {
   475  		for _, ip6Info := range ipv6Info {
   476  			if ip6Info.IPAMData.Pool == nil {
   477  				continue
   478  			}
   479  			iData := network.IPAMConfig{}
   480  			iData.Subnet = ip6Info.IPAMData.Pool.String()
   481  			iData.Gateway = ip6Info.IPAMData.Gateway.String()
   482  			r.IPAM.Config = append(r.IPAM.Config, iData)
   483  		}
   484  	}
   485  }
   486  
   487  func buildEndpointResource(id string, name string, info libnetwork.EndpointInfo) types.EndpointResource {
   488  	er := types.EndpointResource{}
   489  
   490  	er.EndpointID = id
   491  	er.Name = name
   492  	ei := info
   493  	if ei == nil {
   494  		return er
   495  	}
   496  
   497  	if iface := ei.Iface(); iface != nil {
   498  		if mac := iface.MacAddress(); mac != nil {
   499  			er.MacAddress = mac.String()
   500  		}
   501  		if ip := iface.Address(); ip != nil && len(ip.IP) > 0 {
   502  			er.IPv4Address = ip.String()
   503  		}
   504  
   505  		if ipv6 := iface.AddressIPv6(); ipv6 != nil && len(ipv6.IP) > 0 {
   506  			er.IPv6Address = ipv6.String()
   507  		}
   508  	}
   509  	return er
   510  }
   511  
   512  func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   513  	if err := httputils.ParseForm(r); err != nil {
   514  		return err
   515  	}
   516  
   517  	pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
   518  	if err != nil {
   519  		return err
   520  	}
   521  
   522  	pruneReport, err := n.backend.NetworksPrune(ctx, pruneFilters)
   523  	if err != nil {
   524  		return err
   525  	}
   526  	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
   527  }