Browse code

Add container package for container APIs.

Signed-off-by: Dong Chen <dongluo.chen@docker.com>

Dong Chen authored on 2015/11/05 10:38:05
Showing 9 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,56 @@
0
+// +build !windows
1
+
2
+package container
3
+
4
+import (
5
+	"io"
6
+	"time"
7
+
8
+	"github.com/docker/docker/api/types"
9
+	"github.com/docker/docker/api/types/versions/v1p19"
10
+	"github.com/docker/docker/api/types/versions/v1p20"
11
+	"github.com/docker/docker/daemon"
12
+	"github.com/docker/docker/pkg/archive"
13
+	"github.com/docker/docker/runconfig"
14
+)
15
+
16
+// Backend is all the methods that need to be implemented to provide
17
+// container specific functionality
18
+type Backend interface {
19
+	ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error)
20
+	ContainerAttachWithLogs(prefixOrName string, c *daemon.ContainerAttachWithLogsConfig) error
21
+	ContainerChanges(name string) ([]archive.Change, error)
22
+	ContainerCopy(name string, res string) (io.ReadCloser, error)
23
+	ContainerCreate(params *daemon.ContainerCreateConfig) (types.ContainerCreateResponse, error)
24
+	ContainerExecCreate(config *runconfig.ExecConfig) (string, error)
25
+	ContainerExecInspect(id string) (*daemon.ExecConfig, error)
26
+	ContainerExecResize(name string, height, width int) error
27
+	ContainerExecStart(name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error
28
+	ContainerExport(name string, out io.Writer) error
29
+	ContainerExtractToDir(name, path string, noOverwriteDirNonDir bool, content io.Reader) error
30
+	ContainerInspect(name string, size bool) (*types.ContainerJSON, error)
31
+	ContainerInspect120(name string) (*v1p20.ContainerJSON, error)
32
+	// unix version
33
+	ContainerInspectPre120(name string) (*v1p19.ContainerJSON, error)
34
+	// windows version
35
+	//ContainerInspectPre120(name string) (*types.ContainerJSON, error)
36
+	ContainerKill(name string, sig uint64) error
37
+	ContainerLogs(containerName string, config *daemon.ContainerLogsConfig) error
38
+	ContainerPause(name string) error
39
+	ContainerRename(oldName, newName string) error
40
+	ContainerResize(name string, height, width int) error
41
+	ContainerRestart(name string, seconds int) error
42
+	ContainerRm(name string, config *daemon.ContainerRmConfig) error
43
+	Containers(config *daemon.ContainersConfig) ([]*types.Container, error)
44
+	ContainerStart(name string, hostConfig *runconfig.HostConfig) error
45
+	ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error)
46
+	ContainerStats(prefixOrName string, config *daemon.ContainerStatsConfig) error
47
+	ContainerStop(name string, seconds int) error
48
+	ContainerTop(name string, psArgs string) (*types.ContainerProcessList, error)
49
+	ContainerUnpause(name string) error
50
+	ContainerWait(name string, timeout time.Duration) (int, error)
51
+	ContainerWsAttachWithLogs(prefixOrName string, c *daemon.ContainerWsAttachWithLogsConfig) error
52
+	ExecExists(name string) (bool, error)
53
+	Exists(id string) bool
54
+	IsPaused(id string) bool
55
+}
0 56
new file mode 100644
... ...
@@ -0,0 +1,55 @@
0
+// +build windows
1
+
2
+package container
3
+
4
+import (
5
+	"io"
6
+	"time"
7
+
8
+	"github.com/docker/docker/api/types"
9
+	"github.com/docker/docker/api/types/versions/v1p20"
10
+	"github.com/docker/docker/daemon"
11
+	"github.com/docker/docker/pkg/archive"
12
+	"github.com/docker/docker/runconfig"
13
+)
14
+
15
+// Backend is all the methods that need to be implemented to provide
16
+// container specific functionality
17
+type Backend interface {
18
+	ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error)
19
+	ContainerAttachWithLogs(prefixOrName string, c *daemon.ContainerAttachWithLogsConfig) error
20
+	ContainerChanges(name string) ([]archive.Change, error)
21
+	ContainerCopy(name string, res string) (io.ReadCloser, error)
22
+	ContainerCreate(params *daemon.ContainerCreateConfig) (types.ContainerCreateResponse, error)
23
+	ContainerExecCreate(config *runconfig.ExecConfig) (string, error)
24
+	ContainerExecInspect(id string) (*daemon.ExecConfig, error)
25
+	ContainerExecResize(name string, height, width int) error
26
+	ContainerExecStart(name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error
27
+	ContainerExport(name string, out io.Writer) error
28
+	ContainerExtractToDir(name, path string, noOverwriteDirNonDir bool, content io.Reader) error
29
+	ContainerInspect(name string, size bool) (*types.ContainerJSON, error)
30
+	ContainerInspect120(name string) (*v1p20.ContainerJSON, error)
31
+	// unix version
32
+	//ContainerInspectPre120(name string) (*v1p19.ContainerJSON, error)
33
+	// windows version
34
+	ContainerInspectPre120(name string) (*types.ContainerJSON, error)
35
+	ContainerKill(name string, sig uint64) error
36
+	ContainerLogs(containerName string, config *daemon.ContainerLogsConfig) error
37
+	ContainerPause(name string) error
38
+	ContainerRename(oldName, newName string) error
39
+	ContainerResize(name string, height, width int) error
40
+	ContainerRestart(name string, seconds int) error
41
+	ContainerRm(name string, config *daemon.ContainerRmConfig) error
42
+	Containers(config *daemon.ContainersConfig) ([]*types.Container, error)
43
+	ContainerStart(name string, hostConfig *runconfig.HostConfig) error
44
+	ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error)
45
+	ContainerStats(prefixOrName string, config *daemon.ContainerStatsConfig) error
46
+	ContainerStop(name string, seconds int) error
47
+	ContainerTop(name string, psArgs string) (*types.ContainerProcessList, error)
48
+	ContainerUnpause(name string) error
49
+	ContainerWait(name string, timeout time.Duration) (int, error)
50
+	ContainerWsAttachWithLogs(prefixOrName string, c *daemon.ContainerWsAttachWithLogsConfig) error
51
+	ExecExists(name string) (bool, error)
52
+	Exists(id string) bool
53
+	IsPaused(id string) bool
54
+}
0 55
new file mode 100644
... ...
@@ -0,0 +1,65 @@
0
+package container
1
+
2
+import (
3
+	"github.com/docker/docker/api/server/router"
4
+	"github.com/docker/docker/api/server/router/local"
5
+)
6
+
7
+// containerRouter is a router to talk with the container controller
8
+type containerRouter struct {
9
+	backend Backend
10
+	routes  []router.Route
11
+}
12
+
13
+// NewRouter initializes a new container router
14
+func NewRouter(b Backend) router.Router {
15
+	r := &containerRouter{
16
+		backend: b,
17
+	}
18
+	r.initRoutes()
19
+	return r
20
+}
21
+
22
+// Routes returns the available routers to the container controller
23
+func (r *containerRouter) Routes() []router.Route {
24
+	return r.routes
25
+}
26
+
27
+// initRoutes initializes the routes in container router
28
+func (r *containerRouter) initRoutes() {
29
+	r.routes = []router.Route{
30
+		// HEAD
31
+		local.NewHeadRoute("/containers/{name:.*}/archive", r.headContainersArchive),
32
+		// GET
33
+		local.NewGetRoute("/containers/json", r.getContainersJSON),
34
+		local.NewGetRoute("/containers/{name:.*}/export", r.getContainersExport),
35
+		local.NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges),
36
+		local.NewGetRoute("/containers/{name:.*}/json", r.getContainersByName),
37
+		local.NewGetRoute("/containers/{name:.*}/top", r.getContainersTop),
38
+		local.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs),
39
+		local.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats),
40
+		local.NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach),
41
+		local.NewGetRoute("/exec/{id:.*}/json", r.getExecByID),
42
+		local.NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive),
43
+		// POST
44
+		local.NewPostRoute("/containers/create", r.postContainersCreate),
45
+		local.NewPostRoute("/containers/{name:.*}/kill", r.postContainersKill),
46
+		local.NewPostRoute("/containers/{name:.*}/pause", r.postContainersPause),
47
+		local.NewPostRoute("/containers/{name:.*}/unpause", r.postContainersUnpause),
48
+		local.NewPostRoute("/containers/{name:.*}/restart", r.postContainersRestart),
49
+		local.NewPostRoute("/containers/{name:.*}/start", r.postContainersStart),
50
+		local.NewPostRoute("/containers/{name:.*}/stop", r.postContainersStop),
51
+		local.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
52
+		local.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
53
+		local.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
54
+		local.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy),
55
+		local.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
56
+		local.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
57
+		local.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
58
+		local.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
59
+		// PUT
60
+		local.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
61
+		// DELETE
62
+		local.NewDeleteRoute("/containers/{name:.*}", r.deleteContainers),
63
+	}
64
+}
... ...
@@ -1,4 +1,4 @@
1
-package local
1
+package container
2 2
 
