Browse code

Decouple the "container" router from the actual daemon implementation.

This is done by moving the following types to api/types/config.go:
- ContainersConfig
- ContainerAttachWithLogsConfig
- ContainerWsAttachWithLogsConfig
- ContainerLogsConfig
- ContainerStatsConfig

Remove dependency on "version" package from types.ContainerStatsConfig.
Decouple the "container" router from the "daemon/exec" implementation.

* This is done by making daemon.ContainerExecInspect() return an interface{}
value. The same trick is already used by daemon.ContainerInspect().

Improve documentation for router packages.
Extract localRoute and router into separate files.
Move local.router to image.imageRouter.

Changes:
- Move local/image.go to image/image_routes.go.
- Move local/local.go to image/image.go
- Rename router to imageRouter.
- Simplify imports for image/image.go (remove alias for router package).

Merge router/local package into router package.
Decouple the "image" router from the actual daemon implementation.
Add Daemon.GetNetworkByID and Daemon.GetNetworkByName.
Decouple the "network" router from the actual daemon implementation.

This is done by replacing the daemon.NetworkByName constant with
an explicit GetNetworkByName method.

Remove the unused Daemon.GetNetwork method and the associated constants NetworkByID and NetworkByName.

Signed-off-by: Lukas Waslowski <cr7pt0gr4ph7@gmail.com>
Signed-off-by: David Calavera <david.calavera@gmail.com>

Lukas Waslowski authored on 2015/12/31 02:20:41
Showing 22 changed files
... ...
@@ -1,9 +1,6 @@
1 1
 package build
2 2
 
3
-import (
4
-	"github.com/docker/docker/api/server/router"
5
-	"github.com/docker/docker/api/server/router/local"
6
-)
3
+import "github.com/docker/docker/api/server/router"
7 4
 
8 5
 // buildRouter is a router to talk with the build controller
9 6
 type buildRouter struct {
... ...
@@ -27,6 +24,6 @@ func (r *buildRouter) Routes() []router.Route {
27 27
 
28 28
 func (r *buildRouter) initRoutes() {
29 29
 	r.routes = []router.Route{
30
-		local.NewPostRoute("/build", r.postBuild),
30
+		router.NewPostRoute("/build", r.postBuild),
31 31
 	}
32 32
 }
... ...
@@ -4,11 +4,11 @@ import (
4 4
 	"io"
5 5
 	"time"
6 6
 
7
-	"github.com/docker/docker/daemon"
8 7
 	"github.com/docker/docker/daemon/exec"
9 8
 	"github.com/docker/docker/pkg/archive"
10 9
 	"github.com/docker/docker/pkg/version"
11 10
 	"github.com/docker/engine-api/types"
11
+	"github.com/docker/engine-api/types/backend"
12 12
 	"github.com/docker/engine-api/types/container"
13 13
 )
14 14
 
... ...
@@ -51,17 +51,17 @@ type stateBackend interface {
51 51
 type monitorBackend interface {
52 52
 	ContainerChanges(name string) ([]archive.Change, error)
53 53
 	ContainerInspect(name string, size bool, version version.Version) (interface{}, error)
54
-	ContainerLogs(name string, config *daemon.ContainerLogsConfig) error
55
-	ContainerStats(name string, config *daemon.ContainerStatsConfig) error
54
+	ContainerLogs(name string, config *backend.ContainerLogsConfig) error
55
+	ContainerStats(name string, config *backend.ContainerStatsConfig) error
56 56
 	ContainerTop(name string, psArgs string) (*types.ContainerProcessList, error)
57 57
 
58
-	Containers(config *daemon.ContainersConfig) ([]*types.Container, error)
58
+	Containers(config *backend.ContainersConfig) ([]*types.Container, error)
59 59
 }
60 60
 
61 61
 // attachBackend includes function to implement to provide container attaching functionality.
62 62
 type attachBackend interface {
63
-	ContainerAttachWithLogs(name string, c *daemon.ContainerAttachWithLogsConfig) error
64
-	ContainerWsAttachWithLogs(name string, c *daemon.ContainerWsAttachWithLogsConfig) error
63
+	ContainerAttachWithLogs(name string, c *backend.ContainerAttachWithLogsConfig) error
64
+	ContainerWsAttachWithLogs(name string, c *backend.ContainerWsAttachWithLogsConfig) error
65 65
 }
66 66
 
67 67
 // Backend is all the methods that need to be implemented to provide container specific functionality.
... ...
@@ -1,9 +1,6 @@
1 1
 package container
2 2
 
3
-import (
4
-	"github.com/docker/docker/api/server/router"
5
-	"github.com/docker/docker/api/server/router/local"
6
-)
3
+import "github.com/docker/docker/api/server/router"
7 4
 
8 5
 // containerRouter is a router to talk with the container controller
9 6
 type containerRouter struct {
... ...
@@ -20,7 +17,7 @@ func NewRouter(b Backend) router.Router {
20 20
 	return r
21 21
 }
22 22
 
23
-// Routes returns the available routers to the container controller
23
+// Routes returns the available routes to the container controller
24 24
 func (r *containerRouter) Routes() []router.Route {
25 25
 	return r.routes
26 26
 }
... ...
@@ -29,38 +26,38 @@ func (r *containerRouter) Routes() []router.Route {
29 29
 func (r *containerRouter) initRoutes() {
30 30
 	r.routes = []router.Route{
31 31
 		// HEAD
32
-		local.NewHeadRoute("/containers/{name:.*}/archive", r.headContainersArchive),
32
+		router.NewHeadRoute("/containers/{name:.*}/archive", r.headContainersArchive),
33 33
 		// GET
34
-		local.NewGetRoute("/containers/json", r.getContainersJSON),
35
-		local.NewGetRoute("/containers/{name:.*}/export", r.getContainersExport),
36
-		local.NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges),
37
-		local.NewGetRoute("/containers/{name:.*}/json", r.getContainersByName),
38
-		local.NewGetRoute("/containers/{name:.*}/top", r.getContainersTop),
39
-		local.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs),
40
-		local.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats),
41
-		local.NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach),
42
-		local.NewGetRoute("/exec/{id:.*}/json", r.getExecByID),
43
-		local.NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive),
34
+		router.NewGetRoute("/containers/json", r.getContainersJSON),
35
+		router.NewGetRoute("/containers/{name:.*}/export", r.getContainersExport),
36
+		router.NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges),
37
+		router.NewGetRoute("/containers/{name:.*}/json", r.getContainersByName),
38
+		router.NewGetRoute("/containers/{name:.*}/top", r.getContainersTop),
39
+		router.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs),
40
+		router.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats),
41
+		router.NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach),
42
+		router.NewGetRoute("/exec/{id:.*}/json", r.getExecByID),
43
+		router.NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive),
44 44
 		// POST
45
-		local.NewPostRoute("/containers/create", r.postContainersCreate),
46
-		local.NewPostRoute("/containers/{name:.*}/kill", r.postContainersKill),
47
-		local.NewPostRoute("/containers/{name:.*}/pause", r.postContainersPause),
48
-		local.NewPostRoute("/containers/{name:.*}/unpause", r.postContainersUnpause),
49
-		local.NewPostRoute("/containers/{name:.*}/restart", r.postContainersRestart),
50
-		local.NewPostRoute("/containers/{name:.*}/start", r.postContainersStart),
51
-		local.NewPostRoute("/containers/{name:.*}/stop", r.postContainersStop),
52
-		local.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
53
-		local.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
54
-		local.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
55
-		local.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy),
56
-		local.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
57
-		local.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
58
-		local.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
59
-		local.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
60
-		local.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
45
+		router.NewPostRoute("/containers/create", r.postContainersCreate),
46
+		router.NewPostRoute("/containers/{name:.*}/kill", r.postContainersKill),
47
+		router.NewPostRoute("/containers/{name:.*}/pause", r.postContainersPause),
48
+		router.NewPostRoute("/containers/{name:.*}/unpause", r.postContainersUnpause),
49
+		router.NewPostRoute("/containers/{name:.*}/restart", r.postContainersRestart),
50
+		router.NewPostRoute("/containers/{name:.*}/start", r.postContainersStart),
51
+		router.NewPostRoute("/containers/{name:.*}/stop", r.postContainersStop),
52
+		router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
53
+		router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
54
+		router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
55
+		router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy),
56
+		router.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
57
+		router.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
58
+		router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
59
+		router.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
60
+		router.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
61 61
 		// PUT
62
-		local.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
62
+		router.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
63 63
 		// DELETE
64
-		local.NewDeleteRoute("/containers/{name:.*}", r.deleteContainers),
64
+		router.NewDeleteRoute("/containers/{name:.*}", r.deleteContainers),
65 65
 	}
66 66
 }
