* Add Virtual Kubelet provider for VIC Initial virtual kubelet provider for VMware VIC. This provider currently handles creating and starting of a pod VM via the VIC portlayer and persona server. Image store handling via the VIC persona server. This provider currently requires the feature/wolfpack branch of VIC. * Added pod stop and delete. Also added node capacity. Added the ability to stop and delete pod VMs via VIC. Also retrieve node capacity information from the VCH. * Cleanup and readme file Some file clean up and added a Readme.md markdown file for the VIC provider. * Cleaned up errors, added function comments, moved operation code 1. Cleaned up error handling. Set standard for creating errors. 2. Added method prototype comments for all interface functions. 3. Moved PodCreator, PodStarter, PodStopper, and PodDeleter to a new folder. * Add mocking code and unit tests for podcache, podcreator, and podstarter Used the unit test framework used in VIC to handle assertions in the provider's unit test. Mocking code generated using OSS project mockery, which is compatible with the testify assertion framework. * Vendored packages for the VIC provider Requires feature/wolfpack branch of VIC and a few specific commit sha of projects used within VIC. * Implementation of POD Stopper and Deleter unit tests (#4) * Updated files for initial PR
967 lines
27 KiB
Go
967 lines
27 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"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))
|
|
}
|
|
}
|
|
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 "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
|
|
}
|
|
processCreateDefaults(c, &create)
|
|
|
|
options := []libnetwork.NetworkOption{}
|
|
if len(create.NetworkOpts) > 0 {
|
|
if _, ok := create.NetworkOpts[netlabel.Internal]; ok {
|
|
options = append(options, libnetwork.NetworkOptionInternalNetwork())
|
|
}
|
|
}
|
|
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 "", 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
|
|
if ec.ExposedPorts != nil {
|
|
setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts))
|
|
}
|
|
if ec.PortMapping != nil {
|
|
setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping))
|
|
}
|
|
|
|
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
|
|
if sp.ExposedPorts != nil {
|
|
setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(sp.ExposedPorts))
|
|
}
|
|
if sp.PortMapping != nil {
|
|
setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(sp.PortMapping))
|
|
}
|
|
|
|
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)
|
|
}
|