3 3
 import (
4 4
 	"fmt"
... ...
@@ -24,7 +24,7 @@ import (
24 24
 	"golang.org/x/net/websocket"
25 25
 )
26 26
 
27
-func (s *router) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
27
+func (s *containerRouter) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
28 28
 	if err := httputils.ParseForm(r); err != nil {
29 29
 		return err
30 30
 	}
... ...
@@ -45,7 +45,7 @@ func (s *router) getContainersJSON(ctx context.Context, w http.ResponseWriter, r
45 45
 		config.Limit = limit
46 46
 	}
47 47
 
48
-	containers, err := s.daemon.Containers(config)
48
+	containers, err := s.backend.Containers(config)
49 49
 	if err != nil {
50 50
 		return err
51 51
 	}
... ...
@@ -53,7 +53,7 @@ func (s *router) getContainersJSON(ctx context.Context, w http.ResponseWriter, r
53 53
 	return httputils.WriteJSON(w, http.StatusOK, containers)
54 54
 }
55 55
 
56
-func (s *router) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
56
+func (s *containerRouter) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
57 57
 	if err := httputils.ParseForm(r); err != nil {
58 58
 		return err
59 59
 	}
... ...
@@ -81,10 +81,10 @@ func (s *router) getContainersStats(ctx context.Context, w http.ResponseWriter,
81 81
 		Version:   httputils.VersionFromContext(ctx),
82 82
 	}
83 83
 
84
-	return s.daemon.ContainerStats(vars["name"], config)
84
+	return s.backend.ContainerStats(vars["name"], config)
85 85
 }
