github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/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  
   282  func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   283  	var create networkCreate
   284  
   285  	err := json.Unmarshal(body, &create)
   286  	if err != nil {
   287  		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   288  	}
   289  	processCreateDefaults(c, &create)
   290  
   291  	options := []libnetwork.NetworkOption{}
   292  	if val, ok := create.NetworkOpts[netlabel.Internal]; ok {
   293  		internal, err := strconv.ParseBool(val)
   294  		if err != nil {
   295  			return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
   296  		}
   297  		if internal {
   298  			options = append(options, libnetwork.NetworkOptionInternalNetwork())
   299  		}
   300  	}
   301  	if val, ok := create.NetworkOpts[netlabel.EnableIPv6]; ok {
   302  		enableIPv6, err := strconv.ParseBool(val)
   303  		if err != nil {
   304  			return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
   305  		}
   306  		options = append(options, libnetwork.NetworkOptionEnableIPv6(enableIPv6))
   307  	}
   308  	if len(create.DriverOpts) > 0 {
   309  		options = append(options, libnetwork.NetworkOptionDriverOpts(create.DriverOpts))
   310  	}
   311  
   312  	if len(create.IPv4Conf) > 0 {
   313  		ipamV4Conf := &libnetwork.IpamConf{
   314  			PreferredPool: create.IPv4Conf[0].PreferredPool,
   315  			SubPool:       create.IPv4Conf[0].SubPool,
   316  		}
   317  
   318  		options = append(options, libnetwork.NetworkOptionIpam("default", "", []*libnetwork.IpamConf{ipamV4Conf}, nil, nil))
   319  	}
   320  
   321  	nw, err := c.NewNetwork(create.NetworkType, create.Name, create.ID, options...)
   322  	if err != nil {
   323  		return nil, convertNetworkError(err)
   324  	}
   325  
   326  	return nw.ID(), &createdResponse
   327  }
   328  
   329  func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   330  	t, by := detectNetworkTarget(vars)
   331  	nw, errRsp := findNetwork(c, t, by)
   332  	if !errRsp.isOK() {
   333  		return nil, errRsp
   334  	}
   335  	return buildNetworkResource(nw), &successResponse
   336  }
   337  
   338  func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   339  	var list []*networkResource
   340  
   341  	// Look for query filters and validate
   342  	name, queryByName := vars[urlNwName]
   343  	shortID, queryByPid := vars[urlNwPID]
   344  	if queryByName && queryByPid {
   345  		return nil, &badQueryResponse
   346  	}
   347  
   348  	if queryByName {
   349  		if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() {
   350  			list = append(list, buildNetworkResource(nw))
   351  		}
   352  	} else if queryByPid {
   353  		// Return all the prefix-matching networks
   354  		l := func(nw libnetwork.Network) bool {
   355  			if strings.HasPrefix(nw.ID(), shortID) {
   356  				list = append(list, buildNetworkResource(nw))
   357  			}
   358  			return false
   359  		}
   360  		c.WalkNetworks(l)
   361  	} else {
   362  		for _, nw := range c.Networks() {
   363  			list = append(list, buildNetworkResource(nw))
   364  		}
   365  	}
   366  
   367  	return list, &successResponse
   368  }
   369  
   370  func procCreateSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   371  	var create sandboxCreate
   372  
   373  	err := json.Unmarshal(body, &create)
   374  	if err != nil {
   375  		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   376  	}
   377  
   378  	sb, err := c.NewSandbox(create.ContainerID, create.parseOptions()...)
   379  	if err != nil {
   380  		return "", convertNetworkError(err)
   381  	}
   382  
   383  	return sb.ID(), &createdResponse
   384  }
   385  
   386  /******************
   387   Network interface
   388  *******************/
   389  
   390  func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   391  	var ec endpointCreate
   392  
   393  	err := json.Unmarshal(body, &ec)
   394  	if err != nil {
   395  		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   396  	}
   397  
   398  	nwT, nwBy := detectNetworkTarget(vars)
   399  	n, errRsp := findNetwork(c, nwT, nwBy)
   400  	if !errRsp.isOK() {
   401  		return "", errRsp
   402  	}
   403  
   404  	var setFctList []libnetwork.EndpointOption
   405  	for _, str := range ec.MyAliases {
   406  		setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str))
   407  	}
   408  
   409  	ep, err := n.CreateEndpoint(ec.Name, setFctList...)
   410  	if err != nil {
   411  		return "", convertNetworkError(err)
   412  	}
   413  
   414  	return ep.ID(), &createdResponse
   415  }
   416  
   417  func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   418  	nwT, nwBy := detectNetworkTarget(vars)
   419  	epT, epBy := detectEndpointTarget(vars)
   420  
   421  	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
   422  	if !errRsp.isOK() {
   423  		return nil, errRsp
   424  	}
   425  
   426  	return buildEndpointResource(ep), &successResponse
   427  }
   428  
   429  func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   430  	// Look for query filters and validate
   431  	name, queryByName := vars[urlEpName]
   432  	shortID, queryByPid := vars[urlEpPID]
   433  	if queryByName && queryByPid {
   434  		return nil, &badQueryResponse
   435  	}
   436  
   437  	nwT, nwBy := detectNetworkTarget(vars)
   438  	nw, errRsp := findNetwork(c, nwT, nwBy)
   439  	if !errRsp.isOK() {
   440  		return nil, errRsp
   441  	}
   442  
   443  	var list []*endpointResource
   444  
   445  	// If query parameter is specified, return a filtered collection
   446  	if queryByName {
   447  		if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() {
   448  			list = append(list, buildEndpointResource(ep))
   449  		}
   450  	} else if queryByPid {
   451  		// Return all the prefix-matching endpoints
   452  		l := func(ep libnetwork.Endpoint) bool {
   453  			if strings.HasPrefix(ep.ID(), shortID) {
   454  				list = append(list, buildEndpointResource(ep))
   455  			}
   456  			return false
   457  		}
   458  		nw.WalkEndpoints(l)
   459  	} else {
   460  		for _, ep := range nw.Endpoints() {
   461  			epr := buildEndpointResource(ep)
   462  			list = append(list, epr)
   463  		}
   464  	}
   465  
   466  	return list, &successResponse
   467  }
   468  
   469  func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   470  	target, by := detectNetworkTarget(vars)
   471  
   472  	nw, errRsp := findNetwork(c, target, by)
   473  	if !errRsp.isOK() {
   474  		return nil, errRsp
   475  	}
   476  
   477  	err := nw.Delete()
   478  	if err != nil {
   479  		return nil, convertNetworkError(err)
   480  	}
   481  
   482  	return nil, &successResponse
   483  }
   484  
   485  /******************
   486   Endpoint interface
   487  *******************/
   488  
   489  func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   490  	var ej endpointJoin
   491  	var setFctList []libnetwork.EndpointOption
   492  	err := json.Unmarshal(body, &ej)
   493  	if err != nil {
   494  		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   495  	}
   496  
   497  	nwT, nwBy := detectNetworkTarget(vars)
   498  	epT, epBy := detectEndpointTarget(vars)
   499  
   500  	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
   501  	if !errRsp.isOK() {
   502  		return nil, errRsp
   503  	}
   504  
   505  	sb, errRsp := findSandbox(c, ej.SandboxID, byID)
   506  	if !errRsp.isOK() {
   507  		return nil, errRsp
   508  	}
   509  
   510  	for _, str := range ej.Aliases {
   511  		name, alias, err := netutils.ParseAlias(str)
   512  		if err != nil {
   513  			return "", convertNetworkError(err)
   514  		}
   515  		setFctList = append(setFctList, libnetwork.CreateOptionAlias(name, alias))
   516  	}
   517  
   518  	err = ep.Join(sb, setFctList...)
   519  	if err != nil {
   520  		return nil, convertNetworkError(err)
   521  	}
   522  	return sb.Key(), &successResponse
   523  }
   524  
   525  func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   526  	nwT, nwBy := detectNetworkTarget(vars)
   527  	epT, epBy := detectEndpointTarget(vars)
   528  
   529  	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
   530  	if !errRsp.isOK() {
   531  		return nil, errRsp
   532  	}
   533  
   534  	sb, errRsp := findSandbox(c, vars[urlSbID], byID)
   535  	if !errRsp.isOK() {
   536  		return nil, errRsp
   537  	}
   538  
   539  	err := ep.Leave(sb)
   540  	if err != nil {
   541  		return nil, convertNetworkError(err)
   542  	}
   543  
   544  	return nil, &successResponse
   545  }
   546  
   547  func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   548  	nwT, nwBy := detectNetworkTarget(vars)
   549  	epT, epBy := detectEndpointTarget(vars)
   550  
   551  	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
   552  	if !errRsp.isOK() {
   553  		return nil, errRsp
   554  	}
   555  
   556  	err := ep.Delete(false)
   557  	if err != nil {
   558  		return nil, convertNetworkError(err)
   559  	}
   560  
   561  	return nil, &successResponse
   562  }
   563  
   564  /******************
   565   Service interface
   566  *******************/
   567  
   568  func procGetServices(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   569  	// Look for query filters and validate
   570  	nwName, filterByNwName := vars[urlNwName]
   571  	svName, queryBySvName := vars[urlEpName]
   572  	shortID, queryBySvPID := vars[urlEpPID]
   573  
   574  	if filterByNwName && queryBySvName || filterByNwName && queryBySvPID || queryBySvName && queryBySvPID {
   575  		return nil, &badQueryResponse
   576  	}
   577  
   578  	var list []*endpointResource
   579  
   580  	switch {
   581  	case filterByNwName:
   582  		// return all service present on the specified network
   583  		nw, errRsp := findNetwork(c, nwName, byName)
   584  		if !errRsp.isOK() {
   585  			return list, &successResponse
   586  		}
   587  		for _, ep := range nw.Endpoints() {
   588  			epr := buildEndpointResource(ep)
   589  			list = append(list, epr)
   590  		}
   591  	case queryBySvName:
   592  		// Look in each network for the service with the specified name
   593  		l := func(ep libnetwork.Endpoint) bool {
   594  			if ep.Name() == svName {
   595  				list = append(list, buildEndpointResource(ep))
   596  				return true
   597  			}
   598  			return false
   599  		}
   600  		for _, nw := range c.Networks() {
   601  			nw.WalkEndpoints(l)
   602  		}
   603  	case queryBySvPID:
   604  		// Return all the prefix-matching services
   605  		l := func(ep libnetwork.Endpoint) bool {
   606  			if strings.HasPrefix(ep.ID(), shortID) {
   607  				list = append(list, buildEndpointResource(ep))
   608  			}
   609  			return false
   610  		}
   611  		for _, nw := range c.Networks() {
   612  			nw.WalkEndpoints(l)
   613  		}
   614  	default:
   615  		for _, nw := range c.Networks() {
   616  			for _, ep := range nw.Endpoints() {
   617  				epr := buildEndpointResource(ep)
   618  				list = append(list, epr)
   619  			}
   620  		}
   621  	}
   622  
   623  	return list, &successResponse
   624  }
   625  
   626  func procGetService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   627  	epT, epBy := detectEndpointTarget(vars)
   628  	sv, errRsp := findService(c, epT, epBy)
   629  	if !errRsp.isOK() {
   630  		return nil, endpointToService(errRsp)
   631  	}
   632  	return buildEndpointResource(sv), &successResponse
   633  }
   634  
   635  func procPublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   636  	var sp servicePublish
   637  
   638  	err := json.Unmarshal(body, &sp)
   639  	if err != nil {
   640  		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   641  	}
   642  
   643  	n, errRsp := findNetwork(c, sp.Network, byName)
   644  	if !errRsp.isOK() {
   645  		return "", errRsp
   646  	}
   647  
   648  	var setFctList []libnetwork.EndpointOption
   649  	for _, str := range sp.MyAliases {
   650  		setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str))
   651  	}
   652  
   653  	ep, err := n.CreateEndpoint(sp.Name, setFctList...)
   654  	if err != nil {
   655  		return "", endpointToService(convertNetworkError(err))
   656  	}
   657  
   658  	return ep.ID(), &createdResponse
   659  }
   660  
   661  func procUnpublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   662  	var sd serviceDelete
   663  
   664  	if body != nil {
   665  		err := json.Unmarshal(body, &sd)
   666  		if err != nil {
   667  			return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   668  		}
   669  	}
   670  
   671  	epT, epBy := detectEndpointTarget(vars)
   672  	sv, errRsp := findService(c, epT, epBy)
   673  	if !errRsp.isOK() {
   674  		return nil, errRsp
   675  	}
   676  
   677  	if err := sv.Delete(sd.Force); err != nil {
   678  		return nil, endpointToService(convertNetworkError(err))
   679  	}
   680  	return nil, &successResponse
   681  }
   682  
   683  func procAttachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   684  	var bk endpointJoin
   685  	var setFctList []libnetwork.EndpointOption
   686  	err := json.Unmarshal(body, &bk)
   687  	if err != nil {
   688  		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
   689  	}
   690  
   691  	epT, epBy := detectEndpointTarget(vars)
   692  	sv, errRsp := findService(c, epT, epBy)
   693  	if !errRsp.isOK() {
   694  		return nil, errRsp
   695  	}
   696  
   697  	sb, errRsp := findSandbox(c, bk.SandboxID, byID)
   698  	if !errRsp.isOK() {
   699  		return nil, errRsp
   700  	}
   701  
   702  	for _, str := range bk.Aliases {
   703  		name, alias, err := netutils.ParseAlias(str)
   704  		if err != nil {
   705  			return "", convertNetworkError(err)
   706  		}
   707  		setFctList = append(setFctList, libnetwork.CreateOptionAlias(name, alias))
   708  	}
   709  
   710  	err = sv.Join(sb, setFctList...)
   711  	if err != nil {
   712  		return nil, convertNetworkError(err)
   713  	}
   714  
   715  	return sb.Key(), &successResponse
   716  }
   717  
   718  func procDetachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   719  	epT, epBy := detectEndpointTarget(vars)
   720  	sv, errRsp := findService(c, epT, epBy)
   721  	if !errRsp.isOK() {
   722  		return nil, errRsp
   723  	}
   724  
   725  	sb, errRsp := findSandbox(c, vars[urlSbID], byID)
   726  	if !errRsp.isOK() {
   727  		return nil, errRsp
   728  	}
   729  
   730  	err := sv.Leave(sb)
   731  	if err != nil {
   732  		return nil, convertNetworkError(err)
   733  	}
   734  
   735  	return nil, &successResponse
   736  }
   737  
   738  /******************
   739   Sandbox interface
   740  *******************/
   741  
   742  func procGetSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   743  	if epT, ok := vars[urlEpID]; ok {
   744  		sv, errRsp := findService(c, epT, byID)
   745  		if !errRsp.isOK() {
   746  			return nil, endpointToService(errRsp)
   747  		}
   748  		return buildSandboxResource(sv.Info().Sandbox()), &successResponse
   749  	}
   750  
   751  	sbT, by := detectSandboxTarget(vars)
   752  	sb, errRsp := findSandbox(c, sbT, by)
   753  	if !errRsp.isOK() {
   754  		return nil, errRsp
   755  	}
   756  	return buildSandboxResource(sb), &successResponse
   757  }
   758  
   759  type cndFnMkr func(string) cndFn
   760  type cndFn func(libnetwork.Sandbox) bool
   761  
   762  // list of (query type, condition function makers) couples
   763  var cndMkrList = []struct {
   764  	identifier string
   765  	maker      cndFnMkr
   766  }{
   767  	{urlSbPID, func(id string) cndFn {
   768  		return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ID(), id) }
   769  	}},
   770  	{urlCnID, func(id string) cndFn {
   771  		return func(sb libnetwork.Sandbox) bool { return sb.ContainerID() == id }
   772  	}},
   773  	{urlCnPID, func(id string) cndFn {
   774  		return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ContainerID(), id) }
   775  	}},
   776  }
   777  
   778  func getQueryCondition(vars map[string]string) func(libnetwork.Sandbox) bool {
   779  	for _, im := range cndMkrList {
   780  		if val, ok := vars[im.identifier]; ok {
   781  			return im.maker(val)
   782  		}
   783  	}
   784  	return func(sb libnetwork.Sandbox) bool { return true }
   785  }
   786  
   787  func sandboxWalker(condition cndFn, list *[]*sandboxResource) libnetwork.SandboxWalker {
   788  	return func(sb libnetwork.Sandbox) bool {
   789  		if condition(sb) {
   790  			*list = append(*list, buildSandboxResource(sb))
   791  		}
   792  		return false
   793  	}
   794  }
   795  
   796  func procGetSandboxes(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   797  	var list []*sandboxResource
   798  
   799  	cnd := getQueryCondition(vars)
   800  	c.WalkSandboxes(sandboxWalker(cnd, &list))
   801  
   802  	return list, &successResponse
   803  }
   804  
   805  func procDeleteSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
   806  	sbT, by := detectSandboxTarget(vars)
   807  
   808  	sb, errRsp := findSandbox(c, sbT, by)
   809  	if !errRsp.isOK() {
   810  		return nil, errRsp
   811  	}
   812  
   813  	err := sb.Delete()
   814  	if err != nil {
   815  		return nil, convertNetworkError(err)
   816  	}
   817  
   818  	return nil, &successResponse
   819  }
   820  
   821  /***********
   822    Utilities
   823  ************/
   824  
   825  const (
   826  	byID = iota
   827  	byName
   828  )
   829  
   830  func detectNetworkTarget(vars map[string]string) (string, int) {
   831  	if target, ok := vars[urlNwName]; ok {
   832  		return target, byName
   833  	}
   834  	if target, ok := vars[urlNwID]; ok {
   835  		return target, byID
   836  	}
   837  	// vars are populated from the URL, following cannot happen
   838  	panic("Missing URL variable parameter for network")
   839  }
   840  
   841  func detectSandboxTarget(vars map[string]string) (string, int) {
   842  	if target, ok := vars[urlSbID]; ok {
   843  		return target, byID
   844  	}
   845  	// vars are populated from the URL, following cannot happen
   846  	panic("Missing URL variable parameter for sandbox")
   847  }
   848  
   849  func detectEndpointTarget(vars map[string]string) (string, int) {
   850  	if target, ok := vars[urlEpName]; ok {
   851  		return target, byName
   852  	}
   853  	if target, ok := vars[urlEpID]; ok {
   854  		return target, byID
   855  	}
   856  	// vars are populated from the URL, following cannot happen
   857  	panic("Missing URL variable parameter for endpoint")
   858  }
   859  
   860  func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) {
   861  	var (
   862  		nw  libnetwork.Network
   863  		err error
   864  	)
   865  	switch by {
   866  	case byID:
   867  		nw, err = c.NetworkByID(s)
   868  	case byName:
   869  		if s == "" {
   870  			s = c.Config().Daemon.DefaultNetwork
   871  		}
   872  		nw, err = c.NetworkByName(s)
   873  	default:
   874  		panic(fmt.Sprintf("unexpected selector for network search: %d", by))
   875  	}
   876  	if err != nil {
   877  		if _, ok := err.(types.NotFoundError); ok {
   878  			return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound}
   879  		}
   880  		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
   881  	}
   882  	return nw, &successResponse
   883  }
   884  
   885  func findSandbox(c libnetwork.NetworkController, s string, by int) (libnetwork.Sandbox, *responseStatus) {
   886  	var (
   887  		sb  libnetwork.Sandbox
   888  		err error
   889  	)
   890  
   891  	switch by {
   892  	case byID:
   893  		sb, err = c.SandboxByID(s)
   894  	default:
   895  		panic(fmt.Sprintf("unexpected selector for sandbox search: %d", by))
   896  	}
   897  	if err != nil {
   898  		if _, ok := err.(types.NotFoundError); ok {
   899  			return nil, &responseStatus{Status: "Resource not found: Sandbox", StatusCode: http.StatusNotFound}
   900  		}
   901  		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
   902  	}
   903  	return sb, &successResponse
   904  }
   905  
   906  func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) {
   907  	nw, errRsp := findNetwork(c, ns, nwBy)
   908  	if !errRsp.isOK() {
   909  		return nil, errRsp
   910  	}
   911  	var (
   912  		err error
   913  		ep  libnetwork.Endpoint
   914  	)
   915  	switch epBy {
   916  	case byID:
   917  		ep, err = nw.EndpointByID(es)
   918  	case byName:
   919  		ep, err = nw.EndpointByName(es)
   920  	default:
   921  		panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy))
   922  	}
   923  	if err != nil {
   924  		if _, ok := err.(types.NotFoundError); ok {
   925  			return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound}
   926  		}
   927  		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
   928  	}
   929  	return ep, &successResponse
   930  }
   931  
   932  func findService(c libnetwork.NetworkController, svs string, svBy int) (libnetwork.Endpoint, *responseStatus) {
   933  	for _, nw := range c.Networks() {
   934  		var (
   935  			ep  libnetwork.Endpoint
   936  			err error
   937  		)
   938  		switch svBy {
   939  		case byID:
   940  			ep, err = nw.EndpointByID(svs)
   941  		case byName:
   942  			ep, err = nw.EndpointByName(svs)
   943  		default:
   944  			panic(fmt.Sprintf("unexpected selector for service search: %d", svBy))
   945  		}
   946  		if err == nil {
   947  			return ep, &successResponse
   948  		} else if _, ok := err.(types.NotFoundError); !ok {
   949  			return nil, convertNetworkError(err)
   950  		}
   951  	}
   952  	return nil, &responseStatus{Status: "Service not found", StatusCode: http.StatusNotFound}
   953  }
   954  
   955  func endpointToService(rsp *responseStatus) *responseStatus {
   956  	rsp.Status = strings.Replace(rsp.Status, "endpoint", "service", -1)
   957  	return rsp
   958  }
   959  
   960  func convertNetworkError(err error) *responseStatus {
   961  	var code int
   962  	switch err.(type) {
   963  	case types.BadRequestError:
   964  		code = http.StatusBadRequest
   965  	case types.ForbiddenError:
   966  		code = http.StatusForbidden
   967  	case types.NotFoundError:
   968  		code = http.StatusNotFound
   969  	case types.TimeoutError:
   970  		code = http.StatusRequestTimeout
   971  	case types.NotImplementedError:
   972  		code = http.StatusNotImplemented
   973  	case types.NoServiceError:
   974  		code = http.StatusServiceUnavailable
   975  	case types.InternalError:
   976  		code = http.StatusInternalServerError
   977  	default:
   978  		code = http.StatusInternalServerError
   979  	}
   980  	return &responseStatus{Status: err.Error(), StatusCode: code}
   981  }
   982  
   983  func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
   984  	w.Header().Set("Content-Type", "application/json")
   985  	w.WriteHeader(code)
   986  	return json.NewEncoder(w).Encode(v)
   987  }