Browse Source

Network remote APIs using new router, --net=<user-defined-network> changes

* Moving Network Remote APIs out of experimental
* --net can now accept user created networks using network drivers/plugins
* Removed the experimental services concept and --default-network option
* Neccessary backend changes to accomodate multiple networks per container
* Integration Tests

Signed-off-by: David Calavera <david.calavera@gmail.com>
Signed-off-by: Madhu Venugopal <madhu@docker.com>
Madhu Venugopal 9 years ago
parent
commit
2ab94e11a2

+ 8 - 7
api/server/router/local/local.go

@@ -5,11 +5,9 @@ import (
 
 	"golang.org/x/net/context"
 
-	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/server/httputils"
 	dkrouter "github.com/docker/docker/api/server/router"
 	"github.com/docker/docker/daemon"
-	"github.com/gorilla/mux"
 )
 
 // router is a docker router that talks with the local docker daemon.
@@ -31,11 +29,14 @@ func (l localRoute) Handler() httputils.APIFunc {
 	return l.handler
 }
 
-// Register adds the filtered handler to the mux.
-func (l localRoute) Register(m *mux.Router, handler http.Handler) {
-	logrus.Debugf("Registering %s, %s", l.method, l.path)
-	m.Path(dkrouter.VersionMatcher + l.path).Methods(l.method).Handler(handler)
-	m.Path(l.path).Methods(l.method).Handler(handler)
+// Method returns the http method that the route responds to.
+func (l localRoute) Method() string {
+	return l.method
+}
+
+// Path returns the subpath where the route responds to.
+func (l localRoute) Path() string {
+	return l.path
 }
 
 // NewRoute initialies a new local route for the reouter

+ 25 - 10
api/server/router/network/network.go

@@ -1,26 +1,41 @@
 package network
 
 import (
-	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/server/router"
+	"github.com/docker/docker/api/server/router/local"
+	"github.com/docker/docker/daemon"
 )
 
 // networkRouter is a router to talk with the network controller
 type networkRouter struct {
+	daemon *daemon.Daemon
 	routes []router.Route
 }
 
-// Routes returns the available routes to the network controller
-func (n networkRouter) Routes() []router.Route {
-	return n.routes
+// NewRouter initializes a new network router
+func NewRouter(d *daemon.Daemon) router.Router {
+	r := &networkRouter{
+		daemon: d,
+	}
+	r.initRoutes()
+	return r
 }
 
-type networkRoute struct {
-	path    string
-	handler httputils.APIFunc
+// Routes returns the available routes to the network controller
+func (r *networkRouter) Routes() []router.Route {
+	return r.routes
 }
 
-// Handler returns the APIFunc to let the server wrap it in middlewares
-func (l networkRoute) Handler() httputils.APIFunc {
-	return l.handler
+func (r *networkRouter) initRoutes() {
+	r.routes = []router.Route{
+		// GET
+		local.NewGetRoute("/networks", r.getNetworksList),
+		local.NewGetRoute("/networks/{id:.*}", r.getNetwork),
+		// POST
+		local.NewPostRoute("/networks/create", r.postNetworkCreate),
+		local.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect),
+		local.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect),
+		// DELETE
+		local.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork),
+	}
 }

+ 0 - 51
api/server/router/network/network_experimental.go

@@ -1,51 +0,0 @@
-// +build experimental
-
-package network
-
-import (
-	"net/http"
-
-	"golang.org/x/net/context"
-
-	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/api/server/router"
-	"github.com/docker/docker/daemon"
-	"github.com/docker/libnetwork/api"
-	"github.com/gorilla/mux"
-)
-
-var httpMethods = []string{"GET", "POST", "PUT", "DELETE"}
-
-// NewRouter initializes a new network router
-func NewRouter(d *daemon.Daemon) router.Router {
-	c := d.NetworkController()
-	if c == nil {
-		return networkRouter{}
-	}
-
-	var routes []router.Route
-	netHandler := api.NewHTTPHandler(c)
-
-	// TODO: libnetwork should stop hijacking request/response.
-	// It should define API functions to add normally to the router.
-	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
-		netHandler(w, r)
-		return nil
-	}
-
-	for _, path := range []string{"/networks", "/services", "/sandboxes"} {
-		routes = append(routes, networkRoute{path, handler})
-	}
-
-	return networkRouter{routes}
-}
-
-// Register adds the filtered handler to the mux.
-func (n networkRoute) Register(m *mux.Router, handler http.Handler) {
-	logrus.Debugf("Registering %s, %v", n.path, httpMethods)
-	subrouter := m.PathPrefix(router.VersionMatcher + n.path).Subrouter()
-	subrouter.Methods(httpMethods...).Handler(handler)
-
-	subrouter = m.PathPrefix(n.path).Subrouter()
-	subrouter.Methods(httpMethods...).Handler(handler)
-}

+ 216 - 0
api/server/router/network/network_routes.go

@@ -0,0 +1,216 @@
+package network
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"golang.org/x/net/context"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/api/server/httputils"
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/daemon"
+	"github.com/docker/docker/pkg/parsers/filters"
+	"github.com/docker/libnetwork"
+)
+
+func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if err := httputils.ParseForm(r); err != nil {
+		return err
+	}
+
+	filter := r.Form.Get("filters")
+	netFilters, err := filters.FromParam(filter)
+	if err != nil {
+		return err
+	}
+
+	list := []*types.NetworkResource{}
+	var nameFilter, idFilter bool
+	var names, ids []string
+	if names, nameFilter = netFilters["name"]; nameFilter {
+		for _, name := range names {
+			if nw, err := n.daemon.GetNetwork(name, daemon.NetworkByName); err == nil {
+				list = append(list, buildNetworkResource(nw))
+			} else {
+				logrus.Errorf("failed to get network for filter=%s : %v", name, err)
+			}
+		}
+	}
+
+	if ids, idFilter = netFilters["id"]; idFilter {
+		for _, id := range ids {
+			for _, nw := range n.daemon.GetNetworksByID(id) {
+				list = append(list, buildNetworkResource(nw))
+			}
+		}
+	}
+
+	if !nameFilter && !idFilter {
+		nwList := n.daemon.GetNetworksByID("")
+		for _, nw := range nwList {
+			list = append(list, buildNetworkResource(nw))
+		}
+	}
+	return httputils.WriteJSON(w, http.StatusOK, list)
+}
+
+func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if err := httputils.ParseForm(r); err != nil {
+		return err
+	}
+
+	nw, err := n.daemon.FindNetwork(vars["id"])
+	if err != nil {
+		return err
+	}
+	return httputils.WriteJSON(w, http.StatusOK, buildNetworkResource(nw))
+}
+
+func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	var create types.NetworkCreate
+	var warning string
+
+	if err := httputils.ParseForm(r); err != nil {
+		return err
+	}
+
+	if err := httputils.CheckForJSON(r); err != nil {
+		return err
+	}
+
+	if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
+		return err
+	}
+
+	nw, err := n.daemon.GetNetwork(create.Name, daemon.NetworkByName)
+	if _, ok := err.(libnetwork.ErrNoSuchNetwork); err != nil && !ok {
+		return err
+	}
+	if nw != nil {
+		if create.CheckDuplicate {
+			return libnetwork.NetworkNameError(create.Name)
+		}
+		warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
+	}
+
+	nw, err = n.daemon.CreateNetwork(create.Name, create.Driver, create.Options)
+	if err != nil {
+		return err
+	}
+
+	return httputils.WriteJSON(w, http.StatusCreated, &types.NetworkCreateResponse{
+		ID:      nw.ID(),
+		Warning: warning,
+	})
+}
+
+func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	var connect types.NetworkConnect
+	if err := httputils.ParseForm(r); err != nil {
+		return err
+	}
+
+	if err := httputils.CheckForJSON(r); err != nil {
+		return err
+	}
+
+	if err := json.NewDecoder(r.Body).Decode(&connect); err != nil {
+		return err
+	}
+
+	nw, err := n.daemon.FindNetwork(vars["id"])
+	if err != nil {
+		return err
+	}
+
+	container, err := n.daemon.Get(connect.Container)
+	if err != nil {
+		return fmt.Errorf("invalid container %s : %v", container, err)
+	}
+	return container.ConnectToNetwork(nw.Name())
+}
+
+func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	var disconnect types.NetworkDisconnect
+	if err := httputils.ParseForm(r); err != nil {
+		return err
+	}
+
+	if err := httputils.CheckForJSON(r); err != nil {
+		return err
+	}
+
+	if err := json.NewDecoder(r.Body).Decode(&disconnect); err != nil {
+		return err
+	}
+
+	nw, err := n.daemon.FindNetwork(vars["id"])
+	if err != nil {
+		return err
+	}
+
+	container, err := n.daemon.Get(disconnect.Container)
+	if err != nil {
+		return fmt.Errorf("invalid container %s : %v", container, err)
+	}
+	return container.DisconnectFromNetwork(nw)
+}
+
+func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if err := httputils.ParseForm(r); err != nil {
+		return err
+	}
+
+	nw, err := n.daemon.FindNetwork(vars["id"])
+	if err != nil {
+		return err
+	}
+
+	return nw.Delete()
+}
+
+func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
+	r := &types.NetworkResource{}
+	if nw == nil {
+		return r
+	}
+
+	r.Name = nw.Name()
+	r.ID = nw.ID()
+	r.Driver = nw.Type()
+	r.Containers = make(map[string]types.EndpointResource)
+	epl := nw.Endpoints()
+	for _, e := range epl {
+		sb := e.Info().Sandbox()
+		if sb == nil {
+			continue
+		}
+
+		r.Containers[sb.ContainerID()] = buildEndpointResource(e)
+	}
+	return r
+}
+
+func buildEndpointResource(e libnetwork.Endpoint) types.EndpointResource {
+	er := types.EndpointResource{}
+	if e == nil {
+		return er
+	}
+
+	er.EndpointID = e.ID()
+	if iface := e.Info().Iface(); iface != nil {
+		if mac := iface.MacAddress(); mac != nil {
+			er.MacAddress = mac.String()
+		}
+		if ip := iface.Address(); len(ip.IP) > 0 {
+			er.IPv4Address = (&ip).String()
+		}
+
+		if ipv6 := iface.AddressIPv6(); len(ipv6.IP) > 0 {
+			er.IPv6Address = (&ipv6).String()
+		}
+	}
+	return er
+}

