Browse code

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 authored on 2015/09/25 19:19:17
Showing 29 changed files
... ...
@@ -5,11 +5,9 @@ import (
5 5
 
6 6
 	"golang.org/x/net/context"
7 7
 
8
-	"github.com/Sirupsen/logrus"
9 8
 	"github.com/docker/docker/api/server/httputils"
10 9
 	dkrouter "github.com/docker/docker/api/server/router"
11 10
 	"github.com/docker/docker/daemon"
12
-	"github.com/gorilla/mux"
13 11
 )
14 12
 
15 13
 // router is a docker router that talks with the local docker daemon.
... ...
@@ -31,11 +29,14 @@ func (l localRoute) Handler() httputils.APIFunc {
31 31
 	return l.handler
32 32
 }
33 33
 
34
-// Register adds the filtered handler to the mux.
35
-func (l localRoute) Register(m *mux.Router, handler http.Handler) {
36
-	logrus.Debugf("Registering %s, %s", l.method, l.path)
37
-	m.Path(dkrouter.VersionMatcher + l.path).Methods(l.method).Handler(handler)
38
-	m.Path(l.path).Methods(l.method).Handler(handler)
34
+// Method returns the http method that the route responds to.
35
+func (l localRoute) Method() string {
36
+	return l.method
37
+}
38
+
39
+// Path returns the subpath where the route responds to.
40
+func (l localRoute) Path() string {
41
+	return l.path
39 42
 }
40 43
 
41 44
 // NewRoute initialies a new local route for the reouter
... ...
@@ -1,26 +1,41 @@
1 1
 package network
2 2
 
3 3
 import (
4
-	"github.com/docker/docker/api/server/httputils"
5 4
 	"github.com/docker/docker/api/server/router"
5
+	"github.com/docker/docker/api/server/router/local"
6
+	"github.com/docker/docker/daemon"
6 7
 )
7 8
 
8 9
 // networkRouter is a router to talk with the network controller
9 10
 type networkRouter struct {
11
+	daemon *daemon.Daemon
10 12
 	routes []router.Route
11 13
 }
12 14
 
13
-// Routes returns the available routes to the network controller
14
-func (n networkRouter) Routes() []router.Route {
15
-	return n.routes
15
+// NewRouter initializes a new network router
16
+func NewRouter(d *daemon.Daemon) router.Router {
17
+	r := &networkRouter{
18
+		daemon: d,
19
+	}
20
+	r.initRoutes()
21
+	return r
16 22
 }
17 23
 
18
-type networkRoute struct {
19
-	path    string
20
-	handler httputils.APIFunc
24
+// Routes returns the available routes to the network controller
25
+func (r *networkRouter) Routes() []router.Route {
26
+	return r.routes
21 27
 }
22 28
 
23
-// Handler returns the APIFunc to let the server wrap it in middlewares
24
-func (l networkRoute) Handler() httputils.APIFunc {
25
-	return l.handler
29
+func (r *networkRouter) initRoutes() {
30
+	r.routes = []router.Route{
31
+		// GET
32
+		local.NewGetRoute("/networks", r.getNetworksList),
33
+		local.NewGetRoute("/networks/{id:.*}", r.getNetwork),
34
+		// POST
35
+		local.NewPostRoute("/networks/create", r.postNetworkCreate),
36
+		local.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect),
37
+		local.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect),
38
+		// DELETE
39
+		local.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork),
40
+	}
26 41
 }
27 42
deleted file mode 100644
... ...
@@ -1,51 +0,0 @@
1
-// +build experimental
2
-
3
-package network
4
-
5
-import (
6
-	"net/http"
7
-
8
-	"golang.org/x/net/context"
9
-
10
-	"github.com/Sirupsen/logrus"
11
-	"github.com/docker/docker/api/server/router"
12
-	"github.com/docker/docker/daemon"
13
-	"github.com/docker/libnetwork/api"
14
-	"github.com/gorilla/mux"
15
-)
16
-
17
-var httpMethods = []string{"GET", "POST", "PUT", "DELETE"}
18
-
19
-// NewRouter initializes a new network router
20
-func NewRouter(d *daemon.Daemon) router.Router {
21
-	c := d.NetworkController()
22
-	if c == nil {
23
-		return networkRouter{}
24
-	}
25
-
26
-	var routes []router.Route
27
-	netHandler := api.NewHTTPHandler(c)
28
-
29
-	// TODO: libnetwork should stop hijacking request/response.
30
-	// It should define API functions to add normally to the router.
31
-	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
32
-		netHandler(w, r)
33
-		return nil
34
-	}
35
-
36
-	for _, path := range []string{"/networks", "/services", "/sandboxes"} {
37
-		routes = append(routes, networkRoute{path, handler})
38
-	}
39
-
40
-	return networkRouter{routes}
41
-}
42
-
43
-// Register adds the filtered handler to the mux.
44
-func (n networkRoute) Register(m *mux.Router, handler http.Handler) {
45
-	logrus.Debugf("Registering %s, %v", n.path, httpMethods)
46
-	subrouter := m.PathPrefix(router.VersionMatcher + n.path).Subrouter()
47
-	subrouter.Methods(httpMethods...).Handler(handler)
48
-
49
-	subrouter = m.PathPrefix(n.path).Subrouter()
50
-	subrouter.Methods(httpMethods...).Handler(handler)
51
-}
52 1
new file mode 100644
... ...
@@ -0,0 +1,216 @@
0
+package network
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"net/http"
6
+
7
+	"golang.org/x/net/context"
8
+
9
+	"github.com/Sirupsen/logrus"
10
+	"github.com/docker/docker/api/server/httputils"
11
+	"github.com/docker/docker/api/types"
12
+	"github.com/docker/docker/daemon"
13
+	"github.com/docker/docker/pkg/parsers/filters"
14
+	"github.com/docker/libnetwork"
15
+)
16
+
17
+func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
18
+	if err := httputils.ParseForm(r); err != nil {
19
+		return err
20
+	}
21
+
22
+	filter := r.Form.Get("filters")
23
+	netFilters, err := filters.FromParam(filter)
24
+	if err != nil {
25
+		return err
26
+	}
27
+
28
+	list := []*types.NetworkResource{}
29
+	var nameFilter, idFilter bool
30
+	var names, ids []string
31
+	if names, nameFilter = netFilters["name"]; nameFilter {
32
+		for _, name := range names {
33
+			if nw, err := n.daemon.GetNetwork(name, daemon.NetworkByName); err == nil {
34
+				list = append(list, buildNetworkResource(nw))
35
+			} else {
36
+				logrus.Errorf("failed to get network for filter=%s : %v", name, err)
37
+			}
38
+		}
39
+	}
40
+
41
+	if ids, idFilter = netFilters["id"]; idFilter {
42
+		for _, id := range ids {
43
+			for _, nw := range n.daemon.GetNetworksByID(id) {
44
+				list = append(list, buildNetworkResource(nw))
45
+			}
46
+		}
47
+	}
48
+
49
+	if !nameFilter && !idFilter {
50
+		nwList := n.daemon.GetNetworksByID("")
51
+		for _, nw := range nwList {
52
+			list = append(list, buildNetworkResource(nw))
53
+		}
54
+	}
55
+	return httputils.WriteJSON(w, http.StatusOK, list)
56
+}
57
+
58
+func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
59
+	if err := httputils.ParseForm(r); err != nil {
60
+		return err
61
+	}
62
+
63
+	nw, err := n.daemon.FindNetwork(vars["id"])
64
+	if err != nil {
65
+		return err
66
+	}
67
+	return httputils.WriteJSON(w, http.StatusOK, buildNetworkResource(nw))
68
+}
69
+
70
+func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
71
+	var create types.NetworkCreate
72
+	var warning string
73
+
74
+	if err := httputils.ParseForm(r); err != nil {
75
+		return err
76
+	}
77
+
78
+	if err := httputils.CheckForJSON(r); err != nil {
79
+		return err
80
+	}
81
+
82
+	if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
83
+		return err
84
+	}
85
+
86
+	nw, err := n.daemon.GetNetwork(create.Name, daemon.NetworkByName)
87
+	if _, ok := err.(libnetwork.ErrNoSuchNetwork); err != nil && !ok {
88
+		return err
89
+	}
90
+	if nw != nil {
91
+		if create.CheckDuplicate {
92
+			return libnetwork.NetworkNameError(create.Name)
93
+		}
94
+		warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
95
+	}
96
+
97
+	nw, err = n.daemon.CreateNetwork(create.Name, create.Driver, create.Options)
98
+	if err != nil {
99
+		return err
100
+	}
101
+
102
+	return httputils.WriteJSON(w, http.StatusCreated, &types.NetworkCreateResponse{
103
+		ID:      nw.ID(),
104
+		Warning: warning,
105
+	})
106
+}
107
+
108
+func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
109
+	var connect types.NetworkConnect
110
+	if err := httputils.ParseForm(r); err != nil {
111
+		return err
112
+	}
113
+
114
+	if err := httputils.CheckForJSON(r); err != nil {
115
+		return err
116
+	}
117
+
118
+	if err := json.NewDecoder(r.Body).Decode(&connect); err != nil {
119
+		return err
120
+	}
121
+
122
+	nw, err := n.daemon.FindNetwork(vars["id"])
123
+	if err != nil {
124
+		return err
125
+	}
126
+
127
+	container, err := n.daemon.Get(connect.Container)
128
+	if err != nil {
129
+		return fmt.Errorf("invalid container %s : %v", container, err)
130
+	}
131
+	return container.ConnectToNetwork(nw.Name())
132
+}
133
+
134
+func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
135
+	var disconnect types.NetworkDisconnect
136
+	if err := httputils.ParseForm(r); err != nil {
137
+		return err
138
+	}
139
+
140
+	if err := httputils.CheckForJSON(r); err != nil {
141
+		return err
142
+	}
143
+
144
+	if err := json.NewDecoder(r.Body).Decode(&disconnect); err != nil {
145
+		return err
146
+	}
147
+
148
+	nw, err := n.daemon.FindNetwork(vars["id"])
149
+	if err != nil {
150
+		return err
151
+	}
152
+
153
+	container, err := n.daemon.Get(disconnect.Container)
154
+	if err != nil {
155
+		return fmt.Errorf("invalid container %s : %v", container, err)
156
+	}
157
+	return container.DisconnectFromNetwork(nw)
158
+}
159
+
160
+func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
161
+	if err := httputils.ParseForm(r); err != nil {
162
+		return err
163
+	}
164
+
165
+	nw, err := n.daemon.FindNetwork(vars["id"])
166
+	if err != nil {
167
+		return err
168
+	}
169
+
170
+	return nw.Delete()
171
+}
172
+
173
+func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
174
+	r := &types.NetworkResource{}
175
+	if nw == nil {
176
+		return r
177
+	}
178
+
179
+	r.Name = nw.Name()
180
+	r.ID = nw.ID()
181
+	r.Driver = nw.Type()
182
+	r.Containers = make(map[string]types.EndpointResource)
183
+	epl := nw.Endpoints()
184
+	for _, e := range epl {
185
+		sb := e.Info().Sandbox()
186
+		if sb == nil {
187
+			continue
188
+		}
189
+
190
+		r.Containers[sb.ContainerID()] = buildEndpointResource(e)
191
+	}
192
+	return r
193
+}
194
+
195
+func buildEndpointResource(e libnetwork.Endpoint) types.EndpointResource {
196
+	er := types.EndpointResource{}
197
+	if e == nil {
198
+		return er
199
+	}
200
+
201
+	er.EndpointID = e.ID()
202
+	if iface := e.Info().Iface(); iface != nil {
203
+		if mac := iface.MacAddress(); mac != nil {
204
+			er.MacAddress = mac.String()
205
+		}
206
+		if ip := iface.Address(); len(ip.IP) > 0 {
207
+			er.IPv4Address = (&ip).String()
208
+		}
209
+
210
+		if ipv6 := iface.AddressIPv6(); len(ipv6.IP) > 0 {
211
+			er.IPv6Address = (&ipv6).String()
212
+		}
213
+	}
214
+	return er
215
+}
0 216
deleted file mode 100644
... ...
@@ -1,20 +0,0 @@
1
-// +build !experimental
2
-
3
-package network
4
-
5
-import (
6
-	"net/http"
7
-
8
-	"github.com/docker/docker/api/server/router"
9
-	"github.com/docker/docker/daemon"
10
-	"github.com/gorilla/mux"
11
-)
12
-
13
-// NewRouter initializes a new network router
14
-func NewRouter(d *daemon.Daemon) router.Router {
15
-	return networkRouter{}
16
-}
17
-
18
-// Register adds the filtered handler to the mux.
19
-func (n networkRoute) Register(m *mux.Router, handler http.Handler) {
20
-}
... ...
@@ -1,15 +1,6 @@
1 1
 package router
2 2
 