... ...
@@ -13,7 +13,6 @@ import (
13 13
 	"github.com/Sirupsen/logrus"
14 14
 	"github.com/docker/distribution/registry/api/errcode"
15 15
 	"github.com/docker/docker/api/server/httputils"
16
-	"github.com/docker/docker/daemon"
17 16
 	derr "github.com/docker/docker/errors"
18 17
 	"github.com/docker/docker/pkg/ioutils"
19 18
 	"github.com/docker/docker/pkg/signal"
... ...
@@ -21,6 +20,7 @@ import (
21 21
 	"github.com/docker/docker/runconfig"
22 22
 	"github.com/docker/docker/utils"
23 23
 	"github.com/docker/engine-api/types"
24
+	"github.com/docker/engine-api/types/backend"
24 25
 	"github.com/docker/engine-api/types/container"
25 26
 	timetypes "github.com/docker/engine-api/types/time"
26 27
 	"golang.org/x/net/context"
... ...
@@ -32,7 +32,7 @@ func (s *containerRouter) getContainersJSON(ctx context.Context, w http.Response
32 32
 		return err
33 33
 	}
34 34
 
35
-	config := &daemon.ContainersConfig{
35
+	config := &backend.ContainersConfig{
36 36
 		All:     httputils.BoolValue(r, "all"),
37 37
 		Size:    httputils.BoolValue(r, "size"),
38 38
 		Since:   r.Form.Get("since"),
... ...
@@ -77,11 +77,11 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
77 77
 		closeNotifier = notifier.CloseNotify()
78 78
 	}
79 79
 
80
-	config := &daemon.ContainerStatsConfig{
80
+	config := &backend.ContainerStatsConfig{
81 81
 		Stream:    stream,
82 82
 		OutStream: out,
83 83
 		Stop:      closeNotifier,
84
-		Version:   httputils.VersionFromContext(ctx),
84
+		Version:   string(httputils.VersionFromContext(ctx)),
85 85
 	}
86 86
 
87 87
 	return s.backend.ContainerStats(vars["name"], config)
... ...
@@ -133,7 +133,7 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
133 133
 	output := ioutils.NewWriteFlusher(w)
134 134
 	defer output.Close()
135 135
 
136
-	logsConfig := &daemon.ContainerLogsConfig{
136
+	logsConfig := &backend.ContainerLogsConfig{
137 137
 		Follow:     httputils.BoolValue(r, "follow"),
138 138
 		Timestamps: httputils.BoolValue(r, "timestamps"),
139 139
 		Since:      since,
... ...
@@ -446,7 +446,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
446 446
 		}
447 447
 	}
448 448
 
449
-	attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
449
+	attachWithLogsConfig := &backend.ContainerAttachWithLogsConfig{
450 450
 		Hijacker:   w.(http.Hijacker),
451 451
 		Upgrade:    upgrade,
452 452
 		UseStdin:   httputils.BoolValue(r, "stdin"),
... ...
@@ -483,7 +483,7 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
483 483
 	h := websocket.Handler(func(ws *websocket.Conn) {
484 484
 		defer ws.Close()
485 485
 
486
-		wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{
486
+		wsAttachWithLogsConfig := &backend.ContainerWsAttachWithLogsConfig{
487 487
 			InStream:   ws,
488 488
 			OutStream:  ws,
489 489
 			ErrStream:  ws,
490 490
new file mode 100644
... ...
@@ -0,0 +1,44 @@
0
+package image
1
+
2
+import (
3
+	"io"
4
+
5
+	"github.com/docker/docker/reference"
6
+	"github.com/docker/engine-api/types"
7
+	"github.com/docker/engine-api/types/container"
8
+	"github.com/docker/engine-api/types/registry"
9
+)
10
+
11
+// Backend is all the methods that need to be implemented
12
+// to provide image specific functionality.
13
+type Backend interface {
14
+	containerBackend
15
+	imageBackend
16
+	importExportBackend
17
+	registryBackend
18
+}
19
+
20
+type containerBackend interface {
21
+	Commit(name string, config *types.ContainerCommitConfig) (imageID string, err error)
22
+	Exists(containerName string) bool
23
+}
24
+
25
+type imageBackend interface {
26
+	ImageDelete(imageRef string, force, prune bool) ([]types.ImageDelete, error)
27
+	ImageHistory(imageName string) ([]*types.ImageHistory, error)
28
+	Images(filterArgs string, filter string, all bool) ([]*types.Image, error)
29
+	LookupImage(name string) (*types.ImageInspect, error)
30
+	TagImage(newTag reference.Named, imageName string) error
31
+}
32
+
33
+type importExportBackend interface {
34
+	LoadImage(inTar io.ReadCloser, outStream io.Writer) error
35
+	ImportImage(src string, newRef reference.Named, msg string, inConfig io.ReadCloser, outStream io.Writer, config *container.Config) error
36
+	ExportImage(names []string, outStream io.Writer) error
37
+}
38
+
39
+type registryBackend interface {
40
+	PullImage(ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
41
+	PushImage(ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
42
+	SearchRegistryForImages(term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
43
+}
0 44
new file mode 100644
... ...
@@ -0,0 +1,44 @@
0
+package image
1
+
2
+import "github.com/docker/docker/api/server/router"
3
+
4
+// imageRouter is a router to talk with the image controller
5
+type imageRouter struct {
6
+	daemon Backend
7
+	routes []router.Route
8
+}
9
+
10
+// NewRouter initializes a new image router
11
+func NewRouter(daemon Backend) router.Router {
12
+	r := &imageRouter{
13
+		daemon: daemon,
14
+	}
15
+	r.initRoutes()
16
+	return r
17
+}
18
+
19
+// Routes returns the available routes to the image controller
20
+func (r *imageRouter) Routes() []router.Route {
21
+	return r.routes
22
+}
23
+
24
+// initRoutes initializes the routes in the image router
25
+func (r *imageRouter) initRoutes() {
26
+	r.routes = []router.Route{
27
+		// GET
28
+		router.NewGetRoute("/images/json", r.getImagesJSON),
29
+		router.NewGetRoute("/images/search", r.getImagesSearch),
30
+		router.NewGetRoute("/images/get", r.getImagesGet),
31
+		router.NewGetRoute("/images/{name:.*}/get", r.getImagesGet),
32
+		router.NewGetRoute("/images/{name:.*}/history", r.getImagesHistory),
33
+		router.NewGetRoute("/images/{name:.*}/json", r.getImagesByName),
34
+		// POST
35
+		router.NewPostRoute("/commit", r.postCommit),
36
+		router.NewPostRoute("/images/create", r.postImagesCreate),
37
+		router.NewPostRoute("/images/load", r.postImagesLoad),
38
+		router.NewPostRoute("/images/{name:.*}/push", r.postImagesPush),
39
+		router.NewPostRoute("/images/{name:.*}/tag", r.postImagesTag),
40
+		// DELETE
41
+		router.NewDeleteRoute("/images/{name:.*}", r.deleteImages),
42
+	}
43
+}
0 44
new file mode 100644
... ...
@@ -0,0 +1,399 @@
0
+package image
1
+
2
+import (
3
+	"encoding/base64"
4
+	"encoding/json"
5
+	"errors"
6
+	"fmt"
7
+	"io"
8
+	"net/http"
9
+	"net/url"
10
+	"strings"
11
+
12
+	"github.com/docker/distribution/digest"
13
+	"github.com/docker/distribution/registry/api/errcode"
14
+	"github.com/docker/docker/api/server/httputils"
15
+	"github.com/docker/docker/builder/dockerfile"
16
+	derr "github.com/docker/docker/errors"
17
+	"github.com/docker/docker/pkg/ioutils"
18
+	"github.com/docker/docker/pkg/streamformatter"
19
+	"github.com/docker/docker/reference"
20
+	"github.com/docker/docker/runconfig"
21
+	"github.com/docker/engine-api/types"
22
+	"github.com/docker/engine-api/types/container"
23
+	"golang.org/x/net/context"
24
+)
25
+
26
+func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
27
+	if err := httputils.ParseForm(r); err != nil {
28
+		return err
29
+	}
30
+
31
+	if err := httputils.CheckForJSON(r); err != nil {
32
+		return err
33
+	}
34
+
35
+	cname := r.Form.Get("container")
36
+
37
+	pause := httputils.BoolValue(r, "pause")
38
+	version := httputils.VersionFromContext(ctx)
39
+	if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
40
+		pause = true
41
+	}
42
+
43
+	c, _, _, err := runconfig.DecodeContainerConfig(r.Body)
44
+	if err != nil && err != io.EOF { //Do not fail if body is empty.
45
+		return err
46
+	}
47
+	if c == nil {
48
+		c = &container.Config{}
49
+	}
50
+
51
+	if !s.daemon.Exists(cname) {
52
+		return derr.ErrorCodeNoSuchContainer.WithArgs(cname)
53
+	}
54
+
55
+	newConfig, err := dockerfile.BuildFromConfig(c, r.Form["changes"])
56
+	if err != nil {
57
+		return err
58
+	}
59
+
60
+	commitCfg := &types.ContainerCommitConfig{
61
+		Pause:        pause,
62
+		Repo:         r.Form.Get("repo"),
63
+		Tag:          r.Form.Get("tag"),
64
+		Author:       r.Form.Get("author"),
65
+		Comment:      r.Form.Get("comment"),
66
+		Config:       newConfig,
67
+		MergeConfigs: true,
68
+	}
69
+
70
+	imgID, err := s.daemon.Commit(cname, commitCfg)
71
+	if err != nil {
72
+		return err
73
+	}
74
+
75
+	return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{
76
+		ID: string(imgID),
77
+	})
78
+}
79
+
80
+// Creates an image from Pull or from Import
81
+func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
82
+	if err := httputils.ParseForm(r); err != nil {
83
+		return err
84
+	}
85
+
86
+	var (
87
+		image   = r.Form.Get("fromImage")
88
+		repo    = r.Form.Get("repo")
89
+		tag     = r.Form.Get("tag")
90
+		message = r.Form.Get("message")
91
+		err     error
92
+		output  = ioutils.NewWriteFlusher(w)
93
+	)
94
+	defer output.Close()
95
+
96
+	w.Header().Set("Content-Type", "application/json")
97
+
98
+	if image != "" { //pull
99
+		// Special case: "pull -a" may send an image name with a
100
+		// trailing :. This is ugly, but let's not break API
101
+		// compatibility.
102
+		image = strings.TrimSuffix(image, ":")
103
+
104
+		var ref reference.Named
105
+		ref, err = reference.ParseNamed(image)
106
+		if err == nil {
107
+			if tag != "" {
108
+				// The "tag" could actually be a digest.
109
+				var dgst digest.Digest
110
+				dgst, err = digest.ParseDigest(tag)
111
+				if err == nil {
112
+					ref, err = reference.WithDigest(ref, dgst)
113
+				} else {
114
+					ref, err = reference.WithTag(ref, tag)
115
+				}
116
+			}
117
+			if err == nil {
118
+				metaHeaders := map[string][]string{}
119
+				for k, v := range r.Header {
120
+					if strings.HasPrefix(k, "X-Meta-") {
121
+						metaHeaders[k] = v
122
+					}
123
+				}
124
+
125
+				authEncoded := r.Header.Get("X-Registry-Auth")
126
+				authConfig := &types.AuthConfig{}
127
+				if authEncoded != "" {
128
+					authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
129
+					if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
130
+						// for a pull it is not an error if no auth was given
131
+						// to increase compatibility with the existing api it is defaulting to be empty
132
+						authConfig = &types.AuthConfig{}
133
+					}
134
+				}
135
+
136
+				err = s.daemon.PullImage(ref, metaHeaders, authConfig, output)
137
+			}
138
+		}
139
+		// Check the error from pulling an image to make sure the request
140
+		// was authorized. Modify the status if the request was
141
+		// unauthorized to respond with 401 rather than 500.
142
+		if err != nil && isAuthorizedError(err) {
143
+			err = errcode.ErrorCodeUnauthorized.WithMessage(fmt.Sprintf("Authentication is required: %s", err))
144
+		}
145
+	} else { //import
146
+		var newRef reference.Named
147
+		if repo != "" {
148
+			var err error
149
+			newRef, err = reference.ParseNamed(repo)
150
+			if err != nil {
151
+				return err
152
+			}
153
+
154
+			if _, isCanonical := newRef.(reference.Canonical); isCanonical {
155
+				return errors.New("cannot import digest reference")
156
+			}
157
+
158
+			if tag != "" {
159
+				newRef, err = reference.WithTag(newRef, tag)
160
+				if err != nil {
161
+					return err
162
+				}
163
+			}
164
+		}
165
+
166
+		src := r.Form.Get("fromSrc")
167
+
168
+		// 'err' MUST NOT be defined within this block, we need any error
169
+		// generated from the download to be available to the output
170
+		// stream processing below
171
+		var newConfig *container.Config
172
+		newConfig, err = dockerfile.BuildFromConfig(&container.Config{}, r.Form["changes"])
173
+		if err != nil {
174
+			return err
175
+		}
176
+
177
+		err = s.daemon.ImportImage(src, newRef, message, r.Body, output, newConfig)
178
+	}
179
+	if err != nil {
180
+		if !output.Flushed() {
181
+			return err
182
+		}
183
+		sf := streamformatter.NewJSONStreamFormatter()
184
+		output.Write(sf.FormatError(err))
185
+	}
186
+
187
+	return nil
188
+}
189
+
190
+func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
191
+	metaHeaders := map[string][]string{}
192
+	for k, v := range r.Header {
193
+		if strings.HasPrefix(k, "X-Meta-") {
194
+			metaHeaders[k] = v
195
+		}
196
+	}
197
+	if err := httputils.ParseForm(r); err != nil {
198
+		return err
199
+	}
200
+	authConfig := &types.AuthConfig{}
201
+
202
+	authEncoded := r.Header.Get("X-Registry-Auth")
203
+	if authEncoded != "" {
204
+		// the new format is to handle the authConfig as a header
205
+		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
206
+		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
207
+			// to increase compatibility to existing api it is defaulting to be empty
208
+			authConfig = &types.AuthConfig{}
209
+		}
210
+	} else {
211
+		// the old format is supported for compatibility if there was no authConfig header
212
+		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
213
+			return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
214
+		}
215
+	}
216
+
217
+	ref, err := reference.ParseNamed(vars["name"])
218
+	if err != nil {
219
+		return err
220
+	}
221
+	tag := r.Form.Get("tag")
222
+	if tag != "" {
223
+		// Push by digest is not supported, so only tags are supported.
224
+		ref, err = reference.WithTag(ref, tag)
225
+		if err != nil {
226
+			return err
227
+		}
228
+	}
229
+
230
+	output := ioutils.NewWriteFlusher(w)
231
+	defer output.Close()
232
+
233
+	w.Header().Set("Content-Type", "application/json")
234
+
235
+	if err := s.daemon.PushImage(ref, metaHeaders, authConfig, output); err != nil {
236
+		if !output.Flushed() {
237
+			return err
238
+		}
239
+		sf := streamformatter.NewJSONStreamFormatter()
240
+		output.Write(sf.FormatError(err))
241
+	}
242
+	return nil
243
+}
244
+
245
+func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
246
+	if err := httputils.ParseForm(r); err != nil {
247
+		return err
248
+	}
249
+
250
+	w.Header().Set("Content-Type", "application/x-tar")
251
+
252
+	output := ioutils.NewWriteFlusher(w)
253
+	defer output.Close()
254
+	var names []string
255
+	if name, ok := vars["name"]; ok {
256
+		names = []string{name}
257
+	} else {
258
+		names = r.Form["names"]
259
+	}
260
+
261
+	if err := s.daemon.ExportImage(names, output); err != nil {
262
+		if !output.Flushed() {
263
+			return err
264
+		}
265
+		sf := streamformatter.NewJSONStreamFormatter()
266
+		output.Write(sf.FormatError(err))
267
+	}
268
+	return nil
269
+}
270
+
271
+func (s *router) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
272
+	if err := httputils.ParseForm(r); err != nil {
273
+		return err
274
+	}
275
+	quiet := httputils.BoolValueOrDefault(r, "quiet", true)
276
+	w.Header().Set("Content-Type", "application/json")
277
+	return s.daemon.LoadImage(r.Body, w, quiet)
278
+}
279
+
280
+func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
281
+	if err := httputils.ParseForm(r); err != nil {
282
+		return err
283
+	}
284
+
285
+	name := vars["name"]
286
+
287
+	if strings.TrimSpace(name) == "" {
288
+		return fmt.Errorf("image name cannot be blank")
289
+	}
290
+
291
+	force := httputils.BoolValue(r, "force")
292
+	prune := !httputils.BoolValue(r, "noprune")
293
+
294
+	list, err := s.daemon.ImageDelete(name, force, prune)
295
+	if err != nil {
296
+		return err
297
+	}
298
+
299
+	return httputils.WriteJSON(w, http.StatusOK, list)
300
+}
301
+
302
+func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
303
+	imageInspect, err := s.daemon.LookupImage(vars["name"])
304
+	if err != nil {
305
+		return err
306
+	}
307
+
308
+	return httputils.WriteJSON(w, http.StatusOK, imageInspect)
309
+}
310
+
311
+func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
312
+	if err := httputils.ParseForm(r); err != nil {
313
+		return err
314
+	}
315
+
316
+	// FIXME: The filter parameter could just be a match filter
317
+	images, err := s.daemon.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
318
+	if err != nil {
319
+		return err
320
+	}
321
+
322
+	return httputils.WriteJSON(w, http.StatusOK, images)
323
+}
324
+
325
+func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
326
+	name := vars["name"]
327
+	history, err := s.daemon.ImageHistory(name)
328
+	if err != nil {
329
+		return err
330
+	}
331
+
332
+	return httputils.WriteJSON(w, http.StatusOK, history)
333
+}
334
+
335
+func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
336
+	if err := httputils.ParseForm(r); err != nil {
337
+		return err
338
+	}
339
+	repo := r.Form.Get("repo")
340
+	tag := r.Form.Get("tag")
341
+	newTag, err := reference.WithName(repo)
342
+	if err != nil {
343
+		return err
344
+	}
345
+	if tag != "" {
346
+		if newTag, err = reference.WithTag(newTag, tag); err != nil {
347
+			return err
348
+		}
349
+	}
350
+	if err := s.daemon.TagImage(newTag, vars["name"]); err != nil {
351
+		return err
352
+	}
353
+	w.WriteHeader(http.StatusCreated)
354
+	return nil
355
+}
356
+
357
+func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
358
+	if err := httputils.ParseForm(r); err != nil {
359
+		return err
360
+	}
361
+	var (
362
+		config      *types.AuthConfig
363
+		authEncoded = r.Header.Get("X-Registry-Auth")
364
+		headers     = map[string][]string{}
365
+	)
366
+
367
+	if authEncoded != "" {
368
+		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
369
+		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
370
+			// for a search it is not an error if no auth was given
371
+			// to increase compatibility with the existing api it is defaulting to be empty
372
+			config = &types.AuthConfig{}
373
+		}
374
+	}
375
+	for k, v := range r.Header {
376
+		if strings.HasPrefix(k, "X-Meta-") {
377
+			headers[k] = v
378
+		}
379
+	}
380
+	query, err := s.daemon.SearchRegistryForImages(r.Form.Get("term"), config, headers)
381
+	if err != nil {
382
+		return err
383
+	}
384
+	return httputils.WriteJSON(w, http.StatusOK, query.Results)
385
+}
386
+
387
+func isAuthorizedError(err error) bool {
388
+	if urlError, ok := err.(*url.Error); ok {
389
+		err = urlError.Err
390
+	}
391
+
392
+	if dError, ok := err.(errcode.Error); ok {
393
+		if dError.ErrorCode() == errcode.ErrorCodeUnauthorized {
394
+			return true
395
+		}
396
+	}
397
+	return false
398
+}
0 399
new file mode 100644
... ...
@@ -0,0 +1,61 @@
0
+package router
1
+
2
+import "github.com/docker/docker/api/server/httputils"
3
+
4
+// localRoute defines an individual API route to connect
5
+// with the docker daemon. It implements Route.
6
+type localRoute struct {
7
+	method  string
8
+	path    string
9
+	handler httputils.APIFunc
10
+}
11
+
12
+// Handler returns the APIFunc to let the server wrap it in middlewares.
13
+func (l localRoute) Handler() httputils.APIFunc {
14
+	return l.handler
15
+}
16
+
17
+// Method returns the http method that the route responds to.
18
+func (l localRoute) Method() string {
19
+	return l.method
20
+}
21
+
22
+// Path returns the subpath where the route responds to.
23
+func (l localRoute) Path() string {
24
+	return l.path
25
+}
26
+
27
+// NewRoute initializes a new local route for the router.
28
+func NewRoute(method, path string, handler httputils.APIFunc) Route {
29
+	return localRoute{method, path, handler}
30
+}
31
+
32
+// NewGetRoute initializes a new route with the http method GET.
33
+func NewGetRoute(path string, handler httputils.APIFunc) Route {
34
+	return NewRoute("GET", path, handler)
35
+}
36
+
37
+// NewPostRoute initializes a new route with the http method POST.
38
+func NewPostRoute(path string, handler httputils.APIFunc) Route {
39
+	return NewRoute("POST", path, handler)
40
+}
41
+
42
+// NewPutRoute initializes a new route with the http method PUT.
43
+func NewPutRoute(path string, handler httputils.APIFunc) Route {
44
+	return NewRoute("PUT", path, handler)
45
+}
46
+
47
+// NewDeleteRoute initializes a new route with the http method DELETE.
48
+func NewDeleteRoute(path string, handler httputils.APIFunc) Route {
49
+	return NewRoute("DELETE", path, handler)
50
+}
51
+
52
+// NewOptionsRoute initializes a new route with the http method OPTIONS.
53
+func NewOptionsRoute(path string, handler httputils.APIFunc) Route {
54
+	return NewRoute("OPTIONS", path, handler)
55
+}
56
+
57
+// NewHeadRoute initializes a new route with the http method HEAD.
58
+func NewHeadRoute(path string, handler httputils.APIFunc) Route {
59
+	return NewRoute("HEAD", path, handler)
60
+}
0 61
deleted file mode 100644
... ...
@@ -1,399 +0,0 @@
1
-package local
2
-
3
-import (
4
-	"encoding/base64"
5
-	"encoding/json"
6
-	"errors"
7
-	"fmt"
8
-	"io"
9
-	"net/http"
10
-	"net/url"
11
-	"strings"
12
-
13
-	"github.com/docker/distribution/digest"
14
-	"github.com/docker/distribution/registry/api/errcode"
15
-	"github.com/docker/docker/api/server/httputils"
16
-	"github.com/docker/docker/builder/dockerfile"
17
-	derr "github.com/docker/docker/errors"
18
-	"github.com/docker/docker/pkg/ioutils"
19
-	"github.com/docker/docker/pkg/streamformatter"
20
-	"github.com/docker/docker/reference"
21
-	"github.com/docker/docker/runconfig"
22
-	"github.com/docker/engine-api/types"
23
-	"github.com/docker/engine-api/types/container"
24
-	"golang.org/x/net/context"
25
-)
26
-
27
-func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
28
-	if err := httputils.ParseForm(r); err != nil {
29
-		return err
30
-	}
31
-
32
-	if err := httputils.CheckForJSON(r); err != nil {
33
-		return err
34
-	}
35
-
36
-	cname := r.Form.Get("container")
37
-
38
-	pause := httputils.BoolValue(r, "pause")
39
-	version := httputils.VersionFromContext(ctx)
40
-	if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
41
-		pause = true
42
-	}
43
-
44
-	c, _, _, err := runconfig.DecodeContainerConfig(r.Body)
45
-	if err != nil && err != io.EOF { //Do not fail if body is empty.
46
-		return err
47
-	}
48
-	if c == nil {
49
-		c = &container.Config{}
50
-	}
51
-
52
-	if !s.daemon.Exists(cname) {
53
-		return derr.ErrorCodeNoSuchContainer.WithArgs(cname)
54
-	}
55
-
56
-	newConfig, err := dockerfile.BuildFromConfig(c, r.Form["changes"])
57
-	if err != nil {
58
-		return err
59
-	}
60
-
61
-	commitCfg := &types.ContainerCommitConfig{
62
-		Pause:        pause,
63
-		Repo:         r.Form.Get("repo"),
64
-		Tag:          r.Form.Get("tag"),
65
-		Author:       r.Form.Get("author"),
66
-		Comment:      r.Form.Get("comment"),
67
-		Config:       newConfig,
68
-		MergeConfigs: true,
69
-	}
70
-
71
-	imgID, err := s.daemon.Commit(cname, commitCfg)
72
-	if err != nil {
73
-		return err
74
-	}
75
-
76
-	return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{
77
-		ID: string(imgID),
78
-	})
79
-}
80
-
81
-// Creates an image from Pull or from Import
82
-func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
83
-	if err := httputils.ParseForm(r); err != nil {
84
-		return err
85
-	}
86
-
87
-	var (
88
-		image   = r.Form.Get("fromImage")
89
-		repo    = r.Form.Get("repo")
90
-		tag     = r.Form.Get("tag")
91
-		message = r.Form.Get("message")
92
-		err     error
93
-		output  = ioutils.NewWriteFlusher(w)
94
-	)
95
-	defer output.Close()
96
-
97
-	w.Header().Set("Content-Type", "application/json")
98
-
99
-	if image != "" { //pull
100
-		// Special case: "pull -a" may send an image name with a
101
-		// trailing :. This is ugly, but let's not break API
102
-		// compatibility.
103
-		image = strings.TrimSuffix(image, ":")
104
-
105
-		var ref reference.Named
106
-		ref, err = reference.ParseNamed(image)
107
-		if err == nil {
108
-			if tag != "" {
109
-				// The "tag" could actually be a digest.
110
-				var dgst digest.Digest
111
-				dgst, err = digest.ParseDigest(tag)
112
-				if err == nil {
113
-					ref, err = reference.WithDigest(ref, dgst)
114
-				} else {
115
-					ref, err = reference.WithTag(ref, tag)
116
-				}
117
-			}
118
-			if err == nil {
119
-				metaHeaders := map[string][]string{}
120
-				for k, v := range r.Header {
121
-					if strings.HasPrefix(k, "X-Meta-") {
122
-						metaHeaders[k] = v
123
-					}
124
-				}
125
-
126
-				authEncoded := r.Header.Get("X-Registry-Auth")
127
-				authConfig := &types.AuthConfig{}
128
-				if authEncoded != "" {
129
-					authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
130
-					if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
131
-						// for a pull it is not an error if no auth was given
132
-						// to increase compatibility with the existing api it is defaulting to be empty
133
-						authConfig = &types.AuthConfig{}
134
-					}
135
-				}
136
-
137
-				err = s.daemon.PullImage(ref, metaHeaders, authConfig, output)
138
-			}
139
-		}
140
-		// Check the error from pulling an image to make sure the request
141
-		// was authorized. Modify the status if the request was
142
-		// unauthorized to respond with 401 rather than 500.
143
-		if err != nil && isAuthorizedError(err) {
144
-			err = errcode.ErrorCodeUnauthorized.WithMessage(fmt.Sprintf("Authentication is required: %s", err))
145
-		}
146
-	} else { //import
147
-		var newRef reference.Named
148
-		if repo != "" {
149
-			var err error
150
-			newRef, err = reference.ParseNamed(repo)
151
-			if err != nil {
152
-				return err
153
-			}
154
-
155
-			if _, isCanonical := newRef.(reference.Canonical); isCanonical {
156
-				return errors.New("cannot import digest reference")
157
-			}
158
-
159
-			if tag != "" {
160
-				newRef, err = reference.WithTag(newRef, tag)
161
-				if err != nil {
162
-					return err
163
-				}
164
-			}
165
-		}
166
-
167
-		src := r.Form.Get("fromSrc")
168
-
169
-		// 'err' MUST NOT be defined within this block, we need any error
170
-		// generated from the download to be available to the output
171
-		// stream processing below
172
-		var newConfig *container.Config
173
-		newConfig, err = dockerfile.BuildFromConfig(&container.Config{}, r.Form["changes"])
174
-		if err != nil {
175
-			return err
176
-		}
177
-
178
-		err = s.daemon.ImportImage(src, newRef, message, r.Body, output, newConfig)
179
-	}
180
-	if err != nil {
181
-		if !output.Flushed() {
182
-			return err
183
-		}
184
-		sf := streamformatter.NewJSONStreamFormatter()
185
-		output.Write(sf.FormatError(err))
186
-	}
187
-
188
-	return nil
189
-}
190
-
191
-func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
192
-	metaHeaders := map[string][]string{}
193
-	for k, v := range r.Header {
194
-		if strings.HasPrefix(k, "X-Meta-") {
195
-			metaHeaders[k] = v
196
-		}
197
-	}
198
-	if err := httputils.ParseForm(r); err != nil {
199
-		return err
200
-	}
201
-	authConfig := &types.AuthConfig{}
202
-
203
-	authEncoded := r.Header.Get("X-Registry-Auth")
204
-	if authEncoded != "" {
205
-		// the new format is to handle the authConfig as a header
206
-		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
207
-		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
208
-			// to increase compatibility to existing api it is defaulting to be empty
209
-			authConfig = &types.AuthConfig{}
210
-		}
211
-	} else {
212
-		// the old format is supported for compatibility if there was no authConfig header
213
-		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
214
-			return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
215
-		}
216
-	}
217
-
218
-	ref, err := reference.ParseNamed(vars["name"])
219
-	if err != nil {
220
-		return err
221
-	}
222
-	tag := r.Form.Get("tag")
223
-	if tag != "" {
224
-		// Push by digest is not supported, so only tags are supported.
225
-		ref, err = reference.WithTag(ref, tag)
226
-		if err != nil {
227
-			return err
228
-		}
229
-	}
230
-
231
-	output := ioutils.NewWriteFlusher(w)
232
-	defer output.Close()
233
-
234
-	w.Header().Set("Content-Type", "application/json")
235
-
236
-	if err := s.daemon.PushImage(ref, metaHeaders, authConfig, output); err != nil {
237
-		if !output.Flushed() {
238
-			return err
239
-		}
240
-		sf := streamformatter.NewJSONStreamFormatter()
241
-		output.Write(sf.FormatError(err))
242
-	}
243
-	return nil
244
-}
245
-
246
-func (s *router) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
247
-	if err := httputils.ParseForm(r); err != nil {
248
-		return err
249
-	}
250
-
251
-	w.Header().Set("Content-Type", "application/x-tar")
252
-
253
-	output := ioutils.NewWriteFlusher(w)
254
-	defer output.Close()
255
-	var names []string
256
-	if name, ok := vars["name"]; ok {
257
-		names = []string{name}
258
-	} else {
259
-		names = r.Form["names"]
260
-	}
261
-
262
-	if err := s.daemon.ExportImage(names, output); err != nil {
263
-		if !output.Flushed() {
264
-			return err
265
-		}
266
-		sf := streamformatter.NewJSONStreamFormatter()
267
-		output.Write(sf.FormatError(err))
268
-	}
269
-	return nil
270
-}
271
-
272
-func (s *router) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
273
-	if err := httputils.ParseForm(r); err != nil {
274
-		return err
275
-	}
276
-	quiet := httputils.BoolValueOrDefault(r, "quiet", true)
277
-	w.Header().Set("Content-Type", "application/json")
278
-	return s.daemon.LoadImage(r.Body, w, quiet)
279
-}
280
-
281
-func (s *router) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
282
-	if err := httputils.ParseForm(r); err != nil {
283
-		return err
284
-	}
285
-
286
-	name := vars["name"]
287
-
288
-	if strings.TrimSpace(name) == "" {
289
-		return fmt.Errorf("image name cannot be blank")
290
-	}
291
-
292
-	force := httputils.BoolValue(r, "force")
293
-	prune := !httputils.BoolValue(r, "noprune")
294
-
295
-	list, err := s.daemon.ImageDelete(name, force, prune)
296
-	if err != nil {
297
-		return err
298
-	}
299
-
300
-	return httputils.WriteJSON(w, http.StatusOK, list)
301
-}
302
-
303
-func (s *router) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
304
-	imageInspect, err := s.daemon.LookupImage(vars["name"])
305
-	if err != nil {
306
-		return err
307
-	}
308
-
309
-	return httputils.WriteJSON(w, http.StatusOK, imageInspect)
310
-}
311
-
312
-func (s *router) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
313
-	if err := httputils.ParseForm(r); err != nil {
314
-		return err
315
-	}
316
-
317
-	// FIXME: The filter parameter could just be a match filter
318
-	images, err := s.daemon.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
319
-	if err != nil {
320
-		return err
321
-	}
322
-
323
-	return httputils.WriteJSON(w, http.StatusOK, images)
324
-}
325
-
326
-func (s *router) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
327
-	name := vars["name"]
328
-	history, err := s.daemon.ImageHistory(name)
329
-	if err != nil {
330
-		return err
331
-	}
332
-
333
-	return httputils.WriteJSON(w, http.StatusOK, history)
334
-}
335
-
336
-func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
337
-	if err := httputils.ParseForm(r); err != nil {
338
-		return err
339
-	}
340
-	repo := r.Form.Get("repo")
341
-	tag := r.Form.Get("tag")
342
-	newTag, err := reference.WithName(repo)
343
-	if err != nil {
344
-		return err
345
-	}
346
-	if tag != "" {
347
-		if newTag, err = reference.WithTag(newTag, tag); err != nil {
348
-			return err
349
-		}
350
-	}
351
-	if err := s.daemon.TagImage(newTag, vars["name"]); err != nil {
352
-		return err
353
-	}
354
-	w.WriteHeader(http.StatusCreated)
355
-	return nil
356
-}
357
-
358
-func (s *router) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
359
-	if err := httputils.ParseForm(r); err != nil {
360
-		return err
361
-	}
362
-	var (
363
-		config      *types.AuthConfig
364
-		authEncoded = r.Header.Get("X-Registry-Auth")
365
-		headers     = map[string][]string{}
366
-	)
367
-
368
-	if authEncoded != "" {
369
-		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
370
-		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
371
-			// for a search it is not an error if no auth was given
372
-			// to increase compatibility with the existing api it is defaulting to be empty
373
-			config = &types.AuthConfig{}
374
-		}
375
-	}
376
-	for k, v := range r.Header {
377
-		if strings.HasPrefix(k, "X-Meta-") {
378
-			headers[k] = v
379
-		}
380
-	}
381
-	query, err := s.daemon.SearchRegistryForImages(r.Form.Get("term"), config, headers)
382
-	if err != nil {
383
-		return err
384
-	}
385
-	return httputils.WriteJSON(w, http.StatusOK, query.Results)
386
-}
387
-
388
-func isAuthorizedError(err error) bool {
389
-	if urlError, ok := err.(*url.Error); ok {
390
-		err = urlError.Err
391
-	}
392
-
393
-	if dError, ok := err.(errcode.Error); ok {
394
-		if dError.ErrorCode() == errcode.ErrorCodeUnauthorized {
395
-			return true
396
-		}
397
-	}
398
-	return false
399
-}
400 1
deleted file mode 100644
... ...
@@ -1,107 +0,0 @@
1
-package local
2
-
3
-import (
4
-	"github.com/docker/docker/api/server/httputils"
5
-	dkrouter "github.com/docker/docker/api/server/router"
6
-	"github.com/docker/docker/daemon"
7
-)
8
-
9
-// router is a docker router that talks with the local docker daemon.
10
-type router struct {
11
-	daemon *daemon.Daemon
12
-	routes []dkrouter.Route
13
-}
14
-
15
-// localRoute defines an individual API route to connect with the docker daemon.
16
-// It implements router.Route.
17
-type localRoute struct {
18
-	method  string
19
-	path    string
20
-	handler httputils.APIFunc
21
-}
22
-
23
-// Handler returns the APIFunc to let the server wrap it in middlewares
24
-func (l localRoute) Handler() httputils.APIFunc {
25
-	return l.handler
26
-}
27
-
28
-// Method returns the http method that the route responds to.
29
-func (l localRoute) Method() string {
30
-	return l.method
31
-}
32
-
33
-// Path returns the subpath where the route responds to.
34
-func (l localRoute) Path() string {
35
-	return l.path
36
-}
37
-
38
-// NewRoute initializes a new local router for the reouter
39
-func NewRoute(method, path string, handler httputils.APIFunc) dkrouter.Route {
40
-	return localRoute{method, path, handler}
41
-}
42
-
43
-// NewGetRoute initializes a new route with the http method GET.
44
-func NewGetRoute(path string, handler httputils.APIFunc) dkrouter.Route {
45
-	return NewRoute("GET", path, handler)
46
-}
47
-
48
-// NewPostRoute initializes a new route with the http method POST.
49
-func NewPostRoute(path string, handler httputils.APIFunc) dkrouter.Route {
50
-	return NewRoute("POST", path, handler)
51
-}
52
-
53
-// NewPutRoute initializes a new route with the http method PUT.
54
-func NewPutRoute(path string, handler httputils.APIFunc) dkrouter.Route {
55
-	return NewRoute("PUT", path, handler)
56
-}
57
-
58
-// NewDeleteRoute initializes a new route with the http method DELETE.
59
-func NewDeleteRoute(path string, handler httputils.APIFunc) dkrouter.Route {
60
-	return NewRoute("DELETE", path, handler)
61
-}
62
-
63
-// NewOptionsRoute initializes a new route with the http method OPTIONS
64
-func NewOptionsRoute(path string, handler httputils.APIFunc) dkrouter.Route {
65
-	return NewRoute("OPTIONS", path, handler)
66
-}
67
-
68
-// NewHeadRoute initializes a new route with the http method HEAD.
69
-func NewHeadRoute(path string, handler httputils.APIFunc) dkrouter.Route {
70
-	return NewRoute("HEAD", path, handler)
71
-}
72
-
73
-// NewRouter initializes a local router with a new daemon.
74
-func NewRouter(daemon *daemon.Daemon) dkrouter.Router {
75
-	r := &router{
76
-		daemon: daemon,
77
-	}
78
-	r.initRoutes()
79
-	return r
80
-}
81
-
82
-// Routes returns the list of routes registered in the router.
83
-func (r *router) Routes() []dkrouter.Route {
84
-	return r.routes
85
-}
86
-
87
-// initRoutes initializes the routes in this router
88
-func (r *router) initRoutes() {
89
-	r.routes = []dkrouter.Route{
90
-		// OPTIONS
91
-		// GET
92
-		NewGetRoute("/images/json", r.getImagesJSON),
93
-		NewGetRoute("/images/search", r.getImagesSearch),
94
-		NewGetRoute("/images/get", r.getImagesGet),
95
-		NewGetRoute("/images/{name:.*}/get", r.getImagesGet),
96
-		NewGetRoute("/images/{name:.*}/history", r.getImagesHistory),
97
-		NewGetRoute("/images/{name:.*}/json", r.getImagesByName),
98
-		// POST
99
-		NewPostRoute("/commit", r.postCommit),
100
-		NewPostRoute("/images/create", r.postImagesCreate),
101
-		NewPostRoute("/images/load", r.postImagesLoad),
102
-		NewPostRoute("/images/{name:.*}/push", r.postImagesPush),
103
-		NewPostRoute("/images/{name:.*}/tag", r.postImagesTag),
104
-		// DELETE
105
-		NewDeleteRoute("/images/{name:.*}", r.deleteImages),
106
-	}
107
-}
... ...
@@ -5,18 +5,17 @@ import (
5 5
 	"github.com/docker/libnetwork"
6 6
 )