+ 0 - 20
api/server/router/network/network_stable.go

@@ -1,20 +0,0 @@
-// +build !experimental
-
-package network
-
-import (
-	"net/http"
-
-	"github.com/docker/docker/api/server/router"
-	"github.com/docker/docker/daemon"
-	"github.com/gorilla/mux"
-)
-
-// NewRouter initializes a new network router
-func NewRouter(d *daemon.Daemon) router.Router {
-	return networkRouter{}
-}
-
-// Register adds the filtered handler to the mux.
-func (n networkRoute) Register(m *mux.Router, handler http.Handler) {
-}

+ 5 - 12
api/server/router/router.go

@@ -1,15 +1,6 @@
 package router
 
-import (
-	"net/http"
-
-	"github.com/docker/docker/api/server/httputils"
-	"github.com/gorilla/mux"
-)
-
-// VersionMatcher defines a variable matcher to be parsed by the router
-// when a request is about to be served.
-const VersionMatcher = "/v{version:[0-9.]+}"
+import "github.com/docker/docker/api/server/httputils"
 
 // Router defines an interface to specify a group of routes to add the the docker server.
 type Router interface {
@@ -18,8 +9,10 @@ type Router interface {
 
 // Route defines an individual API route in the docker server.
 type Route interface {
-	// Register adds the handler route to the docker mux.
-	Register(*mux.Router, http.Handler)
 	// Handler returns the raw function to create the http handler.
 	Handler() httputils.APIFunc
+	// Method returns the http method that the route responds to.
+	Method() string
+	// Path returns the subpath where the route responds to.
+	Path() string
 }

+ 10 - 3
api/server/server.go

@@ -19,6 +19,10 @@ import (
 	"golang.org/x/net/context"
 )
 
+// versionMatcher defines a variable matcher to be parsed by the router
+// when a request is about to be served.
+const versionMatcher = "/v{version:[0-9.]+}"
+
 // Config provides the configuration for the API server
 type Config struct {
 	Logging     bool
@@ -177,10 +181,13 @@ func (s *Server) CreateMux() *mux.Router {
 	}
 
 	logrus.Debugf("Registering routers")
-	for _, router := range s.routers {
-		for _, r := range router.Routes() {
+	for _, apiRouter := range s.routers {
+		for _, r := range apiRouter.Routes() {
 			f := s.makeHTTPHandler(r.Handler())
-			r.Register(m, f)
+
+			logrus.Debugf("Registering %s, %s", r.Method(), r.Path())
+			m.Path(versionMatcher + r.Path()).Methods(r.Method()).Handler(f)
+			m.Path(r.Path()).Methods(r.Method()).Handler(f)
 		}
 	}
 

+ 41 - 0
api/types/types.go

@@ -308,3 +308,44 @@ type VolumeCreateRequest struct {
 	Driver     string            // Driver is the name of the driver that should be used to create the volume
 	DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume.
 }
+
+// NetworkResource is the body of the "get network" http response message
+type NetworkResource struct {
+	Name       string                      `json:"name"`
+	ID         string                      `json:"id"`
+	Driver     string                      `json:"driver"`
+	Containers map[string]EndpointResource `json:"containers"`
+	Options    map[string]interface{}      `json:"options,omitempty"`
+}
+
+//EndpointResource contains network resources allocated and usd for a container in a network
+type EndpointResource struct {
+	EndpointID  string `json:"endpoint"`
+	MacAddress  string `json:"mac_address"`
+	IPv4Address string `json:"ipv4_address"`
+	IPv6Address string `json:"ipv6_address"`
+}
+
+// NetworkCreate is the expected body of the "create network" http request message
+type NetworkCreate struct {
+	Name           string                 `json:"name"`
+	CheckDuplicate bool                   `json:"check_duplicate"`
+	Driver         string                 `json:"driver"`
+	Options        map[string]interface{} `json:"options"`
+}
+
+// NetworkCreateResponse is the response message sent by the server for network create call
+type NetworkCreateResponse struct {
+	ID      string `json:"id"`
+	Warning string `json:"warning"`
+}
+
+// NetworkConnect represents the data to be used to connect a container to the network
+type NetworkConnect struct {
+	Container string `json:"container"`
+}
+
+// NetworkDisconnect represents the data to be used to disconnect a container from the network
+type NetworkDisconnect struct {
+	Container string `json:"container"`
+}

+ 0 - 9
daemon/config_experimental.go

@@ -1,9 +0,0 @@
-// +build experimental
-
-package daemon
-
-import flag "github.com/docker/docker/pkg/mflag"
-
-func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
-	cmd.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", usageFn("Set default network"))
-}

+ 0 - 8
daemon/config_stub.go

@@ -1,8 +0,0 @@
-// +build !experimental
-
-package daemon
-
-import flag "github.com/docker/docker/pkg/mflag"
-
-func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
-}

+ 0 - 2
daemon/config_unix.go

@@ -77,6 +77,4 @@ func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) strin
 	cmd.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, usageFn("Use userland proxy for loopback traffic"))
 	cmd.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, usageFn("Enable CORS headers in the remote API, this is deprecated by --api-cors-header"))
 	cmd.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API"))
-
-	config.attachExperimentalFlags(cmd, usageFn)
 }

