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