7 7
 
8
-// Backend is all the methods that need to be implemented to provide
9
-// network specific functionality
8
+// Backend is all the methods that need to be implemented
9
+// to provide network specific functionality.
10 10
 type Backend interface {
11
+	NetworkControllerEnabled() bool
12
+
11 13
 	FindNetwork(idName string) (libnetwork.Network, error)
12
-	GetNetwork(idName string, by int) (libnetwork.Network, error)
14
+	GetNetworkByName(idName string) (libnetwork.Network, error)
13 15
 	GetNetworksByID(partialID string) []libnetwork.Network
14 16
 	GetAllNetworks() []libnetwork.Network
15
-	CreateNetwork(name, driver string, ipam network.IPAM,
16
-		options map[string]string, internal bool) (libnetwork.Network, error)
17
+	CreateNetwork(name, driver string, ipam network.IPAM, options map[string]string, internal bool) (libnetwork.Network, error)
17 18
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
18
-	DisconnectContainerFromNetwork(containerName string,
19
-		network libnetwork.Network, force bool) error
20
-	NetworkControllerEnabled() bool
19
+	DisconnectContainerFromNetwork(containerName string, network libnetwork.Network, force bool) error
21 20
 	DeleteNetwork(name string) error
22 21
 }
... ...
@@ -5,7 +5,6 @@ import (
5 5
 
6 6
 	"github.com/docker/docker/api/server/httputils"
7 7
 	"github.com/docker/docker/api/server/router"
8
-	"github.com/docker/docker/api/server/router/local"
9 8
 	"github.com/docker/docker/errors"
10 9
 	"golang.org/x/net/context"
11 10
 )