86 86
 
87
-func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
87
+func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
88 88
 	if err := httputils.ParseForm(r); err != nil {
89 89
 		return err
90 90
 	}
... ...
@@ -115,7 +115,7 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
115 115
 
116 116
 	containerName := vars["name"]
117 117
 
118
-	if !s.daemon.Exists(containerName) {
118
+	if !s.backend.Exists(containerName) {
119 119
 		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
120 120
 	}
121 121
 
... ...
@@ -141,7 +141,7 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
141 141
 		Stop:       closeNotifier,
142 142
 	}
143 143
 
144
-	if err := s.daemon.ContainerLogs(containerName, logsConfig); err != nil {
144
+	if err := s.backend.ContainerLogs(containerName, logsConfig); err != nil {
145 145
 		// The client may be expecting all of the data we're sending to
146 146
 		// be multiplexed, so send it through OutStream, which will
147 147
 		// have been set up to handle that if needed.
... ...
@@ -151,11 +151,11 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
151 151
 	return nil
152 152
 }
153 153
 
154
-func (s *router) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
155
-	return s.daemon.ContainerExport(vars["name"], w)
154
+func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
155
+	return s.backend.ContainerExport(vars["name"], w)
156 156
 }
157 157
 
158
-func (s *router) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
158
+func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
159 159
 	// If contentLength is -1, we can assumed chunked encoding
160 160
 	// or more technically that the length is unknown
161 161
 	// https://golang.org/src/pkg/net/http/request.go#L139
... ...
@@ -176,21 +176,21 @@ func (s *router) postContainersStart(ctx context.Context, w http.ResponseWriter,
176 176
 		hostConfig = c
177 177
 	}
178 178
 
179
-	if err := s.daemon.ContainerStart(vars["name"], hostConfig); err != nil {
179
+	if err := s.backend.ContainerStart(vars["name"], hostConfig); err != nil {
180 180
 		return err
181 181
 	}
182 182
 	w.WriteHeader(http.StatusNoContent)
183 183
 	return nil
184 184
 }
185 185
 
186
-func (s *router) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
186
+func (s *containerRouter) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
187 187
 	if err := httputils.ParseForm(r); err != nil {
188 188
 		return err
189 189
 	}
190 190
 
191 191
 	seconds, _ := strconv.Atoi(r.Form.Get("t"))