+ 186 - 86
daemon/container_unix.go

@@ -412,7 +412,7 @@ func (container *Container) buildHostnameFile() error {
 	return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
 }
 
-func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, error) {
+func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetwork.SandboxOption, error) {
 	var (
 		sboxOptions []libnetwork.SandboxOption
 		err         error
@@ -487,6 +487,23 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e
 		}
 	}
 
+	for _, extraHost := range container.hostConfig.ExtraHosts {
+		// allow IPv6 addresses in extra hosts; only split on first ":"
+		parts := strings.SplitN(extraHost, ":", 2)
+		sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1]))
+	}
+
+	// Link feature is supported only for the default bridge network.
+	// return if this call to build join options is not for default bridge network
+	if n.Name() != "bridge" {
+		return sboxOptions, nil
+	}
+
+	ep, _ := container.getEndpointInNetwork(n)
+	if ep == nil {
+		return sboxOptions, nil
+	}
+
 	var childEndpoints, parentEndpoints []string
 
 	children, err := container.daemon.children(container.Name)
@@ -503,17 +520,12 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e
 			aliasList = aliasList + " " + child.Name[1:]
 		}
 		sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.IPAddress))
-		if child.NetworkSettings.EndpointID != "" {
-			childEndpoints = append(childEndpoints, child.NetworkSettings.EndpointID)
+		cEndpoint, _ := child.getEndpointInNetwork(n)
+		if cEndpoint != nil && cEndpoint.ID() != "" {
+			childEndpoints = append(childEndpoints, cEndpoint.ID())
 		}
 	}
 
-	for _, extraHost := range container.hostConfig.ExtraHosts {
-		// allow IPv6 addresses in extra hosts; only split on first ":"
-		parts := strings.SplitN(extraHost, ":", 2)
-		sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1]))
-	}
-
 	refs := container.daemon.containerGraph().RefPaths(container.ID)
 	for _, ref := range refs {
 		if ref.ParentID == "0" {
@@ -528,8 +540,8 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e
 		if c != nil && !container.daemon.configStore.DisableBridge && container.hostConfig.NetworkMode.IsPrivate() {
 			logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress)
 			sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate(c.ID, ref.Name, container.NetworkSettings.IPAddress))
-			if c.NetworkSettings.EndpointID != "" {
-				parentEndpoints = append(parentEndpoints, c.NetworkSettings.EndpointID)
+			if ep.ID() != "" {
+				parentEndpoints = append(parentEndpoints, ep.ID())
 			}
 		}
 	}
@@ -546,6 +558,11 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e
 	return sboxOptions, nil
 }
 