... ...
@@ -33,14 +32,14 @@ func (r *networkRouter) Routes() []router.Route {
33 33
 func (r *networkRouter) initRoutes() {
34 34
 	r.routes = []router.Route{
35 35
 		// GET
36
-		local.NewGetRoute("/networks", r.controllerEnabledMiddleware(r.getNetworksList)),
37
-		local.NewGetRoute("/networks/{id:.*}", r.controllerEnabledMiddleware(r.getNetwork)),
36
+		router.NewGetRoute("/networks", r.controllerEnabledMiddleware(r.getNetworksList)),
37
+		router.NewGetRoute("/networks/{id:.*}", r.controllerEnabledMiddleware(r.getNetwork)),
38 38
 		// POST
39
-		local.NewPostRoute("/networks/create", r.controllerEnabledMiddleware(r.postNetworkCreate)),
40
-		local.NewPostRoute("/networks/{id:.*}/connect", r.controllerEnabledMiddleware(r.postNetworkConnect)),
41
-		local.NewPostRoute("/networks/{id:.*}/disconnect", r.controllerEnabledMiddleware(r.postNetworkDisconnect)),
39
+		router.NewPostRoute("/networks/create", r.controllerEnabledMiddleware(r.postNetworkCreate)),
40
+		router.NewPostRoute("/networks/{id:.*}/connect", r.controllerEnabledMiddleware(r.postNetworkConnect)),
41
+		router.NewPostRoute("/networks/{id:.*}/disconnect", r.controllerEnabledMiddleware(r.postNetworkDisconnect)),
42 42
 		// DELETE
43
-		local.NewDeleteRoute("/networks/{id:.*}", r.controllerEnabledMiddleware(r.deleteNetwork)),
43
+		router.NewDeleteRoute("/networks/{id:.*}", r.controllerEnabledMiddleware(r.deleteNetwork)),
44 44
 	}
45 45
 }
46 46
 
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"golang.org/x/net/context"
9 9
 
10 10
 	"github.com/docker/docker/api/server/httputils"
11
-	"github.com/docker/docker/daemon"
12 11
 	"github.com/docker/docker/runconfig"
13 12
 	"github.com/docker/engine-api/types"
14 13
 	"github.com/docker/engine-api/types/filters"
... ...
@@ -81,7 +80,7 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr
81 81
 			fmt.Sprintf("%s is a pre-defined network and cannot be created", create.Name))
