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