3
-import (
4
-	"net/http"
5
-
6
-	"github.com/docker/docker/api/server/httputils"
7
-	"github.com/gorilla/mux"
8
-)
9
-
10
-// VersionMatcher defines a variable matcher to be parsed by the router
11
-// when a request is about to be served.
12
-const VersionMatcher = "/v{version:[0-9.]+}"
3
+import "github.com/docker/docker/api/server/httputils"
13 4
 
14 5
 // Router defines an interface to specify a group of routes to add the the docker server.
15 6
 type Router interface {
... ...
@@ -18,8 +9,10 @@ type Router interface {
18 18
 
19 19
 // Route defines an individual API route in the docker server.
20 20
 type Route interface {
21
-	// Register adds the handler route to the docker mux.
22
-	Register(*mux.Router, http.Handler)
23 21
 	// Handler returns the raw function to create the http handler.
24 22
 	Handler() httputils.APIFunc
23
+	// Method returns the http method that the route responds to.
24
+	Method() string
25
+	// Path returns the subpath where the route responds to.
26
+	Path() string
25 27
 }
... ...
@@ -19,6 +19,10 @@ import (
19 19
 	"golang.org/x/net/context"
20 20
 )
21 21
 
22
+// versionMatcher defines a variable matcher to be parsed by the router
23
+// when a request is about to be served.
24
+const versionMatcher = "/v{version:[0-9.]+}"
25
+
22 26
 // Config provides the configuration for the API server
23 27
 type Config struct {
24 28
 	Logging     bool
... ...
@@ -177,10 +181,13 @@ func (s *Server) CreateMux() *mux.Router {
177 177
 	}
178 178
 
179 179
 	logrus.Debugf("Registering routers")
180
-	for _, router := range s.routers {
181
-		for _, r := range router.Routes() {
180
+	for _, apiRouter := range s.routers {
181
+		for _, r := range apiRouter.Routes() {
182 182
 			f := s.makeHTTPHandler(r.Handler())
183
-			r.Register(m, f)
183
+
184
+			logrus.Debugf("Registering %s, %s", r.Method(), r.Path())
185
+			m.Path(versionMatcher + r.Path()).Methods(r.Method()).Handler(f)
186
+			m.Path(r.Path()).Methods(r.Method()).Handler(f)
184 187
 		}
185 188
 	}
186 189
 
... ...
@@ -308,3 +308,44 @@ type VolumeCreateRequest struct {
308 308
 	Driver     string            // Driver is the name of the driver that should be used to create the volume
309 309
 	DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume.
310 310
 }
311
+
312
+// NetworkResource is the body of the "get network" http response message
313
+type NetworkResource struct {
314
+	Name       string                      `json:"name"`
315
+	ID         string                      `json:"id"`
316
+	Driver     string                      `json:"driver"`
317
+	Containers map[string]EndpointResource `json:"containers"`
318
+	Options    map[string]interface{}      `json:"options,omitempty"`
319
+}
320
+
321
+//EndpointResource contains network resources allocated and usd for a container in a network
322
+type EndpointResource struct {
323
+	EndpointID  string `json:"endpoint"`
324
+	MacAddress  string `json:"mac_address"`
325
+	IPv4Address string `json:"ipv4_address"`
326
+	IPv6Address string `json:"ipv6_address"`
327
+}
328
+
329
+// NetworkCreate is the expected body of the "create network" http request message
330
+type NetworkCreate struct {
331
+	Name           string                 `json:"name"`
332
+	CheckDuplicate bool                   `json:"check_duplicate"`
333
+	Driver         string                 `json:"driver"`
334
+	Options        map[string]interface{} `json:"options"`
335
+}
336
+
337
+// NetworkCreateResponse is the response message sent by the server for network create call
338
+type NetworkCreateResponse struct {
339
+	ID      string `json:"id"`
340
+	Warning string `json:"warning"`
341
+}
342
+
343
+// NetworkConnect represents the data to be used to connect a container to the network
344
+type NetworkConnect struct {
345
+	Container string `json:"container"`
346
+}
347
+
348
+// NetworkDisconnect represents the data to be used to disconnect a container from the network
349
+type NetworkDisconnect struct {
350
+	Container string `json:"container"`
351
+}
311 352
deleted file mode 100644
... ...
@@ -1,9 +0,0 @@
1
-// +build experimental
2
-
3
-package daemon
4
-
5
-import flag "github.com/docker/docker/pkg/mflag"
6
-
7
-func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
8
-	cmd.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", usageFn("Set default network"))
9
-}
10 1
deleted file mode 100644
... ...
@@ -1,8 +0,0 @@
1
-// +build !experimental
2
-
3
-package daemon
4
-
5
-import flag "github.com/docker/docker/pkg/mflag"
6
-
7
-func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
8
-}
... ...
@@ -77,6 +77,4 @@ func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) strin
77 77
 	cmd.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, usageFn("Use userland proxy for loopback traffic"))
78 78
 	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"))
79 79
 	cmd.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API"))
80
-
81
-	config.attachExperimentalFlags(cmd, usageFn)
82 80
 }
... ...
@@ -412,7 +412,7 @@ func (container *Container) buildHostnameFile() error {
412 412
 	return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
413 413
 }
414 414
 
415
-func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, error) {
415
+func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetwork.SandboxOption, error) {
416 416
 	var (
417 417
 		sboxOptions []libnetwork.SandboxOption
418 418
 		err         error
... ...
@@ -487,6 +487,23 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e
487 487
 		}
488 488
 	}
489 489
 
490
+	for _, extraHost := range container.hostConfig.ExtraHosts {
491
+		// allow IPv6 addresses in extra hosts; only split on first ":"
492
+		parts := strings.SplitN(extraHost, ":", 2)
493
+		sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1]))
494
+	}
495
+
496
+	// Link feature is supported only for the default bridge network.
497
+	// return if this call to build join options is not for default bridge network
498
+	if n.Name() != "bridge" {
499
+		return sboxOptions, nil
500
+	}
501
+
502
+	ep, _ := container.getEndpointInNetwork(n)
503
+	if ep == nil {
504
+		return sboxOptions, nil
505
+	}
506
+
490 507
 	var childEndpoints, parentEndpoints []string
491 508
 
492 509
 	children, err := container.daemon.children(container.Name)
... ...
@@ -503,17 +520,12 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e
503 503
 			aliasList = aliasList + " " + child.Name[1:]
504 504
 		}
505 505
 		sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.IPAddress))
506
-		if child.NetworkSettings.EndpointID != "" {
507
-			childEndpoints = append(childEndpoints, child.NetworkSettings.EndpointID)
506
+		cEndpoint, _ := child.getEndpointInNetwork(n)
507
+		if cEndpoint != nil && cEndpoint.ID() != "" {
508
+			childEndpoints = append(childEndpoints, cEndpoint.ID())
508 509
 		}
509 510
 	}
510 511
 
511
-	for _, extraHost := range container.hostConfig.ExtraHosts {
512
-		// allow IPv6 addresses in extra hosts; only split on first ":"
513
-		parts := strings.SplitN(extraHost, ":", 2)
514
-		sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1]))
515
-	}
516
-
517 512
 	refs := container.daemon.containerGraph().RefPaths(container.ID)
518 513
 	for _, ref := range refs {
519 514
 		if ref.ParentID == "0" {
... ...
@@ -528,8 +540,8 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e
528 528
 		if c != nil && !container.daemon.configStore.DisableBridge && container.hostConfig.NetworkMode.IsPrivate() {
529 529
 			logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress)
530 530
 			sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate(c.ID, ref.Name, container.NetworkSettings.IPAddress))
531
-			if c.NetworkSettings.EndpointID != "" {
532
-				parentEndpoints = append(parentEndpoints, c.NetworkSettings.EndpointID)
531
+			if ep.ID() != "" {
532
+				parentEndpoints = append(parentEndpoints, ep.ID())
533 533
 			}
534 534
 		}
535 535
 	}
... ...
@@ -546,6 +558,11 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e
546 546
 	return sboxOptions, nil
547 547
 }
548 548
 