82 82
 	}
83 83
 
84
-	nw, err := n.backend.GetNetwork(create.Name, daemon.NetworkByName)
84
+	nw, err := n.backend.GetNetworkByName(create.Name)
85 85
 	if _, ok := err.(libnetwork.ErrNoSuchNetwork); err != nil && !ok {
86 86
 		return err
87 87
 	}
... ...
@@ -2,8 +2,9 @@ package router
2 2
 
3 3
 import "github.com/docker/docker/api/server/httputils"
4 4
 
5
-// Router defines an interface to specify a group of routes to add the the docker server.
5
+// Router defines an interface to specify a group of routes to add to the docker server.
6 6
 type Router interface {
7
+	// Routes returns the list of routes to add to the docker server.
7 8
 	Routes() []Route
8 9
 }
9 10
 
... ...
@@ -1,37 +1,33 @@
1 1
 package system
2 2
 
3
-import (
4
-	"github.com/docker/docker/api/server/router"
5
-	"github.com/docker/docker/api/server/router/local"
6
-)
3
+import "github.com/docker/docker/api/server/router"
7 4
 
8
-// systemRouter is a Router that provides information about
9
-// the Docker system overall. It gathers information about
10
-// host, daemon and container events.
5
+// systemRouter provides information about the Docker system overall.
6
+// It gathers information about host, daemon and container events.
11 7
 type systemRouter struct {
12 8
 	backend Backend
13 9
 	routes  []router.Route
14 10
 }
