github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/api/server/router/network/network_routes.go (about)

     1  package network // import "github.com/docker/docker/api/server/router/network"
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"io"
     7  	"net/http"
     8  	"strconv"
     9  	"strings"
    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/docker/errdefs"
    17  	"github.com/docker/libnetwork"
    18  	netconst "github.com/docker/libnetwork/datastore"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    23  	if err := httputils.ParseForm(r); err != nil {
    24  		return err
    25  	}
    26  
    27  	filter, err := filters.FromJSON(r.Form.Get("filters"))
    28  	if err != nil {
    29  		return err
    30  	}
    31  
    32  	if err := network.ValidateFilters(filter); err != nil {
    33  		return errdefs.InvalidParameter(err)
    34  	}
    35  
    36  	var list []types.NetworkResource
    37  	nr, err := n.cluster.GetNetworks(filter)
    38  	if err == nil {
    39  		list = nr
    40  	}
    41  
    42  	// Combine the network list returned by Docker daemon if it is not already
    43  	// returned by the cluster manager
    44  	localNetworks, err := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: versions.LessThan(httputils.VersionFromContext(ctx), "1.28")})
    45  	if err != nil {
    46  		return err
    47  	}
    48  
    49  	var idx map[string]bool
    50  	if len(list) > 0 {
    51  		idx = make(map[string]bool, len(list))
    52  		for _, n := range list {
    53  			idx[n.ID] = true
    54  		}
    55  	}
    56  	for _, n := range localNetworks {
    57  		if idx[n.ID] {
    58  			continue
    59  		}
    60  		list = append(list, n)
    61  	}
    62  
    63  	if list == nil {
    64  		list = []types.NetworkResource{}
    65  	}
    66  
    67  	return httputils.WriteJSON(w, http.StatusOK, list)
    68  }
    69  
    70  type invalidRequestError struct {
    71  	cause error
    72  }
    73  
    74  func (e invalidRequestError) Error() string {
    75  	return e.cause.Error()
    76  }
    77  
    78  func (e invalidRequestError) InvalidParameter() {}
    79  
    80  type ambigousResultsError string
    81  
    82  func (e ambigousResultsError) Error() string {
    83  	return "network " + string(e) + " is ambiguous"
    84  }
    85  
    86  func (ambigousResultsError) InvalidParameter() {}
    87  
    88  func nameConflict(name string) error {
    89  	return errdefs.Conflict(libnetwork.NetworkNameError(name))
    90  }
    91  
    92  func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    93  	if err := httputils.ParseForm(r); err != nil {
    94  		return err
    95  	}
    96  
    97  	term := vars["id"]
    98  	var (
    99  		verbose bool
   100  		err     error
   101  	)
   102  	if v := r.URL.Query().Get("verbose"); v != "" {
   103  		if verbose, err = strconv.ParseBool(v); err != nil {
   104  			return errors.Wrapf(invalidRequestError{err}, "invalid value for verbose: %s", v)
   105  		}
   106  	}
   107  	scope := r.URL.Query().Get("scope")
   108  
   109  	// In case multiple networks have duplicate names, return error.
   110  	// TODO (yongtang): should we wrap with version here for backward compatibility?
   111  
   112  	// First find based on full ID, return immediately once one is found.
   113  	// If a network appears both in swarm and local, assume it is in local first
   114  
   115  	// For full name and partial ID, save the result first, and process later
   116  	// in case multiple records was found based on the same term
   117  	listByFullName := map[string]types.NetworkResource{}
   118  	listByPartialID := map[string]types.NetworkResource{}
   119  
   120  	// TODO(@cpuguy83): All this logic for figuring out which network to return does not belong here
   121  	// Instead there should be a backend function to just get one network.
   122  	filter := filters.NewArgs(filters.Arg("idOrName", term))
   123  	if scope != "" {
   124  		filter.Add("scope", scope)
   125  	}
   126  	nw, _ := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: true, Verbose: verbose})
   127  	for _, network := range nw {
   128  		if network.ID == term {
   129  			return httputils.WriteJSON(w, http.StatusOK, network)
   130  		}
   131  		if network.Name == term {
   132  			// No need to check the ID collision here as we are still in
   133  			// local scope and the network ID is unique in this scope.
   134  			listByFullName[network.ID] = network
   135  		}
   136  		if strings.HasPrefix(network.ID, term) {
   137  			// No need to check the ID collision here as we are still in
   138  			// local scope and the network ID is unique in this scope.
   139  			listByPartialID[network.ID] = network
   140  		}
   141  	}
   142  
   143  	nwk, err := n.cluster.GetNetwork(term)
   144  	if err == nil {
   145  		// If the get network is passed with a specific network ID / partial network ID
   146  		// or if the get network was passed with a network name and scope as swarm
   147  		// return the network. Skipped using isMatchingScope because it is true if the scope
   148  		// is not set which would be case if the client API v1.30
   149  		if strings.HasPrefix(nwk.ID, term) || (netconst.SwarmScope == scope) {
   150  			// If we have a previous match "backend", return it, we need verbose when enabled
   151  			// ex: overlay/partial_ID or name/swarm_scope
   152  			if nwv, ok := listByPartialID[nwk.ID]; ok {
   153  				nwk = nwv
   154  			} else if nwv, ok := listByFullName[nwk.ID]; ok {
   155  				nwk = nwv
   156  			}
   157  			return httputils.WriteJSON(w, http.StatusOK, nwk)
   158  		}
   159  	}
   160  
   161  	nr, _ := n.cluster.GetNetworks(filter)
   162  	for _, network := range nr {
   163  		if network.ID == term {
   164  			return httputils.WriteJSON(w, http.StatusOK, network)
   165  		}
   166  		if network.Name == term {
   167  			// Check the ID collision as we are in swarm scope here, and
   168  			// the map (of the listByFullName) may have already had a
   169  			// network with the same ID (from local scope previously)
   170  			if _, ok := listByFullName[network.ID]; !ok {
   171  				listByFullName[network.ID] = network
   172  			}
   173  		}
   174  		if strings.HasPrefix(network.ID, term) {
   175  			// Check the ID collision as we are in swarm scope here, and
   176  			// the map (of the listByPartialID) may have already had a
   177  			// network with the same ID (from local scope previously)
   178  			if _, ok := listByPartialID[network.ID]; !ok {
   179  				listByPartialID[network.ID] = network
   180  			}
   181  		}
   182  	}
   183  
   184  	// Find based on full name, returns true only if no duplicates
   185  	if len(listByFullName) == 1 {
   186  		for _, v := range listByFullName {
   187  			return httputils.WriteJSON(w, http.StatusOK, v)
   188  		}
   189  	}
   190  	if len(listByFullName) > 1 {
   191  		return errors.Wrapf(ambigousResultsError(term), "%d matches found based on name", len(listByFullName))
   192  	}
   193  
   194  	// Find based on partial ID, returns true only if no duplicates
   195  	if len(listByPartialID) == 1 {
   196  		for _, v := range listByPartialID {
   197  			return httputils.WriteJSON(w, http.StatusOK, v)
   198  		}
   199  	}
   200  	if len(listByPartialID) > 1 {
   201  		return errors.Wrapf(ambigousResultsError(term), "%d matches found based on ID prefix", len(listByPartialID))
   202  	}
   203  
   204  	return libnetwork.ErrNoSuchNetwork(term)
   205  }
   206  
   207  func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   208  	var create types.NetworkCreateRequest
   209  
   210  	if err := httputils.ParseForm(r); err != nil {
   211  		return err
   212  	}
   213  
   214  	if err := httputils.CheckForJSON(r); err != nil {
   215  		return err
   216  	}
   217  
   218  	if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
   219  		if err == io.EOF {
   220  			return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
   221  		}
   222  		return errdefs.InvalidParameter(err)
   223  	}
   224  
   225  	if nws, err := n.cluster.GetNetworksByName(create.Name); err == nil && len(nws) > 0 {
   226  		return nameConflict(create.Name)
   227  	}
   228  
   229  	nw, err := n.backend.CreateNetwork(create)
   230  	if err != nil {
   231  		var warning string
   232  		if _, ok := err.(libnetwork.NetworkNameError); ok {
   233  			// check if user defined CheckDuplicate, if set true, return err
   234  			// otherwise prepare a warning message
   235  			if create.CheckDuplicate {
   236  				return nameConflict(create.Name)
   237  			}
   238  			warning = libnetwork.NetworkNameError(create.Name).Error()
   239  		}
   240  
   241  		if _, ok := err.(libnetwork.ManagerRedirectError); !ok {
   242  			return err
   243  		}
   244  		id, err := n.cluster.CreateNetwork(create)
   245  		if err != nil {
   246  			return err
   247  		}
   248  		nw = &types.NetworkCreateResponse{
   249  			ID:      id,
   250  			Warning: warning,
   251  		}
   252  	}
   253  
   254  	return httputils.WriteJSON(w, http.StatusCreated, nw)
   255  }
   256  
   257  func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   258  	var connect types.NetworkConnect
   259  	if err := httputils.ParseForm(r); err != nil {
   260  		return err
   261  	}
   262  
   263  	if err := httputils.CheckForJSON(r); err != nil {
   264  		return err
   265  	}
   266  
   267  	if err := json.NewDecoder(r.Body).Decode(&connect); err != nil {
   268  		if err == io.EOF {
   269  			return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
   270  		}
   271  		return errdefs.InvalidParameter(err)
   272  	}
   273  
   274  	// Unlike other operations, we does not check ambiguity of the name/ID here.
   275  	// The reason is that, In case of attachable network in swarm scope, the actual local network
   276  	// may not be available at the time. At the same time, inside daemon `ConnectContainerToNetwork`
   277  	// does the ambiguity check anyway. Therefore, passing the name to daemon would be enough.
   278  	return n.backend.ConnectContainerToNetwork(connect.Container, vars["id"], connect.EndpointConfig)
   279  }
   280  
   281  func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   282  	var disconnect types.NetworkDisconnect
   283  	if err := httputils.ParseForm(r); err != nil {
   284  		return err
   285  	}
   286  
   287  	if err := httputils.CheckForJSON(r); err != nil {
   288  		return err
   289  	}
   290  
   291  	if err := json.NewDecoder(r.Body).Decode(&disconnect); err != nil {
   292  		if err == io.EOF {
   293  			return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
   294  		}
   295  		return errdefs.InvalidParameter(err)
   296  	}
   297  
   298  	return n.backend.DisconnectContainerFromNetwork(disconnect.Container, vars["id"], disconnect.Force)
   299  }
   300  
   301  func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   302  	if err := httputils.ParseForm(r); err != nil {
   303  		return err
   304  	}
   305  
   306  	nw, err := n.findUniqueNetwork(vars["id"])
   307  	if err != nil {
   308  		return err
   309  	}
   310  	if nw.Scope == "swarm" {
   311  		if err = n.cluster.RemoveNetwork(nw.ID); err != nil {
   312  			return err
   313  		}
   314  	} else {
   315  		if err := n.backend.DeleteNetwork(nw.ID); err != nil {
   316  			return err
   317  		}
   318  	}
   319  	w.WriteHeader(http.StatusNoContent)
   320  	return nil
   321  }
   322  
   323  func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   324  	if err := httputils.ParseForm(r); err != nil {
   325  		return err
   326  	}
   327  
   328  	pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
   329  	if err != nil {
   330  		return err
   331  	}
   332  
   333  	pruneReport, err := n.backend.NetworksPrune(ctx, pruneFilters)
   334  	if err != nil {
   335  		return err
   336  	}
   337  	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
   338  }
   339  
   340  // findUniqueNetwork will search network across different scopes (both local and swarm).
   341  // NOTE: This findUniqueNetwork is different from FindNetwork in the daemon.
   342  // In case multiple networks have duplicate names, return error.
   343  // First find based on full ID, return immediately once one is found.
   344  // If a network appears both in swarm and local, assume it is in local first
   345  // For full name and partial ID, save the result first, and process later
   346  // in case multiple records was found based on the same term
   347  // TODO (yongtang): should we wrap with version here for backward compatibility?
   348  func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, error) {
   349  	listByFullName := map[string]types.NetworkResource{}
   350  	listByPartialID := map[string]types.NetworkResource{}
   351  
   352  	filter := filters.NewArgs(filters.Arg("idOrName", term))
   353  	nw, _ := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: true})
   354  	for _, network := range nw {
   355  		if network.ID == term {
   356  			return network, nil
   357  		}
   358  		if network.Name == term && !network.Ingress {
   359  			// No need to check the ID collision here as we are still in
   360  			// local scope and the network ID is unique in this scope.
   361  			listByFullName[network.ID] = network
   362  		}
   363  		if strings.HasPrefix(network.ID, term) {
   364  			// No need to check the ID collision here as we are still in
   365  			// local scope and the network ID is unique in this scope.
   366  			listByPartialID[network.ID] = network
   367  		}
   368  	}
   369  
   370  	nr, _ := n.cluster.GetNetworks(filter)
   371  	for _, network := range nr {
   372  		if network.ID == term {
   373  			return network, nil
   374  		}
   375  		if network.Name == term {
   376  			// Check the ID collision as we are in swarm scope here, and
   377  			// the map (of the listByFullName) may have already had a
   378  			// network with the same ID (from local scope previously)
   379  			if _, ok := listByFullName[network.ID]; !ok {
   380  				listByFullName[network.ID] = network
   381  			}
   382  		}
   383  		if strings.HasPrefix(network.ID, term) {
   384  			// Check the ID collision as we are in swarm scope here, and
   385  			// the map (of the listByPartialID) may have already had a
   386  			// network with the same ID (from local scope previously)
   387  			if _, ok := listByPartialID[network.ID]; !ok {
   388  				listByPartialID[network.ID] = network
   389  			}
   390  		}
   391  	}
   392  
   393  	// Find based on full name, returns true only if no duplicates
   394  	if len(listByFullName) == 1 {
   395  		for _, v := range listByFullName {
   396  			return v, nil
   397  		}
   398  	}
   399  	if len(listByFullName) > 1 {
   400  		return types.NetworkResource{}, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName)))
   401  	}
   402  
   403  	// Find based on partial ID, returns true only if no duplicates
   404  	if len(listByPartialID) == 1 {
   405  		for _, v := range listByPartialID {
   406  			return v, nil
   407  		}
   408  	}
   409  	if len(listByPartialID) > 1 {
   410  		return types.NetworkResource{}, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID)))
   411  	}
   412  
   413  	return types.NetworkResource{}, errdefs.NotFound(libnetwork.ErrNoSuchNetwork(term))
   414  }