github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/libnetwork/api/api.go (about)

     1  package api
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/docker/docker/libnetwork"
    12  	"github.com/docker/docker/libnetwork/netlabel"
    13  	"github.com/docker/docker/libnetwork/netutils"
    14  	"github.com/docker/docker/libnetwork/types"
    15  	"github.com/gorilla/mux"
    16  )
    17  
    18  var (
    19  	successResponse  = responseStatus{Status: "Success", StatusCode: http.StatusOK}
    20  	createdResponse  = responseStatus{Status: "Created", StatusCode: http.StatusCreated}
    21  	badQueryResponse = responseStatus{Status: "Unsupported query", StatusCode: http.StatusBadRequest}
    22  )
    23  
    24  const (
    25  	// Resource name regex
    26  	// Gorilla mux encloses the passed pattern with '^' and '$'. So we need to do some tricks
    27  	// to have mux eventually build a query regex which matches empty or word string (`^$|[\w]+`)
    28  	regex = "[a-zA-Z_0-9-]+"
    29  	qregx = "$|" + regex
    30  	// Router URL variable definition
    31  	nwNameQr = "{" + urlNwName + ":" + qregx + "}"
    32  	nwID     = "{" + urlNwID + ":" + regex + "}"
    33  	nwPIDQr  = "{" + urlNwPID + ":" + qregx + "}"
    34  	epNameQr = "{" + urlEpName + ":" + qregx + "}"
    35  	epID     = "{" + urlEpID + ":" + regex + "}"
    36  	epPIDQr  = "{" + urlEpPID + ":" + qregx + "}"
    37  	sbID     = "{" + urlSbID + ":" + regex + "}"
    38  	sbPIDQr  = "{" + urlSbPID + ":" + qregx + "}"
    39  	cnIDQr   = "{" + urlCnID + ":" + qregx + "}"
    40  	cnPIDQr  = "{" + urlCnPID + ":" + qregx + "}"
    41  
    42  	// Internal URL variable name.They can be anything as
    43  	// long as they do not collide with query fields.
    44  	urlNwName = "network-name"
    45  	urlNwID   = "network-id"
    46  	urlNwPID  = "network-partial-id"
    47  	urlEpName = "endpoint-name"
    48  	urlEpID   = "endpoint-id"
    49  	urlEpPID  = "endpoint-partial-id"
    50  	urlSbID   = "sandbox-id"
    51  	urlSbPID  = "sandbox-partial-id"
    52  	urlCnID   = "container-id"
    53  	urlCnPID  = "container-partial-id"
    54  )
    55  
    56  // NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork
    57  func NewHTTPHandler(c libnetwork.NetworkController) func(w http.ResponseWriter, req *http.Request) {
    58  	h := &httpHandler{c: c}
    59  	h.initRouter()
    60  	return h.handleRequest
    61  }
    62  
    63  type responseStatus struct {
    64  	Status     string
    65  	StatusCode int
    66  }
    67  
    68  func (r *responseStatus) isOK() bool {
    69  	return r.StatusCode == http.StatusOK || r.StatusCode == http.StatusCreated
    70  }
    71  
    72  type processor func(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus)
    73  
    74  type httpHandler struct {
    75  	c libnetwork.NetworkController
    76  	r *mux.Router
    77  }
    78  
    79  func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) {
    80  	// Make sure the service is there
    81  	if h.c == nil {
    82  		http.Error(w, "NetworkController is not available", http.StatusServiceUnavailable)
    83  		return
    84  	}
    85  
    86  	// Get handler from router and execute it
    87  	h.r.ServeHTTP(w, req)
    88  }
    89  
    90  func (h *httpHandler) initRouter() {
    91  	m := map[string][]struct {
    92  		url string
    93  		qrs []string
    94  		fct processor
    95  	}{
    96  		"GET": {
    97  			// Order matters
    98  			{"/networks", []string{"name", nwNameQr}, procGetNetworks},
    99  			{"/networks", []string{"partial-id", nwPIDQr}, procGetNetworks},
   100  			{"/networks", nil, procGetNetworks},
   101  			{"/networks/" + nwID, nil, procGetNetwork},
   102  			{"/networks/" + nwID + "/endpoints", []string{"name", epNameQr}, procGetEndpoints},
   103  			{"/networks/" + nwID + "/endpoints", []string{"partial-id", epPIDQr}, procGetEndpoints},
   104  			{"/networks/" + nwID + "/endpoints", nil, procGetEndpoints},
   105  			{"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint},
   106  			{"/services", []string{"network", nwNameQr}, procGetServices},
   107  			{"/services", []string{"name", epNameQr}, procGetServices},
   108  			{"/services", []string{"partial-id", epPIDQr}, procGetServices},
   109  			{"/services", nil, procGetServices},
   110  			{"/services/" + epID, nil, procGetService},
   111  			{"/services/" + epID + "/backend", nil, procGetSandbox},
   112  			{"/sandboxes", []string{"partial-container-id", cnPIDQr}, procGetSandboxes},
   113  			{"/sandboxes", []string{"container-id", cnIDQr}, procGetSandboxes},
   114  			{"/sandboxes", []string{"partial-id", sbPIDQr}, procGetSandboxes},
   115  			{"/sandboxes", nil, procGetSandboxes},
   116  			{"/sandboxes/" + sbID, nil, procGetSandbox},
   117  		},
   118  		"POST": {
   119  			{"/networks", nil, procCreateNetwork},
   120  			{"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint},
   121  			{"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes", nil, procJoinEndpoint},
   122  			{"/services", nil, procPublishService},
   123  			{"/services/" + epID + "/backend", nil, procAttachBackend},
   124  			{"/sandboxes", nil, procCreateSandbox},
   125  		},
   126  		"DELETE": {
   127  			{"/networks/" + nwID, nil, procDeleteNetwork},
   128  			{"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint},
   129  			{"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes/" + sbID, nil, procLeaveEndpoint},
   130  			{"/services/" + epID, nil, procUnpublishService},
   131  			{"/services/" + epID + "/backend/" + sbID, nil, procDetachBackend},
   132  			{"/sandboxes/" + sbID, nil, procDeleteSandbox},
   133  		},
   134  	}
   135  
   136  	h.r = mux.NewRouter()
   137  	for method, routes := range m {
   138  		for _, route := range routes {
   139  			r := h.r.Path("/{.*}" + route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct))
   140  			if route.qrs != nil {
   141  				r.Queries(route.qrs...)
   142  			}
   143  
   144  			r = h.r.Path(route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct))
   145  			if route.qrs != nil {
   146  				r.Queries(route.qrs...)
   147  			}
   148  		}
   149  	}
   150  }
   151  
   152  func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerFunc {
   153  	return func(w http.ResponseWriter, req *http.Request) {
   154  		var (
   155  			body []byte
   156  			err  error
   157  		)
   158  		if req.Body != nil {
   159  			body, err = ioutil.ReadAll(req.Body)
   160  			if err != nil {
   161  				http.Error(w, "Invalid body: "+err.Error(), http.StatusBadRequest)
   162  				return
   163  			}
   164  		}
   165  
   166  		res, rsp := fct(ctrl, mux.Vars(req), body)
   167  		if !rsp.isOK() {
   168  			http.Error(w, rsp.Status, rsp.StatusCode)
   169  			return
   170  		}
   171  		if res != nil {
   172  			writeJSON(w, rsp.StatusCode, res)
   173  		}
   174  	}
   175  }
   176  
   177  /*****************
   178   Resource Builders
   179  ******************/
   180  
   181  func buildNetworkResource(nw libnetwork.Network) *networkResource {
   182  	r := &networkResource{}
   183  	if nw != nil {
   184  		r.Name = nw.Name()
   185  		r.ID = nw.ID()
   186  		r.Type = nw.Type()
   187  		epl := nw.Endpoints()
   188  		r.Endpoints = make([]*endpointResource, 0, len(epl))
   189  		for _, e := range epl {
   190  			epr := buildEndpointResource(e)
   191  			r.Endpoints = append(r.Endpoints, epr)
   192  		}
   193  	}
   194  	return r
   195  }
   196  
   197  func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource {
   198  	r := &endpointResource{}
   199  	if ep != nil {
   200  		r.Name = ep.Name()
   201  		r.ID = ep.ID()
   202  		r.Network = ep.Network()
   203  	}
   204  	return r
   205  }
   206  
   207  func buildSandboxResource(sb libnetwork.Sandbox) *sandboxResource {
   208  	r := &sandboxResource{}
   209  	if sb != nil {
   210  		r.ID = sb.ID()
   211  		r.Key = sb.Key()
   212  		r.ContainerID = sb.ContainerID()
   213  	}
   214  	return r
   215  }
   216  
   217  /****************
   218   Options Parsers
   219  *****************/
   220  
   221  func (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption {
   222  	var setFctList []libnetwork.SandboxOption
   223  	if sc.HostName != "" {
   224  		setFctList = append(setFctList, libnetwork.OptionHostname(sc.HostName))
   225  	}
   226  	if sc.DomainName != "" {
   227  		setFctList = append(setFctList, libnetwork.OptionDomainname(sc.DomainName))
   228  	}
   229  	if sc.HostsPath != "" {
   230  		setFctList = append(setFctList, libnetwork.OptionHostsPath(sc.HostsPath))
   231  	}
   232  	if sc.ResolvConfPath != "" {
   233  		setFctList = append(setFctList, libnetwork.OptionResolvConfPath(sc.ResolvConfPath))
   234  	}
   235  	if sc.UseDefaultSandbox {
   236  		setFctList = append(setFctList, libnetwork.OptionUseDefaultSandbox())
   237  	}
   238  	if sc.UseExternalKey {
   239  		setFctList = append(setFctList, libnetwork.OptionUseExternalKey())
   240  	}
   241  	if sc.DNS != nil {
   242  		for _, d := range sc.DNS {
   243  			setFctList = append(setFctList, libnetwork.OptionDNS(d))
   244  		}
   245  	}
   246  	if sc.ExtraHosts != nil {
   247  		for _, e := range sc.ExtraHosts {
   248  			setFctList = append(setFctList, libnetwork.OptionExtraHost(e.Name, e.Address))
   249  		}
   250  	}
   251  	if sc.ExposedPorts != nil {
   252  		setFctList = append(setFctList, libnetwork.OptionExposedPorts(sc.ExposedPorts))
   253  	}
   254  	if sc.PortMapping != nil {
   255  		setFctList = append(setFctList, libnetwork.OptionPortMapping(sc.PortMapping))
   256  	}
   257  	return setFctList
   258  }
   259  
   260  /******************
   261   Process functions
   262  *******************/
   263  
   264  func processCreateDefaults(c libnetwork.NetworkController, nc *networkCreate) {
   265  	if nc.NetworkType == "" {
   266  		nc.NetworkType = c.Config().Daemon.DefaultDriver
   267  	}
   268  }
   269  
   270  /***************************
   271   NetworkController interface
   272  ****************************/
   273  func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   274  	var create networkCreate
   275  
   276  	err := json.Unmarshal(body, &create)
   277  	if err != nil {
   278  		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   279  	}
   280  	processCreateDefaults(c, &create)
   281  
   282  	options := []libnetwork.NetworkOption{}
   283  	if val, ok := create.NetworkOpts[netlabel.Internal]; ok {
   284  		internal, err := strconv.ParseBool(val)
   285  		if err != nil {
   286  			return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
   287  		}
   288  		if internal {
   289  			options = append(options, libnetwork.NetworkOptionInternalNetwork())
   290  		}
   291  	}
   292  	if val, ok := create.NetworkOpts[netlabel.EnableIPv6]; ok {
   293  		enableIPv6, err := strconv.ParseBool(val)
   294  		if err != nil {
   295  			return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
   296  		}
   297  		options = append(options, libnetwork.NetworkOptionEnableIPv6(enableIPv6))
   298  	}
   299  	if len(create.DriverOpts) > 0 {
   300  		options = append(options, libnetwork.NetworkOptionDriverOpts(create.DriverOpts))
   301  	}
   302  
   303  	if len(create.IPv4Conf) > 0 {
   304  		ipamV4Conf := &libnetwork.IpamConf{
   305  			PreferredPool: create.IPv4Conf[0].PreferredPool,
   306  			SubPool:       create.IPv4Conf[0].SubPool,
   307  		}
   308  
   309  		options = append(options, libnetwork.NetworkOptionIpam("default", "", []*libnetwork.IpamConf{ipamV4Conf}, nil, nil))
   310  	}
   311  
   312  	nw, err := c.NewNetwork(create.NetworkType, create.Name, create.ID, options...)
   313  	if err != nil {
   314  		return nil, convertNetworkError(err)
   315  	}
   316  
   317  	return nw.ID(), &createdResponse
   318  }
   319  
   320  func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   321  	t, by := detectNetworkTarget(vars)
   322  	nw, errRsp := findNetwork(c, t, by)
   323  	if !errRsp.isOK() {
   324  		return nil, errRsp
   325  	}
   326  	return buildNetworkResource(nw), &successResponse
   327  }
   328  
   329  func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   330  	var list []*networkResource
   331  
   332  	// Look for query filters and validate
   333  	name, queryByName := vars[urlNwName]
   334  	shortID, queryByPid := vars[urlNwPID]
   335  	if queryByName && queryByPid {
   336  		return nil, &badQueryResponse
   337  	}
   338  
   339  	if queryByName {
   340  		if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() {
   341  			list = append(list, buildNetworkResource(nw))
   342  		}
   343  	} else if queryByPid {
   344  		// Return all the prefix-matching networks
   345  		l := func(nw libnetwork.Network) bool {
   346  			if strings.HasPrefix(nw.ID(), shortID) {
   347  				list = append(list, buildNetworkResource(nw))
   348  			}
   349  			return false
   350  		}
   351  		c.WalkNetworks(l)
   352  	} else {
   353  		for _, nw := range c.Networks() {
   354  			list = append(list, buildNetworkResource(nw))
   355  		}
   356  	}
   357  
   358  	return list, &successResponse
   359  }
   360  
   361  func procCreateSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   362  	var create sandboxCreate
   363  
   364  	err := json.Unmarshal(body, &create)
   365  	if err != nil {
   366  		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   367  	}
   368  
   369  	sb, err := c.NewSandbox(create.ContainerID, create.parseOptions()...)
   370  	if err != nil {
   371  		return "", convertNetworkError(err)
   372  	}
   373  
   374  	return sb.ID(), &createdResponse
   375  }
   376  
   377  /******************
   378   Network interface
   379  *******************/
   380  func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   381  	var ec endpointCreate
   382  
   383  	err := json.Unmarshal(body, &ec)
   384  	if err != nil {
   385  		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   386  	}
   387  
   388  	nwT, nwBy := detectNetworkTarget(vars)
   389  	n, errRsp := findNetwork(c, nwT, nwBy)
   390  	if !errRsp.isOK() {
   391  		return "", errRsp
   392  	}
   393  
   394  	var setFctList []libnetwork.EndpointOption
   395  	for _, str := range ec.MyAliases {
   396  		setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str))
   397  	}
   398  
   399  	ep, err := n.CreateEndpoint(ec.Name, setFctList...)
   400  	if err != nil {
   401  		return "", convertNetworkError(err)
   402  	}
   403  
   404  	return ep.ID(), &createdResponse
   405  }
   406  
   407  func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   408  	nwT, nwBy := detectNetworkTarget(vars)
   409  	epT, epBy := detectEndpointTarget(vars)
   410  
   411  	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
   412  	if !errRsp.isOK() {
   413  		return nil, errRsp
   414  	}
   415  
   416  	return buildEndpointResource(ep), &successResponse
   417  }
   418  
   419  func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   420  	// Look for query filters and validate
   421  	name, queryByName := vars[urlEpName]
   422  	shortID, queryByPid := vars[urlEpPID]
   423  	if queryByName && queryByPid {
   424  		return nil, &badQueryResponse
   425  	}
   426  
   427  	nwT, nwBy := detectNetworkTarget(vars)
   428  	nw, errRsp := findNetwork(c, nwT, nwBy)
   429  	if !errRsp.isOK() {
   430  		return nil, errRsp
   431  	}
   432  
   433  	var list []*endpointResource
   434  
   435  	// If query parameter is specified, return a filtered collection
   436  	if queryByName {
   437  		if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() {
   438  			list = append(list, buildEndpointResource(ep))
   439  		}
   440  	} else if queryByPid {
   441  		// Return all the prefix-matching endpoints
   442  		l := func(ep libnetwork.Endpoint) bool {
   443  			if strings.HasPrefix(ep.ID(), shortID) {
   444  				list = append(list, buildEndpointResource(ep))
   445  			}
   446  			return false
   447  		}
   448  		nw.WalkEndpoints(l)
   449  	} else {
   450  		for _, ep := range nw.Endpoints() {
   451  			epr := buildEndpointResource(ep)
   452  			list = append(list, epr)
   453  		}
   454  	}
   455  
   456  	return list, &successResponse
   457  }
   458  
   459  func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   460  	target, by := detectNetworkTarget(vars)
   461  
   462  	nw, errRsp := findNetwork(c, target, by)
   463  	if !errRsp.isOK() {
   464  		return nil, errRsp
   465  	}
   466  
   467  	err := nw.Delete()
   468  	if err != nil {
   469  		return nil, convertNetworkError(err)
   470  	}
   471  
   472  	return nil, &successResponse
   473  }
   474  
   475  /******************
   476   Endpoint interface
   477  *******************/
   478  func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   479  	var ej endpointJoin
   480  	var setFctList []libnetwork.EndpointOption
   481  	err := json.Unmarshal(body, &ej)
   482  	if err != nil {
   483  		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   484  	}
   485  
   486  	nwT, nwBy := detectNetworkTarget(vars)
   487  	epT, epBy := detectEndpointTarget(vars)
   488  
   489  	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
   490  	if !errRsp.isOK() {
   491  		return nil, errRsp
   492  	}
   493  
   494  	sb, errRsp := findSandbox(c, ej.SandboxID, byID)
   495  	if !errRsp.isOK() {
   496  		return nil, errRsp
   497  	}
   498  
   499  	for _, str := range ej.Aliases {
   500  		name, alias, err := netutils.ParseAlias(str)
   501  		if err != nil {
   502  			return "", convertNetworkError(err)
   503  		}
   504  		setFctList = append(setFctList, libnetwork.CreateOptionAlias(name, alias))
   505  	}
   506  
   507  	err = ep.Join(sb, setFctList...)
   508  	if err != nil {
   509  		return nil, convertNetworkError(err)
   510  	}
   511  	return sb.Key(), &successResponse
   512  }
   513  
   514  func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   515  	nwT, nwBy := detectNetworkTarget(vars)
   516  	epT, epBy := detectEndpointTarget(vars)
   517  
   518  	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
   519  	if !errRsp.isOK() {
   520  		return nil, errRsp
   521  	}
   522  
   523  	sb, errRsp := findSandbox(c, vars[urlSbID], byID)
   524  	if !errRsp.isOK() {
   525  		return nil, errRsp
   526  	}
   527  
   528  	err := ep.Leave(sb)
   529  	if err != nil {
   530  		return nil, convertNetworkError(err)
   531  	}
   532  
   533  	return nil, &successResponse
   534  }
   535  
   536  func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   537  	nwT, nwBy := detectNetworkTarget(vars)
   538  	epT, epBy := detectEndpointTarget(vars)
   539  
   540  	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
   541  	if !errRsp.isOK() {
   542  		return nil, errRsp
   543  	}
   544  
   545  	err := ep.Delete(false)
   546  	if err != nil {
   547  		return nil, convertNetworkError(err)
   548  	}
   549  
   550  	return nil, &successResponse
   551  }
   552  
   553  /******************
   554   Service interface
   555  *******************/
   556  func procGetServices(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   557  	// Look for query filters and validate
   558  	nwName, filterByNwName := vars[urlNwName]
   559  	svName, queryBySvName := vars[urlEpName]
   560  	shortID, queryBySvPID := vars[urlEpPID]
   561  
   562  	if filterByNwName && queryBySvName || filterByNwName && queryBySvPID || queryBySvName && queryBySvPID {
   563  		return nil, &badQueryResponse
   564  	}
   565  
   566  	var list []*endpointResource
   567  
   568  	switch {
   569  	case filterByNwName:
   570  		// return all service present on the specified network
   571  		nw, errRsp := findNetwork(c, nwName, byName)
   572  		if !errRsp.isOK() {
   573  			return list, &successResponse
   574  		}
   575  		for _, ep := range nw.Endpoints() {
   576  			epr := buildEndpointResource(ep)
   577  			list = append(list, epr)
   578  		}
   579  	case queryBySvName:
   580  		// Look in each network for the service with the specified name
   581  		l := func(ep libnetwork.Endpoint) bool {
   582  			if ep.Name() == svName {
   583  				list = append(list, buildEndpointResource(ep))
   584  				return true
   585  			}
   586  			return false
   587  		}
   588  		for _, nw := range c.Networks() {
   589  			nw.WalkEndpoints(l)
   590  		}
   591  	case queryBySvPID:
   592  		// Return all the prefix-matching services
   593  		l := func(ep libnetwork.Endpoint) bool {
   594  			if strings.HasPrefix(ep.ID(), shortID) {
   595  				list = append(list, buildEndpointResource(ep))
   596  			}
   597  			return false
   598  		}
   599  		for _, nw := range c.Networks() {
   600  			nw.WalkEndpoints(l)
   601  		}
   602  	default:
   603  		for _, nw := range c.Networks() {
   604  			for _, ep := range nw.Endpoints() {
   605  				epr := buildEndpointResource(ep)
   606  				list = append(list, epr)
   607  			}
   608  		}
   609  	}
   610  
   611  	return list, &successResponse
   612  }
   613  
   614  func procGetService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   615  	epT, epBy := detectEndpointTarget(vars)
   616  	sv, errRsp := findService(c, epT, epBy)
   617  	if !errRsp.isOK() {
   618  		return nil, endpointToService(errRsp)
   619  	}
   620  	return buildEndpointResource(sv), &successResponse
   621  }
   622  
   623  func procPublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   624  	var sp servicePublish
   625  
   626  	err := json.Unmarshal(body, &sp)
   627  	if err != nil {
   628  		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   629  	}
   630  
   631  	n, errRsp := findNetwork(c, sp.Network, byName)
   632  	if !errRsp.isOK() {
   633  		return "", errRsp
   634  	}
   635  
   636  	var setFctList []libnetwork.EndpointOption
   637  	for _, str := range sp.MyAliases {
   638  		setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str))
   639  	}
   640  
   641  	ep, err := n.CreateEndpoint(sp.Name, setFctList...)
   642  	if err != nil {
   643  		return "", endpointToService(convertNetworkError(err))
   644  	}
   645  
   646  	return ep.ID(), &createdResponse
   647  }
   648  
   649  func procUnpublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   650  	var sd serviceDelete
   651  
   652  	if body != nil {
   653  		err := json.Unmarshal(body, &sd)
   654  		if err != nil {
   655  			return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   656  		}
   657  	}
   658  
   659  	epT, epBy := detectEndpointTarget(vars)
   660  	sv, errRsp := findService(c, epT, epBy)
   661  	if !errRsp.isOK() {
   662  		return nil, errRsp
   663  	}
   664  
   665  	if err := sv.Delete(sd.Force); err != nil {
   666  		return nil, endpointToService(convertNetworkError(err))
   667  	}
   668  	return nil, &successResponse
   669  }
   670  
   671  func procAttachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   672  	var bk endpointJoin
   673  	var setFctList []libnetwork.EndpointOption
   674  	err := json.Unmarshal(body, &bk)
   675  	if err != nil {
   676  		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   677  	}
   678  
   679  	epT, epBy := detectEndpointTarget(vars)
   680  	sv, errRsp := findService(c, epT, epBy)
   681  	if !errRsp.isOK() {
   682  		return nil, errRsp
   683  	}
   684  
   685  	sb, errRsp := findSandbox(c, bk.SandboxID, byID)
   686  	if !errRsp.isOK() {
   687  		return nil, errRsp
   688  	}
   689  
   690  	for _, str := range bk.Aliases {
   691  		name, alias, err := netutils.ParseAlias(str)
   692  		if err != nil {
   693  			return "", convertNetworkError(err)
   694  		}
   695  		setFctList = append(setFctList, libnetwork.CreateOptionAlias(name, alias))
   696  	}
   697  
   698  	err = sv.Join(sb, setFctList...)
   699  	if err != nil {
   700  		return nil, convertNetworkError(err)
   701  	}
   702  
   703  	return sb.Key(), &successResponse
   704  }
   705  
   706  func procDetachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   707  	epT, epBy := detectEndpointTarget(vars)
   708  	sv, errRsp := findService(c, epT, epBy)
   709  	if !errRsp.isOK() {
   710  		return nil, errRsp
   711  	}
   712  
   713  	sb, errRsp := findSandbox(c, vars[urlSbID], byID)
   714  	if !errRsp.isOK() {
   715  		return nil, errRsp
   716  	}
   717  
   718  	err := sv.Leave(sb)
   719  	if err != nil {
   720  		return nil, convertNetworkError(err)
   721  	}
   722  
   723  	return nil, &successResponse
   724  }
   725  
   726  /******************
   727   Sandbox interface
   728  *******************/
   729  func procGetSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   730  	if epT, ok := vars[urlEpID]; ok {
   731  		sv, errRsp := findService(c, epT, byID)
   732  		if !errRsp.isOK() {
   733  			return nil, endpointToService(errRsp)
   734  		}
   735  		return buildSandboxResource(sv.Info().Sandbox()), &successResponse
   736  	}
   737  
   738  	sbT, by := detectSandboxTarget(vars)
   739  	sb, errRsp := findSandbox(c, sbT, by)
   740  	if !errRsp.isOK() {
   741  		return nil, errRsp
   742  	}
   743  	return buildSandboxResource(sb), &successResponse
   744  }
   745  
   746  type cndFnMkr func(string) cndFn
   747  type cndFn func(libnetwork.Sandbox) bool
   748  
   749  // list of (query type, condition function makers) couples
   750  var cndMkrList = []struct {
   751  	identifier string
   752  	maker      cndFnMkr
   753  }{
   754  	{urlSbPID, func(id string) cndFn {
   755  		return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ID(), id) }
   756  	}},
   757  	{urlCnID, func(id string) cndFn {
   758  		return func(sb libnetwork.Sandbox) bool { return sb.ContainerID() == id }
   759  	}},
   760  	{urlCnPID, func(id string) cndFn {
   761  		return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ContainerID(), id) }
   762  	}},
   763  }
   764  
   765  func getQueryCondition(vars map[string]string) func(libnetwork.Sandbox) bool {
   766  	for _, im := range cndMkrList {
   767  		if val, ok := vars[im.identifier]; ok {
   768  			return im.maker(val)
   769  		}
   770  	}
   771  	return func(sb libnetwork.Sandbox) bool { return true }
   772  }
   773  
   774  func sandboxWalker(condition cndFn, list *[]*sandboxResource) libnetwork.SandboxWalker {
   775  	return func(sb libnetwork.Sandbox) bool {
   776  		if condition(sb) {
   777  			*list = append(*list, buildSandboxResource(sb))
   778  		}
   779  		return false
   780  	}
   781  }
   782  
   783  func procGetSandboxes(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   784  	var list []*sandboxResource
   785  
   786  	cnd := getQueryCondition(vars)
   787  	c.WalkSandboxes(sandboxWalker(cnd, &list))
   788  
   789  	return list, &successResponse
   790  }
   791  
   792  func procDeleteSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   793  	sbT, by := detectSandboxTarget(vars)
   794  
   795  	sb, errRsp := findSandbox(c, sbT, by)
   796  	if !errRsp.isOK() {
   797  		return nil, errRsp
   798  	}
   799  
   800  	err := sb.Delete()
   801  	if err != nil {
   802  		return nil, convertNetworkError(err)
   803  	}
   804  
   805  	return nil, &successResponse
   806  }
   807  
   808  /***********
   809    Utilities
   810  ************/
   811  const (
   812  	byID = iota
   813  	byName
   814  )
   815  
   816  func detectNetworkTarget(vars map[string]string) (string, int) {
   817  	if target, ok := vars[urlNwName]; ok {
   818  		return target, byName
   819  	}
   820  	if target, ok := vars[urlNwID]; ok {
   821  		return target, byID
   822  	}
   823  	// vars are populated from the URL, following cannot happen
   824  	panic("Missing URL variable parameter for network")
   825  }
   826  
   827  func detectSandboxTarget(vars map[string]string) (string, int) {
   828  	if target, ok := vars[urlSbID]; ok {
   829  		return target, byID
   830  	}
   831  	// vars are populated from the URL, following cannot happen
   832  	panic("Missing URL variable parameter for sandbox")
   833  }
   834  
   835  func detectEndpointTarget(vars map[string]string) (string, int) {
   836  	if target, ok := vars[urlEpName]; ok {
   837  		return target, byName
   838  	}
   839  	if target, ok := vars[urlEpID]; ok {
   840  		return target, byID
   841  	}
   842  	// vars are populated from the URL, following cannot happen
   843  	panic("Missing URL variable parameter for endpoint")
   844  }
   845  
   846  func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) {
   847  	var (
   848  		nw  libnetwork.Network
   849  		err error
   850  	)
   851  	switch by {
   852  	case byID:
   853  		nw, err = c.NetworkByID(s)
   854  	case byName:
   855  		if s == "" {
   856  			s = c.Config().Daemon.DefaultNetwork
   857  		}
   858  		nw, err = c.NetworkByName(s)
   859  	default:
   860  		panic(fmt.Sprintf("unexpected selector for network search: %d", by))
   861  	}
   862  	if err != nil {
   863  		if _, ok := err.(types.NotFoundError); ok {
   864  			return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound}
   865  		}
   866  		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
   867  	}
   868  	return nw, &successResponse
   869  }
   870  
   871  func findSandbox(c libnetwork.NetworkController, s string, by int) (libnetwork.Sandbox, *responseStatus) {
   872  	var (
   873  		sb  libnetwork.Sandbox
   874  		err error
   875  	)
   876  
   877  	switch by {
   878  	case byID:
   879  		sb, err = c.SandboxByID(s)
   880  	default:
   881  		panic(fmt.Sprintf("unexpected selector for sandbox search: %d", by))
   882  	}
   883  	if err != nil {
   884  		if _, ok := err.(types.NotFoundError); ok {
   885  			return nil, &responseStatus{Status: "Resource not found: Sandbox", StatusCode: http.StatusNotFound}
   886  		}
   887  		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
   888  	}
   889  	return sb, &successResponse
   890  }
   891  
   892  func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) {
   893  	nw, errRsp := findNetwork(c, ns, nwBy)
   894  	if !errRsp.isOK() {
   895  		return nil, errRsp
   896  	}
   897  	var (
   898  		err error
   899  		ep  libnetwork.Endpoint
   900  	)
   901  	switch epBy {
   902  	case byID:
   903  		ep, err = nw.EndpointByID(es)
   904  	case byName:
   905  		ep, err = nw.EndpointByName(es)
   906  	default:
   907  		panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy))
   908  	}
   909  	if err != nil {
   910  		if _, ok := err.(types.NotFoundError); ok {
   911  			return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound}
   912  		}
   913  		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
   914  	}
   915  	return ep, &successResponse
   916  }
   917  
   918  func findService(c libnetwork.NetworkController, svs string, svBy int) (libnetwork.Endpoint, *responseStatus) {
   919  	for _, nw := range c.Networks() {
   920  		var (
   921  			ep  libnetwork.Endpoint
   922  			err error
   923  		)
   924  		switch svBy {
   925  		case byID:
   926  			ep, err = nw.EndpointByID(svs)
   927  		case byName:
   928  			ep, err = nw.EndpointByName(svs)
   929  		default:
   930  			panic(fmt.Sprintf("unexpected selector for service search: %d", svBy))
   931  		}
   932  		if err == nil {
   933  			return ep, &successResponse
   934  		} else if _, ok := err.(types.NotFoundError); !ok {
   935  			return nil, convertNetworkError(err)
   936  		}
   937  	}
   938  	return nil, &responseStatus{Status: "Service not found", StatusCode: http.StatusNotFound}
   939  }
   940  
   941  func endpointToService(rsp *responseStatus) *responseStatus {
   942  	rsp.Status = strings.Replace(rsp.Status, "endpoint", "service", -1)
   943  	return rsp
   944  }
   945  
   946  func convertNetworkError(err error) *responseStatus {
   947  	var code int
   948  	switch err.(type) {
   949  	case types.BadRequestError:
   950  		code = http.StatusBadRequest
   951  	case types.ForbiddenError:
   952  		code = http.StatusForbidden
   953  	case types.NotFoundError:
   954  		code = http.StatusNotFound
   955  	case types.TimeoutError:
   956  		code = http.StatusRequestTimeout
   957  	case types.NotImplementedError:
   958  		code = http.StatusNotImplemented
   959  	case types.NoServiceError:
   960  		code = http.StatusServiceUnavailable
   961  	case types.InternalError:
   962  		code = http.StatusInternalServerError
   963  	default:
   964  		code = http.StatusInternalServerError
   965  	}
   966  	return &responseStatus{Status: err.Error(), StatusCode: code}
   967  }
   968  
   969  func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
   970  	w.Header().Set("Content-Type", "application/json")
   971  	w.WriteHeader(code)
   972  	return json.NewEncoder(w).Encode(v)
   973  }