mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00

Currently the libnetwork function `NewNetwork` does not allow caller to pass a network ID and it is always generated internally. This is sufficient for engine use. But it doesn't satisfy the needs of libnetwork being used as an independent library in programs other than the engine. This enhancement is one of the many needed to facilitate a generic libnetwork. Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
970 lines
27 KiB
Go
970 lines
27 KiB
Go
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))
|
|
}
|
|
nw, err := c.NewNetwork(create.NetworkType, create.Name, "", 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)
|
|
}
|