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