15 11
 
16
-// NewRouter initializes a new systemRouter
12
+// NewRouter initializes a new system router
17 13
 func NewRouter(b Backend) router.Router {
18 14
 	r := &systemRouter{
19 15
 		backend: b,
20 16
 	}
21 17
 
22 18
 	r.routes = []router.Route{
23
-		local.NewOptionsRoute("/{anyroute:.*}", optionsHandler),
24
-		local.NewGetRoute("/_ping", pingHandler),
25
-		local.NewGetRoute("/events", r.getEvents),
26
-		local.NewGetRoute("/info", r.getInfo),
27
-		local.NewGetRoute("/version", r.getVersion),
28
-		local.NewPostRoute("/auth", r.postAuth),
19
+		router.NewOptionsRoute("/{anyroute:.*}", optionsHandler),
20
+		router.NewGetRoute("/_ping", pingHandler),
21
+		router.NewGetRoute("/events", r.getEvents),
22
+		router.NewGetRoute("/info", r.getInfo),
23
+		router.NewGetRoute("/version", r.getVersion),
24
+		router.NewPostRoute("/auth", r.postAuth),
29 25
 	}
30 26
 
31 27
 	return r
32 28
 }
33 29
 
34
-// Routes return all the API routes dedicated to the docker system.
30
+// Routes returns all the API routes dedicated to the docker system
35 31
 func (s *systemRouter) Routes() []router.Route {
36 32
 	return s.routes
37 33
 }
