package api import ( "encoding/json" "fmt" "io/ioutil" "net/http" "strconv" "strings" "github.com/docker/libnetwork" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/types" "github.com/gorilla/mux" ) var ( successResponse = responseStatus{Status: "Success", StatusCode: http.StatusOK} createdResponse = responseStatus{Status: "Created", StatusCode: http.StatusCreated} mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest} badQueryResponse = responseStatus{Status: "Unsupported query", StatusCode: http.StatusBadRequest} ) const ( // Resource name regex // Gorilla mux encloses the passed pattern with '^' and '$'. So we need to do some tricks // to have mux eventually build a query regex which matches empty or word string (`^$|[\w]+`) regex = "[a-zA-Z_0-9-]+" qregx = "$|" + regex // Router URL variable definition nwName = "{" + urlNwName + ":" + regex + "}" nwNameQr = "{" + urlNwName + ":" + qregx + "}" nwID = "{" + urlNwID + ":" + regex + "}" nwPIDQr = "{" + urlNwPID + ":" + qregx + "}" epName = "{" + urlEpName + ":" + regex + "}" epNameQr = "{" + urlEpName + ":" + qregx + "}" epID = "{" + urlEpID + ":" + regex + "}" epPIDQr = "{" + urlEpPID + ":" + qregx + "}" sbID = "{" + urlSbID + ":" + regex + "}" sbPIDQr = "{" + urlSbPID + ":" + qregx + "}" cnIDQr = "{" + urlCnID + ":" + qregx + "}" cnPIDQr = "{" + urlCnPID + ":" + qregx + "}" // Internal URL variable name.They can be anything as // long as they do not collide with query fields. urlNwName = "network-name" urlNwID = "network-id" urlNwPID = "network-partial-id" urlEpName = "endpoint-name" urlEpID = "endpoint-id" urlEpPID = "endpoint-partial-id" urlSbID = "sandbox-id" urlSbPID = "sandbox-partial-id" urlCnID = "container-id" urlCnPID = "container-partial-id" ) // NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork func NewHTTPHandler(c libnetwork.NetworkController) func(w http.ResponseWriter, req *http.Request) { h := &httpHandler{c: c} h.initRouter() return h.handleRequest } type responseStatus struct { Status string StatusCode int } func (r *responseStatus) isOK() bool { return r.StatusCode == http.StatusOK || r.StatusCode == http.StatusCreated } type processor func(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) type httpHandler struct { c libnetwork.NetworkController r *mux.Router } func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) { // Make sure the service is there if h.c == nil { http.Error(w, "NetworkController is not available", http.StatusServiceUnavailable) return } // Get handler from router and execute it h.r.ServeHTTP(w, req) } func (h *httpHandler) initRouter() { m := map[string][]struct { url string qrs []string fct processor }{ "GET": { // Order matters {"/networks", []string{"name", nwNameQr}, procGetNetworks}, {"/networks", []string{"partial-id", nwPIDQr}, procGetNetworks}, {"/networks", nil, procGetNetworks}, {"/networks/" + nwID, nil, procGetNetwork}, {"/networks/" + nwID + "/endpoints", []string{"name", epNameQr}, procGetEndpoints}, {"/networks/" + nwID + "/endpoints", []string{"partial-id", epPIDQr}, procGetEndpoints}, {"/networks/" + nwID + "/endpoints", nil, procGetEndpoints}, {"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint}, {"/services", []string{"network", nwNameQr}, procGetServices}, {"/services", []string{"name", epNameQr}, procGetServices}, {"/services", []string{"partial-id", epPIDQr}, procGetServices}, {"/services", nil, procGetServices}, {"/services/" + epID, nil, procGetService}, {"/services/" + epID + "/backend", nil, procGetSandbox}, {"/sandboxes", []string{"partial-container-id", cnPIDQr}, procGetSandboxes}, {"/sandboxes", []string{"container-id", cnIDQr}, procGetSandboxes}, {"/sandboxes", []string{"partial-id", sbPIDQr}, procGetSandboxes}, {"/sandboxes", nil, procGetSandboxes}, {"/sandboxes/" + sbID, nil, procGetSandbox}, }, "POST": { {"/networks", nil, procCreateNetwork}, {"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint}, {"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes", nil, procJoinEndpoint}, {"/services", nil, procPublishService}, {"/services/" + epID + "/backend", nil, procAttachBackend}, {"/sandboxes", nil, procCreateSandbox}, }, "DELETE": { {"/networks/" + nwID, nil, procDeleteNetwork}, {"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint}, {"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes/" + sbID, nil, procLeaveEndpoint}, {"/services/" + epID, nil, procUnpublishService}, {"/services/" + epID + "/backend/" + sbID, nil, procDetachBackend}, {"/sandboxes/" + sbID, nil, procDeleteSandbox}, }, } h.r = mux.NewRouter() for method, routes := range m { for _, route := range routes { r := h.r.Path("/{.*}" + route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct)) if route.qrs != nil { r.Queries(route.qrs...) } r = h.r.Path(route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct)) if route.qrs != nil { r.Queries(route.qrs...) } } } } func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { var ( body []byte err error ) if req.Body != nil { body, err = ioutil.ReadAll(req.Body) if err != nil { http.Error(w, "Invalid body: "+err.Error(), http.StatusBadRequest) return } } res, rsp := fct(ctrl, mux.Vars(req), body) if !rsp.isOK() { http.Error(w, rsp.Status, rsp.StatusCode) return } if res != nil { writeJSON(w, rsp.StatusCode, res) } } } /***************** Resource Builders ******************/ func buildNetworkResource(nw libnetwork.Network) *networkResource { r := &networkResource{} if nw != nil { r.Name = nw.Name() r.ID = nw.ID() r.Type = nw.Type() epl := nw.Endpoints() r.Endpoints = make([]*endpointResource, 0, len(epl)) for _, e := range epl { epr := buildEndpointResource(e) r.Endpoints = append(r.Endpoints, epr) } } return r } func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource { r := &endpointResource{} if ep != nil { r.Name = ep.Name() r.ID = ep.ID() r.Network = ep.Network() } return r } func buildSandboxResource(sb libnetwork.Sandbox) *sandboxResource { r := &sandboxResource{} if sb != nil { r.ID = sb.ID() r.Key = sb.Key() r.ContainerID = sb.ContainerID() } return r } /**************** Options Parsers *****************/ func (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption { var setFctList []libnetwork.SandboxOption if sc.HostName != "" { setFctList = append(setFctList, libnetwork.OptionHostname(sc.HostName)) } if sc.DomainName != "" { setFctList = append(setFctList, libnetwork.OptionDomainname(sc.DomainName)) } if sc.HostsPath != "" { setFctList = append(setFctList, libnetwork.OptionHostsPath(sc.HostsPath)) } if sc.ResolvConfPath != "" { setFctList = append(setFctList, libnetwork.OptionResolvConfPath(sc.ResolvConfPath)) } if sc.UseDefaultSandbox { setFctList = append(setFctList, libnetwork.OptionUseDefaultSandbox()) } if sc.UseExternalKey { setFctList = append(setFctList, libnetwork.OptionUseExternalKey()) } if sc.DNS != nil { for _, d := range sc.DNS { setFctList = append(setFctList, libnetwork.OptionDNS(d)) } } if sc.ExtraHosts != nil { for _, e := range sc.ExtraHosts { setFctList = append(setFctList, libnetwork.OptionExtraHost(e.Name, e.Address)) } } if sc.ExposedPorts != nil { setFctList = append(setFctList, libnetwork.OptionExposedPorts(sc.ExposedPorts)) } if sc.PortMapping != nil { setFctList = append(setFctList, libnetwork.OptionPortMapping(sc.PortMapping)) } return setFctList } func (ej *endpointJoin) parseOptions() []libnetwork.EndpointOption { // priority will go here return []libnetwork.EndpointOption{} } /****************** Process functions *******************/ func processCreateDefaults(c libnetwork.NetworkController, nc *networkCreate) { if nc.NetworkType == "" { nc.NetworkType = c.Config().Daemon.DefaultDriver } } /*************************** NetworkController interface ****************************/ func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { var create networkCreate err := json.Unmarshal(body, &create) if err != nil { return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} } processCreateDefaults(c, &create) options := []libnetwork.NetworkOption{} if val, ok := create.NetworkOpts[netlabel.Internal]; ok { internal, err := strconv.ParseBool(val) if err != nil { return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} } if internal { options = append(options, libnetwork.NetworkOptionInternalNetwork()) } } if val, ok := create.NetworkOpts[netlabel.EnableIPv6]; ok { enableIPv6, err := strconv.ParseBool(val) if err != nil { return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} } options = append(options, libnetwork.NetworkOptionEnableIPv6(enableIPv6)) } if len(create.DriverOpts) > 0 { options = append(options, libnetwork.NetworkOptionDriverOpts(create.DriverOpts)) } if len(create.IPv4Conf) > 0 { ipamV4Conf := &libnetwork.IpamConf{ PreferredPool: create.IPv4Conf[0].PreferredPool, SubPool: create.IPv4Conf[0].SubPool, } options = append(options, libnetwork.NetworkOptionIpam("default", "", []*libnetwork.IpamConf{ipamV4Conf}, nil, nil)) } nw, err := c.NewNetwork(create.NetworkType, create.Name, create.ID, options...) if err != nil { return nil, convertNetworkError(err) } return nw.ID(), &createdResponse } func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { t, by := detectNetworkTarget(vars) nw, errRsp := findNetwork(c, t, by) if !errRsp.isOK() { return nil, errRsp } return buildNetworkResource(nw), &successResponse } func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { var list []*networkResource // Look for query filters and validate name, queryByName := vars[urlNwName] shortID, queryByPid := vars[urlNwPID] if queryByName && queryByPid { return nil, &badQueryResponse } if queryByName { if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() { list = append(list, buildNetworkResource(nw)) } } else if queryByPid { // Return all the prefix-matching networks l := func(nw libnetwork.Network) bool { if strings.HasPrefix(nw.ID(), shortID) { list = append(list, buildNetworkResource(nw)) } return false } c.WalkNetworks(l) } else { for _, nw := range c.Networks() { list = append(list, buildNetworkResource(nw)) } } return list, &successResponse } func procCreateSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { var create sandboxCreate err := json.Unmarshal(body, &create) if err != nil { return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} } sb, err := c.NewSandbox(create.ContainerID, create.parseOptions()...) if err != nil { return "", convertNetworkError(err) } return sb.ID(), &createdResponse } /****************** Network interface *******************/ func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { var ec endpointCreate err := json.Unmarshal(body, &ec) if err != nil { return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} } nwT, nwBy := detectNetworkTarget(vars) n, errRsp := findNetwork(c, nwT, nwBy) if !errRsp.isOK() { return "", errRsp } var setFctList []libnetwork.EndpointOption for _, str := range ec.MyAliases { setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str)) } ep, err := n.CreateEndpoint(ec.Name, setFctList...) if err != nil { return "", convertNetworkError(err) } return ep.ID(), &createdResponse } func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { nwT, nwBy := detectNetworkTarget(vars) epT, epBy := detectEndpointTarget(vars) ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) if !errRsp.isOK() { return nil, errRsp } return buildEndpointResource(ep), &successResponse } func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { // Look for query filters and validate name, queryByName := vars[urlEpName] shortID, queryByPid := vars[urlEpPID] if queryByName && queryByPid { return nil, &badQueryResponse } nwT, nwBy := detectNetworkTarget(vars) nw, errRsp := findNetwork(c, nwT, nwBy) if !errRsp.isOK() { return nil, errRsp } var list []*endpointResource // If query parameter is specified, return a filtered collection if queryByName { if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() { list = append(list, buildEndpointResource(ep)) } } else if queryByPid { // Return all the prefix-matching endpoints l := func(ep libnetwork.Endpoint) bool { if strings.HasPrefix(ep.ID(), shortID) { list = append(list, buildEndpointResource(ep)) } return false } nw.WalkEndpoints(l) } else { for _, ep := range nw.Endpoints() { epr := buildEndpointResource(ep) list = append(list, epr) } } return list, &successResponse } func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { target, by := detectNetworkTarget(vars) nw, errRsp := findNetwork(c, target, by) if !errRsp.isOK() { return nil, errRsp } err := nw.Delete() if err != nil { return nil, convertNetworkError(err) } return nil, &successResponse } /****************** Endpoint interface *******************/ func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { var ej endpointJoin var setFctList []libnetwork.EndpointOption err := json.Unmarshal(body, &ej) if err != nil { return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} } nwT, nwBy := detectNetworkTarget(vars) epT, epBy := detectEndpointTarget(vars) ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) if !errRsp.isOK() { return nil, errRsp } sb, errRsp := findSandbox(c, ej.SandboxID, byID) if !errRsp.isOK() { return nil, errRsp } for _, str := range ej.Aliases { name, alias, err := netutils.ParseAlias(str) if err != nil { return "", convertNetworkError(err) } setFctList = append(setFctList, libnetwork.CreateOptionAlias(name, alias)) } err = ep.Join(sb, setFctList...) if err != nil { return nil, convertNetworkError(err) } return sb.Key(), &successResponse } func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { nwT, nwBy := detectNetworkTarget(vars) epT, epBy := detectEndpointTarget(vars) ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) if !errRsp.isOK() { return nil, errRsp } sb, errRsp := findSandbox(c, vars[urlSbID], byID) if !errRsp.isOK() { return nil, errRsp } err := ep.Leave(sb) if err != nil { return nil, convertNetworkError(err) } return nil, &successResponse } func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { nwT, nwBy := detectNetworkTarget(vars) epT, epBy := detectEndpointTarget(vars) ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) if !errRsp.isOK() { return nil, errRsp } err := ep.Delete(false) if err != nil { return nil, convertNetworkError(err) } return nil, &successResponse } /****************** Service interface *******************/ func procGetServices(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { // Look for query filters and validate nwName, filterByNwName := vars[urlNwName] svName, queryBySvName := vars[urlEpName] shortID, queryBySvPID := vars[urlEpPID] if filterByNwName && queryBySvName || filterByNwName && queryBySvPID || queryBySvName && queryBySvPID { return nil, &badQueryResponse } var list []*endpointResource switch { case filterByNwName: // return all service present on the specified network nw, errRsp := findNetwork(c, nwName, byName) if !errRsp.isOK() { return list, &successResponse } for _, ep := range nw.Endpoints() { epr := buildEndpointResource(ep) list = append(list, epr) } case queryBySvName: // Look in each network for the service with the specified name l := func(ep libnetwork.Endpoint) bool { if ep.Name() == svName { list = append(list, buildEndpointResource(ep)) return true } return false } for _, nw := range c.Networks() { nw.WalkEndpoints(l) } case queryBySvPID: // Return all the prefix-matching services l := func(ep libnetwork.Endpoint) bool { if strings.HasPrefix(ep.ID(), shortID) { list = append(list, buildEndpointResource(ep)) } return false } for _, nw := range c.Networks() { nw.WalkEndpoints(l) } default: for _, nw := range c.Networks() { for _, ep := range nw.Endpoints() { epr := buildEndpointResource(ep) list = append(list, epr) } } } return list, &successResponse } func procGetService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { epT, epBy := detectEndpointTarget(vars) sv, errRsp := findService(c, epT, epBy) if !errRsp.isOK() { return nil, endpointToService(errRsp) } return buildEndpointResource(sv), &successResponse } func procPublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { var sp servicePublish err := json.Unmarshal(body, &sp) if err != nil { return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} } n, errRsp := findNetwork(c, sp.Network, byName) if !errRsp.isOK() { return "", errRsp } var setFctList []libnetwork.EndpointOption for _, str := range sp.MyAliases { setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str)) } ep, err := n.CreateEndpoint(sp.Name, setFctList...) if err != nil { return "", endpointToService(convertNetworkError(err)) } return ep.ID(), &createdResponse } func procUnpublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { var sd serviceDelete if body != nil { err := json.Unmarshal(body, &sd) if err != nil { return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} } } epT, epBy := detectEndpointTarget(vars) sv, errRsp := findService(c, epT, epBy) if !errRsp.isOK() { return nil, errRsp } if err := sv.Delete(sd.Force); err != nil { return nil, endpointToService(convertNetworkError(err)) } return nil, &successResponse } func procAttachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { var bk endpointJoin var setFctList []libnetwork.EndpointOption err := json.Unmarshal(body, &bk) if err != nil { return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} } epT, epBy := detectEndpointTarget(vars) sv, errRsp := findService(c, epT, epBy) if !errRsp.isOK() { return nil, errRsp } sb, errRsp := findSandbox(c, bk.SandboxID, byID) if !errRsp.isOK() { return nil, errRsp } for _, str := range bk.Aliases { name, alias, err := netutils.ParseAlias(str) if err != nil { return "", convertNetworkError(err) } setFctList = append(setFctList, libnetwork.CreateOptionAlias(name, alias)) } err = sv.Join(sb, setFctList...) if err != nil { return nil, convertNetworkError(err) } return sb.Key(), &successResponse } func procDetachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { epT, epBy := detectEndpointTarget(vars) sv, errRsp := findService(c, epT, epBy) if !errRsp.isOK() { return nil, errRsp } sb, errRsp := findSandbox(c, vars[urlSbID], byID) if !errRsp.isOK() { return nil, errRsp } err := sv.Leave(sb) if err != nil { return nil, convertNetworkError(err) } return nil, &successResponse } /****************** Sandbox interface *******************/ func procGetSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { if epT, ok := vars[urlEpID]; ok { sv, errRsp := findService(c, epT, byID) if !errRsp.isOK() { return nil, endpointToService(errRsp) } return buildSandboxResource(sv.Info().Sandbox()), &successResponse } sbT, by := detectSandboxTarget(vars) sb, errRsp := findSandbox(c, sbT, by) if !errRsp.isOK() { return nil, errRsp } return buildSandboxResource(sb), &successResponse } type cndFnMkr func(string) cndFn type cndFn func(libnetwork.Sandbox) bool // list of (query type, condition function makers) couples var cndMkrList = []struct { identifier string maker cndFnMkr }{ {urlSbPID, func(id string) cndFn { return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ID(), id) } }}, {urlCnID, func(id string) cndFn { return func(sb libnetwork.Sandbox) bool { return sb.ContainerID() == id } }}, {urlCnPID, func(id string) cndFn { return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ContainerID(), id) } }}, } func getQueryCondition(vars map[string]string) func(libnetwork.Sandbox) bool { for _, im := range cndMkrList { if val, ok := vars[im.identifier]; ok { return im.maker(val) } } return func(sb libnetwork.Sandbox) bool { return true } } func sandboxWalker(condition cndFn, list *[]*sandboxResource) libnetwork.SandboxWalker { return func(sb libnetwork.Sandbox) bool { if condition(sb) { *list = append(*list, buildSandboxResource(sb)) } return false } } func procGetSandboxes(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { var list []*sandboxResource cnd := getQueryCondition(vars) c.WalkSandboxes(sandboxWalker(cnd, &list)) return list, &successResponse } func procDeleteSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { sbT, by := detectSandboxTarget(vars) sb, errRsp := findSandbox(c, sbT, by) if !errRsp.isOK() { return nil, errRsp } err := sb.Delete() if err != nil { return nil, convertNetworkError(err) } return nil, &successResponse } /*********** Utilities ************/ const ( byID = iota byName ) func detectNetworkTarget(vars map[string]string) (string, int) { if target, ok := vars[urlNwName]; ok { return target, byName } if target, ok := vars[urlNwID]; ok { return target, byID } // vars are populated from the URL, following cannot happen panic("Missing URL variable parameter for network") } func detectSandboxTarget(vars map[string]string) (string, int) { if target, ok := vars[urlSbID]; ok { return target, byID } // vars are populated from the URL, following cannot happen panic("Missing URL variable parameter for sandbox") } func detectEndpointTarget(vars map[string]string) (string, int) { if target, ok := vars[urlEpName]; ok { return target, byName } if target, ok := vars[urlEpID]; ok { return target, byID } // vars are populated from the URL, following cannot happen panic("Missing URL variable parameter for endpoint") } func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) { var ( nw libnetwork.Network err error ) switch by { case byID: nw, err = c.NetworkByID(s) case byName: if s == "" { s = c.Config().Daemon.DefaultNetwork } nw, err = c.NetworkByName(s) default: panic(fmt.Sprintf("unexpected selector for network search: %d", by)) } if err != nil { if _, ok := err.(types.NotFoundError); ok { return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound} } return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} } return nw, &successResponse } func findSandbox(c libnetwork.NetworkController, s string, by int) (libnetwork.Sandbox, *responseStatus) { var ( sb libnetwork.Sandbox err error ) switch by { case byID: sb, err = c.SandboxByID(s) default: panic(fmt.Sprintf("unexpected selector for sandbox search: %d", by)) } if err != nil { if _, ok := err.(types.NotFoundError); ok { return nil, &responseStatus{Status: "Resource not found: Sandbox", StatusCode: http.StatusNotFound} } return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} } return sb, &successResponse } func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) { nw, errRsp := findNetwork(c, ns, nwBy) if !errRsp.isOK() { return nil, errRsp } var ( err error ep libnetwork.Endpoint ) switch epBy { case byID: ep, err = nw.EndpointByID(es) case byName: ep, err = nw.EndpointByName(es) default: panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy)) } if err != nil { if _, ok := err.(types.NotFoundError); ok { return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound} } return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} } return ep, &successResponse } func findService(c libnetwork.NetworkController, svs string, svBy int) (libnetwork.Endpoint, *responseStatus) { for _, nw := range c.Networks() { var ( ep libnetwork.Endpoint err error ) switch svBy { case byID: ep, err = nw.EndpointByID(svs) case byName: ep, err = nw.EndpointByName(svs) default: panic(fmt.Sprintf("unexpected selector for service search: %d", svBy)) } if err == nil { return ep, &successResponse } else if _, ok := err.(types.NotFoundError); !ok { return nil, convertNetworkError(err) } } return nil, &responseStatus{Status: "Service not found", StatusCode: http.StatusNotFound} } func endpointToService(rsp *responseStatus) *responseStatus { rsp.Status = strings.Replace(rsp.Status, "endpoint", "service", -1) return rsp } func convertNetworkError(err error) *responseStatus { var code int switch err.(type) { case types.BadRequestError: code = http.StatusBadRequest case types.ForbiddenError: code = http.StatusForbidden case types.NotFoundError: code = http.StatusNotFound case types.TimeoutError: code = http.StatusRequestTimeout case types.NotImplementedError: code = http.StatusNotImplemented case types.NoServiceError: code = http.StatusServiceUnavailable case types.InternalError: code = http.StatusInternalServerError default: code = http.StatusInternalServerError } return &responseStatus{Status: err.Error(), StatusCode: code} } func writeJSON(w http.ResponseWriter, code int, v interface{}) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) return json.NewEncoder(w).Encode(v) }