549
+func (container *Container) getEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) {
550
+	endpointName := strings.TrimPrefix(container.Name, "/")
551
+	return n.EndpointByName(endpointName)
552
+}
553
+
549 554
 func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) {
550 555
 	if ep == nil {
551 556
 		return nil, derr.ErrorCodeEmptyEndpoint
... ...
@@ -650,10 +667,38 @@ func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error {
650 650
 	return nil
651 651
 }
652 652
 
653
-func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error {
654
-	networkSettings := &network.Settings{NetworkID: n.ID(), EndpointID: ep.ID()}
653
+func (container *Container) updateNetworkSettings(n libnetwork.Network) error {
654
+	if container.NetworkSettings == nil {
655
+		container.NetworkSettings = &network.Settings{Networks: []string{}}
656
+	}
657
+	settings := container.NetworkSettings
658
+
659
+	for _, s := range settings.Networks {
660
+		sn, err := container.daemon.FindNetwork(s)
661
+		if err != nil {
662
+			continue
663
+		}
664
+
665
+		if sn.Name() == n.Name() {
666
+			// Avoid duplicate config
667
+			return nil
668
+		}
669
+		if !runconfig.NetworkMode(sn.Type()).IsPrivate() ||
670
+			!runconfig.NetworkMode(n.Type()).IsPrivate() {
671
+			return runconfig.ErrConflictSharedNetwork
672
+		}
673
+		if runconfig.NetworkMode(sn.Name()).IsNone() ||
674
+			runconfig.NetworkMode(n.Name()).IsNone() {
675
+			return runconfig.ErrConflictNoNetwork
676
+		}
677
+	}
678
+	settings.Networks = append(settings.Networks, n.Name())
655 679
 
656
-	networkSettings, err := container.buildPortMapInfo(ep, networkSettings)
680
+	return nil
681
+}
682
+
683
+func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error {
684
+	networkSettings, err := container.buildPortMapInfo(ep, container.NetworkSettings)
657 685
 	if err != nil {
658 686
 		return err
659 687
 	}
... ...
@@ -667,7 +712,6 @@ func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network,
667 667
 		networkSettings.Bridge = container.daemon.configStore.Bridge.Iface
668 668
 	}
669 669
 
670
-	container.NetworkSettings = networkSettings
671 670
 	return nil
672 671
 }
673 672
 
... ...
@@ -688,7 +732,25 @@ func (container *Container) updateNetwork() error {
688 688
 		return derr.ErrorCodeNoSandbox.WithArgs(sid, err)
689 689
 	}
690 690
 
691
-	options, err := container.buildSandboxOptions()
691
+	// Find if container is connected to the default bridge network
692
+	var n libnetwork.Network
693
+	for _, name := range container.NetworkSettings.Networks {
694
+		sn, err := container.daemon.FindNetwork(name)
695
+		if err != nil {
696
+			continue
697
+		}
698
+		if sn.Name() == "bridge" {
699
+			n = sn
700
+			break
701
+		}
702
+	}
703
+
704
+	if n == nil {
705
+		// Not connected to the default bridge network; Nothing to do
706
+		return nil
707
+	}
708
+
709
+	options, err := container.buildSandboxOptions(n)
692 710
 	if err != nil {
693 711
 		return derr.ErrorCodeNetworkUpdate.WithArgs(err)
694 712
 	}
... ...
@@ -781,20 +843,6 @@ func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointO
781 781
 	return createOptions, nil
782 782
 }
783 783
 
784
-func parseService(controller libnetwork.NetworkController, service string) (string, string, string) {
785
-	dn := controller.Config().Daemon.DefaultNetwork
786
-	dd := controller.Config().Daemon.DefaultDriver
787
-
788
-	snd := strings.Split(service, ".")
789
-	if len(snd) > 2 {
790
-		return strings.Join(snd[:len(snd)-2], "."), snd[len(snd)-2], snd[len(snd)-1]
791
-	}
792
-	if len(snd) > 1 {
793
-		return snd[0], snd[1], dd
794
-	}
795
-	return snd[0], dn, dd
796
-}
797
-
798 784
 func createNetwork(controller libnetwork.NetworkController, dnet string, driver string) (libnetwork.Network, error) {
799 785
 	createOptions := []libnetwork.NetworkOption{}
800 786
 	genericOption := options.Generic{}
... ...
@@ -813,61 +861,69 @@ func createNetwork(controller libnetwork.NetworkController, dnet string, driver
813 813
 }
814 814
 
815 815
 func (container *Container) allocateNetwork() error {
816
-	mode := container.hostConfig.NetworkMode
817
-	controller := container.daemon.netController
818
-	if container.Config.NetworkDisabled || mode.IsContainer() {
819
-		return nil
820
-	}
816
+	settings := container.NetworkSettings.Networks
817
+	updateSettings := false
818
+	if settings == nil {
819
+		mode := container.hostConfig.NetworkMode
820
+		controller := container.daemon.netController
821
+		if container.Config.NetworkDisabled || mode.IsContainer() {
822
+			return nil
823
+		}
821 824
 
822
-	networkDriver := string(mode)
823
-	service := container.Config.PublishService
824
-	networkName := mode.NetworkName()
825
-	if mode.IsDefault() {
826
-		if service != "" {
827
-			service, networkName, networkDriver = parseService(controller, service)
828
-		} else {
825
+		networkName := mode.NetworkName()
826
+		if mode.IsDefault() {
829 827
 			networkName = controller.Config().Daemon.DefaultNetwork
830
-			networkDriver = controller.Config().Daemon.DefaultDriver
831 828
 		}
832
-	} else if service != "" {
833
-		return derr.ErrorCodeNetworkConflict
829
+		settings = []string{networkName}
830
+		updateSettings = true
834 831
 	}
835 832
 
836
-	if runconfig.NetworkMode(networkDriver).IsBridge() && container.daemon.configStore.DisableBridge {
837
-		container.Config.NetworkDisabled = true
838
-		return nil
833
+	for _, n := range settings {
834
+		if err := container.connectToNetwork(n, updateSettings); err != nil {
835
+			if updateSettings {
836
+				return err
837
+			}
838
+			// dont fail a container restart case if the user removed the network
839
+			logrus.Warnf("Could not connect container %s : %v", container.ID, err)
840
+		}
839 841
 	}
840 842
 
841
-	if service == "" {
842
-		// dot character "." has a special meaning to support SERVICE[.NETWORK] format.
843
-		// For backward compatibility, replacing "." with "-", instead of failing
844
-		service = strings.Replace(container.Name, ".", "-", -1)
845
-		// Service names dont like "/" in them. removing it instead of failing for backward compatibility
846
-		service = strings.Replace(service, "/", "", -1)
843
+	return container.writeHostConfig()
844
+}
845
+
846
+// ConnectToNetwork connects a container to a netork
847
+func (container *Container) ConnectToNetwork(idOrName string) error {
848
+	if !container.Running {
849
+		return derr.ErrorCodeNotRunning.WithArgs(container.ID)
847 850
 	}
851
+	return container.connectToNetwork(idOrName, true)
852
+}
848 853
 
849
-	if err := container.configureNetwork(networkName, service, networkDriver, mode.IsDefault()); err != nil {
850
-		return err
854
+func (container *Container) connectToNetwork(idOrName string, updateSettings bool) error {
855
+	if container.hostConfig.NetworkMode.IsContainer() {
856
+		return runconfig.ErrConflictSharedNetwork
851 857
 	}
852 858
 
853
-	return container.writeHostConfig()
854
-}
859
+	if runconfig.NetworkMode(idOrName).IsBridge() &&
860
+		container.daemon.configStore.DisableBridge {
861
+		container.Config.NetworkDisabled = true
862
+		return nil
863
+	}
855 864
 
856
-func (container *Container) configureNetwork(networkName, service, networkDriver string, canCreateNetwork bool) error {
857 865
 	controller := container.daemon.netController
858 866
 
859
-	n, err := controller.NetworkByName(networkName)
867
+	n, err := container.daemon.FindNetwork(idOrName)
860 868
 	if err != nil {
861
-		if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok || !canCreateNetwork {
862
-			return err
863
-		}
869
+		return err
870
+	}
864 871
 
865
-		if n, err = createNetwork(controller, networkName, networkDriver); err != nil {
872
+	if updateSettings {
873
+		if err := container.updateNetworkSettings(n); err != nil {
866 874
 			return err
867 875
 		}
868 876
 	}
869 877
 
870
-	ep, err := n.EndpointByName(service)
878
+	ep, err := container.getEndpointInNetwork(n)
871 879
 	if err != nil {
872 880
 		if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok {
873 881
 			return err
... ...
@@ -878,7 +934,8 @@ func (container *Container) configureNetwork(networkName, service, networkDriver
878 878
 			return err
879 879
 		}
880 880
 
881
-		ep, err = n.CreateEndpoint(service, createOptions...)
881
+		endpointName := strings.TrimPrefix(container.Name, "/")
882
+		ep, err = n.CreateEndpoint(endpointName, createOptions...)
882 883
 		if err != nil {
883 884
 			return err
884 885
 		}
... ...
@@ -897,7 +954,7 @@ func (container *Container) configureNetwork(networkName, service, networkDriver
897 897
 		return false
898 898
 	})
899 899
 	if sb == nil {
900
-		options, err := container.buildSandboxOptions()
900
+		options, err := container.buildSandboxOptions(n)
901 901
 		if err != nil {
902 902
 			return err
903 903
 		}
... ...
@@ -1039,12 +1096,11 @@ func (container *Container) releaseNetwork() {
1039 1039
 	}
1040 1040
 
1041 1041
 	sid := container.NetworkSettings.SandboxID
1042
-	eid := container.NetworkSettings.EndpointID
1043
-	nid := container.NetworkSettings.NetworkID
1042
+	networks := container.NetworkSettings.Networks
1044 1043
 
1045
-	container.NetworkSettings = &network.Settings{}
1044
+	container.NetworkSettings = &network.Settings{Networks: networks}
1046 1045
 
1047
-	if sid == "" || nid == "" || eid == "" {
1046
+	if sid == "" || len(networks) == 0 {
1048 1047
 		return
1049 1048
 	}
1050 1049
 
... ...
@@ -1054,29 +1110,73 @@ func (container *Container) releaseNetwork() {
1054 1054
 		return
1055 1055
 	}
1056 1056
 
1057
-	n, err := container.daemon.netController.NetworkByID(nid)
1058
-	if err != nil {
1059
-		logrus.Errorf("error locating network id %s: %v", nid, err)
1060
-		return
1061
-	}
1062
-
1063
-	ep, err := n.EndpointByID(eid)
1064
-	if err != nil {
1065
-		logrus.Errorf("error locating endpoint id %s: %v", eid, err)
1066
-		return
1057
+	for _, ns := range networks {
1058
+		n, err := container.daemon.FindNetwork(ns)
1059
+		if err != nil {
1060
+			continue
1061
+		}
1062
+		container.disconnectFromNetwork(n, false)
1067 1063
 	}
1068 1064
 
1069 1065
 	if err := sb.Delete(); err != nil {
1070 1066
 		logrus.Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err)
1071
-		return
1067
+	}
1068
+}
1069
+
1070
+// DisconnectFromNetwork disconnects a container from a network
1071
+func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error {
1072
+	if !container.Running {
1073
+		return derr.ErrorCodeNotRunning.WithArgs(container.ID)
1074
+	}
1075
+
1076
+	return container.disconnectFromNetwork(n, true)
1077
+}
1078
+
1079
+func (container *Container) disconnectFromNetwork(n libnetwork.Network, updateSettings bool) error {
1080
+	var (
1081
+		ep   libnetwork.Endpoint
1082
+		sbox libnetwork.Sandbox
1083
+	)
1084
+
1085
+	s := func(current libnetwork.Endpoint) bool {
1086
+		if sb := current.Info().Sandbox(); sb != nil {
1087
+			if sb.ContainerID() == container.ID {
1088
+				ep = current
1089
+				sbox = sb
1090
+				return true
1091
+			}
1092
+		}
1093
+		return false
1094
+	}
1095
+	n.WalkEndpoints(s)
1096
+
1097
+	if ep == nil {
1098
+		return fmt.Errorf("could not locate network endpoint for container %s", container.ID)
1099
+	}
1100
+
1101
+	if err := ep.Leave(sbox); err != nil {
1102
+		return fmt.Errorf("container %s failed to leave network %s: %v", container.ID, n.Name(), err)
1072 1103
 	}
1073 1104
 
1074
-	// In addition to leaving all endpoints, delete implicitly created endpoint
1075
-	if container.Config.PublishService == "" {
1076
-		if err := ep.Delete(); err != nil {
1077
-			logrus.Errorf("deleting endpoint failed: %v", err)
1105
+	if err := ep.Delete(); err != nil {
1106
+		return fmt.Errorf("endpoint delete failed for container %s on network %s: %v", container.ID, n.Name(), err)
1107
+	}
1108
+
1109
+	if updateSettings {
1110
+		networks := container.NetworkSettings.Networks
1111
+		for i, s := range networks {
1112
+			sn, err := container.daemon.FindNetwork(s)
1113
+			if err != nil {
1114
+				continue
1115
+			}
1116
+			if sn.Name() == n.Name() {
1117
+				networks = append(networks[:i], networks[i+1:]...)
1118
+				container.NetworkSettings.Networks = networks
1119
+				break
1120
+			}
1078 1121
 		}
1079 1122
 	}
1123
+	return nil
1080 1124
 }
1081 1125
 
1082 1126
 func (container *Container) unmountVolumes(forceSyscall bool) error {
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/docker/docker/daemon/execdriver"
9 9
 	derr "github.com/docker/docker/errors"
10
+	"github.com/docker/libnetwork"
10 11
 )
11 12
 
12 13
 // DefaultPathEnv is deliberately empty on Windows as the default path will be set by
... ...
@@ -38,6 +39,16 @@ func (container *Container) initializeNetworking() error {
38 38
 	return nil
39 39
 }
40 40
 
41
+// ConnectToNetwork connects a container to the network
42
+func (container *Container) ConnectToNetwork(idOrName string) error {
43
+	return nil
44
+}
45
+
46
+// DisconnectFromNetwork disconnects a container from, the network
47
+func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error {
48
+	return nil
49
+}
50
+
41 51
 func (container *Container) setupWorkingDirectory() error {
42 52
 	return nil
43 53
 }
... ...
@@ -1162,11 +1162,6 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig,
1162 1162
 	return verifyPlatformContainerSettings(daemon, hostConfig, config)
1163 1163
 }
1164 1164
 
1165
-// NetworkController exposes the libnetwork interface to manage networks.
1166
-func (daemon *Daemon) NetworkController() libnetwork.NetworkController {
1167
-	return daemon.netController
1168
-}
1169
-
1170 1165
 func configureVolumes(config *Config) (*store.VolumeStore, error) {
1171 1166
 	volumesDriver, err := local.New(config.Root)
1172 1167
 	if err != nil {
1173 1168
new file mode 100644
... ...
@@ -0,0 +1,106 @@
0
+package daemon
1
+
2
+import (
3
+	"errors"
4
+	"strings"
5
+
6
+	"github.com/docker/libnetwork"
7
+	"github.com/docker/libnetwork/netlabel"
8
+)
9
+
10
+const (
11
+	// NetworkByID represents a constant to find a network by its ID
12
+	NetworkByID = iota + 1
13
+	// NetworkByName represents a constant to find a network by its Name
14
+	NetworkByName
15
+)
16
+
17
+// FindNetwork function finds a network for a given string that can represent network name or id
18
+func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) {
19
+	// Find by Name
20
+	n, err := daemon.GetNetwork(idName, NetworkByName)
21
+	if _, ok := err.(libnetwork.ErrNoSuchNetwork); err != nil && !ok {
22
+		return nil, err
23
+	}
24
+
25
+	if n != nil {
26
+		return n, nil
27
+	}
28
+
29
+	// Find by id
30
+	n, err = daemon.GetNetwork(idName, NetworkByID)
31
+	if err != nil {
32
+		return nil, err
33
+	}
34
+
35
+	return n, nil
36
+}
37
+
38
+// GetNetwork function returns a network for a given string that represents the network and
39
+// a hint to indicate if the string is an Id or Name of the network
40
+func (daemon *Daemon) GetNetwork(idName string, by int) (libnetwork.Network, error) {
41
+	c := daemon.netController
42
+	switch by {
43
+	case NetworkByID:
44
+		list := daemon.GetNetworksByID(idName)
45
+
46
+		if len(list) == 0 {
47
+			return nil, libnetwork.ErrNoSuchNetwork(idName)
48
+		}
49
+
50
+		if len(list) > 1 {
51
+			return nil, libnetwork.ErrInvalidID(idName)
52
+		}
53
+
54
+		return list[0], nil
55
+	case NetworkByName:
56
+		if idName == "" {
57
+			idName = c.Config().Daemon.DefaultNetwork
58
+		}
59
+		return c.NetworkByName(idName)
60
+	}
61
+	return nil, errors.New("unexpected selector for GetNetwork")
62
+}
63
+
64
+// GetNetworksByID returns a list of networks whose ID partially matches zero or more networks
65
+func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network {
66
+	c := daemon.netController
67
+	list := []libnetwork.Network{}
68
+	l := func(nw libnetwork.Network) bool {
69
+		if strings.HasPrefix(nw.ID(), partialID) {
70
+			list = append(list, nw)
71
+		}
72
+		return false
73
+	}
74
+	c.WalkNetworks(l)
75
+
76
+	return list
77
+}
78
+
79
+// CreateNetwork creates a network with the given name, driver and other optional parameters
80
+func (daemon *Daemon) CreateNetwork(name, driver string, options map[string]interface{}) (libnetwork.Network, error) {
81
+	c := daemon.netController
82
+	if driver == "" {
83
+		driver = c.Config().Daemon.DefaultDriver
84
+	}
85
+
86
+	if options == nil {
87
+		options = make(map[string]interface{})
88
+	}
89
+	_, ok := options[netlabel.GenericData]
90
+	if !ok {
91
+		options[netlabel.GenericData] = make(map[string]interface{})
92
+	}
93
+
94
+	return c.NewNetwork(driver, name, parseOptions(options)...)
95
+}
96
+
97
+func parseOptions(options map[string]interface{}) []libnetwork.NetworkOption {
98
+	var setFctList []libnetwork.NetworkOption
99
+
100
+	if options != nil {
101
+		setFctList = append(setFctList, libnetwork.NetworkOptionGeneric(options))
102
+	}
103
+
104
+	return setFctList
105
+}
... ...
@@ -24,7 +24,7 @@ type Settings struct {
24 24
 	LinkLocalIPv6Address   string
25 25
 	LinkLocalIPv6PrefixLen int
26 26
 	MacAddress             string
27
-	NetworkID              string
27
+	Networks               []string
28 28
 	Ports                  nat.PortMap
29 29
 	SandboxKey             string
30 30
 	SecondaryIPAddresses   []Address
... ...
@@ -1,72 +1,201 @@
1
-// +build experimental
2
-
3 1
 package main
4 2
 
5 3
 import (
6 4
 	"encoding/json"
7
-	"fmt"
5
+	"net"
8 6
 	"net/http"
7
+	"net/url"
8
+	"strings"
9 9
 
10
+	"github.com/docker/docker/api/types"
11
+	"github.com/docker/docker/pkg/parsers/filters"
10 12
 	"github.com/go-check/check"
11 13
 )
12 14
 
15
+func (s *DockerSuite) TestApiNetworkGetDefaults(c *check.C) {
16
+	// By default docker daemon creates 3 networks. check if they are present
17
+	defaults := []string{"bridge", "host", "none"}
18
+	for _, nn := range defaults {
19
+		c.Assert(isNetworkAvailable(c, nn), check.Equals, true)
20
+	}
21
+}
22
+
23
+func (s *DockerSuite) TestApiNetworkCreateDelete(c *check.C) {
24
+	// Create a network
25
+	name := "testnetwork"
26
+	id := createNetwork(c, name, true)
27
+	c.Assert(isNetworkAvailable(c, name), check.Equals, true)
28
+
29
+	// POST another network with same name and CheckDuplicate must fail
30
+	createNetwork(c, name, false)
31
+
32
+	// delete the network and make sure it is deleted
33
+	deleteNetwork(c, id, true)
34
+	c.Assert(isNetworkAvailable(c, name), check.Equals, false)
35
+}
36
+
37
+func (s *DockerSuite) TestApiNetworkFilter(c *check.C) {
38
+	nr := getNetworkResource(c, getNetworkIDByName(c, "bridge"))
39
+	c.Assert(nr.Name, check.Equals, "bridge")
40
+}
41
+
42
+func (s *DockerSuite) TestApiNetworkInspect(c *check.C) {
43
+	// Inspect default bridge network
44
+	nr := getNetworkResource(c, "bridge")
45
+	c.Assert(nr.Name, check.Equals, "bridge")
46
+
47
+	// run a container and attach it to the default bridge network
48
+	out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
49
+	containerID := strings.TrimSpace(out)
50
+	containerIP := findContainerIP(c, "test")
51
+
52
+	// inspect default bridge network again and make sure the container is connected
53
+	nr = getNetworkResource(c, nr.ID)
54
+	c.Assert(len(nr.Containers), check.Equals, 1)
55
+	c.Assert(nr.Containers[containerID], check.NotNil)
56
+
57
+	ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
58
+	c.Assert(err, check.IsNil)
59
+	c.Assert(ip.String(), check.Equals, containerIP)
60
+}
61
+
62
+func (s *DockerSuite) TestApiNetworkConnectDisconnect(c *check.C) {
63
+	// Create test network
64
+	name := "testnetwork"
65
+	id := createNetwork(c, name, true)
66
+	nr := getNetworkResource(c, id)
67
+	c.Assert(nr.Name, check.Equals, name)
68
+	c.Assert(nr.ID, check.Equals, id)
69
+	c.Assert(len(nr.Containers), check.Equals, 0)
70
+
71
+	// run a container
72
+	out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
73
+	containerID := strings.TrimSpace(out)
74
+
75
+	// connect the container to the test network
76
+	connectNetwork(c, nr.ID, containerID)
77
+
78
+	// inspect the network to make sure container is connected
79
+	nr = getNetworkResource(c, nr.ID)
80
+	c.Assert(len(nr.Containers), check.Equals, 1)
81
+	c.Assert(nr.Containers[containerID], check.NotNil)
82
+
83
+	// check if container IP matches network inspect
84
+	ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
85
+	c.Assert(err, check.IsNil)
86
+	containerIP := findContainerIP(c, "test")
87
+	c.Assert(ip.String(), check.Equals, containerIP)
88
+
89
+	// disconnect container from the network
90
+	disconnectNetwork(c, nr.ID, containerID)
91
+	nr = getNetworkResource(c, nr.ID)
92
+	c.Assert(nr.Name, check.Equals, name)
93
+	c.Assert(len(nr.Containers), check.Equals, 0)
94
+
95
+	// delete the network
96
+	deleteNetwork(c, nr.ID, true)
97
+}
98
+
13 99
 func isNetworkAvailable(c *check.C, name string) bool {
14 100
 	status, body, err := sockRequest("GET", "/networks", nil)
15 101
 	c.Assert(status, check.Equals, http.StatusOK)
16 102
 	c.Assert(err, check.IsNil)
17 103
 
18
-	var inspectJSON []struct {
19
-		Name string
20
-		ID   string
21
-		Type string
22
-	}
23
-	if err = json.Unmarshal(body, &inspectJSON); err != nil {
24
-		c.Fatalf("unable to unmarshal response body: %v", err)
25
-	}
26
-	for _, n := range inspectJSON {
104
+	nJSON := []types.NetworkResource{}
105
+	err = json.Unmarshal(body, &nJSON)
106
+	c.Assert(err, check.IsNil)
107
+
108
+	for _, n := range nJSON {
27 109
 		if n.Name == name {
28 110
 			return true
29 111
 		}
30 112
 	}
31 113
 	return false
114
+}
115
+
116
+func getNetworkIDByName(c *check.C, name string) string {
117
+	var (
118
+		v          = url.Values{}
119
+		filterArgs = filters.Args{}
120
+	)
121
+	filterArgs["name"] = []string{name}
122
+	filterJSON, err := filters.ToParam(filterArgs)
123
+	c.Assert(err, check.IsNil)
124
+	v.Set("filters", filterJSON)
125
+
126
+	status, body, err := sockRequest("GET", "/networks?"+v.Encode(), nil)
127
+	c.Assert(status, check.Equals, http.StatusOK)
128
+	c.Assert(err, check.IsNil)
129
+
130
+	nJSON := []types.NetworkResource{}
131
+	err = json.Unmarshal(body, &nJSON)
132
+	c.Assert(err, check.IsNil)
133
+	c.Assert(len(nJSON), check.Equals, 1)
32 134
 
135
+	return nJSON[0].ID
33 136
 }
34 137
 
35
-func (s *DockerSuite) TestNetworkApiGetAll(c *check.C) {
36
-	defaults := []string{"bridge", "host", "none"}
37
-	for _, nn := range defaults {
38
-		if !isNetworkAvailable(c, nn) {
39
-			c.Fatalf("Missing Default network : %s", nn)
40
-		}
41
-	}
138
+func getNetworkResource(c *check.C, id string) *types.NetworkResource {
139
+	_, obj, err := sockRequest("GET", "/networks/"+id, nil)
140
+	c.Assert(err, check.IsNil)
141
+
142
+	nr := types.NetworkResource{}
143
+	err = json.Unmarshal(obj, &nr)
144
+	c.Assert(err, check.IsNil)
145
+
146
+	return &nr
42 147
 }
43 148
 
44
-func (s *DockerSuite) TestNetworkApiCreateDelete(c *check.C) {
45
-	name := "testnetwork"
46
-	config := map[string]interface{}{
47
-		"name":         name,
48
-		"network_type": "bridge",
149
+func createNetwork(c *check.C, name string, shouldSucceed bool) string {
150
+	config := types.NetworkCreate{
151
+		Name:           name,
152
+		Driver:         "bridge",
153
+		CheckDuplicate: true,
154
+	}
155
+
156
+	status, resp, err := sockRequest("POST", "/networks/create", config)
157
+	if !shouldSucceed {
158
+		c.Assert(status, check.Not(check.Equals), http.StatusCreated)
159
+		return ""
49 160
 	}
50 161
 
51
-	status, resp, err := sockRequest("POST", "/networks", config)
52 162
 	c.Assert(status, check.Equals, http.StatusCreated)
53 163
 	c.Assert(err, check.IsNil)
54 164
 
55
-	if !isNetworkAvailable(c, name) {
56
-		c.Fatalf("Network %s not found", name)
165
+	var nr types.NetworkCreateResponse
166
+	err = json.Unmarshal(resp, &nr)
167
+	c.Assert(err, check.IsNil)
168
+
169
+	return nr.ID
170
+}
171
+
172
+func connectNetwork(c *check.C, nid, cid string) {
173
+	config := types.NetworkConnect{
174
+		Container: cid,
57 175
 	}
58 176
 
59
-	var id string
60
-	err = json.Unmarshal(resp, &id)
61
-	if err != nil {
62
-		c.Fatal(err)
177
+	status, _, err := sockRequest("POST", "/networks/"+nid+"/connect", config)
178
+	c.Assert(status, check.Equals, http.StatusOK)
179
+	c.Assert(err, check.IsNil)
180
+}
181
+
182
+func disconnectNetwork(c *check.C, nid, cid string) {
183
+	config := types.NetworkConnect{
184
+		Container: cid,
63 185
 	}
64 186
 
65
-	status, _, err = sockRequest("DELETE", fmt.Sprintf("/networks/%s", id), nil)
187
+	status, _, err := sockRequest("POST", "/networks/"+nid+"/disconnect", config)
66 188
 	c.Assert(status, check.Equals, http.StatusOK)
67 189
 	c.Assert(err, check.IsNil)
190
+}
68 191
 
69
-	if isNetworkAvailable(c, name) {
70
-		c.Fatalf("Network %s not deleted", name)
192
+func deleteNetwork(c *check.C, id string, shouldSucceed bool) {
193
+	status, _, err := sockRequest("DELETE", "/networks/"+id, nil)
194
+	if !shouldSucceed {
195
+		c.Assert(status, check.Not(check.Equals), http.StatusOK)
196
+		c.Assert(err, check.NotNil)
197
+		return
71 198
 	}
199
+	c.Assert(status, check.Equals, http.StatusOK)
200
+	c.Assert(err, check.IsNil)
72 201
 }
73 202
deleted file mode 100644
... ...
@@ -1,40 +0,0 @@
1
-// +build daemon,experimental,!windows
2
-
3
-package main
4
-
5
-import (
6
-	"os/exec"
7
-	"strings"
8
-
9
-	"github.com/go-check/check"
10
-)
11
-
12
-func assertNetwork(c *check.C, d *Daemon, name string) {
13
-	out, err := d.Cmd("network", "ls")
14
-	c.Assert(err, check.IsNil)
15
-	lines := strings.Split(out, "\n")
16
-	for i := 1; i < len(lines)-1; i++ {
17
-		if strings.Contains(lines[i], name) {
18
-			return
19
-		}
20
-	}
21
-	c.Fatalf("Network %s not found in network ls o/p", name)
22
-}
23
-
24
-func (s *DockerDaemonSuite) TestDaemonDefaultNetwork(c *check.C) {
25
-	testRequires(c, SameHostDaemon)
26
-	d := s.d
27
-
28
-	networkName := "testdefault"
29
-	err := d.StartWithBusybox("--default-network", "bridge:"+networkName)
30
-	c.Assert(err, check.IsNil)
31
-
32
-	_, err = d.Cmd("run", "busybox", "true")
33
-	c.Assert(err, check.IsNil)
34
-
35
-	assertNetwork(c, d, networkName)
36
-
37
-	ifconfigCmd := exec.Command("ifconfig", networkName)
38
-	_, _, _, err = runCommandWithStdoutStderr(ifconfigCmd)
39
-	c.Assert(err, check.IsNil)
40
-}
... ...
@@ -77,7 +77,7 @@ func (s *DockerSuite) TestNetHostname(c *check.C) {
77 77
 	if out, _, err = runCommandWithOutput(runCmd); err == nil {
78 78
 		c.Fatalf(out, err)
79 79
 	}
80
-	checkContains("invalid --net: weird", out, c)
80
+	checkContains("network weird not found", out, c)
81 81
 }
82 82
 
83 83
 func (s *DockerSuite) TestConflictContainerNetworkAndLinks(c *check.C) {
... ...
@@ -3168,7 +3168,7 @@ func (s *DockerSuite) TestRunCreateContainerFailedCleanUp(c *check.C) {
3168 3168
 	testRequires(c, DaemonIsLinux)
3169 3169
 	name := "unique_name"
3170 3170
 	_, _, err := dockerCmdWithError("run", "--name", name, "--link", "nothing:nothing", "busybox")
3171
-	c.Assert(err, check.Not(check.IsNil), check.Commentf("Expected docker run to fail!"))
3171
+	c.Assert(err, check.NotNil, check.Commentf("Expected docker run to fail!"))
3172 3172
 
3173 3173
 	containerID, err := inspectField(name, "Id")
3174 3174
 	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) {
3404 3404
 	dockerCmd(c, "stop", "second")
3405 3405
 }
3406 3406
 
3407
+func (s *DockerSuite) TestContainersInUserDefinedNetwork(c *check.C) {
3408
+	testRequires(c, DaemonIsLinux)
3409
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork")
3410
+	dockerCmd(c, "run", "-d", "--net=testnetwork", "--name=first", "busybox", "top")
3411
+	dockerCmd(c, "run", "-t", "--net=testnetwork", "--name=second", "busybox", "ping", "-c", "1", "first")
3412
+	dockerCmd(c, "stop", "first")
3413
+	dockerCmd(c, "stop", "second")
3414
+	dockerCmd(c, "network", "rm", "testnetwork")
3415
+}
3416
+
3417
+func (s *DockerSuite) TestContainersInMultipleNetworks(c *check.C) {
3418
+	testRequires(c, DaemonIsLinux)
3419
+	// Create 2 networks using bridge driver
3420
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
3421
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
3422
+	// Run and connect containers to testnetwork1
3423
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
3424
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
3425
+	// Check connectivity between containers in testnetwork2
3426
+	dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1")
3427
+	// Connect containers to testnetwork2
3428
+	dockerCmd(c, "network", "connect", "testnetwork2", "first")
3429
+	dockerCmd(c, "network", "connect", "testnetwork2", "second")
3430
+	// Check connectivity between containers
3431
+	dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2")
3432
+	dockerCmd(c, "stop", "first")
3433
+	dockerCmd(c, "stop", "second")
3434
+	dockerCmd(c, "network", "rm", "testnetwork1")
3435
+	dockerCmd(c, "network", "rm", "testnetwork2")
3436
+}
3437
+
3438
+func (s *DockerSuite) TestContainersNetworkIsolation(c *check.C) {
3439
+	testRequires(c, DaemonIsLinux)
3440
+	// Create 2 networks using bridge driver
3441
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
3442
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
3443
+	// Run 1 containers in testnetwork1 and another in testnetwork2
3444
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
3445
+	dockerCmd(c, "run", "-d", "--net=testnetwork2", "--name=second", "busybox", "top")
3446
+
3447
+	// Check Isolation between containers : ping must fail
3448
+	_, _, err := dockerCmdWithError("exec", "first", "ping", "-c", "1", "second")
3449
+	c.Assert(err, check.NotNil)
3450
+	// Connect first container to testnetwork2
3451
+	dockerCmd(c, "network", "connect", "testnetwork2", "first")
3452
+	// ping must succeed now
3453
+	_, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second")
3454
+	c.Assert(err, check.IsNil)
3455
+
3456
+	// Disconnect first container from testnetwork2
3457
+	dockerCmd(c, "network", "disconnect", "testnetwork2", "first")
3458
+	// ping must fail again
3459
+	_, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second")
3460
+	c.Assert(err, check.NotNil)
3461
+
3462
+	dockerCmd(c, "stop", "first")
3463
+	dockerCmd(c, "stop", "second")
3464
+	dockerCmd(c, "network", "rm", "testnetwork1")
3465
+	dockerCmd(c, "network", "rm", "testnetwork2")
3466
+}
3467
+
3468
+func (s *DockerSuite) TestNetworkRmWithActiveContainers(c *check.C) {
3469
+	testRequires(c, DaemonIsLinux)
3470
+	// Create 2 networks using bridge driver
3471
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
3472
+	// Run and connect containers to testnetwork1
3473
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
3474
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
3475
+	// Network delete with active containers must fail
3476
+	_, _, err := dockerCmdWithError("network", "rm", "testnetwork1")
3477
+	c.Assert(err, check.NotNil)
3478
+
3479
+	dockerCmd(c, "stop", "first")
3480
+	_, _, err = dockerCmdWithError("network", "rm", "testnetwork1")
3481
+	c.Assert(err, check.NotNil)
3482
+
3483
+	dockerCmd(c, "stop", "second")
3484
+	// Network delete must succeed after all the connected containers are inactive
3485
+	dockerCmd(c, "network", "rm", "testnetwork1")
3486
+}
3487
+
3488
+func (s *DockerSuite) TestContainerRestartInMultipleNetworks(c *check.C) {
3489
+	testRequires(c, DaemonIsLinux)
3490
+	// Create 2 networks using bridge driver
3491
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
3492
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
3493
+	// Run and connect containers to testnetwork1
3494
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
3495
+	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
3496
+	// Check connectivity between containers in testnetwork2
3497
+	dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1")
3498
+	// Connect containers to testnetwork2
3499
+	dockerCmd(c, "network", "connect", "testnetwork2", "first")
3500
+	dockerCmd(c, "network", "connect", "testnetwork2", "second")
3501
+	// Check connectivity between containers
3502
+	dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2")
3503
+
3504
+	// Stop second container and test ping failures on both networks
3505
+	dockerCmd(c, "stop", "second")
3506
+	_, _, err := dockerCmdWithError("exec", "first", "ping", "-c", "1", "second.testnetwork1")
3507
+	c.Assert(err, check.NotNil)
3508
+	_, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second.testnetwork2")
3509
+	c.Assert(err, check.NotNil)
3510
+
3511
+	// Start second container and connectivity must be restored on both networks
3512
+	dockerCmd(c, "start", "second")
3513
+	dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1")
3514
+	dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2")
3515
+
3516
+	dockerCmd(c, "stop", "first")
3517
+	dockerCmd(c, "stop", "second")
3518
+	dockerCmd(c, "network", "rm", "testnetwork1")
3519
+	dockerCmd(c, "network", "rm", "testnetwork2")
3520
+}
3521
+
3522
+func (s *DockerSuite) TestContainerWithConflictingHostNetworks(c *check.C) {
3523
+	testRequires(c, DaemonIsLinux)
3524
+	// Run a container with --net=host
3525
+	dockerCmd(c, "run", "-d", "--net=host", "--name=first", "busybox", "top")
3526
+
3527
+	// Create a network using bridge driver
3528
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
3529
+
3530
+	// Connecting to the user defined network must fail
3531
+	_, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "first")
3532
+	c.Assert(err, check.NotNil)
3533
+	dockerCmd(c, "stop", "first")
3534
+	dockerCmd(c, "network", "rm", "testnetwork1")
3535
+}
3536
+
3537
+func (s *DockerSuite) TestContainerWithConflictingSharedNetwork(c *check.C) {
3538
+	testRequires(c, DaemonIsLinux)
3539
+	dockerCmd(c, "run", "-d", "--name=first", "busybox", "top")
3540
+	// Run second container in first container's network namespace
3541
+	dockerCmd(c, "run", "-d", "--net=container:first", "--name=second", "busybox", "top")
3542
+
3543
+	// Create a network using bridge driver
3544
+	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
3545
+
3546
+	// Connecting to the user defined network must fail
3547
+	_, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "second")
3548
+	c.Assert(err, check.NotNil)
3549
+
3550
+	dockerCmd(c, "stop", "first")
3551
+	dockerCmd(c, "stop", "second")
3552
+	dockerCmd(c, "network", "rm", "testnetwork1")
3553
+}
3554
+
3407 3555
 // #11957 - stdin with no tty does not exit if stdin is not closed even though container exited
3408 3556
 func (s *DockerSuite) TestRunStdinBlockedAfterContainerExit(c *check.C) {
3409 3557
 	cmd := exec.Command(dockerBinary, "run", "-i", "--name=test", "busybox", "true")
... ...
@@ -20,7 +20,6 @@ type Config struct {
20 20
 	AttachStdout    bool                  // Attach the standard output
21 21
 	AttachStderr    bool                  // Attach the standard error
22 22
 	ExposedPorts    map[nat.Port]struct{} // List of exposed ports
23
-	PublishService  string                // Name of the network service exposed by the container
24 23
 	Tty             bool                  // Attach standard streams to a tty, including stdin if it is not closed.
25 24
 	OpenStdin       bool                  // Open stdin
26 25
 	StdinOnce       bool                  // If true, close stdin after the 1 attached client disconnects.
... ...
@@ -24,7 +24,7 @@ func TestNetworkModeTest(t *testing.T) {
24 24
 	}
25 25
 	networkModeNames := map[NetworkMode]string{
26 26
 		"":                         "",
27
-		"something:weird":          "",
27
+		"something:weird":          "something:weird",
28 28
 		"bridge":                   "bridge",
29 29
 		DefaultDaemonNetworkMode(): "bridge",
30 30
 		"host":           "host",
... ...
@@ -34,6 +34,8 @@ func (n NetworkMode) NetworkName() string {
34 34
 		return "none"
35 35
 	} else if n.IsDefault() {
36 36
 		return "default"
37
+	} else if n.IsUserDefined() {
38
+		return n.UserDefined()
37 39
 	}
38 40
 	return ""
39 41
 }
... ...
@@ -59,6 +61,19 @@ func (n NetworkMode) IsNone() bool {
59 59
 	return n == "none"
60 60
 }
61 61
 
62
+// IsUserDefined indicates user-created network
63
+func (n NetworkMode) IsUserDefined() bool {
64
+	return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
65
+}
66
+
67
+//UserDefined indicates user-created network
68
+func (n NetworkMode) UserDefined() string {
69
+	if n.IsUserDefined() {
70
+		return string(n)
71
+	}
72
+	return ""
73
+}
74
+
62 75
 // MergeConfigs merges the specified container Config and HostConfig.
63 76
 // It creates a ContainerConfigWrapper.
64 77
 func MergeConfigs(config *Config, hostConfig *HostConfig) *ContainerConfigWrapper {
... ...
@@ -17,6 +17,12 @@ import (
17 17
 var (
18 18
 	// ErrConflictContainerNetworkAndLinks conflict between --net=container and links
19 19
 	ErrConflictContainerNetworkAndLinks = fmt.Errorf("Conflicting options: --net=container can't be used with links. This would result in undefined behavior")
20
+	// ErrConflictUserDefinedNetworkAndLinks conflict between --net=<NETWORK> and links
21
+	ErrConflictUserDefinedNetworkAndLinks = fmt.Errorf("Conflicting options: --net=<NETWORK> can't be used with links. This would result in undefined behavior")
22
+	// ErrConflictSharedNetwork conflict between private and other networks
23
+	ErrConflictSharedNetwork = fmt.Errorf("Container sharing network namespace with another container or host cannot be connected to any other network")
24
+	// ErrConflictNoNetwork conflict between private and other networks
25
+	ErrConflictNoNetwork = fmt.Errorf("Container cannot be connected to multiple networks with one of the networks in --none mode")
20 26
 	// ErrConflictNetworkAndDNS conflict between --dns and the network mode
21 27
 	ErrConflictNetworkAndDNS = fmt.Errorf("Conflicting options: --dns and the network mode (--net)")
22 28
 	// ErrConflictNetworkHostname conflict between the hostname and the network mode
... ...
@@ -88,7 +94,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
88 88
 		flCpusetMems        = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
89 89
 		flBlkioWeight       = cmd.Int64([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000")
90 90
 		flSwappiness        = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tuning container memory swappiness (0 to 100)")
91
-		flNetMode           = cmd.String([]string{"-net"}, "default", "Set the Network mode for the container")
91
+		flNetMode           = cmd.String([]string{"-net"}, "default", "Set the Network for the container")
92 92
 		flMacAddress        = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
93 93
 		flIpcMode           = cmd.String([]string{"-ipc"}, "", "IPC namespace to use")
94 94
 		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
122 122
 	cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
123 123
 	cmd.Var(&flLoggingOpts, []string{"-log-opt"}, "Log driver options")
124 124
 
125
-	expFlags := attachExperimentalFlags(cmd)
126
-
127 125
 	cmd.Require(flag.Min, 1)
128 126
 
129 127
 	if err := cmd.ParseFlags(args, true); err != nil {
... ...
@@ -379,8 +383,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
379 379
 		VolumeDriver:      *flVolumeDriver,
380 380
 	}
381 381
 
382
-	applyExperimentalFlags(expFlags, config, hostConfig)
383
-
384 382
 	// When allocating stdin in attached mode, close stdin at client disconnect
385 383
 	if config.OpenStdin && config.AttachStdin {
386 384
 		config.StdinOnce = true
387 385
deleted file mode 100644
... ...
@@ -1,19 +0,0 @@
1
-// +build experimental
2
-
3
-package runconfig
4
-
5
-import flag "github.com/docker/docker/pkg/mflag"
6
-
7
-type experimentalFlags struct {
8
-	flags map[string]interface{}
9
-}
10
-
11
-func attachExperimentalFlags(cmd *flag.FlagSet) *experimentalFlags {
12
-	flags := make(map[string]interface{})
13
-	flags["publish-service"] = cmd.String([]string{"-publish-service"}, "", "Publish this container as a service")
14
-	return &experimentalFlags{flags: flags}
15
-}
16
-
17
-func applyExperimentalFlags(exp *experimentalFlags, config *Config, hostConfig *HostConfig) {
18
-	config.PublishService = *(exp.flags["publish-service"]).(*string)
19
-}
20 1
deleted file mode 100644
... ...
@@ -1,14 +0,0 @@
1
-// +build !experimental
2
-
3
-package runconfig
4
-
5
-import flag "github.com/docker/docker/pkg/mflag"
6
-
7
-type experimentalFlags struct{}
8
-
9
-func attachExperimentalFlags(cmd *flag.FlagSet) *experimentalFlags {
10
-	return nil
11
-}
12
-
13
-func applyExperimentalFlags(flags *experimentalFlags, config *Config, hostConfig *HostConfig) {
14
-}
... ...
@@ -15,14 +15,10 @@ func ValidateNetMode(c *Config, hc *HostConfig) error {
15 15
 		return nil
16 16
 	}
17 17
 	parts := strings.Split(string(hc.NetworkMode), ":")
18
-	switch mode := parts[0]; mode {
19
-	case "default", "bridge", "none", "host":
20
-	case "container":
18
+	if parts[0] == "container" {
21 19
 		if len(parts) < 2 || parts[1] == "" {
22 20
 			return fmt.Errorf("--net: invalid net mode: invalid container format container:<name|id>")
23 21
 		}
24
-	default:
25
-		return fmt.Errorf("invalid --net: %s", hc.NetworkMode)
26 22
 	}
27 23
 
28 24
 	if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && c.Hostname != "" {
... ...
@@ -37,6 +33,10 @@ func ValidateNetMode(c *Config, hc *HostConfig) error {
37 37
 		return ErrConflictContainerNetworkAndLinks
38 38
 	}
39 39
 
40
+	if hc.NetworkMode.IsUserDefined() && len(hc.Links) > 0 {
41
+		return ErrConflictUserDefinedNetworkAndLinks
42
+	}
43
+
40 44
 	if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && len(hc.DNS) > 0 {
41 45
 		return ErrConflictNetworkAndDNS
42 46
 	}
43 47
deleted file mode 100644
... ...
@@ -1,949 +0,0 @@
1
-package api
2
-
3
-import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"io/ioutil"
7
-	"net/http"
8
-	"strings"
9
-
10
-	"github.com/docker/libnetwork"
11
-	"github.com/docker/libnetwork/netlabel"
12
-	"github.com/docker/libnetwork/types"
13
-	"github.com/gorilla/mux"
14
-)
15
-
16
-var (
17
-	successResponse  = responseStatus{Status: "Success", StatusCode: http.StatusOK}
18
-	createdResponse  = responseStatus{Status: "Created", StatusCode: http.StatusCreated}
19
-	mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest}
20
-	badQueryResponse = responseStatus{Status: "Unsupported query", StatusCode: http.StatusBadRequest}
21
-)
22
-
23
-const (
24
-	// Resource name regex
25
-	// Gorilla mux encloses the passed pattern with '^' and '$'. So we need to do some tricks
26
-	// to have mux eventually build a query regex which matches empty or word string (`^$|[\w]+`)
27
-	regex = "[a-zA-Z_0-9-]+"
28
-	qregx = "$|" + regex
29
-	// Router URL variable definition
30
-	nwName   = "{" + urlNwName + ":" + regex + "}"
31
-	nwNameQr = "{" + urlNwName + ":" + qregx + "}"
32
-	nwID     = "{" + urlNwID + ":" + regex + "}"
33
-	nwPIDQr  = "{" + urlNwPID + ":" + qregx + "}"
34
-	epName   = "{" + urlEpName + ":" + regex + "}"
35
-	epNameQr = "{" + urlEpName + ":" + qregx + "}"
36
-	epID     = "{" + urlEpID + ":" + regex + "}"
37
-	epPIDQr  = "{" + urlEpPID + ":" + qregx + "}"
38
-	sbID     = "{" + urlSbID + ":" + regex + "}"
39
-	sbPIDQr  = "{" + urlSbPID + ":" + qregx + "}"
40
-	cnIDQr   = "{" + urlCnID + ":" + qregx + "}"
41
-	cnPIDQr  = "{" + urlCnPID + ":" + qregx + "}"
42
-
43
-	// Internal URL variable name.They can be anything as
44
-	// long as they do not collide with query fields.
45
-	urlNwName = "network-name"
46
-	urlNwID   = "network-id"
47
-	urlNwPID  = "network-partial-id"
48
-	urlEpName = "endpoint-name"
49
-	urlEpID   = "endpoint-id"
50
-	urlEpPID  = "endpoint-partial-id"
51
-	urlSbID   = "sandbox-id"
52
-	urlSbPID  = "sandbox-partial-id"
53
-	urlCnID   = "container-id"
54
-	urlCnPID  = "container-partial-id"
55
-
56
-	// BridgeNetworkDriver is the built-in default for Network Driver
57
-	BridgeNetworkDriver = "bridge"
58
-)
59
-
60
-// NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork
61
-func NewHTTPHandler(c libnetwork.NetworkController) func(w http.ResponseWriter, req *http.Request) {
62
-	h := &httpHandler{c: c}
63
-	h.initRouter()
64
-	return h.handleRequest
65
-}
66
-
67
-type responseStatus struct {
68
-	Status     string
69
-	StatusCode int
70
-}
71
-
72
-func (r *responseStatus) isOK() bool {
73
-	return r.StatusCode == http.StatusOK || r.StatusCode == http.StatusCreated
74
-}
75
-
76
-type processor func(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus)
77
-
78
-type httpHandler struct {
79
-	c libnetwork.NetworkController
80
-	r *mux.Router
81
-}
82
-
83
-func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) {
84
-	// Make sure the service is there
85
-	if h.c == nil {
86
-		http.Error(w, "NetworkController is not available", http.StatusServiceUnavailable)
87
-		return
88
-	}
89
-
90
-	// Get handler from router and execute it
91
-	h.r.ServeHTTP(w, req)
92
-}
93
-
94
-func (h *httpHandler) initRouter() {
95
-	m := map[string][]struct {
96
-		url string
97
-		qrs []string
98
-		fct processor
99
-	}{
100
-		"GET": {
101
-			// Order matters
102
-			{"/networks", []string{"name", nwNameQr}, procGetNetworks},
103
-			{"/networks", []string{"partial-id", nwPIDQr}, procGetNetworks},
104
-			{"/networks", nil, procGetNetworks},
105
-			{"/networks/" + nwID, nil, procGetNetwork},
106
-			{"/networks/" + nwID + "/endpoints", []string{"name", epNameQr}, procGetEndpoints},
107
-			{"/networks/" + nwID + "/endpoints", []string{"partial-id", epPIDQr}, procGetEndpoints},
108
-			{"/networks/" + nwID + "/endpoints", nil, procGetEndpoints},
109
-			{"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint},
110
-			{"/services", []string{"network", nwNameQr}, procGetServices},
111
-			{"/services", []string{"name", epNameQr}, procGetServices},
112
-			{"/services", []string{"partial-id", epPIDQr}, procGetServices},
113
-			{"/services", nil, procGetServices},
114
-			{"/services/" + epID, nil, procGetService},
115
-			{"/services/" + epID + "/backend", nil, procGetSandbox},
116
-			{"/sandboxes", []string{"partial-container-id", cnPIDQr}, procGetSandboxes},
117
-			{"/sandboxes", []string{"container-id", cnIDQr}, procGetSandboxes},
118
-			{"/sandboxes", []string{"partial-id", sbPIDQr}, procGetSandboxes},
119
-			{"/sandboxes", nil, procGetSandboxes},
120
-			{"/sandboxes/" + sbID, nil, procGetSandbox},
121
-		},
122
-		"POST": {
123
-			{"/networks", nil, procCreateNetwork},
124
-			{"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint},
125
-			{"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes", nil, procJoinEndpoint},
126
-			{"/services", nil, procPublishService},
127
-			{"/services/" + epID + "/backend", nil, procAttachBackend},
128
-			{"/sandboxes", nil, procCreateSandbox},
129
-		},
130
-		"DELETE": {
131
-			{"/networks/" + nwID, nil, procDeleteNetwork},
132
-			{"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint},
133
-			{"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes/" + sbID, nil, procLeaveEndpoint},
134
-			{"/services/" + epID, nil, procUnpublishService},
135
-			{"/services/" + epID + "/backend/" + sbID, nil, procDetachBackend},
136
-			{"/sandboxes/" + sbID, nil, procDeleteSandbox},
137
-		},
138
-	}
139
-
140
-	h.r = mux.NewRouter()
141
-	for method, routes := range m {
142
-		for _, route := range routes {
143
-			r := h.r.Path("/{.*}" + route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct))
144
-			if route.qrs != nil {
145
-				r.Queries(route.qrs...)
146
-			}
147
-
148
-			r = h.r.Path(route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct))
149
-			if route.qrs != nil {
150
-				r.Queries(route.qrs...)
151
-			}
152
-		}
153
-	}
154
-}
155
-
156
-func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerFunc {
157
-	return func(w http.ResponseWriter, req *http.Request) {
158
-		var (
159
-			body []byte
160
-			err  error
161
-		)
162
-		if req.Body != nil {
163
-			body, err = ioutil.ReadAll(req.Body)
164
-			if err != nil {
165
-				http.Error(w, "Invalid body: "+err.Error(), http.StatusBadRequest)
166
-				return
167
-			}
168
-		}
169
-
170
-		res, rsp := fct(ctrl, mux.Vars(req), body)
171
-		if !rsp.isOK() {
172
-			http.Error(w, rsp.Status, rsp.StatusCode)
173
-			return
174
-		}
175
-		if res != nil {
176
-			writeJSON(w, rsp.StatusCode, res)
177
-		}
178
-	}
179
-}
180
-
181
-/*****************
182
- Resource Builders
183
-******************/
184
-
185
-func buildNetworkResource(nw libnetwork.Network) *networkResource {
186
-	r := &networkResource{}
187
-	if nw != nil {
188
-		r.Name = nw.Name()
189
-		r.ID = nw.ID()
190
-		r.Type = nw.Type()
191
-		epl := nw.Endpoints()
192
-		r.Endpoints = make([]*endpointResource, 0, len(epl))
193
-		for _, e := range epl {
194
-			epr := buildEndpointResource(e)
195
-			r.Endpoints = append(r.Endpoints, epr)
196
-		}
197
-	}
198
-	return r
199
-}
200
-
201
-func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource {
202
-	r := &endpointResource{}
203
-	if ep != nil {
204
-		r.Name = ep.Name()
205
-		r.ID = ep.ID()
206
-		r.Network = ep.Network()
207
-	}
208
-	return r
209
-}
210
-
211
-func buildSandboxResource(sb libnetwork.Sandbox) *sandboxResource {
212
-	r := &sandboxResource{}
213
-	if sb != nil {
214
-		r.ID = sb.ID()
215
-		r.Key = sb.Key()
216
-		r.ContainerID = sb.ContainerID()
217
-	}
218
-	return r
219
-}
220
-
221
-/****************
222
- Options Parsers
223
-*****************/
224
-
225
-func (nc *networkCreate) parseOptions() []libnetwork.NetworkOption {
226
-	var setFctList []libnetwork.NetworkOption
227
-
228
-	if nc.Options != nil {
229
-		setFctList = append(setFctList, libnetwork.NetworkOptionGeneric(nc.Options))
230
-	}
231
-
232
-	return setFctList
233
-}
234
-
235
-func (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption {
236
-	var setFctList []libnetwork.SandboxOption
237
-	if sc.HostName != "" {
238
-		setFctList = append(setFctList, libnetwork.OptionHostname(sc.HostName))
239
-	}
240
-	if sc.DomainName != "" {
241
-		setFctList = append(setFctList, libnetwork.OptionDomainname(sc.DomainName))
242
-	}
243
-	if sc.HostsPath != "" {
244
-		setFctList = append(setFctList, libnetwork.OptionHostsPath(sc.HostsPath))
245
-	}
246
-	if sc.ResolvConfPath != "" {
247
-		setFctList = append(setFctList, libnetwork.OptionResolvConfPath(sc.ResolvConfPath))
248
-	}
249
-	if sc.UseDefaultSandbox {
250
-		setFctList = append(setFctList, libnetwork.OptionUseDefaultSandbox())
251
-	}
252
-	if sc.UseExternalKey {
253
-		setFctList = append(setFctList, libnetwork.OptionUseExternalKey())
254
-	}
255
-	if sc.DNS != nil {
256
-		for _, d := range sc.DNS {
257
-			setFctList = append(setFctList, libnetwork.OptionDNS(d))
258
-		}
259
-	}
260
-	if sc.ExtraHosts != nil {
261
-		for _, e := range sc.ExtraHosts {
262
-			setFctList = append(setFctList, libnetwork.OptionExtraHost(e.Name, e.Address))
263
-		}
264
-	}
265
-	return setFctList
266
-}
267
-
268
-func (ej *endpointJoin) parseOptions() []libnetwork.EndpointOption {
269
-	// priority will go here
270
-	return []libnetwork.EndpointOption{}
271
-}
272
-
273
-/******************
274
- Process functions
275
-*******************/
276
-
277
-func processCreateDefaults(c libnetwork.NetworkController, nc *networkCreate) {
278
-	if nc.NetworkType == "" {
279
-		nc.NetworkType = c.Config().Daemon.DefaultDriver
280
-	}
281
-	if nc.NetworkType == BridgeNetworkDriver {
282
-		if nc.Options == nil {
283
-			nc.Options = make(map[string]interface{})
284
-		}
285
-		genericData, ok := nc.Options[netlabel.GenericData]
286
-		if !ok {
287
-			genericData = make(map[string]interface{})
288
-		}
289
-		gData := genericData.(map[string]interface{})
290
-
291
-		if _, ok := gData["BridgeName"]; !ok {
292
-			gData["BridgeName"] = nc.Name
293
-		}
294
-		nc.Options[netlabel.GenericData] = genericData
295
-	}
296
-}
297
-
298
-/***************************
299
- NetworkController interface
300
-****************************/
301
-func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
302
-	var create networkCreate
303
-
304
-	err := json.Unmarshal(body, &create)
305
-	if err != nil {
306
-		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
307
-	}
308
-	processCreateDefaults(c, &create)
309
-
310
-	nw, err := c.NewNetwork(create.NetworkType, create.Name, create.parseOptions()...)
311
-	if err != nil {
312
-		return "", convertNetworkError(err)
313
-	}
314
-
315
-	return nw.ID(), &createdResponse
316
-}
317
-
318
-func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
319
-	t, by := detectNetworkTarget(vars)
320
-	nw, errRsp := findNetwork(c, t, by)
321
-	if !errRsp.isOK() {
322
-		return nil, errRsp
323
-	}
324
-	return buildNetworkResource(nw), &successResponse
325
-}
326
-
327
-func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
328
-	var list []*networkResource
329
-
330
-	// Look for query filters and validate
331
-	name, queryByName := vars[urlNwName]
332
-	shortID, queryByPid := vars[urlNwPID]
333
-	if queryByName && queryByPid {
334
-		return nil, &badQueryResponse
335
-	}
336
-
337
-	if queryByName {
338
-		if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() {
339
-			list = append(list, buildNetworkResource(nw))
340
-		}
341
-	} else if queryByPid {
342
-		// Return all the prefix-matching networks
343
-		l := func(nw libnetwork.Network) bool {
344
-			if strings.HasPrefix(nw.ID(), shortID) {
345
-				list = append(list, buildNetworkResource(nw))
346
-			}
347
-			return false
348
-		}
349
-		c.WalkNetworks(l)
350
-	} else {
351
-		for _, nw := range c.Networks() {
352
-			list = append(list, buildNetworkResource(nw))
353
-		}
354
-	}
355
-
356
-	return list, &successResponse
357
-}
358
-
359
-func procCreateSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
360
-	var create sandboxCreate
361
-
362
-	err := json.Unmarshal(body, &create)
363
-	if err != nil {
364
-		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
365
-	}
366
-
367
-	sb, err := c.NewSandbox(create.ContainerID, create.parseOptions()...)
368
-	if err != nil {
369
-		return "", convertNetworkError(err)
370
-	}
371
-
372
-	return sb.ID(), &createdResponse
373
-}
374
-
375
-/******************
376
- Network interface
377
-*******************/
378
-func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
379
-	var ec endpointCreate
380
-
381
-	err := json.Unmarshal(body, &ec)
382
-	if err != nil {
383
-		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
384
-	}
385
-
386
-	nwT, nwBy := detectNetworkTarget(vars)
387
-	n, errRsp := findNetwork(c, nwT, nwBy)
388
-	if !errRsp.isOK() {
389
-		return "", errRsp
390
-	}
391
-
392
-	var setFctList []libnetwork.EndpointOption
393
-	if ec.ExposedPorts != nil {
394
-		setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts))
395
-	}
396
-	if ec.PortMapping != nil {
397
-		setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping))
398
-	}
399
-
400
-	ep, err := n.CreateEndpoint(ec.Name, setFctList...)
401
-	if err != nil {
402
-		return "", convertNetworkError(err)
403
-	}
404
-
405
-	return ep.ID(), &createdResponse
406
-}
407
-
408
-func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
409
-	nwT, nwBy := detectNetworkTarget(vars)
410
-	epT, epBy := detectEndpointTarget(vars)
411
-
412
-	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
413
-	if !errRsp.isOK() {
414
-		return nil, errRsp
415
-	}
416
-
417
-	return buildEndpointResource(ep), &successResponse
418
-}
419
-
420
-func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
421
-	// Look for query filters and validate
422
-	name, queryByName := vars[urlEpName]
423
-	shortID, queryByPid := vars[urlEpPID]
424
-	if queryByName && queryByPid {
425
-		return nil, &badQueryResponse
426
-	}
427
-
428
-	nwT, nwBy := detectNetworkTarget(vars)
429
-	nw, errRsp := findNetwork(c, nwT, nwBy)
430
-	if !errRsp.isOK() {
431
-		return nil, errRsp
432
-	}
433
-
434
-	var list []*endpointResource
435
-
436
-	// If query parameter is specified, return a filtered collection
437
-	if queryByName {
438
-		if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() {
439
-			list = append(list, buildEndpointResource(ep))
440
-		}
441
-	} else if queryByPid {
442
-		// Return all the prefix-matching endpoints
443
-		l := func(ep libnetwork.Endpoint) bool {
444
-			if strings.HasPrefix(ep.ID(), shortID) {
445
-				list = append(list, buildEndpointResource(ep))
446
-			}
447
-			return false
448
-		}
449
-		nw.WalkEndpoints(l)
450
-	} else {
451
-		for _, ep := range nw.Endpoints() {
452
-			epr := buildEndpointResource(ep)
453
-			list = append(list, epr)
454
-		}
455
-	}
456
-
457
-	return list, &successResponse
458
-}
459
-
460
-func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
461
-	target, by := detectNetworkTarget(vars)
462
-
463
-	nw, errRsp := findNetwork(c, target, by)
464
-	if !errRsp.isOK() {
465
-		return nil, errRsp
466
-	}
467
-
468
-	err := nw.Delete()
469
-	if err != nil {
470
-		return nil, convertNetworkError(err)
471
-	}
472
-
473
-	return nil, &successResponse
474
-}
475
-
476
-/******************
477
- Endpoint interface
478
-*******************/
479
-func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
480
-	var ej endpointJoin
481
-	err := json.Unmarshal(body, &ej)
482
-	if err != nil {
483
-		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
484
-	}
485
-
486
-	nwT, nwBy := detectNetworkTarget(vars)
487
-	epT, epBy := detectEndpointTarget(vars)
488
-
489
-	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
490
-	if !errRsp.isOK() {
491
-		return nil, errRsp
492
-	}
493
-
494
-	sb, errRsp := findSandbox(c, ej.SandboxID, byID)
495
-	if !errRsp.isOK() {
496
-		return nil, errRsp
497
-	}
498
-
499
-	err = ep.Join(sb)
500
-	if err != nil {
501
-		return nil, convertNetworkError(err)
502
-	}
503
-	return sb.Key(), &successResponse
504
-}
505
-
506
-func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
507
-	nwT, nwBy := detectNetworkTarget(vars)
508
-	epT, epBy := detectEndpointTarget(vars)
509
-
510
-	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
511
-	if !errRsp.isOK() {
512
-		return nil, errRsp
513
-	}
514
-
515
-	sb, errRsp := findSandbox(c, vars[urlSbID], byID)
516
-	if !errRsp.isOK() {
517
-		return nil, errRsp
518
-	}
519
-
520
-	err := ep.Leave(sb)
521
-	if err != nil {
522
-		return nil, convertNetworkError(err)
523
-	}
524
-
525
-	return nil, &successResponse
526
-}
527
-
528
-func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
529
-	nwT, nwBy := detectNetworkTarget(vars)
530
-	epT, epBy := detectEndpointTarget(vars)
531
-
532
-	ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
533
-	if !errRsp.isOK() {
534
-		return nil, errRsp
535
-	}
536
-
537
-	err := ep.Delete()
538
-	if err != nil {
539
-		return nil, convertNetworkError(err)
540
-	}
541
-
542
-	return nil, &successResponse
543
-}
544
-
545
-/******************
546
- Service interface
547
-*******************/
548
-func procGetServices(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
549
-	// Look for query filters and validate
550
-	nwName, filterByNwName := vars[urlNwName]
551
-	svName, queryBySvName := vars[urlEpName]
552
-	shortID, queryBySvPID := vars[urlEpPID]
553
-
554
-	if filterByNwName && queryBySvName || filterByNwName && queryBySvPID || queryBySvName && queryBySvPID {
555
-		return nil, &badQueryResponse
556
-	}
557
-
558
-	var list []*endpointResource
559
-
560
-	switch {
561
-	case filterByNwName:
562
-		// return all service present on the specified network
563
-		nw, errRsp := findNetwork(c, nwName, byName)
564
-		if !errRsp.isOK() {
565
-			return list, &successResponse
566
-		}
567
-		for _, ep := range nw.Endpoints() {
568
-			epr := buildEndpointResource(ep)
569
-			list = append(list, epr)
570
-		}
571
-	case queryBySvName:
572
-		// Look in each network for the service with the specified name
573
-		l := func(ep libnetwork.Endpoint) bool {
574
-			if ep.Name() == svName {
575
-				list = append(list, buildEndpointResource(ep))
576
-				return true
577
-			}
578
-			return false
579
-		}
580
-		for _, nw := range c.Networks() {
581
-			nw.WalkEndpoints(l)
582
-		}
583
-	case queryBySvPID:
584
-		// Return all the prefix-matching services
585
-		l := func(ep libnetwork.Endpoint) bool {
586
-			if strings.HasPrefix(ep.ID(), shortID) {
587
-				list = append(list, buildEndpointResource(ep))
588
-			}
589
-			return false
590
-		}
591
-		for _, nw := range c.Networks() {
592
-			nw.WalkEndpoints(l)
593
-		}
594
-	default:
595
-		for _, nw := range c.Networks() {
596
-			for _, ep := range nw.Endpoints() {
597
-				epr := buildEndpointResource(ep)
598
-				list = append(list, epr)
599
-			}
600
-		}
601
-	}
602
-
603
-	return list, &successResponse
604
-}
605
-
606
-func procGetService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
607
-	epT, epBy := detectEndpointTarget(vars)
608
-	sv, errRsp := findService(c, epT, epBy)
609
-	if !errRsp.isOK() {
610
-		return nil, endpointToService(errRsp)
611
-	}
612
-	return buildEndpointResource(sv), &successResponse
613
-}
614
-
615
-func procPublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
616
-	var sp servicePublish
617
-
618
-	err := json.Unmarshal(body, &sp)
619
-	if err != nil {
620
-		return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
621
-	}
622
-
623
-	n, errRsp := findNetwork(c, sp.Network, byName)
624
-	if !errRsp.isOK() {
625
-		return "", errRsp
626
-	}
627
-
628
-	var setFctList []libnetwork.EndpointOption
629
-	if sp.ExposedPorts != nil {
630
-		setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(sp.ExposedPorts))
631
-	}
632
-	if sp.PortMapping != nil {
633
-		setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(sp.PortMapping))
634
-	}
635
-
636
-	ep, err := n.CreateEndpoint(sp.Name, setFctList...)
637
-	if err != nil {
638
-		return "", endpointToService(convertNetworkError(err))
639
-	}
640
-
641
-	return ep.ID(), &createdResponse
642
-}
643
-
644
-func procUnpublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
645
-	epT, epBy := detectEndpointTarget(vars)
646
-	sv, errRsp := findService(c, epT, epBy)
647
-	if !errRsp.isOK() {
648
-		return nil, errRsp
649
-	}
650
-	err := sv.Delete()
651
-	if err != nil {
652
-		return nil, endpointToService(convertNetworkError(err))
653
-	}
654
-	return nil, &successResponse
655
-}
656
-
657
-func procAttachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
658
-	var bk endpointJoin
659
-	err := json.Unmarshal(body, &bk)
660
-	if err != nil {
661
-		return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
662
-	}
663
-
664
-	epT, epBy := detectEndpointTarget(vars)
665
-	sv, errRsp := findService(c, epT, epBy)
666
-	if !errRsp.isOK() {
667
-		return nil, errRsp
668
-	}
669
-
670
-	sb, errRsp := findSandbox(c, bk.SandboxID, byID)
671
-	if !errRsp.isOK() {
672
-		return nil, errRsp
673
-	}
674
-
675
-	err = sv.Join(sb)
676
-	if err != nil {
677
-		return nil, convertNetworkError(err)
678
-	}
679
-	return sb.Key(), &successResponse
680
-}
681
-
682
-func procDetachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
683
-	epT, epBy := detectEndpointTarget(vars)
684
-	sv, errRsp := findService(c, epT, epBy)
685
-	if !errRsp.isOK() {
686
-		return nil, errRsp
687
-	}
688
-
689
-	sb, errRsp := findSandbox(c, vars[urlSbID], byID)
690
-	if !errRsp.isOK() {
691
-		return nil, errRsp
692
-	}
693
-
694
-	err := sv.Leave(sb)
695
-	if err != nil {
696
-		return nil, convertNetworkError(err)
697
-	}
698
-
699
-	return nil, &successResponse
700
-}
701
-
702
-/******************
703
- Sandbox interface
704
-*******************/
705
-func procGetSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
706
-	if epT, ok := vars[urlEpID]; ok {
707
-		sv, errRsp := findService(c, epT, byID)
708
-		if !errRsp.isOK() {
709
-			return nil, endpointToService(errRsp)
710
-		}
711
-		return buildSandboxResource(sv.Info().Sandbox()), &successResponse
712
-	}
713
-
714
-	sbT, by := detectSandboxTarget(vars)
715
-	sb, errRsp := findSandbox(c, sbT, by)
716
-	if !errRsp.isOK() {
717
-		return nil, errRsp
718
-	}
719
-	return buildSandboxResource(sb), &successResponse
720
-}
721
-
722
-type cndFnMkr func(string) cndFn
723
-type cndFn func(libnetwork.Sandbox) bool
724
-
725
-// list of (query type, condition function makers) couples
726
-var cndMkrList = []struct {
727
-	identifier string
728
-	maker      cndFnMkr
729
-}{
730
-	{urlSbPID, func(id string) cndFn {
731
-		return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ID(), id) }
732
-	}},
733
-	{urlCnID, func(id string) cndFn {
734
-		return func(sb libnetwork.Sandbox) bool { return sb.ContainerID() == id }
735
-	}},
736
-	{urlCnPID, func(id string) cndFn {
737
-		return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ContainerID(), id) }
738
-	}},
739
-}
740
-
741
-func getQueryCondition(vars map[string]string) func(libnetwork.Sandbox) bool {
742
-	for _, im := range cndMkrList {
743
-		if val, ok := vars[im.identifier]; ok {
744
-			return im.maker(val)
745
-		}
746
-	}
747
-	return func(sb libnetwork.Sandbox) bool { return true }
748
-}
749
-
750
-func sandboxWalker(condition cndFn, list *[]*sandboxResource) libnetwork.SandboxWalker {
751
-	return func(sb libnetwork.Sandbox) bool {
752
-		if condition(sb) {
753
-			*list = append(*list, buildSandboxResource(sb))
754
-		}
755
-		return false
756
-	}
757
-}
758
-
759
-func procGetSandboxes(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
760
-	var list []*sandboxResource
761
-
762
-	cnd := getQueryCondition(vars)
763
-	c.WalkSandboxes(sandboxWalker(cnd, &list))
764
-
765
-	return list, &successResponse
766
-}
767
-
768
-func procDeleteSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
769
-	sbT, by := detectSandboxTarget(vars)
770
-
771
-	sb, errRsp := findSandbox(c, sbT, by)
772
-	if !errRsp.isOK() {
773
-		return nil, errRsp
774
-	}
775
-
776
-	err := sb.Delete()
777
-	if err != nil {
778
-		return nil, convertNetworkError(err)
779
-	}
780
-
781
-	return nil, &successResponse
782
-}
783
-
784
-/***********
785
-  Utilities
786
-************/
787
-const (
788
-	byID = iota
789
-	byName
790
-)
791
-
792
-func detectNetworkTarget(vars map[string]string) (string, int) {
793
-	if target, ok := vars[urlNwName]; ok {
794
-		return target, byName
795
-	}
796
-	if target, ok := vars[urlNwID]; ok {
797
-		return target, byID
798
-	}
799
-	// vars are populated from the URL, following cannot happen
800
-	panic("Missing URL variable parameter for network")
801
-}
802
-
803
-func detectSandboxTarget(vars map[string]string) (string, int) {
804
-	if target, ok := vars[urlSbID]; ok {
805
-		return target, byID
806
-	}
807
-	// vars are populated from the URL, following cannot happen
808
-	panic("Missing URL variable parameter for sandbox")
809
-}
810
-
811
-func detectEndpointTarget(vars map[string]string) (string, int) {
812
-	if target, ok := vars[urlEpName]; ok {
813
-		return target, byName
814
-	}
815
-	if target, ok := vars[urlEpID]; ok {
816
-		return target, byID
817
-	}
818
-	// vars are populated from the URL, following cannot happen
819
-	panic("Missing URL variable parameter for endpoint")
820
-}
821
-
822
-func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) {
823
-	var (
824
-		nw  libnetwork.Network
825
-		err error
826
-	)
827
-	switch by {
828
-	case byID:
829
-		nw, err = c.NetworkByID(s)
830
-	case byName:
831
-		if s == "" {
832
-			s = c.Config().Daemon.DefaultNetwork
833
-		}
834
-		nw, err = c.NetworkByName(s)
835
-	default:
836
-		panic(fmt.Sprintf("unexpected selector for network search: %d", by))
837
-	}
838
-	if err != nil {
839
-		if _, ok := err.(types.NotFoundError); ok {
840
-			return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound}
841
-		}
842
-		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
843
-	}
844
-	return nw, &successResponse
845
-}
846
-
847
-func findSandbox(c libnetwork.NetworkController, s string, by int) (libnetwork.Sandbox, *responseStatus) {
848
-	var (
849
-		sb  libnetwork.Sandbox
850
-		err error
851
-	)
852
-
853
-	switch by {
854
-	case byID:
855
-		sb, err = c.SandboxByID(s)
856
-	default:
857
-		panic(fmt.Sprintf("unexpected selector for sandbox search: %d", by))
858
-	}
859
-	if err != nil {
860
-		if _, ok := err.(types.NotFoundError); ok {
861
-			return nil, &responseStatus{Status: "Resource not found: Sandbox", StatusCode: http.StatusNotFound}
862
-		}
863
-		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
864
-	}
865
-	return sb, &successResponse
866
-}
867
-
868
-func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) {
869
-	nw, errRsp := findNetwork(c, ns, nwBy)
870
-	if !errRsp.isOK() {
871
-		return nil, errRsp
872
-	}
873
-	var (
874
-		err error
875
-		ep  libnetwork.Endpoint
876
-	)
877
-	switch epBy {
878
-	case byID:
879
-		ep, err = nw.EndpointByID(es)
880
-	case byName:
881
-		ep, err = nw.EndpointByName(es)
882
-	default:
883
-		panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy))
884
-	}
885
-	if err != nil {
886
-		if _, ok := err.(types.NotFoundError); ok {
887
-			return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound}
888
-		}
889
-		return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
890
-	}
891
-	return ep, &successResponse
892
-}
893
-
894
-func findService(c libnetwork.NetworkController, svs string, svBy int) (libnetwork.Endpoint, *responseStatus) {
895
-	for _, nw := range c.Networks() {
896
-		var (
897
-			ep  libnetwork.Endpoint
898
-			err error
899
-		)
900
-		switch svBy {
901
-		case byID:
902
-			ep, err = nw.EndpointByID(svs)
903
-		case byName:
904
-			ep, err = nw.EndpointByName(svs)
905
-		default:
906
-			panic(fmt.Sprintf("unexpected selector for service search: %d", svBy))
907
-		}
908
-		if err == nil {
909
-			return ep, &successResponse
910
-		} else if _, ok := err.(types.NotFoundError); !ok {
911
-			return nil, convertNetworkError(err)
912
-		}
913
-	}
914
-	return nil, &responseStatus{Status: "Service not found", StatusCode: http.StatusNotFound}
915
-}
916
-
917
-func endpointToService(rsp *responseStatus) *responseStatus {
918
-	rsp.Status = strings.Replace(rsp.Status, "endpoint", "service", -1)
919
-	return rsp
920
-}
921
-
922
-func convertNetworkError(err error) *responseStatus {
923
-	var code int
924
-	switch err.(type) {
925
-	case types.BadRequestError:
926
-		code = http.StatusBadRequest
927
-	case types.ForbiddenError:
928
-		code = http.StatusForbidden
929
-	case types.NotFoundError:
930
-		code = http.StatusNotFound
931
-	case types.TimeoutError:
932
-		code = http.StatusRequestTimeout
933
-	case types.NotImplementedError:
934
-		code = http.StatusNotImplemented
935
-	case types.NoServiceError:
936
-		code = http.StatusServiceUnavailable
937
-	case types.InternalError:
938
-		code = http.StatusInternalServerError
939
-	default:
940
-		code = http.StatusInternalServerError
941
-	}
942
-	return &responseStatus{Status: err.Error(), StatusCode: code}
943
-}
944
-
945
-func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
946
-	w.Header().Set("Content-Type", "application/json")
947
-	w.WriteHeader(code)
948
-	return json.NewEncoder(w).Encode(v)
949
-}
950 1
deleted file mode 100644
... ...
@@ -1,80 +0,0 @@
1
-package api
2
-
3
-import "github.com/docker/libnetwork/types"
4
-
5
-/***********
6
- Resources
7
-************/
8
-
9
-// networkResource is the body of the "get network" http response message
10
-type networkResource struct {
11
-	Name      string              `json:"name"`
12
-	ID        string              `json:"id"`
13
-	Type      string              `json:"type"`
14
-	Endpoints []*endpointResource `json:"endpoints"`
15
-}
16
-
17
-// endpointResource is the body of the "get endpoint" http response message
18
-type endpointResource struct {
19
-	Name    string `json:"name"`
20
-	ID      string `json:"id"`
21
-	Network string `json:"network"`
22
-}
23
-
24
-// sandboxResource is the body of "get service backend" response message
25
-type sandboxResource struct {
26
-	ID          string `json:"id"`
27
-	Key         string `json:"key"`
28
-	ContainerID string `json:"container_id"`
29
-	// will add more fields once labels change is in
30
-}
31
-
32
-/***********
33
-  Body types
34
-  ************/
35
-
36
-// networkCreate is the expected body of the "create network" http request message
37
-type networkCreate struct {
38
-	Name        string                 `json:"name"`
39
-	NetworkType string                 `json:"network_type"`
40
-	Options     map[string]interface{} `json:"options"`
41
-}
42
-
43
-// endpointCreate represents the body of the "create endpoint" http request message
44
-type endpointCreate struct {
45
-	Name         string                `json:"name"`
46
-	ExposedPorts []types.TransportPort `json:"exposed_ports"`
47
-	PortMapping  []types.PortBinding   `json:"port_mapping"`
48
-}
49
-
50
-// sandboxCreate is the expected body of the "create sandbox" http request message
51
-type sandboxCreate struct {
52
-	ContainerID       string      `json:"container_id"`
53
-	HostName          string      `json:"host_name"`
54
-	DomainName        string      `json:"domain_name"`
55
-	HostsPath         string      `json:"hosts_path"`
56
-	ResolvConfPath    string      `json:"resolv_conf_path"`
57
-	DNS               []string    `json:"dns"`
58
-	ExtraHosts        []extraHost `json:"extra_hosts"`
59
-	UseDefaultSandbox bool        `json:"use_default_sandbox"`
60
-	UseExternalKey    bool        `json:"use_external_key"`
61
-}
62
-
63
-// endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages
64
-type endpointJoin struct {
65
-	SandboxID string `json:"sandbox_id"`
66
-}
67
-
68
-// servicePublish represents the body of the "publish service" http request message
69
-type servicePublish struct {
70
-	Name         string                `json:"name"`
71
-	Network      string                `json:"network_name"`
72
-	ExposedPorts []types.TransportPort `json:"exposed_ports"`
73
-	PortMapping  []types.PortBinding   `json:"port_mapping"`
74
-}
75
-
76
-// extraHost represents the extra host object
77
-type extraHost struct {
78
-	Name    string `json:"name"`
79
-	Address string `json:"address"`
80
-}