... ...
@@ -1,9 +1,6 @@
1 1
 package volume
2 2
 
3
-import (
4
-	"github.com/docker/docker/api/server/router"
5
-	"github.com/docker/docker/api/server/router/local"
6
-)
3
+import "github.com/docker/docker/api/server/router"
7 4
 
8 5
 // volumeRouter is a router to talk with the volumes controller
9 6
 type volumeRouter struct {
... ...
@@ -11,7 +8,7 @@ type volumeRouter struct {
11 11
 	routes  []router.Route
12 12
 }
13 13
 
14
-// NewRouter initializes a new volumeRouter
14
+// NewRouter initializes a new volume router
15 15
 func NewRouter(b Backend) router.Router {
16 16
 	r := &volumeRouter{
17 17
 		backend: b,
... ...
@@ -20,7 +17,7 @@ func NewRouter(b Backend) router.Router {
20 20
 	return r
21 21
 }
22 22
 
23
-//Routes returns the available routers to the volumes controller
23
+// Routes returns the available routes to the volumes controller
24 24
 func (r *volumeRouter) Routes() []router.Route {
25 25
 	return r.routes
26 26
 }
... ...
@@ -28,11 +25,11 @@ func (r *volumeRouter) Routes() []router.Route {
28 28
 func (r *volumeRouter) initRoutes() {
29 29
 	r.routes = []router.Route{
30 30
 		// GET
31
-		local.NewGetRoute("/volumes", r.getVolumesList),
32
-		local.NewGetRoute("/volumes/{name:.*}", r.getVolumeByName),
31
+		router.NewGetRoute("/volumes", r.getVolumesList),
32
+		router.NewGetRoute("/volumes/{name:.*}", r.getVolumeByName),
33 33
 		// POST
34
-		local.NewPostRoute("/volumes/create", r.postVolumesCreate),
34
+		router.NewPostRoute("/volumes/create", r.postVolumesCreate),
35 35
 		// DELETE
36
-		local.NewDeleteRoute("/volumes/{name:.*}", r.deleteVolumes),
36
+		router.NewDeleteRoute("/volumes/{name:.*}", r.deleteVolumes),
37 37
 	}
38 38
 }
... ...
@@ -11,7 +11,7 @@ import (
11 11
 	"github.com/docker/docker/api/server/router"
12 12
 	"github.com/docker/docker/api/server/router/build"
13 13
 	"github.com/docker/docker/api/server/router/container"
14
-	"github.com/docker/docker/api/server/router/local"
14
+	"github.com/docker/docker/api/server/router/image"
15 15
 	"github.com/docker/docker/api/server/router/network"
16 16
 	"github.com/docker/docker/api/server/router/system"
17 17
 	"github.com/docker/docker/api/server/router/volume"
... ...
@@ -177,7 +177,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
177 177
 // InitRouters initializes a list of routers for the server.
178 178
 func (s *Server) InitRouters(d *daemon.Daemon) {
179 179
 	s.addRouter(container.NewRouter(d))
180
-	s.addRouter(local.NewRouter(d))
180
+	s.addRouter(image.NewRouter(d))
181 181
 	s.addRouter(network.NewRouter(d))
182 182
 	s.addRouter(system.NewRouter(d))
183 183
 	s.addRouter(volume.NewRouter(d))
... ...
@@ -3,7 +3,6 @@ package daemon
3 3
 import (
4 4
 	"fmt"
5 5
 	"io"
6
-	"net/http"
7 6
 	"time"
8 7
 
9 8
 	"github.com/Sirupsen/logrus"
... ...
@@ -11,22 +10,11 @@ import (
11 11
 	"github.com/docker/docker/daemon/logger"
12 12
 	derr "github.com/docker/docker/errors"
13 13
 	"github.com/docker/docker/pkg/stdcopy"
14
+	"github.com/docker/engine-api/types/backend"
14 15
 )
15 16
 
16
-// ContainerAttachWithLogsConfig holds the streams to use when connecting to a container to view logs.
17
-type ContainerAttachWithLogsConfig struct {
18
-	Hijacker   http.Hijacker
19
-	Upgrade    bool
20
-	UseStdin   bool
21
-	UseStdout  bool
22
-	UseStderr  bool
23
-	Logs       bool
24
-	Stream     bool
25
-	DetachKeys []byte
26
-}
27
-
28 17
 // ContainerAttachWithLogs attaches to logs according to the config passed in. See ContainerAttachWithLogsConfig.
29
-func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *ContainerAttachWithLogsConfig) error {
18
+func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *backend.ContainerAttachWithLogsConfig) error {
30 19
 	if c.Hijacker == nil {
31 20
 		return derr.ErrorCodeNoHijackConnection.WithArgs(prefixOrName)
32 21
 	}
... ...
@@ -82,17 +70,8 @@ func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *ContainerA
82 82
 	return nil
83 83
 }
84 84
 
85
-// ContainerWsAttachWithLogsConfig attach with websockets, since all
86
-// stream data is delegated to the websocket to handle there.
87
-type ContainerWsAttachWithLogsConfig struct {
88
-	InStream             io.ReadCloser
89
-	OutStream, ErrStream io.Writer
90
-	Logs, Stream         bool
91
-	DetachKeys           []byte
92
-}
93
-
94 85
 // ContainerWsAttachWithLogs websocket connection
95
-func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *ContainerWsAttachWithLogsConfig) error {
86
+func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *backend.ContainerWsAttachWithLogsConfig) error {
96 87
 	container, err := daemon.GetContainer(prefixOrName)
97 88
 	if err != nil {
98 89
 		return err
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"github.com/docker/docker/container"
11 11
 	"github.com/docker/docker/image"
12 12
 	"github.com/docker/engine-api/types"
13
+	"github.com/docker/engine-api/types/backend"
13 14
 	"github.com/docker/engine-api/types/filters"
14 15
 	networktypes "github.com/docker/engine-api/types/network"
15 16
 	"github.com/docker/go-connections/nat"
... ...
@@ -43,24 +44,8 @@ func (daemon *Daemon) List() []*container.Container {
43 43
 	return daemon.containers.List()
44 44
 }
45 45
 
46
-// ContainersConfig is the filtering specified by the user to iterate over containers.
47
-type ContainersConfig struct {
48
-	// if true show all containers, otherwise only running containers.
49
-	All bool
50
-	// show all containers created after this container id
51
-	Since string
52
-	// show all containers created before this container id
53
-	Before string
54
-	// number of containers to return at most
55
-	Limit int
56
-	// if true include the sizes of the containers
57
-	Size bool
58
-	// return only containers that match filters
59
-	Filters string
60
-}
61
-
62 46
 // listContext is the daemon generated filtering to iterate over containers.
63
-// This is created based on the user specification.
47
+// This is created based on the user specification from backend.ContainersConfig.
64 48
 type listContext struct {
65 49
 	// idx is the container iteration index for this context
66 50
 	idx int
... ...
@@ -81,16 +66,16 @@ type listContext struct {
81 81
 	// this is used for --filter=since= and --since=, the latter is deprecated.
82 82
 	sinceFilter *container.Container
83 83
 	// ContainersConfig is the filters set by the user
84
-	*ContainersConfig
84
+	*backend.ContainersConfig
85 85
 }
86 86
 
87 87
 // Containers returns the list of containers to show given the user's filtering.
88
-func (daemon *Daemon) Containers(config *ContainersConfig) ([]*types.Container, error) {
88
+func (daemon *Daemon) Containers(config *backend.ContainersConfig) ([]*types.Container, error) {
89 89
 	return daemon.reduceContainers(config, daemon.transformContainer)
90 90
 }
91 91
 
92
-// reduceContainer parses the user filtering and generates the list of containers to return based on a reducer.
93
-func (daemon *Daemon) reduceContainers(config *ContainersConfig, reducer containerReducer) ([]*types.Container, error) {
92
+// reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
93
+func (daemon *Daemon) reduceContainers(config *backend.ContainersConfig, reducer containerReducer) ([]*types.Container, error) {
94 94
 	containers := []*types.Container{}
95 95
 
96 96
 	ctx, err := daemon.foldFilter(config)
... ...
@@ -132,8 +117,8 @@ func (daemon *Daemon) reducePsContainer(container *container.Container, ctx *lis
132 132
 	return reducer(container, ctx)
133 133
 }
134 134
 
135
-// foldFilter generates the container filter based in the user's filtering options.
136
-func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error) {
135
+// foldFilter generates the container filter based on the user's filtering options.
136
+func (daemon *Daemon) foldFilter(config *backend.ContainersConfig) (*listContext, error) {
137 137
 	psFilters, err := filters.FromParam(config.Filters)
138 138
 	if err != nil {
139 139
 		return nil, err
... ...
@@ -3,7 +3,6 @@ package daemon
3 3
 import (
4 4
 	"io"
5 5
 	"strconv"
6
-	"time"
7 6
 
8 7
 	"github.com/Sirupsen/logrus"
9 8
 	"github.com/docker/docker/container"
... ...
@@ -11,28 +10,12 @@ import (
11 11
 	"github.com/docker/docker/daemon/logger/jsonfilelog"
12 12
 	derr "github.com/docker/docker/errors"
13 13
 	"github.com/docker/docker/pkg/stdcopy"
14
+	"github.com/docker/engine-api/types/backend"
14 15
 )
15 16
 
16
-// ContainerLogsConfig holds configs for logging operations. Exists
17
-// for users of the daemon to to pass it a logging configuration.
18
-type ContainerLogsConfig struct {
19
-	// if true stream log output
20
-	Follow bool
21
-	// if true include timestamps for each line of log output
22
-	Timestamps bool
23
-	// return that many lines of log output from the end
24
-	Tail string
25
-	// filter logs by returning on those entries after this time
26
-	Since time.Time
27
-	// whether or not to show stdout and stderr as well as log entries.
28
-	UseStdout, UseStderr bool
29
-	OutStream            io.Writer
30
-	Stop                 <-chan bool
31
-}
32
-
33 17
 // ContainerLogs hooks up a container's stdout and stderr streams
34 18
 // configured with the given struct.
35
-func (daemon *Daemon) ContainerLogs(containerName string, config *ContainerLogsConfig) error {
19
+func (daemon *Daemon) ContainerLogs(containerName string, config *backend.ContainerLogsConfig) error {
36 20
 	container, err := daemon.GetContainer(containerName)
37 21
 	if err != nil {
38 22
 		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
... ...
@@ -1,7 +1,6 @@
1 1
 package daemon
2 2
 
3 3
 import (
4
-	"errors"
5 4
 	"fmt"
6 5
 	"net"
7 6
 	"strings"
... ...
@@ -12,13 +11,6 @@ import (
12 12
 	"github.com/docker/libnetwork"
13 13
 )
14 14
 
15
-const (
16
-	// NetworkByID represents a constant to find a network by its ID
17
-	NetworkByID = iota + 1
18
-	// NetworkByName represents a constant to find a network by its Name
19
-	NetworkByName
20
-)
21
-
22 15
 // NetworkControllerEnabled checks if the networking stack is enabled.
23 16
 // This feature depends on OS primitives and it's disabled in systems like Windows.
24 17
 func (daemon *Daemon) NetworkControllerEnabled() bool {
... ...
@@ -28,8 +20,8 @@ func (daemon *Daemon) NetworkControllerEnabled() bool {
28 28
 // FindNetwork function finds a network for a given string that can represent network name or id
29 29
 func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) {
30 30
 	// Find by Name
31
-	n, err := daemon.GetNetwork(idName, NetworkByName)
32
-	if _, ok := err.(libnetwork.ErrNoSuchNetwork); err != nil && !ok {
31
+	n, err := daemon.GetNetworkByName(idName)
32
+	if err != nil && !isNoSuchNetworkError(err) {
33 33
 		return nil, err
34 34
 	}
35 35
 
... ...
@@ -38,38 +30,35 @@ func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) {
38 38
 	}
39 39
 
40 40
 	// Find by id
41
-	n, err = daemon.GetNetwork(idName, NetworkByID)
42
-	if err != nil {
43
-		return nil, err
44
-	}
45
-
46
-	return n, nil
41
+	return daemon.GetNetworkByID(idName)
47 42
 }
48 43
 
49
-// GetNetwork function returns a network for a given string that represents the network and
50
-// a hint to indicate if the string is an Id or Name of the network
51
-func (daemon *Daemon) GetNetwork(idName string, by int) (libnetwork.Network, error) {
52
-	c := daemon.netController
53
-	switch by {
54
-	case NetworkByID:
55
-		list := daemon.GetNetworksByID(idName)
44
+func isNoSuchNetworkError(err error) bool {
45
+	_, ok := err.(libnetwork.ErrNoSuchNetwork)
46
+	return ok
47
+}
56 48
 
57
-		if len(list) == 0 {
58
-			return nil, libnetwork.ErrNoSuchNetwork(idName)
59
-		}
49
+// GetNetworkByID function returns a network whose ID begins with the given prefix.
50
+// It fails with an error if no matching, or more than one matching, networks are found.
51
+func (daemon *Daemon) GetNetworkByID(partialID string) (libnetwork.Network, error) {
52
+	list := daemon.GetNetworksByID(partialID)
60 53
 
61
-		if len(list) > 1 {
62
-			return nil, libnetwork.ErrInvalidID(idName)
63
-		}
54
+	if len(list) == 0 {
55
+		return nil, libnetwork.ErrNoSuchNetwork(partialID)
56
+	}
57
+	if len(list) > 1 {
58
+		return nil, libnetwork.ErrInvalidID(partialID)
59
+	}
60
+	return list[0], nil
61
+}
64 62
 
65
-		return list[0], nil
66
-	case NetworkByName:
67
-		if idName == "" {
68
-			idName = c.Config().Daemon.DefaultNetwork
69
-		}
70
-		return c.NetworkByName(idName)
63
+// GetNetworkByName function returns a network for a given network name.
64
+func (daemon *Daemon) GetNetworkByName(name string) (libnetwork.Network, error) {
65
+	c := daemon.netController
66
+	if name == "" {
67
+		name = c.Config().Daemon.DefaultNetwork
71 68
 	}
72
-	return nil, errors.New("unexpected selector for GetNetwork")
69
+	return c.NetworkByName(name)
73 70
 }
74 71
 
75 72
 // GetNetworksByID returns a list of networks whose ID partially matches zero or more networks
... ...
@@ -3,30 +3,23 @@ package daemon
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"errors"
6
-	"io"
7 6
 	"runtime"
8 7
 
9 8
 	"github.com/docker/docker/daemon/execdriver"
10 9
 	"github.com/docker/docker/pkg/version"
11 10
 	"github.com/docker/engine-api/types"
11
+	"github.com/docker/engine-api/types/backend"
12 12
 	"github.com/docker/engine-api/types/versions/v1p20"
13 13
 )
14 14
 
15
-// ContainerStatsConfig holds information for configuring the runtime
16
-// behavior of a daemon.ContainerStats() call.
17
-type ContainerStatsConfig struct {
18
-	Stream    bool
19
-	OutStream io.Writer
20
-	Stop      <-chan bool
21
-	Version   version.Version
22
-}
23
-
24 15
 // ContainerStats writes information about the container to the stream
25 16
 // given in the config object.
26
-func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStatsConfig) error {
17
+func (daemon *Daemon) ContainerStats(prefixOrName string, config *backend.ContainerStatsConfig) error {
27 18
 	if runtime.GOOS == "windows" {
28 19
 		return errors.New("Windows does not support stats")
29 20
 	}
21
+	// Remote API version (used for backwards compatibility)
22
+	apiVersion := version.Version(config.Version)
30 23
 
31 24
 	container, err := daemon.GetContainer(prefixOrName)
32 25
 	if err != nil {
... ...
@@ -72,7 +65,7 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStats
72 72
 
73 73
 			var statsJSON interface{}
74 74
 			statsJSONPost120 := getStatJSON(v)
75
-			if config.Version.LessThan("1.21") {
75
+			if apiVersion.LessThan("1.21") {
76 76
 				var (
77 77
 					rxBytes   uint64
78 78
 					rxPackets uint64