192 192
 
193
-	if err := s.daemon.ContainerStop(vars["name"], seconds); err != nil {
193
+	if err := s.backend.ContainerStop(vars["name"], seconds); err != nil {
194 194
 		return err
195 195
 	}
196 196
 	w.WriteHeader(http.StatusNoContent)
... ...
@@ -198,7 +198,7 @@ func (s *router) postContainersStop(ctx context.Context, w http.ResponseWriter,
198 198
 	return nil
199 199
 }
200 200
 
201
-func (s *router) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
201
+func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
202 202
 	if err := httputils.ParseForm(r); err != nil {
203 203
 		return err
204 204
 	}
... ...
@@ -214,7 +214,7 @@ func (s *router) postContainersKill(ctx context.Context, w http.ResponseWriter,
214 214
 		}
215 215
 	}
216 216
 
217
-	if err := s.daemon.ContainerKill(name, uint64(sig)); err != nil {
217
+	if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
218 218
 		theErr, isDerr := err.(errcode.ErrorCoder)
219 219
 		isStopped := isDerr && theErr.ErrorCode() == derr.ErrorCodeNotRunning
220 220
 
... ...
@@ -231,14 +231,14 @@ func (s *router) postContainersKill(ctx context.Context, w http.ResponseWriter,
231 231
 	return nil
232 232
 }
233 233
 
234
-func (s *router) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
234
+func (s *containerRouter) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
235 235
 	if err := httputils.ParseForm(r); err != nil {
236 236
 		return err
237 237
 	}
238 238
 
239 239
 	timeout, _ := strconv.Atoi(r.Form.Get("t"))
240 240
 
241
-	if err := s.daemon.ContainerRestart(vars["name"], timeout); err != nil {
241
+	if err := s.backend.ContainerRestart(vars["name"], timeout); err != nil {
242 242
 		return err
243 243
 	}
244 244
 
... ...
@@ -247,12 +247,12 @@ func (s *router) postContainersRestart(ctx context.Context, w http.ResponseWrite
247 247
 	return nil
248 248
 }
249 249
 
250
-func (s *router) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
250
+func (s *containerRouter) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
251 251
 	if err := httputils.ParseForm(r); err != nil {
252 252
 		return err
253 253
 	}
254 254
 
255
-	if err := s.daemon.ContainerPause(vars["name"]); err != nil {
255
+	if err := s.backend.ContainerPause(vars["name"]); err != nil {
256 256
 		return err
257 257
 	}
258 258
 
... ...
@@ -261,12 +261,12 @@ func (s *router) postContainersPause(ctx context.Context, w http.ResponseWriter,
261 261
 	return nil
262 262
 }
263 263
 
264
-func (s *router) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
264
+func (s *containerRouter) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
265 265
 	if err := httputils.ParseForm(r); err != nil {
266 266
 		return err
267 267
 	}
268 268
 
269
-	if err := s.daemon.ContainerUnpause(vars["name"]); err != nil {
269
+	if err := s.backend.ContainerUnpause(vars["name"]); err != nil {
270 270
 		return err
271 271
 	}
272 272
 
... ...
@@ -275,8 +275,8 @@ func (s *router) postContainersUnpause(ctx context.Context, w http.ResponseWrite
275 275
 	return nil
276 276
 }
277 277
 
278
-func (s *router) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
279
-	status, err := s.daemon.ContainerWait(vars["name"], -1*time.Second)
278
+func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
279
+	status, err := s.backend.ContainerWait(vars["name"], -1*time.Second)
280 280
 	if err != nil {
281 281
 		return err
282 282
 	}
... ...
@@ -286,8 +286,8 @@ func (s *router) postContainersWait(ctx context.Context, w http.ResponseWriter,
286 286
 	})
287 287
 }
288 288
 
289
-func (s *router) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
290
-	changes, err := s.daemon.ContainerChanges(vars["name"])
289
+func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
290
+	changes, err := s.backend.ContainerChanges(vars["name"])
291 291
 	if err != nil {
292 292
 		return err
293 293
 	}
... ...
@@ -295,12 +295,12 @@ func (s *router) getContainersChanges(ctx context.Context, w http.ResponseWriter
295 295
 	return httputils.WriteJSON(w, http.StatusOK, changes)
296 296
 }
297 297
 
298
-func (s *router) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
298
+func (s *containerRouter) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
299 299
 	if err := httputils.ParseForm(r); err != nil {
300 300
 		return err
301 301
 	}
302 302
 
303
-	procList, err := s.daemon.ContainerTop(vars["name"], r.Form.Get("ps_args"))
303
+	procList, err := s.backend.ContainerTop(vars["name"], r.Form.Get("ps_args"))
304 304
 	if err != nil {
305 305
 		return err
306 306
 	}
... ...
@@ -308,21 +308,21 @@ func (s *router) getContainersTop(ctx context.Context, w http.ResponseWriter, r
308 308
 	return httputils.WriteJSON(w, http.StatusOK, procList)
309 309
 }
310 310
 
311
-func (s *router) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
311
+func (s *containerRouter) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
312 312
 	if err := httputils.ParseForm(r); err != nil {
313 313
 		return err
314 314
 	}
315 315
 
316 316
 	name := vars["name"]
317 317
 	newName := r.Form.Get("name")
318
-	if err := s.daemon.ContainerRename(name, newName); err != nil {
318
+	if err := s.backend.ContainerRename(name, newName); err != nil {
319 319
 		return err
320 320
 	}
321 321
 	w.WriteHeader(http.StatusNoContent)
322 322
 	return nil
323 323
 }
324 324
 
325
-func (s *router) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
325
+func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
326 326
 	if err := httputils.ParseForm(r); err != nil {
327 327
 		return err
328 328
 	}
... ...
@@ -339,7 +339,7 @@ func (s *router) postContainersCreate(ctx context.Context, w http.ResponseWriter
339 339
 	version := httputils.VersionFromContext(ctx)
340 340
 	adjustCPUShares := version.LessThan("1.19")
341 341
 
342
-	ccr, err := s.daemon.ContainerCreate(&daemon.ContainerCreateConfig{
342
+	ccr, err := s.backend.ContainerCreate(&daemon.ContainerCreateConfig{
343 343
 		Name:            name,
344 344
 		Config:          config,
345 345
 		HostConfig:      hostConfig,
... ...
@@ -352,7 +352,7 @@ func (s *router) postContainersCreate(ctx context.Context, w http.ResponseWriter
352 352
 	return httputils.WriteJSON(w, http.StatusCreated, ccr)
353 353
 }
354 354
 
355
-func (s *router) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
355
+func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
356 356
 	if err := httputils.ParseForm(r); err != nil {
357 357
 		return err
358 358
 	}
... ...
@@ -364,7 +364,7 @@ func (s *router) deleteContainers(ctx context.Context, w http.ResponseWriter, r
364 364
 		RemoveLink:   httputils.BoolValue(r, "link"),
365 365
 	}
366 366
 
367
-	if err := s.daemon.ContainerRm(name, config); err != nil {
367
+	if err := s.backend.ContainerRm(name, config); err != nil {
368 368
 		// Force a 404 for the empty string
369 369
 		if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") {
370 370
 			return fmt.Errorf("no such id: \"\"")
... ...
@@ -377,7 +377,7 @@ func (s *router) deleteContainers(ctx context.Context, w http.ResponseWriter, r
377 377
 	return nil
378 378
 }
379 379
 
380
-func (s *router) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
380
+func (s *containerRouter) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
381 381
 	if err := httputils.ParseForm(r); err != nil {
382 382
 		return err
383 383
 	}
... ...
@@ -391,20 +391,20 @@ func (s *router) postContainersResize(ctx context.Context, w http.ResponseWriter
391 391
 		return err
392 392
 	}
393 393
 
394
-	return s.daemon.ContainerResize(vars["name"], height, width)
394
+	return s.backend.ContainerResize(vars["name"], height, width)
395 395
 }
396 396
 
397
-func (s *router) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
397
+func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
398 398
 	if err := httputils.ParseForm(r); err != nil {
399 399
 		return err
400 400
 	}
401 401
 	containerName := vars["name"]
402 402
 
403
-	if !s.daemon.Exists(containerName) {
403
+	if !s.backend.Exists(containerName) {
404 404
 		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
405 405
 	}
406 406
 
407
-	if s.daemon.IsPaused(containerName) {
407
+	if s.backend.IsPaused(containerName) {
408 408
 		return derr.ErrorCodePausedContainer.WithArgs(containerName)
409 409
 	}
410 410
 
... ...
@@ -430,20 +430,20 @@ func (s *router) postContainersAttach(ctx context.Context, w http.ResponseWriter
430 430
 		Stream:    httputils.BoolValue(r, "stream"),
431 431
 	}
432 432
 
433
-	if err := s.daemon.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil {
433
+	if err := s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil {
434 434
 		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
435 435
 	}
436 436
 
437 437
 	return nil
438 438
 }
439 439
 
440
-func (s *router) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
440
+func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
441 441
 	if err := httputils.ParseForm(r); err != nil {
442 442
 		return err
443 443
 	}
444 444
 	containerName := vars["name"]
445 445
 
446
-	if !s.daemon.Exists(containerName) {
446
+	if !s.backend.Exists(containerName) {
447 447
 		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
448 448
 	}
449 449
 
... ...
@@ -458,7 +458,7 @@ func (s *router) wsContainersAttach(ctx context.Context, w http.ResponseWriter,
458 458
 			Stream:    httputils.BoolValue(r, "stream"),
459 459
 		}
460 460
 
461
-		if err := s.daemon.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {
461
+		if err := s.backend.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {
462 462
 			logrus.Errorf("Error attaching websocket: %s", err)
463 463
 		}
464 464
 	})
... ...
@@ -1,4 +1,4 @@
1
-package local
1
+package container
2 2
 
3 3
 import (
4 4
 	"encoding/base64"
... ...
@@ -15,7 +15,7 @@ import (
15 15
 )
16 16
 
17 17
 // postContainersCopy is deprecated in favor of getContainersArchive.
18
-func (s *router) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
18
+func (s *containerRouter) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
19 19
 	if err := httputils.CheckForJSON(r); err != nil {
20 20
 		return err
21 21
 	}
... ...
@@ -29,7 +29,7 @@ func (s *router) postContainersCopy(ctx context.Context, w http.ResponseWriter,
29 29
 		return fmt.Errorf("Path cannot be empty")
30 30
 	}
31 31
 
32
-	data, err := s.daemon.ContainerCopy(vars["name"], cfg.Resource)
32
+	data, err := s.backend.ContainerCopy(vars["name"], cfg.Resource)
33 33
 	if err != nil {
34 34
 		if strings.Contains(strings.ToLower(err.Error()), "no such id") {
35 35
 			w.WriteHeader(http.StatusNotFound)
... ...
@@ -65,13 +65,13 @@ func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Heade
65 65
 	return nil
66 66
 }
67 67
 
68
-func (s *router) headContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
68
+func (s *containerRouter) headContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
69 69
 	v, err := httputils.ArchiveFormValues(r, vars)
70 70
 	if err != nil {
71 71
 		return err
72 72
 	}
73 73
 
74
-	stat, err := s.daemon.ContainerStatPath(v.Name, v.Path)
74
+	stat, err := s.backend.ContainerStatPath(v.Name, v.Path)
75 75
 	if err != nil {
76 76
 		return err
77 77
 	}
... ...
@@ -79,13 +79,13 @@ func (s *router) headContainersArchive(ctx context.Context, w http.ResponseWrite
79 79
 	return setContainerPathStatHeader(stat, w.Header())
80 80
 }
81 81
 
82
-func (s *router) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
82
+func (s *containerRouter) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
83 83
 	v, err := httputils.ArchiveFormValues(r, vars)
84 84
 	if err != nil {
85 85
 		return err
86 86
 	}
87 87
 
88
-	tarArchive, stat, err := s.daemon.ContainerArchivePath(v.Name, v.Path)
88
+	tarArchive, stat, err := s.backend.ContainerArchivePath(v.Name, v.Path)
89 89
 	if err != nil {
90 90
 		return err
91 91
 	}
... ...
@@ -101,12 +101,12 @@ func (s *router) getContainersArchive(ctx context.Context, w http.ResponseWriter
101 101
 	return err
102 102
 }
103 103
 
104
-func (s *router) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
104
+func (s *containerRouter) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
105 105
 	v, err := httputils.ArchiveFormValues(r, vars)
106 106
 	if err != nil {
107 107
 		return err
108 108
 	}
109 109
 
110 110
 	noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir")
111
-	return s.daemon.ContainerExtractToDir(v.Name, v.Path, noOverwriteDirNonDir, r.Body)
111
+	return s.backend.ContainerExtractToDir(v.Name, v.Path, noOverwriteDirNonDir, r.Body)
112 112
 }
... ...
@@ -1,4 +1,4 @@
1
-package local
1
+package container
2 2
 
3 3
 import (
4 4
 	"encoding/json"
... ...
@@ -15,8 +15,8 @@ import (
15 15
 	"golang.org/x/net/context"
16 16
 )
17 17
 
18
-func (s *router) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
19
-	eConfig, err := s.daemon.ContainerExecInspect(vars["id"])
18
+func (s *containerRouter) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
19
+	eConfig, err := s.backend.ContainerExecInspect(vars["id"])
20 20
 	if err != nil {
21 21
 		return err
22 22
 	}
... ...
@@ -24,7 +24,7 @@ func (s *router) getExecByID(ctx context.Context, w http.ResponseWriter, r *http
24 24
 	return httputils.WriteJSON(w, http.StatusOK, eConfig)
25 25
 }
26 26
 
27
-func (s *router) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
27
+func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
28 28
 	if err := httputils.ParseForm(r); err != nil {
29 29
 		return err
30 30
 	}
... ...
@@ -44,7 +44,7 @@ func (s *router) postContainerExecCreate(ctx context.Context, w http.ResponseWri
44 44
 	}
45 45
 
46 46
 	// Register an instance of Exec in container.
47
-	id, err := s.daemon.ContainerExecCreate(execConfig)
47
+	id, err := s.backend.ContainerExecCreate(execConfig)
48 48
 	if err != nil {
49 49
 		logrus.Errorf("Error setting up exec command in container %s: %s", name, err)
50 50
 		return err
... ...
@@ -56,7 +56,7 @@ func (s *router) postContainerExecCreate(ctx context.Context, w http.ResponseWri
56 56
 }
57 57
 
58 58
 // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
59
-func (s *router) postContainerExecStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
59
+func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
60 60
 	if err := httputils.ParseForm(r); err != nil {
61 61
 		return err
62 62
 	}
... ...
@@ -79,7 +79,7 @@ func (s *router) postContainerExecStart(ctx context.Context, w http.ResponseWrit
79 79
 		return err
80 80
 	}
81 81
 
82
-	if exists, err := s.daemon.ExecExists(execName); !exists {
82
+	if exists, err := s.backend.ExecExists(execName); !exists {
83 83
 		return err
84 84
 	}
85 85
 
... ...
@@ -109,7 +109,7 @@ func (s *router) postContainerExecStart(ctx context.Context, w http.ResponseWrit
109 109
 	}
110 110
 
111 111
 	// Now run the user process in container.
112
-	if err := s.daemon.ContainerExecStart(execName, stdin, stdout, stderr); err != nil {
112
+	if err := s.backend.ContainerExecStart(execName, stdin, stdout, stderr); err != nil {
113 113
 		if execStartCheck.Detach {
114 114
 			return err
115 115
 		}
... ...
@@ -118,7 +118,7 @@ func (s *router) postContainerExecStart(ctx context.Context, w http.ResponseWrit
118 118
 	return nil
119 119
 }
120 120
 
121
-func (s *router) postContainerExecResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
121
+func (s *containerRouter) postContainerExecResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
122 122
 	if err := httputils.ParseForm(r); err != nil {
123 123
 		return err
124 124
 	}
... ...
@@ -131,5 +131,5 @@ func (s *router) postContainerExecResize(ctx context.Context, w http.ResponseWri
131 131
 		return err
132 132
 	}
133 133
 
134
-	return s.daemon.ContainerExecResize(vars["name"], height, width)
134
+	return s.backend.ContainerExecResize(vars["name"], height, width)
135 135
 }
... ...
@@ -1,4 +1,4 @@
1
-package local
1
+package container
2 2
 
3 3
 import (
4 4
 	"net/http"
... ...
@@ -8,7 +8,7 @@ import (
8 8
 )
9 9
 
10 10
 // getContainersByName inspects containers configuration and serializes it as json.
11
-func (s *router) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
11
+func (s *containerRouter) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
12 12
 	displaySize := httputils.BoolValue(r, "size")
13 13
 
14 14
 	var json interface{}
... ...
@@ -18,11 +18,11 @@ func (s *router) getContainersByName(ctx context.Context, w http.ResponseWriter,
18 18
 
19 19
 	switch {
20 20
 	case version.LessThan("1.20"):
21
-		json, err = s.daemon.ContainerInspectPre120(vars["name"])
21
+		json, err = s.backend.ContainerInspectPre120(vars["name"])
22 22
 	case version.Equal("1.20"):
23
-		json, err = s.daemon.ContainerInspect120(vars["name"])
23
+		json, err = s.backend.ContainerInspect120(vars["name"])
24 24
 	default:
25
-		json, err = s.daemon.ContainerInspect(vars["name"], displaySize)
25
+		json, err = s.backend.ContainerInspect(vars["name"], displaySize)
26 26
 	}
27 27
 
28 28
 	if err != nil {
... ...
@@ -91,8 +91,6 @@ func (r *router) Routes() []dkrouter.Route {
91 91
 // initRoutes initializes the routes in this router
92 92
 func (r *router) initRoutes() {
93 93
 	r.routes = []dkrouter.Route{
94
-		// HEAD
95
-		NewHeadRoute("/containers/{name:.*}/archive", r.headContainersArchive),
96 94
 		// OPTIONS
97 95
 		NewOptionsRoute("/", optionsHandler),
98 96
 		// GET
... ...
@@ -106,16 +104,6 @@ func (r *router) initRoutes() {
106 106
 		NewGetRoute("/images/{name:.*}/get", r.getImagesGet),
107 107
 		NewGetRoute("/images/{name:.*}/history", r.getImagesHistory),
108 108
 		NewGetRoute("/images/{name:.*}/json", r.getImagesByName),
109
-		NewGetRoute("/containers/json", r.getContainersJSON),
110
-		NewGetRoute("/containers/{name:.*}/export", r.getContainersExport),
111
-		NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges),
112
-		NewGetRoute("/containers/{name:.*}/json", r.getContainersByName),
113
-		NewGetRoute("/containers/{name:.*}/top", r.getContainersTop),
114
-		NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs),
115
-		NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats),
116
-		NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach),
117
-		NewGetRoute("/exec/{id:.*}/json", r.getExecByID),
118
-		NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive),
119 109
 		// POST
120 110
 		NewPostRoute("/auth", r.postAuth),
121 111
 		NewPostRoute("/commit", r.postCommit),
... ...
@@ -124,25 +112,7 @@ func (r *router) initRoutes() {
124 124
 		NewPostRoute("/images/load", r.postImagesLoad),
125 125
 		NewPostRoute("/images/{name:.*}/push", r.postImagesPush),
126 126
 		NewPostRoute("/images/{name:.*}/tag", r.postImagesTag),
127
-		NewPostRoute("/containers/create", r.postContainersCreate),
128
-		NewPostRoute("/containers/{name:.*}/kill", r.postContainersKill),
129
-		NewPostRoute("/containers/{name:.*}/pause", r.postContainersPause),
130
-		NewPostRoute("/containers/{name:.*}/unpause", r.postContainersUnpause),
131
-		NewPostRoute("/containers/{name:.*}/restart", r.postContainersRestart),
132
-		NewPostRoute("/containers/{name:.*}/start", r.postContainersStart),
133
-		NewPostRoute("/containers/{name:.*}/stop", r.postContainersStop),
134
-		NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
135
-		NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
136
-		NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
137
-		NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy),
138
-		NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
139
-		NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
140
-		NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
141
-		NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
142
-		// PUT
143
-		NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
144 127
 		// DELETE
145
-		NewDeleteRoute("/containers/{name:.*}", r.deleteContainers),
146 128
 		NewDeleteRoute("/images/{name:.*}", r.deleteImages),
147 129
 	}
148 130
 }
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"github.com/Sirupsen/logrus"
11 11
 	"github.com/docker/docker/api/server/httputils"
12 12
 	"github.com/docker/docker/api/server/router"
13
+	"github.com/docker/docker/api/server/router/container"
13 14
 	"github.com/docker/docker/api/server/router/local"
14 15
 	"github.com/docker/docker/api/server/router/network"
15 16
 	"github.com/docker/docker/api/server/router/volume"
... ...
@@ -172,6 +173,7 @@ func (s *Server) InitRouters(d *daemon.Daemon) {
172 172
 	s.addRouter(local.NewRouter(d))
173 173
 	s.addRouter(network.NewRouter(d))
174 174
 	s.addRouter(volume.NewRouter(d))
175
+	s.addRouter(container.NewRouter(d))
175 176
 
176 177
 	for _, srv := range s.servers {
177 178
 		srv.srv.Handler = s.CreateMux()