+func (container *Container) getEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) {
+	endpointName := strings.TrimPrefix(container.Name, "/")
+	return n.EndpointByName(endpointName)
+}
+
 func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) {
 	if ep == nil {
 		return nil, derr.ErrorCodeEmptyEndpoint
@@ -650,10 +667,38 @@ func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error {
 	return nil
 }
 
-func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error {
-	networkSettings := &network.Settings{NetworkID: n.ID(), EndpointID: ep.ID()}
+func (container *Container) updateNetworkSettings(n libnetwork.Network) error {
+	if container.NetworkSettings == nil {
+		container.NetworkSettings = &network.Settings{Networks: []string{}}
+	}
+	settings := container.NetworkSettings
+
+	for _, s := range settings.Networks {
+		sn, err := container.daemon.FindNetwork(s)
+		if err != nil {
+			continue
+		}
+
+		if sn.Name() == n.Name() {
+			// Avoid duplicate config
+			return nil
+		}
+		if !runconfig.NetworkMode(sn.Type()).IsPrivate() ||
+			!runconfig.NetworkMode(n.Type()).IsPrivate() {
+			return runconfig.ErrConflictSharedNetwork
+		}
+		if runconfig.NetworkMode(sn.Name()).IsNone() ||
+			runconfig.NetworkMode(n.Name()).IsNone() {
+			return runconfig.ErrConflictNoNetwork
+		}
+	}
+	settings.Networks = append(settings.Networks, n.Name())
 
-	networkSettings, err := container.buildPortMapInfo(ep, networkSettings)
+	return nil
+}
+
+func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error {
+	networkSettings, err := container.buildPortMapInfo(ep, container.NetworkSettings)
 	if err != nil {
 		return err
 	}
@@ -667,7 +712,6 @@ func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network,
 		networkSettings.Bridge = container.daemon.configStore.Bridge.Iface
 	}
 
-	container.NetworkSettings = networkSettings
 	return nil
 }
 
@@ -688,7 +732,25 @@ func (container *Container) updateNetwork() error {
 		return derr.ErrorCodeNoSandbox.WithArgs(sid, err)
 	}
 
-	options, err := container.buildSandboxOptions()
+	// Find if container is connected to the default bridge network
+	var n libnetwork.Network
+	for _, name := range container.NetworkSettings.Networks {
+		sn, err := container.daemon.FindNetwork(name)
+		if err != nil {
+			continue
+		}
+		if sn.Name() == "bridge" {
+			n = sn
+			break
+		}
+	}
+
+	if n == nil {
+		// Not connected to the default bridge network; Nothing to do
+		return nil
+	}
+
+	options, err := container.buildSandboxOptions(n)
 	if err != nil {
 		return derr.ErrorCodeNetworkUpdate.WithArgs(err)
 	}
@@ -781,20 +843,6 @@ func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointO
 	return createOptions, nil
 }
 
-func parseService(controller libnetwork.NetworkController, service string) (string, string, string) {
-	dn := controller.Config().Daemon.DefaultNetwork
-	dd := controller.Config().Daemon.DefaultDriver
-
-	snd := strings.Split(service, ".")
-	if len(snd) > 2 {
-		return strings.Join(snd[:len(snd)-2], "."), snd[len(snd)-2], snd[len(snd)-1]
-	}
-	if len(snd) > 1 {
-		return snd[0], snd[1], dd
-	}
-	return snd[0], dn, dd
-}
-
 func createNetwork(controller libnetwork.NetworkController, dnet string, driver string) (libnetwork.Network, error) {
 	createOptions := []libnetwork.NetworkOption{}
 	genericOption := options.Generic{}
@@ -813,61 +861,69 @@ func createNetwork(controller libnetwork.NetworkController, dnet string, driver
 }
 
 func (container *Container) allocateNetwork() error {
-	mode := container.hostConfig.NetworkMode
-	controller := container.daemon.netController
-	if container.Config.NetworkDisabled || mode.IsContainer() {
-		return nil
-	}
+	settings := container.NetworkSettings.Networks
+	updateSettings := false
+	if settings == nil {
+		mode := container.hostConfig.NetworkMode
+		controller := container.daemon.netController
+		if container.Config.NetworkDisabled || mode.IsContainer() {
+			return nil
+		}
 
-	networkDriver := string(mode)
-	service := container.Config.PublishService
-	networkName := mode.NetworkName()
-	if mode.IsDefault() {
-		if service != "" {
-			service, networkName, networkDriver = parseService(controller, service)
-		} else {
+		networkName := mode.NetworkName()
+		if mode.IsDefault() {
 			networkName = controller.Config().Daemon.DefaultNetwork
-			networkDriver = controller.Config().Daemon.DefaultDriver
 		}
-	} else if service != "" {
-		return derr.ErrorCodeNetworkConflict
+		settings = []string{networkName}
+		updateSettings = true
 	}
 
-	if runconfig.NetworkMode(networkDriver).IsBridge() && container.daemon.configStore.DisableBridge {
-		container.Config.NetworkDisabled = true
-		return nil
+	for _, n := range settings {
+		if err := container.connectToNetwork(n, updateSettings); err != nil {
+			if updateSettings {
+				return err
+			}
+			// dont fail a container restart case if the user removed the network
+			logrus.Warnf("Could not connect container %s : %v", container.ID, err)
+		}
 	}
 
-	if service == "" {
-		// dot character "." has a special meaning to support SERVICE[.NETWORK] format.
-		// For backward compatibility, replacing "." with "-", instead of failing
-		service = strings.Replace(container.Name, ".", "-", -1)
-		// Service names dont like "/" in them. removing it instead of failing for backward compatibility
-		service = strings.Replace(service, "/", "", -1)
+	return container.writeHostConfig()
+}
+
+// ConnectToNetwork connects a container to a netork
+func (container *Container) ConnectToNetwork(idOrName string) error {
+	if !container.Running {
+		return derr.ErrorCodeNotRunning.WithArgs(container.ID)
 	}
+	return container.connectToNetwork(idOrName, true)
+}
 
-	if err := container.configureNetwork(networkName, service, networkDriver, mode.IsDefault()); err != nil {
-		return err
+func (container *Container) connectToNetwork(idOrName string, updateSettings bool) error {
+	if container.hostConfig.NetworkMode.IsContainer() {
+		return runconfig.ErrConflictSharedNetwork
 	}
 
-	return container.writeHostConfig()
-}
+	if runconfig.NetworkMode(idOrName).IsBridge() &&
+		container.daemon.configStore.DisableBridge {
+		container.Config.NetworkDisabled = true
+		return nil
+	}
 
-func (container *Container) configureNetwork(networkName, service, networkDriver string, canCreateNetwork bool) error {
 	controller := container.daemon.netController
 
-	n, err := controller.NetworkByName(networkName)
+	n, err := container.daemon.FindNetwork(idOrName)
 	if err != nil {
-		if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok || !canCreateNetwork {
-			return err
-		}
+		return err
+	}
 
-		if n, err = createNetwork(controller, networkName, networkDriver); err != nil {
+	if updateSettings {
+		if err := container.updateNetworkSettings(n); err != nil {
 			return err
 		}
 	}
 
-	ep, err := n.EndpointByName(service)
+	ep, err := container.getEndpointInNetwork(n)
 	if err != nil {
 		if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok {
 			return err
@@ -878,7 +934,8 @@ func (container *Container) configureNetwork(networkName, service, networkDriver
 			return err
 		}
 
-		ep, err = n.CreateEndpoint(service, createOptions...)
+		endpointName := strings.TrimPrefix(container.Name, "/")
+		ep, err = n.CreateEndpoint(endpointName, createOptions...)
 		if err != nil {
 			return err
 		}
@@ -897,7 +954,7 @@ func (container *Container) configureNetwork(networkName, service, networkDriver
 		return false
 	})
 	if sb == nil {
-		options, err := container.buildSandboxOptions()
+		options, err := container.buildSandboxOptions(n)
 		if err != nil {
 			return err
 		}
@@ -1039,12 +1096,11 @@ func (container *Container) releaseNetwork() {
 	}
 
 	sid := container.NetworkSettings.SandboxID
-	eid := container.NetworkSettings.EndpointID
-	nid := container.NetworkSettings.NetworkID
+	networks := container.NetworkSettings.Networks
 
-	container.NetworkSettings = &network.Settings{}
+	container.NetworkSettings = &network.Settings{Networks: networks}
 
-	if sid == "" || nid == "" || eid == "" {
+	if sid == "" || len(networks) == 0 {
 		return
 	}
 
@@ -1054,29 +1110,73 @@ func (container *Container) releaseNetwork() {
 		return
 	}
 
-	n, err := container.daemon.netController.NetworkByID(nid)
-	if err != nil {
-		logrus.Errorf("error locating network id %s: %v", nid, err)
-		return
-	}
-
-	ep, err := n.EndpointByID(eid)
-	if err != nil {
-		logrus.Errorf("error locating endpoint id %s: %v", eid, err)
-		return
+	for _, ns := range networks {
+		n, err := container.daemon.FindNetwork(ns)
+		if err != nil {
+			continue
+		}
+		container.disconnectFromNetwork(n, false)
 	}
 
 	if err := sb.Delete(); err != nil {
 		logrus.Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err)
-		return
+	}
+}
+
+// DisconnectFromNetwork disconnects a container from a network
+func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error {
+	if !container.Running {
+		return derr.ErrorCodeNotRunning.WithArgs(container.ID)
+	}
+
+	return container.disconnectFromNetwork(n, true)
+}
+
+func (container *Container) disconnectFromNetwork(n libnetwork.Network, updateSettings bool) error {
+	var (
+		ep   libnetwork.Endpoint
+		sbox libnetwork.Sandbox
+	)
+
+	s := func(current libnetwork.Endpoint) bool {
+		if sb := current.Info().Sandbox(); sb != nil {
+			if sb.ContainerID() == container.ID {
+				ep = current
+				sbox = sb
+				return true
+			}
+		}
+		return false
+	}
+	n.WalkEndpoints(s)
+
+	if ep == nil {
+		return fmt.Errorf("could not locate network endpoint for container %s", container.ID)
+	}
+
+	if err := ep.Leave(sbox); err != nil {
+		return fmt.Errorf("container %s failed to leave network %s: %v", container.ID, n.Name(), err)
 	}
 
-	// In addition to leaving all endpoints, delete implicitly created endpoint
-	if container.Config.PublishService == "" {
-		if err := ep.Delete(); err != nil {
-			logrus.Errorf("deleting endpoint failed: %v", err)
+	if err := ep.Delete(); err != nil {
+		return fmt.Errorf("endpoint delete failed for container %s on network %s: %v", container.ID, n.Name(), err)
+	}
+
+	if updateSettings {
+		networks := container.NetworkSettings.Networks
+		for i, s := range networks {
+			sn, err := container.daemon.FindNetwork(s)
+			if err != nil {
+				continue
+			}
+			if sn.Name() == n.Name() {
+				networks = append(networks[:i], networks[i+1:]...)
+				container.NetworkSettings.Networks = networks
+				break
+			}
 		}
 	}
+	return nil
 }
 
 func (container *Container) unmountVolumes(forceSyscall bool) error {

+ 11 - 0
daemon/container_windows.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/docker/docker/daemon/execdriver"
 	derr "github.com/docker/docker/errors"
+	"github.com/docker/libnetwork"
 )
 
 // DefaultPathEnv is deliberately empty on Windows as the default path will be set by
@@ -38,6 +39,16 @@ func (container *Container) initializeNetworking() error {
 	return nil
 }
 
+// ConnectToNetwork connects a container to the network
+func (container *Container) ConnectToNetwork(idOrName string) error {
+	return nil
+}
+
+// DisconnectFromNetwork disconnects a container from, the network
+func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error {
+	return nil
+}
+
 func (container *Container) setupWorkingDirectory() error {
 	return nil
 }

+ 0 - 5
daemon/daemon.go

@@ -1162,11 +1162,6 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig,
 	return verifyPlatformContainerSettings(daemon, hostConfig, config)
 }
 
-// NetworkController exposes the libnetwork interface to manage networks.
-func (daemon *Daemon) NetworkController() libnetwork.NetworkController {
-	return daemon.netController
-}
-
 func configureVolumes(config *Config) (*store.VolumeStore, error) {
 	volumesDriver, err := local.New(config.Root)
 	if err != nil {

+ 106 - 0
daemon/network.go

@@ -0,0 +1,106 @@
+package daemon
+
+import (
+	"errors"
+	"strings"
+
+	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netlabel"
+)
+
+const (
+	// NetworkByID represents a constant to find a network by its ID
+	NetworkByID = iota + 1
+	// NetworkByName represents a constant to find a network by its Name
+	NetworkByName
+)
+
+// FindNetwork function finds a network for a given string that can represent network name or id
+func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) {
+	// Find by Name
+	n, err := daemon.GetNetwork(idName, NetworkByName)
+	if _, ok := err.(libnetwork.ErrNoSuchNetwork); err != nil && !ok {
+		return nil, err
+	}
+
+	if n != nil {
+		return n, nil
+	}
+
+	// Find by id
+	n, err = daemon.GetNetwork(idName, NetworkByID)
+	if err != nil {
+		return nil, err
+	}
+
+	return n, nil
+}
+
+// GetNetwork function returns a network for a given string that represents the network and
+// a hint to indicate if the string is an Id or Name of the network
+func (daemon *Daemon) GetNetwork(idName string, by int) (libnetwork.Network, error) {
+	c := daemon.netController
+	switch by {
+	case NetworkByID:
+		list := daemon.GetNetworksByID(idName)
+
+		if len(list) == 0 {
+			return nil, libnetwork.ErrNoSuchNetwork(idName)
+		}
+
+		if len(list) > 1 {
+			return nil, libnetwork.ErrInvalidID(idName)
+		}
+
+		return list[0], nil
+	case NetworkByName:
+		if idName == "" {
+			idName = c.Config().Daemon.DefaultNetwork
+		}
+		return c.NetworkByName(idName)
+	}
+	return nil, errors.New("unexpected selector for GetNetwork")
+}
+
+// GetNetworksByID returns a list of networks whose ID partially matches zero or more networks
+func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network {
+	c := daemon.netController
+	list := []libnetwork.Network{}
+	l := func(nw libnetwork.Network) bool {
+		if strings.HasPrefix(nw.ID(), partialID) {
+			list = append(list, nw)
+		}
+		return false
+	}
+	c.WalkNetworks(l)
+
+	return list
+}
+
+// CreateNetwork creates a network with the given name, driver and other optional parameters
+func (daemon *Daemon) CreateNetwork(name, driver string, options map[string]interface{}) (libnetwork.Network, error) {
+	c := daemon.netController
+	if driver == "" {
+		driver = c.Config().Daemon.DefaultDriver
+	}
+
+	if options == nil {
+		options = make(map[string]interface{})
+	}
+	_, ok := options[netlabel.GenericData]
+	if !ok {
+		options[netlabel.GenericData] = make(map[string]interface{})
+	}
+
+	return c.NewNetwork(driver, name, parseOptions(options)...)
+}
+
+func parseOptions(options map[string]interface{}) []libnetwork.NetworkOption {
+	var setFctList []libnetwork.NetworkOption
+
+	if options != nil {
+		setFctList = append(setFctList, libnetwork.NetworkOptionGeneric(options))
+	}
+
+	return setFctList
+}

+ 1 - 1
daemon/network/settings.go

@@ -24,7 +24,7 @@ type Settings struct {
 	LinkLocalIPv6Address   string
 	LinkLocalIPv6PrefixLen int
 	MacAddress             string
-	NetworkID              string
+	Networks               []string
 	Ports                  nat.PortMap
 	SandboxKey             string
 	SecondaryIPAddresses   []Address

+ 163 - 34
integration-cli/docker_api_network_test.go

@@ -1,72 +1,201 @@
-// +build experimental
-
 package main
 
 import (
 	"encoding/json"
-	"fmt"
+	"net"
 	"net/http"
+	"net/url"
+	"strings"
 
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/pkg/parsers/filters"
 	"github.com/go-check/check"
 )
 
+func (s *DockerSuite) TestApiNetworkGetDefaults(c *check.C) {
+	// By default docker daemon creates 3 networks. check if they are present
+	defaults := []string{"bridge", "host", "none"}
+	for _, nn := range defaults {
+		c.Assert(isNetworkAvailable(c, nn), check.Equals, true)
+	}
+}
+
+func (s *DockerSuite) TestApiNetworkCreateDelete(c *check.C) {
+	// Create a network
+	name := "testnetwork"
+	id := createNetwork(c, name, true)
+	c.Assert(isNetworkAvailable(c, name), check.Equals, true)
+
+	// POST another network with same name and CheckDuplicate must fail
+	createNetwork(c, name, false)
+
+	// delete the network and make sure it is deleted
+	deleteNetwork(c, id, true)
+	c.Assert(isNetworkAvailable(c, name), check.Equals, false)
+}
+
+func (s *DockerSuite) TestApiNetworkFilter(c *check.C) {
+	nr := getNetworkResource(c, getNetworkIDByName(c, "bridge"))
+	c.Assert(nr.Name, check.Equals, "bridge")
+}
+
+func (s *DockerSuite) TestApiNetworkInspect(c *check.C) {
+	// Inspect default bridge network
+	nr := getNetworkResource(c, "bridge")
+	c.Assert(nr.Name, check.Equals, "bridge")
+
+	// run a container and attach it to the default bridge network
+	out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
+	containerID := strings.TrimSpace(out)
+	containerIP := findContainerIP(c, "test")
+
+	// inspect default bridge network again and make sure the container is connected
+	nr = getNetworkResource(c, nr.ID)
+	c.Assert(len(nr.Containers), check.Equals, 1)
+	c.Assert(nr.Containers[containerID], check.NotNil)
+
+	ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
+	c.Assert(err, check.IsNil)
+	c.Assert(ip.String(), check.Equals, containerIP)
+}
+
+func (s *DockerSuite) TestApiNetworkConnectDisconnect(c *check.C) {
+	// Create test network
+	name := "testnetwork"
+	id := createNetwork(c, name, true)
+	nr := getNetworkResource(c, id)
+	c.Assert(nr.Name, check.Equals, name)
+	c.Assert(nr.ID, check.Equals, id)
+	c.Assert(len(nr.Containers), check.Equals, 0)
+
+	// run a container
+	out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
+	containerID := strings.TrimSpace(out)
+
+	// connect the container to the test network
+	connectNetwork(c, nr.ID, containerID)
+
+	// inspect the network to make sure container is connected
+	nr = getNetworkResource(c, nr.ID)
+	c.Assert(len(nr.Containers), check.Equals, 1)
+	c.Assert(nr.Containers[containerID], check.NotNil)
+
+	// check if container IP matches network inspect
+	ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
+	c.Assert(err, check.IsNil)
+	containerIP := findContainerIP(c, "test")
+	c.Assert(ip.String(), check.Equals, containerIP)
+
+	// disconnect container from the network
+	disconnectNetwork(c, nr.ID, containerID)
+	nr = getNetworkResource(c, nr.ID)
+	c.Assert(nr.Name, check.Equals, name)
+	c.Assert(len(nr.Containers), check.Equals, 0)
+
+	// delete the network
+	deleteNetwork(c, nr.ID, true)
+}
+
 func isNetworkAvailable(c *check.C, name string) bool {
 	status, body, err := sockRequest("GET", "/networks", nil)
 	c.Assert(status, check.Equals, http.StatusOK)
 	c.Assert(err, check.IsNil)
 
-	var inspectJSON []struct {
-		Name string
-		ID   string
-		Type string
-	}
-	if err = json.Unmarshal(body, &inspectJSON); err != nil {
-		c.Fatalf("unable to unmarshal response body: %v", err)
-	}
-	for _, n := range inspectJSON {
+	nJSON := []types.NetworkResource{}
+	err = json.Unmarshal(body, &nJSON)
+	c.Assert(err, check.IsNil)
+
+	for _, n := range nJSON {
 		if n.Name == name {
 			return true
 		}
 	}
 	return false
+}
+
+func getNetworkIDByName(c *check.C, name string) string {
+	var (
+		v          = url.Values{}
+		filterArgs = filters.Args{}
+	)
+	filterArgs["name"] = []string{name}
+	filterJSON, err := filters.ToParam(filterArgs)
+	c.Assert(err, check.IsNil)
+	v.Set("filters", filterJSON)
+
+	status, body, err := sockRequest("GET", "/networks?"+v.Encode(), nil)
+	c.Assert(status, check.Equals, http.StatusOK)
+	c.Assert(err, check.IsNil)
+
+	nJSON := []types.NetworkResource{}
+	err = json.Unmarshal(body, &nJSON)
+	c.Assert(err, check.IsNil)
+	c.Assert(len(nJSON), check.Equals, 1)
 
+	return nJSON[0].ID
 }
 
-func (s *DockerSuite) TestNetworkApiGetAll(c *check.C) {
-	defaults := []string{"bridge", "host", "none"}
-	for _, nn := range defaults {
-		if !isNetworkAvailable(c, nn) {
-			c.Fatalf("Missing Default network : %s", nn)
-		}
-	}
+func getNetworkResource(c *check.C, id string) *types.NetworkResource {
+	_, obj, err := sockRequest("GET", "/networks/"+id, nil)
+	c.Assert(err, check.IsNil)
+
+	nr := types.NetworkResource{}
+	err = json.Unmarshal(obj, &nr)
+	c.Assert(err, check.IsNil)
+
+	return &nr
 }
 
-func (s *DockerSuite) TestNetworkApiCreateDelete(c *check.C) {
-	name := "testnetwork"
-	config := map[string]interface{}{
-		"name":         name,
-		"network_type": "bridge",
+func createNetwork(c *check.C, name string, shouldSucceed bool) string {
+	config := types.NetworkCreate{
+		Name:           name,
+		Driver:         "bridge",
+		CheckDuplicate: true,
+	}
+
+	status, resp, err := sockRequest("POST", "/networks/create", config)
+	if !shouldSucceed {
+		c.Assert(status, check.Not(check.Equals), http.StatusCreated)
+		return ""
 	}
 
-	status, resp, err := sockRequest("POST", "/networks", config)
 	c.Assert(status, check.Equals, http.StatusCreated)
 	c.Assert(err, check.IsNil)
 
-	if !isNetworkAvailable(c, name) {
-		c.Fatalf("Network %s not found", name)
+	var nr types.NetworkCreateResponse
+	err = json.Unmarshal(resp, &nr)
+	c.Assert(err, check.IsNil)
+
+	return nr.ID
+}
+
+func connectNetwork(c *check.C, nid, cid string) {
+	config := types.NetworkConnect{
+		Container: cid,
 	}
 
-	var id string
-	err = json.Unmarshal(resp, &id)
-	if err != nil {
-		c.Fatal(err)
+	status, _, err := sockRequest("POST", "/networks/"+nid+"/connect", config)
+	c.Assert(status, check.Equals, http.StatusOK)
+	c.Assert(err, check.IsNil)
+}
+
+func disconnectNetwork(c *check.C, nid, cid string) {
+	config := types.NetworkConnect{
+		Container: cid,
 	}
 
-	status, _, err = sockRequest("DELETE", fmt.Sprintf("/networks/%s", id), nil)
+	status, _, err := sockRequest("POST", "/networks/"+nid+"/disconnect", config)
 	c.Assert(status, check.Equals, http.StatusOK)
 	c.Assert(err, check.IsNil)
+}
 
-	if isNetworkAvailable(c, name) {
-		c.Fatalf("Network %s not deleted", name)
+func deleteNetwork(c *check.C, id string, shouldSucceed bool) {
+	status, _, err := sockRequest("DELETE", "/networks/"+id, nil)
+	if !shouldSucceed {
+		c.Assert(status, check.Not(check.Equals), http.StatusOK)
+		c.Assert(err, check.NotNil)
+		return
 	}
+	c.Assert(status, check.Equals, http.StatusOK)
+	c.Assert(err, check.IsNil)
 }

+ 0 - 40
integration-cli/docker_cli_daemon_experimental_test.go

@@ -1,40 +0,0 @@
-// +build daemon,experimental,!windows
-
-package main
-
-import (
-	"os/exec"
-	"strings"
-
-	"github.com/go-check/check"
-)
-
-func assertNetwork(c *check.C, d *Daemon, name string) {
-	out, err := d.Cmd("network", "ls")
-	c.Assert(err, check.IsNil)
-	lines := strings.Split(out, "\n")
-	for i := 1; i < len(lines)-1; i++ {
-		if strings.Contains(lines[i], name) {
-			return
-		}
-	}
-	c.Fatalf("Network %s not found in network ls o/p", name)
-}
-
-func (s *DockerDaemonSuite) TestDaemonDefaultNetwork(c *check.C) {
-	testRequires(c, SameHostDaemon)
-	d := s.d
-
-	networkName := "testdefault"
-	err := d.StartWithBusybox("--default-network", "bridge:"+networkName)
-	c.Assert(err, check.IsNil)
-
-	_, err = d.Cmd("run", "busybox", "true")
-	c.Assert(err, check.IsNil)
-
-	assertNetwork(c, d, networkName)
-
-	ifconfigCmd := exec.Command("ifconfig", networkName)
-	_, _, _, err = runCommandWithStdoutStderr(ifconfigCmd)
-	c.Assert(err, check.IsNil)
-}

+ 1 - 1
integration-cli/docker_cli_netmode_test.go

@@ -77,7 +77,7 @@ func (s *DockerSuite) TestNetHostname(c *check.C) {
 	if out, _, err = runCommandWithOutput(runCmd); err == nil {
 		c.Fatalf(out, err)
 	}
-	checkContains("invalid --net: weird", out, c)
+	checkContains("network weird not found", out, c)
 }
 
 func (s *DockerSuite) TestConflictContainerNetworkAndLinks(c *check.C) {

+ 149 - 1
integration-cli/docker_cli_run_test.go

@@ -3168,7 +3168,7 @@ func (s *DockerSuite) TestRunCreateContainerFailedCleanUp(c *check.C) {
 	testRequires(c, DaemonIsLinux)
 	name := "unique_name"
 	_, _, err := dockerCmdWithError("run", "--name", name, "--link", "nothing:nothing", "busybox")
-	c.Assert(err, check.Not(check.IsNil), check.Commentf("Expected docker run to fail!"))
+	c.Assert(err, check.NotNil, check.Commentf("Expected docker run to fail!"))
 
 	containerID, err := inspectField(name, "Id")
 	c.Assert(containerID, check.Equals, "", check.Commentf("Expected not to have this container: %s!", containerID))
@@ -3404,6 +3404,154 @@ func (s *DockerSuite) TestTwoContainersInNetHost(c *check.C) {
 	dockerCmd(c, "stop", "second")
 }
 
+func (s *DockerSuite) TestContainersInUserDefinedNetwork(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork")
+	dockerCmd(c, "run", "-d", "--net=testnetwork", "--name=first", "busybox", "top")
+	dockerCmd(c, "run", "-t", "--net=testnetwork", "--name=second", "busybox", "ping", "-c", "1", "first")
+	dockerCmd(c, "stop", "first")
+	dockerCmd(c, "stop", "second")
+	dockerCmd(c, "network", "rm", "testnetwork")
+}
+
+func (s *DockerSuite) TestContainersInMultipleNetworks(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	// Create 2 networks using bridge driver
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
+	// Run and connect containers to testnetwork1
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
+	// Check connectivity between containers in testnetwork2
+	dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1")
+	// Connect containers to testnetwork2
+	dockerCmd(c, "network", "connect", "testnetwork2", "first")
+	dockerCmd(c, "network", "connect", "testnetwork2", "second")
+	// Check connectivity between containers
+	dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2")
+	dockerCmd(c, "stop", "first")
+	dockerCmd(c, "stop", "second")
+	dockerCmd(c, "network", "rm", "testnetwork1")
+	dockerCmd(c, "network", "rm", "testnetwork2")
+}
+
+func (s *DockerSuite) TestContainersNetworkIsolation(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	// Create 2 networks using bridge driver
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
+	// Run 1 containers in testnetwork1 and another in testnetwork2
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
+	dockerCmd(c, "run", "-d", "--net=testnetwork2", "--name=second", "busybox", "top")
+
+	// Check Isolation between containers : ping must fail
+	_, _, err := dockerCmdWithError("exec", "first", "ping", "-c", "1", "second")
+	c.Assert(err, check.NotNil)
+	// Connect first container to testnetwork2
+	dockerCmd(c, "network", "connect", "testnetwork2", "first")
+	// ping must succeed now
+	_, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second")
+	c.Assert(err, check.IsNil)
+
+	// Disconnect first container from testnetwork2
+	dockerCmd(c, "network", "disconnect", "testnetwork2", "first")
+	// ping must fail again
+	_, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second")
+	c.Assert(err, check.NotNil)
+
+	dockerCmd(c, "stop", "first")
+	dockerCmd(c, "stop", "second")
+	dockerCmd(c, "network", "rm", "testnetwork1")
+	dockerCmd(c, "network", "rm", "testnetwork2")
+}
+
+func (s *DockerSuite) TestNetworkRmWithActiveContainers(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	// Create 2 networks using bridge driver
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
+	// Run and connect containers to testnetwork1
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
+	// Network delete with active containers must fail
+	_, _, err := dockerCmdWithError("network", "rm", "testnetwork1")
+	c.Assert(err, check.NotNil)
+
+	dockerCmd(c, "stop", "first")
+	_, _, err = dockerCmdWithError("network", "rm", "testnetwork1")
+	c.Assert(err, check.NotNil)
+
+	dockerCmd(c, "stop", "second")
+	// Network delete must succeed after all the connected containers are inactive
+	dockerCmd(c, "network", "rm", "testnetwork1")
+}
+
+func (s *DockerSuite) TestContainerRestartInMultipleNetworks(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	// Create 2 networks using bridge driver
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
+	// Run and connect containers to testnetwork1
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
+	// Check connectivity between containers in testnetwork2
+	dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1")
+	// Connect containers to testnetwork2
+	dockerCmd(c, "network", "connect", "testnetwork2", "first")
+	dockerCmd(c, "network", "connect", "testnetwork2", "second")
+	// Check connectivity between containers
+	dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2")
+
+	// Stop second container and test ping failures on both networks
+	dockerCmd(c, "stop", "second")
+	_, _, err := dockerCmdWithError("exec", "first", "ping", "-c", "1", "second.testnetwork1")
+	c.Assert(err, check.NotNil)
+	_, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second.testnetwork2")
+	c.Assert(err, check.NotNil)
+
+	// Start second container and connectivity must be restored on both networks
+	dockerCmd(c, "start", "second")
+	dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1")
+	dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2")
+
+	dockerCmd(c, "stop", "first")
+	dockerCmd(c, "stop", "second")
+	dockerCmd(c, "network", "rm", "testnetwork1")
+	dockerCmd(c, "network", "rm", "testnetwork2")
+}
+
+func (s *DockerSuite) TestContainerWithConflictingHostNetworks(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	// Run a container with --net=host
+	dockerCmd(c, "run", "-d", "--net=host", "--name=first", "busybox", "top")
+
+	// Create a network using bridge driver
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
+
+	// Connecting to the user defined network must fail
+	_, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "first")
+	c.Assert(err, check.NotNil)
+	dockerCmd(c, "stop", "first")
+	dockerCmd(c, "network", "rm", "testnetwork1")
+}
+
+func (s *DockerSuite) TestContainerWithConflictingSharedNetwork(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+	dockerCmd(c, "run", "-d", "--name=first", "busybox", "top")
+	// Run second container in first container's network namespace
+	dockerCmd(c, "run", "-d", "--net=container:first", "--name=second", "busybox", "top")
+
+	// Create a network using bridge driver
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
+
+	// Connecting to the user defined network must fail
+	_, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "second")
+	c.Assert(err, check.NotNil)
+
+	dockerCmd(c, "stop", "first")
+	dockerCmd(c, "stop", "second")
+	dockerCmd(c, "network", "rm", "testnetwork1")
+}
+
 // #11957 - stdin with no tty does not exit if stdin is not closed even though container exited
 func (s *DockerSuite) TestRunStdinBlockedAfterContainerExit(c *check.C) {
 	cmd := exec.Command(dockerBinary, "run", "-i", "--name=test", "busybox", "true")

+ 0 - 1
runconfig/config.go

@@ -20,7 +20,6 @@ type Config struct {
 	AttachStdout    bool                  // Attach the standard output
 	AttachStderr    bool                  // Attach the standard error
 	ExposedPorts    map[nat.Port]struct{} // List of exposed ports
-	PublishService  string                // Name of the network service exposed by the container
 	Tty             bool                  // Attach standard streams to a tty, including stdin if it is not closed.
 	OpenStdin       bool                  // Open stdin
 	StdinOnce       bool                  // If true, close stdin after the 1 attached client disconnects.

+ 1 - 1
runconfig/hostconfig_test.go

@@ -24,7 +24,7 @@ func TestNetworkModeTest(t *testing.T) {
 	}
 	networkModeNames := map[NetworkMode]string{
 		"":                         "",
-		"something:weird":          "",
+		"something:weird":          "something:weird",
 		"bridge":                   "bridge",
 		DefaultDaemonNetworkMode(): "bridge",
 		"host":           "host",

+ 15 - 0
runconfig/hostconfig_unix.go

@@ -34,6 +34,8 @@ func (n NetworkMode) NetworkName() string {
 		return "none"
 	} else if n.IsDefault() {
 		return "default"
+	} else if n.IsUserDefined() {
+		return n.UserDefined()
 	}
 	return ""
 }
@@ -59,6 +61,19 @@ func (n NetworkMode) IsNone() bool {
 	return n == "none"
 }
 
+// IsUserDefined indicates user-created network
+func (n NetworkMode) IsUserDefined() bool {
+	return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
+}
+
+//UserDefined indicates user-created network
+func (n NetworkMode) UserDefined() string {
+	if n.IsUserDefined() {
+		return string(n)
+	}
+	return ""
+}
+
 // MergeConfigs merges the specified container Config and HostConfig.
 // It creates a ContainerConfigWrapper.
 func MergeConfigs(config *Config, hostConfig *HostConfig) *ContainerConfigWrapper {

+ 7 - 5
runconfig/parse.go

@@ -17,6 +17,12 @@ import (
 var (
 	// ErrConflictContainerNetworkAndLinks conflict between --net=container and links
 	ErrConflictContainerNetworkAndLinks = fmt.Errorf("Conflicting options: --net=container can't be used with links. This would result in undefined behavior")
+	// ErrConflictUserDefinedNetworkAndLinks conflict between --net=<NETWORK> and links
+	ErrConflictUserDefinedNetworkAndLinks = fmt.Errorf("Conflicting options: --net=<NETWORK> can't be used with links. This would result in undefined behavior")
+	// ErrConflictSharedNetwork conflict between private and other networks
+	ErrConflictSharedNetwork = fmt.Errorf("Container sharing network namespace with another container or host cannot be connected to any other network")
+	// ErrConflictNoNetwork conflict between private and other networks
+	ErrConflictNoNetwork = fmt.Errorf("Container cannot be connected to multiple networks with one of the networks in --none mode")
 	// ErrConflictNetworkAndDNS conflict between --dns and the network mode
 	ErrConflictNetworkAndDNS = fmt.Errorf("Conflicting options: --dns and the network mode (--net)")
 	// ErrConflictNetworkHostname conflict between the hostname and the network mode
@@ -88,7 +94,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 		flCpusetMems        = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
 		flBlkioWeight       = cmd.Int64([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000")
 		flSwappiness        = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tuning container memory swappiness (0 to 100)")
-		flNetMode           = cmd.String([]string{"-net"}, "default", "Set the Network mode for the container")
+		flNetMode           = cmd.String([]string{"-net"}, "default", "Set the Network for the container")
 		flMacAddress        = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
 		flIpcMode           = cmd.String([]string{"-ipc"}, "", "IPC namespace to use")
 		flRestartPolicy     = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits")
@@ -122,8 +128,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 	cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
 	cmd.Var(&flLoggingOpts, []string{"-log-opt"}, "Log driver options")
 
-	expFlags := attachExperimentalFlags(cmd)
-
 	cmd.Require(flag.Min, 1)
 
 	if err := cmd.ParseFlags(args, true); err != nil {
@@ -379,8 +383,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 		VolumeDriver:      *flVolumeDriver,
 	}
 
-	applyExperimentalFlags(expFlags, config, hostConfig)
-
 	// When allocating stdin in attached mode, close stdin at client disconnect
 	if config.OpenStdin && config.AttachStdin {
 		config.StdinOnce = true

+ 0 - 19
runconfig/parse_experimental.go

@@ -1,19 +0,0 @@
-// +build experimental
-
-package runconfig
-
-import flag "github.com/docker/docker/pkg/mflag"
-
-type experimentalFlags struct {
-	flags map[string]interface{}
-}
-
-func attachExperimentalFlags(cmd *flag.FlagSet) *experimentalFlags {
-	flags := make(map[string]interface{})
-	flags["publish-service"] = cmd.String([]string{"-publish-service"}, "", "Publish this container as a service")
-	return &experimentalFlags{flags: flags}
-}
-
-func applyExperimentalFlags(exp *experimentalFlags, config *Config, hostConfig *HostConfig) {
-	config.PublishService = *(exp.flags["publish-service"]).(*string)
-}

+ 0 - 14
runconfig/parse_stub.go

@@ -1,14 +0,0 @@
-// +build !experimental
-
-package runconfig
-
-import flag "github.com/docker/docker/pkg/mflag"
-
-type experimentalFlags struct{}
-
-func attachExperimentalFlags(cmd *flag.FlagSet) *experimentalFlags {
-	return nil
-}
-
-func applyExperimentalFlags(flags *experimentalFlags, config *Config, hostConfig *HostConfig) {
-}

+ 5 - 5
runconfig/parse_unix.go

@@ -15,14 +15,10 @@ func ValidateNetMode(c *Config, hc *HostConfig) error {
 		return nil
 	}
 	parts := strings.Split(string(hc.NetworkMode), ":")
-	switch mode := parts[0]; mode {
-	case "default", "bridge", "none", "host":
-	case "container":
+	if parts[0] == "container" {
 		if len(parts) < 2 || parts[1] == "" {
 			return fmt.Errorf("--net: invalid net mode: invalid container format container:<name|id>")
 		}
-	default:
-		return fmt.Errorf("invalid --net: %s", hc.NetworkMode)
 	}
 
 	if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && c.Hostname != "" {
@@ -37,6 +33,10 @@ func ValidateNetMode(c *Config, hc *HostConfig) error {
 		return ErrConflictContainerNetworkAndLinks
 	}
 
+	if hc.NetworkMode.IsUserDefined() && len(hc.Links) > 0 {
+		return ErrConflictUserDefinedNetworkAndLinks
+	}
+
 	if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && len(hc.DNS) > 0 {
 		return ErrConflictNetworkAndDNS
 	}

+ 0 - 949
vendor/src/github.com/docker/libnetwork/api/api.go

@@ -1,949 +0,0 @@
-package api
-
-import (
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"strings"
-
-	"github.com/docker/libnetwork"
-	"github.com/docker/libnetwork/netlabel"
-	"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"
-
-	// BridgeNetworkDriver is the built-in default for Network Driver
-	BridgeNetworkDriver = "bridge"
-)
-
-// 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 (nc *networkCreate) parseOptions() []libnetwork.NetworkOption {
-	var setFctList []libnetwork.NetworkOption
-
-	if nc.Options != nil {
-		setFctList = append(setFctList, libnetwork.NetworkOptionGeneric(nc.Options))
-	}
-
-	return setFctList
-}
-
-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
-	}
-	if nc.NetworkType == BridgeNetworkDriver {
-		if nc.Options == nil {
-			nc.Options = make(map[string]interface{})
-		}
-		genericData, ok := nc.Options[netlabel.GenericData]
-		if !ok {
-			genericData = make(map[string]interface{})
-		}
-		gData := genericData.(map[string]interface{})
-
-		if _, ok := gData["BridgeName"]; !ok {
-			gData["BridgeName"] = nc.Name
-		}
-		nc.Options[netlabel.GenericData] = genericData
-	}
-}
-
-/***************************
- 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)
-
-	nw, err := c.NewNetwork(create.NetworkType, create.Name, create.parseOptions()...)
-	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))
-	}
-
-	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
-	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
-	}
-
-	err = ep.Join(sb)
-	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()
-	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))
-	}
-
-	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) {
-	epT, epBy := detectEndpointTarget(vars)
-	sv, errRsp := findService(c, epT, epBy)
-	if !errRsp.isOK() {
-		return nil, errRsp
-	}
-	err := sv.Delete()
-	if 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
-	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
-	}
-
-	err = sv.Join(sb)
-	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)
-}

+ 0 - 80
vendor/src/github.com/docker/libnetwork/api/types.go

@@ -1,80 +0,0 @@
-package api
-
-import "github.com/docker/libnetwork/types"
-
-/***********
- Resources
-************/
-
-// networkResource is the body of the "get network" http response message
-type networkResource struct {
-	Name      string              `json:"name"`
-	ID        string              `json:"id"`
-	Type      string              `json:"type"`
-	Endpoints []*endpointResource `json:"endpoints"`
-}
-
-// endpointResource is the body of the "get endpoint" http response message
-type endpointResource struct {
-	Name    string `json:"name"`
-	ID      string `json:"id"`
-	Network string `json:"network"`
-}
-
-// sandboxResource is the body of "get service backend" response message
-type sandboxResource struct {
-	ID          string `json:"id"`
-	Key         string `json:"key"`
-	ContainerID string `json:"container_id"`
-	// will add more fields once labels change is in
-}
-
-/***********
-  Body types
-  ************/
-
-// networkCreate is the expected body of the "create network" http request message
-type networkCreate struct {
-	Name        string                 `json:"name"`
-	NetworkType string                 `json:"network_type"`
-	Options     map[string]interface{} `json:"options"`
-}
-
-// endpointCreate represents the body of the "create endpoint" http request message
-type endpointCreate struct {
-	Name         string                `json:"name"`
-	ExposedPorts []types.TransportPort `json:"exposed_ports"`
-	PortMapping  []types.PortBinding   `json:"port_mapping"`
-}
-
-// sandboxCreate is the expected body of the "create sandbox" http request message
-type sandboxCreate struct {
-	ContainerID       string      `json:"container_id"`
-	HostName          string      `json:"host_name"`
-	DomainName        string      `json:"domain_name"`
-	HostsPath         string      `json:"hosts_path"`
-	ResolvConfPath    string      `json:"resolv_conf_path"`
-	DNS               []string    `json:"dns"`
-	ExtraHosts        []extraHost `json:"extra_hosts"`
-	UseDefaultSandbox bool        `json:"use_default_sandbox"`
-	UseExternalKey    bool        `json:"use_external_key"`
-}
-
-// endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages
-type endpointJoin struct {
-	SandboxID string `json:"sandbox_id"`
-}
-
-// servicePublish represents the body of the "publish service" http request message
-type servicePublish struct {
-	Name         string                `json:"name"`
-	Network      string                `json:"network_name"`
-	ExposedPorts []types.TransportPort `json:"exposed_ports"`
-	PortMapping  []types.PortBinding   `json:"port_mapping"`
-}
-
-// extraHost represents the extra host object
-type extraHost struct {
-	Name    string `json:"name"`
-	Address string `json:"address"`
-}