Browse code

split api and server. run return exit code. import, pull and commit uses the smae endpoint. non zero status code on failure

Victor Vieux authored on 2013/05/06 18:31:22
Showing 7 changed files
... ...
@@ -38,7 +38,7 @@ $(DOCKER_BIN): $(DOCKER_DIR)
38 38
 
39 39
 $(DOCKER_DIR):
40 40
 	@mkdir -p $(dir $@)
41
-	@if [ -h $@ ]; then rm -f $@; ln -sf $(CURDIR)/ $@; fi
41
+	@if [ -h $@ ]; then rm -f $@; fi; ln -sf $(CURDIR)/ $@
42 42
 	@(cd $(DOCKER_MAIN); go get $(GO_OPTIONS))
43 43
 
44 44
 whichrelease:
... ...
@@ -5,24 +5,46 @@ import (
5 5
 	"encoding/json"
6 6
 	"fmt"
7 7
 	"github.com/gorilla/mux"
8
-	"io"
9 8
 	"log"
10 9
 	"net"
11 10
 	"net/http"
12
-	"net/url"
13 11
 	"os"
14
-	"runtime"
15 12
 	"strconv"
16 13
 	"strings"
17 14
 )
18 15
 
19
-func ListenAndServe(addr string, rtime *Runtime) error {
16
+func hijackServer(w http.ResponseWriter) (*os.File, net.Conn, error) {
17
+	rwc, _, err := w.(http.Hijacker).Hijack()
18
+	if err != nil {
19
+		return nil, nil, err
20
+	}
21
+
22
+	file, err := rwc.(*net.TCPConn).File()
23
+	if err != nil {
24
+		return nil, rwc, err
25
+	}
26
+
27
+	// Flush the options to make sure the client sets the raw mode
28
+	rwc.Write([]byte{})
29
+
30
+	return file, rwc, nil
31
+}
32
+
33
+func httpError(w http.ResponseWriter, err error) {
34
+	if strings.HasPrefix(err.Error(), "No such") {
35
+		http.Error(w, err.Error(), http.StatusNotFound)
36
+	} else {
37
+		http.Error(w, err.Error(), http.StatusInternalServerError)
38
+	}
39
+}
40
+
41
+func ListenAndServe(addr string, srv *Server) error {
20 42
 	r := mux.NewRouter()
21 43
 	log.Printf("Listening for HTTP on %s\n", addr)
22 44
 
23 45
 	r.Path("/version").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
24 46
 		log.Println(r.Method, r.RequestURI)
25
-		m := ApiVersion{VERSION, GIT_COMMIT, rtime.capabilities.MemoryLimit, rtime.capabilities.SwapLimit}
47
+		m := srv.DockerVersion()
26 48
 		b, err := json.Marshal(m)
27 49
 		if err != nil {
28 50
 			http.Error(w, err.Error(), http.StatusInternalServerError)
... ...
@@ -35,16 +57,11 @@ func ListenAndServe(addr string, rtime *Runtime) error {
35 35
 		log.Println(r.Method, r.RequestURI)
36 36
 		vars := mux.Vars(r)
37 37
 		name := vars["name"]
38
-		if container := rtime.Get(name); container != nil {
39
-			if err := container.Kill(); err != nil {
40
-				http.Error(w, "Error restarting container "+name+": "+err.Error(), http.StatusInternalServerError)
41
-				return
42
-			}
38
+		if err := srv.ContainerKill(name); err != nil {
39
+			httpError(w, err)
43 40
 		} else {
44
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
45
-			return
41
+			w.WriteHeader(http.StatusOK)
46 42
 		}
47
-		w.WriteHeader(http.StatusOK)
48 43
 	})
49 44
 
50 45
 	r.Path("/containers/{name:.*}/export").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
... ...
@@ -52,36 +69,21 @@ func ListenAndServe(addr string, rtime *Runtime) error {
52 52
 		vars := mux.Vars(r)
53 53
 		name := vars["name"]
54 54
 
55
-		if container := rtime.Get(name); container != nil {
56
-
57
-			data, err := container.Export()
58
-			if err != nil {
59
-				http.Error(w, err.Error(), http.StatusInternalServerError)
60
-				return
61
-			}
62
-			conn, _, err := w.(http.Hijacker).Hijack()
63
-			if err != nil {
64
-				http.Error(w, err.Error(), http.StatusInternalServerError)
65
-				return
66
-			}
67
-			defer conn.Close()
68
-			file, err := conn.(*net.TCPConn).File()
69
-			if err != nil {
70
-				http.Error(w, err.Error(), http.StatusInternalServerError)
71
-				return
72
-			}
55
+		file, rwc, err := hijackServer(w)
56
+		if file != nil {
73 57
 			defer file.Close()
74
-
75
-			fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
76
-			// Stream the entire contents of the container (basically a volatile snapshot)
77
-			if _, err := io.Copy(file, data); err != nil {
78
-				fmt.Fprintln(file, "Error: "+err.Error())
79
-				return
80
-			}
81
-		} else {
82
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
83 58
 		}
84
-
59
+		if rwc != nil {
60
+			defer rwc.Close()
61
+		}
62
+		if err != nil {
63
+			httpError(w, err)
64
+			return
65
+		}
66
+		fmt.Fprintf(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
67
+		if err := srv.ContainerExport(name, file); err != nil {
68
+			fmt.Fprintln(file, "Error: "+err.Error())
69
+		}
85 70
 	})
86 71
 
87 72
 	r.Path("/images").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
... ...
@@ -89,61 +91,14 @@ func ListenAndServe(addr string, rtime *Runtime) error {
89 89
 		if err := r.ParseForm(); err != nil {
90 90
 			http.Error(w, err.Error(), http.StatusInternalServerError)
91 91
 		}
92
-		All := r.Form.Get("all")
93
-		NameFilter := r.Form.Get("filter")
94
-		Quiet := r.Form.Get("quiet")
92
+		all := r.Form.Get("all")
93
+		filter := r.Form.Get("filter")
94
+		quiet := r.Form.Get("quiet")
95 95
 
96
-		var allImages map[string]*Image
97
-		var err error
98
-		if All == "1" {
99
-			allImages, err = rtime.graph.Map()
100
-		} else {
101
-			allImages, err = rtime.graph.Heads()
102
-		}
96
+		outs, err := srv.Images(all, filter, quiet)
103 97
 		if err != nil {
104
-			w.WriteHeader(500)
105
-			return
106
-		}
107
-		var outs []ApiImages = []ApiImages{} //produce [] when empty instead of 'null'
108
-		for name, repository := range rtime.repositories.Repositories {
109
-			if NameFilter != "" && name != NameFilter {
110
-				continue
111
-			}
112
-			for tag, id := range repository {
113
-				var out ApiImages
114
-				image, err := rtime.graph.Get(id)
115
-				if err != nil {
116
-					log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
117
-					continue
118
-				}
119
-				delete(allImages, id)
120
-				if Quiet != "1" {
121
-					out.Repository = name
122
-					out.Tag = tag
123
-					out.Id = TruncateId(id)
124
-					out.Created = image.Created.Unix()
125
-				} else {
126
-					out.Id = image.ShortId()
127
-				}
128
-				outs = append(outs, out)
129
-			}
130
-		}
131
-		// Display images which aren't part of a
132
-		if NameFilter == "" {
133
-			for id, image := range allImages {
134
-				var out ApiImages
135
-				if Quiet != "1" {
136
-					out.Repository = "<none>"
137
-					out.Tag = "<none>"
138
-					out.Id = TruncateId(id)
139
-					out.Created = image.Created.Unix()
140
-				} else {
141
-					out.Id = image.ShortId()
142
-				}
143
-				outs = append(outs, out)
144
-			}
98
+			httpError(w, err)
145 99
 		}
146
-
147 100
 		b, err := json.Marshal(outs)
148 101
 		if err != nil {
149 102
 			http.Error(w, err.Error(), http.StatusInternalServerError)
... ...
@@ -154,22 +109,7 @@ func ListenAndServe(addr string, rtime *Runtime) error {
154 154
 
155 155
 	r.Path("/info").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
156 156
 		log.Println(r.Method, r.RequestURI)
157
-		images, _ := rtime.graph.All()
158
-		var imgcount int
159
-		if images == nil {
160
-			imgcount = 0
161
-		} else {
162
-			imgcount = len(images)
163
-		}
164
-		var out ApiInfo
165
-		out.Containers = len(rtime.List())
166
-		out.Version = VERSION
167
-		out.Images = imgcount
168
-		if os.Getenv("DEBUG") == "1" {
169
-			out.Debug = true
170
-			out.NFd = getTotalUsedFds()
171
-			out.NGoroutines = runtime.NumGoroutine()
172
-		}
157
+		out := srv.DockerInfo()
173 158
 		b, err := json.Marshal(out)
174 159
 		if err != nil {
175 160
 			http.Error(w, err.Error(), http.StatusInternalServerError)
... ...
@@ -182,22 +122,10 @@ func ListenAndServe(addr string, rtime *Runtime) error {
182 182
 		log.Println(r.Method, r.RequestURI)
183 183
 		vars := mux.Vars(r)
184 184
 		name := vars["name"]
185
-
186
-		image, err := rtime.repositories.LookupImage(name)
185
+		outs, err := srv.ImageHistory(name)
187 186
 		if err != nil {
188
-			http.Error(w, err.Error(), http.StatusInternalServerError)
189
-			return
187
+			httpError(w, err)
190 188
 		}
191
-
192
-		var outs []ApiHistory = []ApiHistory{} //produce [] when empty instead of 'null'
193
-		err = image.WalkHistory(func(img *Image) error {
194
-			var out ApiHistory
195
-			out.Id = rtime.repositories.ImageName(img.ShortId())
196
-			out.Created = img.Created.Unix()
197
-			out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
198
-			return nil
199
-		})
200
-
201 189
 		b, err := json.Marshal(outs)
202 190
 		if err != nil {
203 191
 			http.Error(w, err.Error(), http.StatusInternalServerError)
... ...
@@ -210,25 +138,15 @@ func ListenAndServe(addr string, rtime *Runtime) error {
210 210
 		log.Println(r.Method, r.RequestURI)
211 211
 		vars := mux.Vars(r)
212 212
 		name := vars["name"]
213
-
214
-		if container := rtime.Get(name); container != nil {
215
-			changes, err := container.Changes()
216
-			if err != nil {
217
-				http.Error(w, err.Error(), http.StatusInternalServerError)
218
-				return
219
-			}
220
-			var changesStr []string
221
-			for _, name := range changes {
222
-				changesStr = append(changesStr, name.String())
223
-			}
224
-			b, err := json.Marshal(changesStr)
225
-			if err != nil {
226
-				http.Error(w, err.Error(), http.StatusInternalServerError)
227
-			} else {
228
-				w.Write(b)
229
-			}
213
+		changesStr, err := srv.ContainerChanges(name)
214
+		if err != nil {
215
+			httpError(w, err)
216
+		}
217
+		b, err := json.Marshal(changesStr)
218
+		if err != nil {
219
+			http.Error(w, err.Error(), http.StatusInternalServerError)
230 220
 		} else {
231
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
221
+			w.Write(b)
232 222
 		}
233 223
 	})
234 224
 
... ...
@@ -237,99 +155,36 @@ func ListenAndServe(addr string, rtime *Runtime) error {
237 237
 		if err := r.ParseForm(); err != nil {
238 238
 			http.Error(w, err.Error(), http.StatusInternalServerError)
239 239
 		}
240
-		privatePort := r.Form.Get("port")
241 240
 		vars := mux.Vars(r)
242 241
 		name := vars["name"]
243
-
244
-		if container := rtime.Get(name); container == nil {
245
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
246
-			return
247
-		} else {
248
-			if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; !exists {
249
-				http.Error(w, "No private port '"+privatePort+"' allocated on "+name, http.StatusInternalServerError)
250
-				return
251
-			} else {
252
-				b, err := json.Marshal(ApiPort{frontend})
253
-				if err != nil {
254
-					http.Error(w, err.Error(), http.StatusInternalServerError)
255
-				} else {
256
-					w.Write(b)
257
-				}
258
-			}
259
-		}
260
-	})
261
-
262
-	r.Path("/containers").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
263
-		log.Println(r.Method, r.RequestURI)
264
-		if err := r.ParseForm(); err != nil {
265
-			http.Error(w, err.Error(), http.StatusInternalServerError)
266
-		}
267
-		All := r.Form.Get("all")
268
-		NoTrunc := r.Form.Get("notrunc")
269
-		Quiet := r.Form.Get("quiet")
270
-		Last := r.Form.Get("n")
271
-		n, err := strconv.Atoi(Last)
242
+		out, err := srv.ContainerPort(name, r.Form.Get("port"))
272 243
 		if err != nil {
273
-			n = -1
274
-		}
275
-		var outs []ApiContainers = []ApiContainers{} //produce [] when empty instead of 'null'
276
-		for i, container := range rtime.List() {
277
-			if !container.State.Running && All != "1" && n == -1 {
278
-				continue
279
-			}
280
-			if i == n {
281
-				break
282
-			}
283
-			var out ApiContainers
284
-			out.Id = container.ShortId()
285
-			if Quiet != "1" {
286
-				command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
287
-				if NoTrunc != "1" {
288
-					command = Trunc(command, 20)
289
-				}
290
-				out.Image = rtime.repositories.ImageName(container.Image)
291
-				out.Command = command
292
-				out.Created = container.Created.Unix()
293
-				out.Status = container.State.String()
294
-				out.Ports = container.NetworkSettings.PortMappingHuman()
295
-			}
296
-			outs = append(outs, out)
244
+			httpError(w, err)
297 245
 		}
298
-
299
-		b, err := json.Marshal(outs)
246
+		b, err := json.Marshal(ApiPort{out})
300 247
 		if err != nil {
301 248
 			http.Error(w, err.Error(), http.StatusInternalServerError)
302 249
 		} else {
303 250
 			w.Write(b)
304 251
 		}
252
+
305 253
 	})
306 254
 
307
-	r.Path("/containers/{name:.*}/commit").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
255
+	r.Path("/containers").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
308 256
 		log.Println(r.Method, r.RequestURI)
309
-		var config Config
310
-		if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
311
-			http.Error(w, err.Error(), http.StatusInternalServerError)
312
-			return
313
-		}
314
-
315 257
 		if err := r.ParseForm(); err != nil {
316 258
 			http.Error(w, err.Error(), http.StatusInternalServerError)
317
-			return
318 259
 		}
319
-		vars := mux.Vars(r)
320
-		name := vars["name"]
321
-		repo := r.Form.Get("repo")
322
-		tag := r.Form.Get("tag")
323
-		author := r.Form.Get("author")
324
-		comment := r.Form.Get("comment")
325
-
326
-		img, err := rtime.Commit(name, repo, tag, comment, author, &config)
260
+		all := r.Form.Get("all")
261
+		notrunc := r.Form.Get("notrunc")
262
+		quiet := r.Form.Get("quiet")
263
+		n, err := strconv.Atoi(r.Form.Get("n"))
327 264
 		if err != nil {
328
-			http.Error(w, err.Error(), http.StatusInternalServerError)
329
-			return
265
+			n = -1
330 266
 		}
331 267
 
332
-		b, err := json.Marshal(ApiId{img.ShortId()})
268
+		outs := srv.Containers(all, notrunc, quiet, n)
269
+		b, err := json.Marshal(outs)
333 270
 		if err != nil {
334 271
 			http.Error(w, err.Error(), http.StatusInternalServerError)
335 272
 		} else {
... ...
@@ -342,121 +197,79 @@ func ListenAndServe(addr string, rtime *Runtime) error {
342 342
 		if err := r.ParseForm(); err != nil {
343 343
 			http.Error(w, err.Error(), http.StatusInternalServerError)
344 344
 		}
345
-		vars := mux.Vars(r)
346
-		name := vars["name"]
347 345
 		repo := r.Form.Get("repo")
348 346
 		tag := r.Form.Get("tag")
347
+		vars := mux.Vars(r)
348
+		name := vars["name"]
349 349
 		var force bool
350 350
 		if r.Form.Get("force") == "1" {
351 351
 			force = true
352 352
 		}
353 353
 
354
-		if err := rtime.repositories.Set(repo, tag, name, force); err != nil {
354
+		if err := srv.ContainerTag(name, repo, tag, force); err != nil {
355 355
 			http.Error(w, err.Error(), http.StatusInternalServerError)
356 356
 			return
357 357
 		}
358 358
 		w.WriteHeader(http.StatusCreated)
359 359
 	})
360 360
 
361
-	r.Path("/images/{name:.*}/pull").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
362
-		log.Println(r.Method, r.RequestURI)
363
-		vars := mux.Vars(r)
364
-		name := vars["name"]
365
-
366
-		conn, _, err := w.(http.Hijacker).Hijack()
367
-		if err != nil {
368
-			http.Error(w, err.Error(), http.StatusInternalServerError)
369
-			return
370
-		}
371
-		defer conn.Close()
372
-		file, err := conn.(*net.TCPConn).File()
373
-		if err != nil {
374
-			http.Error(w, err.Error(), http.StatusInternalServerError)
375
-			return
376
-		}
377
-		defer file.Close()
378
-
379
-		fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
380
-		if rtime.graph.LookupRemoteImage(name, rtime.authConfig) {
381
-			if err := rtime.graph.PullImage(file, name, rtime.authConfig); err != nil {
382
-				fmt.Fprintln(file, "Error: "+err.Error())
383
-			}
384
-			return
385
-		}
386
-		if err := rtime.graph.PullRepository(file, name, "", rtime.repositories, rtime.authConfig); err != nil {
387
-			fmt.Fprintln(file, "Error: "+err.Error())
388
-		}
389
-	})
390
-
391
-	/* /!\ W.I.P /!\ */
392 361
 	r.Path("/images").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
393 362
 		log.Println(r.Method, r.RequestURI)
394 363
 		if err := r.ParseForm(); err != nil {
395 364
 			http.Error(w, err.Error(), http.StatusInternalServerError)
396 365
 		}
397
-		src := r.Form.Get("src")
366
+
367
+		src := r.Form.Get("fromSrc")
368
+		image := r.Form.Get("fromImage")
369
+		container := r.Form.Get("fromContainer")
398 370
 		repo := r.Form.Get("repo")
399 371
 		tag := r.Form.Get("tag")
400 372
 
401
-		var archive io.Reader
402
-		var resp *http.Response
373
+		if container != "" { //commit
374
+			var config Config
375
+			if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
376
+				http.Error(w, err.Error(), http.StatusInternalServerError)
377
+				return
378
+			}
379
+			author := r.Form.Get("author")
380
+			comment := r.Form.Get("comment")
403 381
 
404
-		conn, _, err := w.(http.Hijacker).Hijack()
405
-		if err != nil {
406
-			http.Error(w, err.Error(), http.StatusInternalServerError)
407
-			return
408
-		}
409
-		defer conn.Close()
410
-		file, err := conn.(*net.TCPConn).File()
411
-		if err != nil {
412
-			http.Error(w, err.Error(), http.StatusInternalServerError)
413
-			return
414
-		}
415
-		defer file.Close()
416
-
417
-		fmt.Fprintln(file, "HTTP/1.1 201 Created\r\nContent-Type: application/json\r\n\r\n")
418
-		if src == "-" {
419
-			r, w := io.Pipe()
420
-			go func() {
421
-				defer w.Close()
422
-				defer Debugf("Closing buffered stdin pipe")
423
-				io.Copy(w, file)
424
-			}()
425
-			archive = r
426
-		} else {
427
-			u, err := url.Parse(src)
382
+			id, err := srv.ContainerCommit(container, repo, tag, author, comment, &config)
428 383
 			if err != nil {
429
-				fmt.Fprintln(file, "Error: "+err.Error())
384
+				httpError(w, err)
430 385
 			}
431
-			if u.Scheme == "" {
432
-				u.Scheme = "http"
433
-				u.Host = src
434
-				u.Path = ""
435
-			}
436
-			fmt.Fprintln(file, "Downloading from", u)
437
-			// Download with curl (pretty progress bar)
438
-			// If curl is not available, fallback to http.Get()
439
-			resp, err = Download(u.String(), file)
386
+			b, err := json.Marshal(ApiId{id})
440 387
 			if err != nil {
441 388
 				http.Error(w, err.Error(), http.StatusInternalServerError)
442
-				return
389
+			} else {
390
+				w.Write(b)
443 391
 			}
444
-			archive = ProgressReader(resp.Body, int(resp.ContentLength), file, "Importing %v/%v (%v)")
445
-		}
446
-		img, err := rtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
447
-		if err != nil {
448
-			http.Error(w, err.Error(), http.StatusInternalServerError)
449
-			return
450
-		}
451
-		// Optionally register the image at REPO/TAG
452
-		if repo != "" {
453
-			if err := rtime.repositories.Set(repo, tag, img.Id, true); err != nil {
454
-				http.Error(w, err.Error(), http.StatusInternalServerError)
392
+		} else if image != "" || src != "" {
393
+			file, rwc, err := hijackServer(w)
394
+			if file != nil {
395
+				defer file.Close()
396
+			}
397
+			if rwc != nil {
398
+				defer rwc.Close()
399
+			}
400
+			if err != nil {
401
+				httpError(w, err)
455 402
 				return
456 403
 			}
457
-		}
404
+			fmt.Fprintf(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
458 405
 
459
-		fmt.Fprintln(file, img.ShortId())
406
+			if image != "" { //pull
407
+				if err := srv.ImagePull(image, file); err != nil {
408
+					fmt.Fprintln(file, "Error: "+err.Error())
409
+				}
410
+			} else { //import
411
+				if err := srv.ImageImport(src, repo, tag, file); err != nil {
412
+					fmt.Fprintln(file, "Error: "+err.Error())
413
+				}
414
+			}
415
+		} else {
416
+			w.WriteHeader(http.StatusNotFound)
417
+		}
460 418
 	})
461 419
 
462 420
 	r.Path("/containers").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
... ...
@@ -466,37 +279,19 @@ func ListenAndServe(addr string, rtime *Runtime) error {
466 466
 			http.Error(w, err.Error(), http.StatusInternalServerError)
467 467
 			return
468 468
 		}
469
-		var memoryW, swapW bool
470
-
471
-		if config.Memory > 0 && !rtime.capabilities.MemoryLimit {
472
-			memoryW = true
473
-			log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
474
-			config.Memory = 0
475
-		}
476
-
477
-		if config.Memory > 0 && !rtime.capabilities.SwapLimit {
478
-			swapW = true
479
-			log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.")
480
-			config.MemorySwap = -1
481
-		}
482
-		container, err := rtime.Create(&config)
469
+		id, memoryW, swapW, err := srv.ContainerCreate(config)
483 470
 		if err != nil {
484
-			if rtime.graph.IsNotExist(err) {
485
-				http.Error(w, "No such image: "+config.Image, http.StatusNotFound)
486
-			} else {
487
-				http.Error(w, err.Error(), http.StatusInternalServerError)
488
-			}
471
+			httpError(w, err)
489 472
 			return
490 473
 		}
491 474
 		var out ApiRun
492
-		out.Id = container.ShortId()
475
+		out.Id = id
493 476
 		if memoryW {
494 477
 			out.Warnings = append(out.Warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.")
495 478
 		}
496 479
 		if swapW {
497 480
 			out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.")
498 481
 		}
499
-
500 482
 		b, err := json.Marshal(out)
501 483
 		if err != nil {
502 484
 			http.Error(w, err.Error(), http.StatusInternalServerError)
... ...
@@ -516,66 +311,44 @@ func ListenAndServe(addr string, rtime *Runtime) error {
516 516
 		}
517 517
 		vars := mux.Vars(r)
518 518
 		name := vars["name"]
519
-		if container := rtime.Get(name); container != nil {
520
-			if err := container.Restart(t); err != nil {
521
-				http.Error(w, "Error restarting container "+name+": "+err.Error(), http.StatusInternalServerError)
522
-				return
523
-			}
519
+		if err := srv.ContainerRestart(name, t); err != nil {
520
+			httpError(w, err)
524 521
 		} else {
525
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
526
-			return
522
+			w.WriteHeader(http.StatusOK)
527 523
 		}
528
-		w.WriteHeader(http.StatusOK)
529 524
 	})
530 525
 
531 526
 	r.Path("/containers/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
532 527
 		log.Println(r.Method, r.RequestURI)
533 528
 		vars := mux.Vars(r)
534 529
 		name := vars["name"]
535
-		if container := rtime.Get(name); container != nil {
536
-			if err := rtime.Destroy(container); err != nil {
537
-				http.Error(w, "Error destroying container "+name+": "+err.Error(), http.StatusInternalServerError)
538
-				return
539
-			}
530
+		if err := srv.ContainerDestroy(name); err != nil {
531
+			httpError(w, err)
540 532
 		} else {
541
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
542
-			return
533
+			w.WriteHeader(http.StatusOK)
543 534
 		}
544
-		w.WriteHeader(http.StatusOK)
545 535
 	})
546 536
 
547 537
 	r.Path("/images/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
548 538
 		log.Println(r.Method, r.RequestURI)
549 539
 		vars := mux.Vars(r)
550 540
 		name := vars["name"]
551
-
552
-		img, err := rtime.repositories.LookupImage(name)
553
-		if err != nil {
554
-			http.Error(w, "No such image: "+name, http.StatusNotFound)
555
-			return
541
+		if err := srv.ImageDelete(name); err != nil {
542
+			httpError(w, err)
556 543
 		} else {
557
-			if err := rtime.graph.Delete(img.Id); err != nil {
558
-				http.Error(w, "Error deleting image "+name+": "+err.Error(), http.StatusInternalServerError)
559
-				return
560
-			}
544
+			w.WriteHeader(http.StatusOK)
561 545
 		}
562
-		w.WriteHeader(http.StatusOK)
563 546
 	})
564 547
 
565 548
 	r.Path("/containers/{name:.*}/start").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
566 549
 		log.Println(r.Method, r.RequestURI)
567 550
 		vars := mux.Vars(r)
568 551
 		name := vars["name"]
569
-		if container := rtime.Get(name); container != nil {
570
-			if err := container.Start(); err != nil {
571
-				http.Error(w, "Error starting container "+name+": "+err.Error(), http.StatusInternalServerError)
572
-				return
573
-			}
552
+		if err := srv.ContainerStart(name); err != nil {
553
+			httpError(w, err)
574 554
 		} else {
575
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
576
-			return
555
+			w.WriteHeader(http.StatusOK)
577 556
 		}
578
-		w.WriteHeader(http.StatusOK)
579 557
 	})
580 558
 
581 559
 	r.Path("/containers/{name:.*}/stop").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
... ...
@@ -589,33 +362,27 @@ func ListenAndServe(addr string, rtime *Runtime) error {
589 589
 		}
590 590
 		vars := mux.Vars(r)
591 591
 		name := vars["name"]
592
-		if container := rtime.Get(name); container != nil {
593
-			if err := container.Stop(t); err != nil {
594
-				http.Error(w, "Error stopping container "+name+": "+err.Error(), http.StatusInternalServerError)
595
-				return
596
-			}
592
+
593
+		if err := srv.ContainerStop(name, t); err != nil {
594
+			httpError(w, err)
597 595
 		} else {
598
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
599
-			return
596
+			w.WriteHeader(http.StatusOK)
600 597
 		}
601
-		w.WriteHeader(http.StatusOK)
602 598
 	})
603 599
 
604 600
 	r.Path("/containers/{name:.*}/wait").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
605 601
 		log.Println(r.Method, r.RequestURI)
606 602
 		vars := mux.Vars(r)
607 603
 		name := vars["name"]
608
-		if container := rtime.Get(name); container != nil {
609
-			b, err := json.Marshal(ApiWait{container.Wait()})
610
-			if err != nil {
611
-				http.Error(w, err.Error(), http.StatusInternalServerError)
612
-			} else {
613
-				w.Write(b)
614
-			}
615
-			return
604
+		status, err := srv.ContainerWait(name)
605
+		if err != nil {
606
+			httpError(w, err)
607
+		}
608
+		b, err := json.Marshal(ApiWait{status})
609
+		if err != nil {
610
+			http.Error(w, err.Error(), http.StatusInternalServerError)
616 611
 		} else {
617
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
618
-			return
612
+			w.Write(b)
619 613
 		}
620 614
 	})
621 615
 
... ...
@@ -632,90 +399,21 @@ func ListenAndServe(addr string, rtime *Runtime) error {
632 632
 		vars := mux.Vars(r)
633 633
 		name := vars["name"]
634 634
 
635
-		if container := rtime.Get(name); container != nil {
636
-			conn, _, err := w.(http.Hijacker).Hijack()
637
-			if err != nil {
638
-				http.Error(w, err.Error(), http.StatusInternalServerError)
639
-				return
640
-			}
641
-			defer conn.Close()
642
-			file, err := conn.(*net.TCPConn).File()
643
-			if err != nil {
644
-				http.Error(w, err.Error(), http.StatusInternalServerError)
645
-				return
646
-			}
635
+		file, rwc, err := hijackServer(w)
636
+		if file != nil {
647 637
 			defer file.Close()
638
+		}
639
+		if rwc != nil {
640
+			defer rwc.Close()
641
+		}
642
+		if err != nil {
643
+			httpError(w, err)
644
+			return
645
+		}
648 646
 
649
-			// Flush the options to make sure the client sets the raw mode
650
-			conn.Write([]byte{})
651
-
652
-			fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
653
-			//logs
654
-			if logs == "1" {
655
-				if stdout == "1" {
656
-					cLog, err := container.ReadLog("stdout")
657
-					if err != nil {
658
-						Debugf(err.Error())
659
-					} else if _, err := io.Copy(file, cLog); err != nil {
660
-						Debugf(err.Error())
661
-					}
662
-				}
663
-				if stderr == "1" {
664
-					cLog, err := container.ReadLog("stderr")
665
-					if err != nil {
666
-						Debugf(err.Error())
667
-					} else if _, err := io.Copy(file, cLog); err != nil {
668
-						Debugf(err.Error())
669
-					}
670
-				}
671
-			}
672
-
673
-			//stream
674
-			if stream == "1" {
675
-
676
-				if container.State.Ghost {
677
-					fmt.Fprintf(file, "error: Impossible to attach to a ghost container")
678
-					return
679
-				}
680
-
681
-				if container.Config.Tty {
682
-					oldState, err := SetRawTerminal()
683
-					if err != nil {
684
-						if os.Getenv("DEBUG") != "" {
685
-							log.Printf("Can't set the terminal in raw mode: %s", err)
686
-						}
687
-					} else {
688
-						defer RestoreTerminal(oldState)
689
-					}
690
-
691
-				}
692
-				var (
693
-					cStdin           io.ReadCloser
694
-					cStdout, cStderr io.Writer
695
-					cStdinCloser     io.Closer
696
-				)
697
-
698
-				if stdin == "1" {
699
-					r, w := io.Pipe()
700
-					go func() {
701
-						defer w.Close()
702
-						defer Debugf("Closing buffered stdin pipe")
703
-						io.Copy(w, file)
704
-					}()
705
-					cStdin = r
706
-					cStdinCloser = file
707
-				}
708
-				if stdout == "1" {
709
-					cStdout = file
710
-				}
711
-				if stderr == "1" {
712
-					cStderr = file
713
-				}
714
-
715
-				<-container.Attach(cStdin, cStdinCloser, cStdout, cStderr)
716
-			}
717
-		} else {
718
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
647
+		fmt.Fprintf(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
648
+		if err := srv.ContainerAttach(name, logs, stream, stdin, stdout, stderr, file); err != nil {
649
+			fmt.Fprintln(file, "Error: "+err.Error())
719 650
 		}
720 651
 	})
721 652
 
... ...
@@ -724,16 +422,16 @@ func ListenAndServe(addr string, rtime *Runtime) error {
724 724
 		vars := mux.Vars(r)
725 725
 		name := vars["name"]
726 726
 
727
-		if container := rtime.Get(name); container != nil {
728
-			b, err := json.Marshal(container)
729
-			if err != nil {
730
-				http.Error(w, err.Error(), http.StatusInternalServerError)
731
-			} else {
732
-				w.Write(b)
733
-			}
734
-			return
727
+		container, err := srv.ContainerInspect(name)
728
+		if err != nil {
729
+			httpError(w, err)
730
+		}
731
+		b, err := json.Marshal(container)
732
+		if err != nil {
733
+			http.Error(w, err.Error(), http.StatusInternalServerError)
734
+		} else {
735
+			w.Write(b)
735 736
 		}
736
-		http.Error(w, "No such container: "+name, http.StatusNotFound)
737 737
 	})
738 738
 
739 739
 	r.Path("/images/{name:.*}").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
... ...
@@ -741,16 +439,16 @@ func ListenAndServe(addr string, rtime *Runtime) error {
741 741
 		vars := mux.Vars(r)
742 742
 		name := vars["name"]
743 743
 
744
-		if image, err := rtime.repositories.LookupImage(name); err == nil && image != nil {
745
-			b, err := json.Marshal(image)
746
-			if err != nil {
747
-				http.Error(w, err.Error(), http.StatusInternalServerError)
748
-			} else {
749
-				w.Write(b)
750
-			}
751
-			return
744
+		image, err := srv.ImageInspect(name)
745
+		if err != nil {
746
+			httpError(w, err)
747
+		}
748
+		b, err := json.Marshal(image)
749
+		if err != nil {
750
+			http.Error(w, err.Error(), http.StatusInternalServerError)
751
+		} else {
752
+			w.Write(b)
752 753
 		}
753
-		http.Error(w, "No such image: "+name, http.StatusNotFound)
754 754
 	})
755 755
 
756 756
 	return http.ListenAndServe(addr, r)
... ...
@@ -24,9 +24,30 @@ var (
24 24
 	GIT_COMMIT string
25 25
 )
26 26
 
27
-func ParseCommands(args []string) error {
27
+func checkRemoteVersion() error {
28
+	body, _, err := call("GET", "/version", nil)
29
+	if err != nil {
30
+		return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
31
+	}
32
+
33
+	var out ApiVersion
34
+	err = json.Unmarshal(body, &out)
35
+	if err != nil {
36
+		return err
37
+	}
38
+	if out.Version != VERSION {
39
+		fmt.Fprintf(os.Stderr, "Warning: client and server don't have the same version (client: %s, server: %)", VERSION, out.Version)
40
+	}
41
+	return nil
42
+}
43
+
44
+func ParseCommands(args ...string) error {
45
+
46
+	if err := checkRemoteVersion(); err != nil {
47
+		return err
48
+	}
28 49
 
29
-	cmds := map[string]func(args []string) error{
50
+	cmds := map[string]func(args ...string) error{
30 51
 		"attach":  CmdAttach,
31 52
 		"commit":  CmdCommit,
32 53
 		"diff":    CmdDiff,
... ...
@@ -34,7 +55,7 @@ func ParseCommands(args []string) error {
34 34
 		"images":  CmdImages,
35 35
 		"info":    CmdInfo,
36 36
 		"inspect": CmdInspect,
37
-		//"import":  CmdImport,
37
+		"import":  CmdImport,
38 38
 		"history": CmdHistory,
39 39
 		"kill":    CmdKill,
40 40
 		"logs":    CmdLogs,
... ...
@@ -56,14 +77,14 @@ func ParseCommands(args []string) error {
56 56
 		cmd, exists := cmds[args[0]]
57 57
 		if !exists {
58 58
 			fmt.Println("Error: Command not found:", args[0])
59
-			return cmdHelp(args)
59
+			return cmdHelp(args...)
60 60
 		}
61
-		return cmd(args[1:])
61
+		return cmd(args[1:]...)
62 62
 	}
63
-	return cmdHelp(args)
63
+	return cmdHelp(args...)
64 64
 }
65 65
 
66
-func cmdHelp(args []string) error {
66
+func cmdHelp(args ...string) error {
67 67
 	help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
68 68
 	for _, cmd := range [][]string{
69 69
 		{"attach", "Attach to a running container"},
... ...
@@ -72,7 +93,7 @@ func cmdHelp(args []string) error {
72 72
 		{"export", "Stream the contents of a container as a tar archive"},
73 73
 		{"history", "Show the history of an image"},
74 74
 		{"images", "List images"},
75
-		//{"import", "Create a new filesystem image from the contents of a tarball"},
75
+		{"import", "Create a new filesystem image from the contents of a tarball"},
76 76
 		{"info", "Display system-wide information"},
77 77
 		{"inspect", "Return low-level information on a container/image"},
78 78
 		{"kill", "Kill a running container"},
... ...
@@ -199,7 +220,7 @@ func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ..
199 199
 */
200 200
 
201 201
 // 'docker wait': block until a container stops
202
-func CmdWait(args []string) error {
202
+func CmdWait(args ...string) error {
203 203
 	cmd := Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.")
204 204
 	if err := cmd.Parse(args); err != nil {
205 205
 		return nil
... ...
@@ -225,7 +246,7 @@ func CmdWait(args []string) error {
225 225
 }
226 226
 
227 227
 // 'docker version': show version information
228
-func CmdVersion(args []string) error {
228
+func CmdVersion(args ...string) error {
229 229
 	cmd := Subcmd("version", "", "Show the docker version information.")
230 230
 	if err := cmd.Parse(args); err != nil {
231 231
 		return nil
... ...
@@ -258,7 +279,7 @@ func CmdVersion(args []string) error {
258 258
 }
259 259
 
260 260
 // 'docker info': display system-wide information.
261
-func CmdInfo(args []string) error {
261
+func CmdInfo(args ...string) error {
262 262
 	cmd := Subcmd("info", "", "Display system-wide information")
263 263
 	if err := cmd.Parse(args); err != nil {
264 264
 		return nil
... ...
@@ -286,7 +307,7 @@ func CmdInfo(args []string) error {
286 286
 	return nil
287 287
 }
288 288
 
289
-func CmdStop(args []string) error {
289
+func CmdStop(args ...string) error {
290 290
 	cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container")
291 291
 	nSeconds := cmd.Int("t", 10, "wait t seconds before killing the container")
292 292
 	if err := cmd.Parse(args); err != nil {
... ...
@@ -311,7 +332,7 @@ func CmdStop(args []string) error {
311 311
 	return nil
312 312
 }
313 313
 
314
-func CmdRestart(args []string) error {
314
+func CmdRestart(args ...string) error {
315 315
 	cmd := Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container")
316 316
 	nSeconds := cmd.Int("t", 10, "wait t seconds before killing the container")
317 317
 	if err := cmd.Parse(args); err != nil {
... ...
@@ -336,7 +357,7 @@ func CmdRestart(args []string) error {
336 336
 	return nil
337 337
 }
338 338
 
339
-func CmdStart(args []string) error {
339
+func CmdStart(args ...string) error {
340 340
 	cmd := Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
341 341
 	if err := cmd.Parse(args); err != nil {
342 342
 		return nil
... ...
@@ -357,7 +378,7 @@ func CmdStart(args []string) error {
357 357
 	return nil
358 358
 }
359 359
 
360
-func CmdInspect(args []string) error {
360
+func CmdInspect(args ...string) error {
361 361
 	cmd := Subcmd("inspect", "CONTAINER|IMAGE", "Return low-level information on a container/image")
362 362
 	if err := cmd.Parse(args); err != nil {
363 363
 		return nil
... ...
@@ -377,7 +398,7 @@ func CmdInspect(args []string) error {
377 377
 	return nil
378 378
 }
379 379
 
380
-func CmdPort(args []string) error {
380
+func CmdPort(args ...string) error {
381 381
 	cmd := Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
382 382
 	if err := cmd.Parse(args); err != nil {
383 383
 		return nil
... ...
@@ -403,7 +424,7 @@ func CmdPort(args []string) error {
403 403
 }
404 404
 
405 405
 // 'docker rmi IMAGE' removes all images with the name IMAGE
406
-func CmdRmi(args []string) error {
406
+func CmdRmi(args ...string) error {
407 407
 	cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove an image")
408 408
 	if err := cmd.Parse(args); err != nil {
409 409
 		return nil
... ...
@@ -424,7 +445,7 @@ func CmdRmi(args []string) error {
424 424
 	return nil
425 425
 }
426 426
 
427
-func CmdHistory(args []string) error {
427
+func CmdHistory(args ...string) error {
428 428
 	cmd := Subcmd("history", "IMAGE", "Show the history of an image")
429 429
 	if err := cmd.Parse(args); err != nil {
430 430
 		return nil
... ...
@@ -454,7 +475,7 @@ func CmdHistory(args []string) error {
454 454
 	return nil
455 455
 }
456 456
 
457
-func CmdRm(args []string) error {
457
+func CmdRm(args ...string) error {
458 458
 	cmd := Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove a container")
459 459
 	if err := cmd.Parse(args); err != nil {
460 460
 		return nil
... ...
@@ -476,7 +497,7 @@ func CmdRm(args []string) error {
476 476
 }
477 477
 
478 478
 // 'docker kill NAME' kills a running container
479
-func CmdKill(args []string) error {
479
+func CmdKill(args ...string) error {
480 480
 	cmd := Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container")
481 481
 	if err := cmd.Parse(args); err != nil {
482 482
 		return nil
... ...
@@ -498,7 +519,7 @@ func CmdKill(args []string) error {
498 498
 }
499 499
 
500 500
 /* /!\ W.I.P /!\ */
501
-func CmdImport(args []string) error {
501
+func CmdImport(args ...string) error {
502 502
 	cmd := Subcmd("import", "URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball")
503 503
 
504 504
 	if err := cmd.Parse(args); err != nil {
... ...
@@ -512,7 +533,7 @@ func CmdImport(args []string) error {
512 512
 	v := url.Values{}
513 513
 	v.Set("repo", repository)
514 514
 	v.Set("tag", tag)
515
-	v.Set("src", src)
515
+	v.Set("fromSrc", src)
516 516
 
517 517
 	err := hijack("POST", "/images?"+v.Encode(), false)
518 518
 	if err != nil {
... ...
@@ -582,7 +603,7 @@ func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...
582 582
 }
583 583
 */
584 584
 
585
-func CmdPull(args []string) error {
585
+func CmdPull(args ...string) error {
586 586
 	cmd := Subcmd("pull", "NAME", "Pull an image or a repository from the registry")
587 587
 	if err := cmd.Parse(args); err != nil {
588 588
 		return nil
... ...
@@ -592,15 +613,17 @@ func CmdPull(args []string) error {
592 592
 		cmd.Usage()
593 593
 		return nil
594 594
 	}
595
+	v := url.Values{}
596
+	v.Set("fromImage", cmd.Arg(0))
595 597
 
596
-	if err := hijack("POST", "/images/"+cmd.Arg(0)+"/pull", false); err != nil {
598
+	if err := hijack("POST", "/images?"+v.Encode(), false); err != nil {
597 599
 		return err
598 600
 	}
599 601
 
600 602
 	return nil
601 603
 }
602 604
 
603
-func CmdImages(args []string) error {
605
+func CmdImages(args ...string) error {
604 606
 	cmd := Subcmd("images", "[OPTIONS] [NAME]", "List images")
605 607
 	quiet := cmd.Bool("q", false, "only show numeric IDs")
606 608
 	all := cmd.Bool("a", false, "show all images")
... ...
@@ -652,7 +675,7 @@ func CmdImages(args []string) error {
652 652
 	return nil
653 653
 }
654 654
 
655
-func CmdPs(args []string) error {
655
+func CmdPs(args ...string) error {
656 656
 	cmd := Subcmd("ps", "[OPTIONS]", "List containers")
657 657
 	quiet := cmd.Bool("q", false, "Only display numeric IDs")
658 658
 	all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.")
... ...
@@ -709,7 +732,7 @@ func CmdPs(args []string) error {
709 709
 	return nil
710 710
 }
711 711
 
712
-func CmdCommit(args []string) error {
712
+func CmdCommit(args ...string) error {
713 713
 	cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
714 714
 	flComment := cmd.String("m", "", "Commit message")
715 715
 	flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
... ...
@@ -724,6 +747,7 @@ func CmdCommit(args []string) error {
724 724
 	}
725 725
 
726 726
 	v := url.Values{}
727
+	v.Set("fromContainer", name)
727 728
 	v.Set("repo", repository)
728 729
 	v.Set("tag", tag)
729 730
 	v.Set("comment", *flComment)
... ...
@@ -736,7 +760,7 @@ func CmdCommit(args []string) error {
736 736
 		}
737 737
 	}
738 738
 
739
-	body, _, err := call("POST", "/containers/"+name+"/commit?"+v.Encode(), config)
739
+	body, _, err := call("POST", "/images?"+v.Encode(), config)
740 740
 	if err != nil {
741 741
 		return err
742 742
 	}
... ...
@@ -751,7 +775,7 @@ func CmdCommit(args []string) error {
751 751
 	return nil
752 752
 }
753 753
 
754
-func CmdExport(args []string) error {
754
+func CmdExport(args ...string) error {
755 755
 	cmd := Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive")
756 756
 	if err := cmd.Parse(args); err != nil {
757 757
 		return nil
... ...
@@ -768,7 +792,7 @@ func CmdExport(args []string) error {
768 768
 	return nil
769 769
 }
770 770
 
771
-func CmdDiff(args []string) error {
771
+func CmdDiff(args ...string) error {
772 772
 	cmd := Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem")
773 773
 	if err := cmd.Parse(args); err != nil {
774 774
 		return nil
... ...
@@ -794,7 +818,7 @@ func CmdDiff(args []string) error {
794 794
 	return nil
795 795
 }
796 796
 
797
-func CmdLogs(args []string) error {
797
+func CmdLogs(args ...string) error {
798 798
 	cmd := Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
799 799
 	if err := cmd.Parse(args); err != nil {
800 800
 		return nil
... ...
@@ -815,7 +839,7 @@ func CmdLogs(args []string) error {
815 815
 	return nil
816 816
 }
817 817
 
818
-func CmdAttach(args []string) error {
818
+func CmdAttach(args ...string) error {
819 819
 	cmd := Subcmd("attach", "CONTAINER", "Attach to a running container")
820 820
 	if err := cmd.Parse(args); err != nil {
821 821
 		return nil
... ...
@@ -906,7 +930,7 @@ func (opts AttachOpts) Get(val string) bool {
906 906
 	return false
907 907
 }
908 908
 
909
-func CmdTag(args []string) error {
909
+func CmdTag(args ...string) error {
910 910
 	cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
911 911
 	force := cmd.Bool("f", false, "Force")
912 912
 	if err := cmd.Parse(args); err != nil {
... ...
@@ -933,7 +957,7 @@ func CmdTag(args []string) error {
933 933
 	return nil
934 934
 }
935 935
 
936
-func CmdRun(args []string) error {
936
+func CmdRun(args ...string) error {
937 937
 	config, cmd, err := ParseRun(args)
938 938
 	if err != nil {
939 939
 		return err
... ...
@@ -952,11 +976,18 @@ func CmdRun(args []string) error {
952 952
 
953 953
 	//if image not found try to pull it
954 954
 	if statusCode == 404 {
955
-		err = hijack("POST", "/images/"+config.Image+"/pull", false)
955
+		v := url.Values{}
956
+		v.Set("fromImage", config.Image)
957
+		err = hijack("POST", "/images?"+v.Encode(), false)
956 958
 		if err != nil {
957 959
 			return err
958 960
 		}
959 961
 		body, _, err = call("POST", "/containers", *config)
962
+		if err != nil {
963
+			return err
964
+		}
965
+		return nil
966
+
960 967
 	}
961 968
 	if err != nil {
962 969
 		return err
... ...
@@ -1001,6 +1032,18 @@ func CmdRun(args []string) error {
1001 1001
 		if err := hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty); err != nil {
1002 1002
 			return err
1003 1003
 		}
1004
+		body, _, err := call("POST", "/containers/"+out.Id+"/wait", nil)
1005
+		if err != nil {
1006
+			fmt.Printf("%s", err)
1007
+		} else {
1008
+			var out ApiWait
1009
+			err = json.Unmarshal(body, &out)
1010
+			if err != nil {
1011
+				return err
1012
+			}
1013
+			os.Exit(out.StatusCode)
1014
+		}
1015
+
1004 1016
 	} else {
1005 1017
 		fmt.Println(out.Id)
1006 1018
 	}
... ...
@@ -1054,9 +1097,14 @@ func hijack(method, path string, setRawTerminal bool) error {
1054 1054
 	clientconn.Do(req)
1055 1055
 	defer clientconn.Close()
1056 1056
 
1057
-	rwc, _ := clientconn.Hijack()
1057
+	rwc, br := clientconn.Hijack()
1058 1058
 	defer rwc.Close()
1059 1059
 
1060
+	receiveStdout := Go(func() error {
1061
+		_, err := io.Copy(os.Stdout, br)
1062
+		return err
1063
+	})
1064
+
1060 1065
 	if setRawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" {
1061 1066
 		if oldState, err := SetRawTerminal(); err != nil {
1062 1067
 			return err
... ...
@@ -1065,10 +1113,6 @@ func hijack(method, path string, setRawTerminal bool) error {
1065 1065
 		}
1066 1066
 	}
1067 1067
 
1068
-	receiveStdout := Go(func() error {
1069
-		_, err := io.Copy(os.Stdout, rwc)
1070
-		return err
1071
-	})
1072 1068
 	sendStdin := Go(func() error {
1073 1069
 		_, err := io.Copy(rwc, os.Stdin)
1074 1070
 		rwc.Close()
... ...
@@ -1,16 +1,17 @@
1 1
 package docker
2 2
 
3 3
 import (
4
-	"bufio"
4
+	/*"bufio"
5 5
 	"fmt"
6 6
 	"github.com/dotcloud/docker/rcli"
7 7
 	"io"
8 8
 	"io/ioutil"
9
-	"strings"
9
+	"strings"*/
10 10
 	"testing"
11 11
 	"time"
12 12
 )
13 13
 
14
+/*TODO
14 15
 func closeWrap(args ...io.Closer) error {
15 16
 	e := false
16 17
 	ret := fmt.Errorf("Error closing elements")
... ...
@@ -25,7 +26,7 @@ func closeWrap(args ...io.Closer) error {
25 25
 	}
26 26
 	return nil
27 27
 }
28
-
28
+*/
29 29
 func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
30 30
 	c := make(chan bool)
31 31
 
... ...
@@ -43,6 +44,7 @@ func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
43 43
 	}
44 44
 }
45 45
 
46
+/*TODO
46 47
 func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error {
47 48
 	for i := 0; i < count; i++ {
48 49
 		if _, err := w.Write([]byte(input)); err != nil {
... ...
@@ -396,3 +398,4 @@ func TestAttachDisconnect(t *testing.T) {
396 396
 	cStdin.Close()
397 397
 	container.Wait()
398 398
 }
399
+*/
... ...
@@ -4,14 +4,10 @@ import (
4 4
 	"flag"
5 5
 	"fmt"
6 6
 	"github.com/dotcloud/docker"
7
-	"github.com/dotcloud/docker/rcli"
8
-	"github.com/dotcloud/docker/term"
9
-	"io"
10 7
 	"io/ioutil"
11 8
 	"log"
12 9
 	"os"
13 10
 	"os/signal"
14
-	"runtime"
15 11
 	"strconv"
16 12
 	"syscall"
17 13
 )
... ...
@@ -49,10 +45,12 @@ func main() {
49 49
 		}
50 50
 		if err := daemon(*pidfile, *flAutoRestart); err != nil {
51 51
 			log.Fatal(err)
52
+			os.Exit(-1)
52 53
 		}
53 54
 	} else {
54
-		if err := docker.ParseCommands(flag.Args()); err != nil {
55
+		if err := docker.ParseCommands(flag.Args()...); err != nil {
55 56
 			log.Fatal(err)
57
+			os.Exit(-1)
56 58
 		}
57 59
 	}
58 60
 }
... ...
@@ -99,54 +97,10 @@ func daemon(pidfile string, autoRestart bool) error {
99 99
 		os.Exit(0)
100 100
 	}()
101 101
 
102
-	if runtime.GOARCH != "amd64" {
103
-		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
104
-	}
105
-	runtime, err := docker.NewRuntime(autoRestart)
102
+	server, err := docker.NewServer(autoRestart)
106 103
 	if err != nil {
107 104
 		return err
108 105
 	}
109 106
 
110
-	return docker.ListenAndServe("0.0.0.0:4243", runtime)
111
-}
112
-
113
-func runCommand(args []string) error {
114
-	// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
115
-	// CloseWrite(), which we need to cleanly signal that stdin is closed without
116
-	// closing the connection.
117
-	// See http://code.google.com/p/go/issues/detail?id=3345
118
-	if conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...); err == nil {
119
-		options := conn.GetOptions()
120
-		if options.RawTerminal &&
121
-			term.IsTerminal(int(os.Stdin.Fd())) &&
122
-			os.Getenv("NORAW") == "" {
123
-			if oldState, err := rcli.SetRawTerminal(); err != nil {
124
-				return err
125
-			} else {
126
-				defer rcli.RestoreTerminal(oldState)
127
-			}
128
-		}
129
-		receiveStdout := docker.Go(func() error {
130
-			_, err := io.Copy(os.Stdout, conn)
131
-			return err
132
-		})
133
-		sendStdin := docker.Go(func() error {
134
-			_, err := io.Copy(conn, os.Stdin)
135
-			if err := conn.CloseWrite(); err != nil {
136
-				log.Printf("Couldn't send EOF: " + err.Error())
137
-			}
138
-			return err
139
-		})
140
-		if err := <-receiveStdout; err != nil {
141
-			return err
142
-		}
143
-		if !term.IsTerminal(int(os.Stdin.Fd())) {
144
-			if err := <-sendStdin; err != nil {
145
-				return err
146
-			}
147
-		}
148
-	} else {
149
-		return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
150
-	}
151
-	return nil
107
+	return docker.ListenAndServe("0.0.0.0:4243", server)
152 108
 }
... ...
@@ -2,7 +2,6 @@ package docker
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"github.com/dotcloud/docker/rcli"
6 5
 	"io"
7 6
 	"io/ioutil"
8 7
 	"net"
... ...
@@ -67,12 +66,13 @@ func init() {
67 67
 	if err != nil {
68 68
 		panic(err)
69 69
 	}
70
+
70 71
 	// Create the "Server"
71 72
 	srv := &Server{
72 73
 		runtime: runtime,
73 74
 	}
74 75
 	// Retrieve the Image
75
-	if err := srv.CmdPull(os.Stdin, rcli.NewDockerLocalConn(os.Stdout), unitTestImageName); err != nil {
76
+	if err := srv.ImagePull(unitTestImageName, os.Stdout); err != nil {
76 77
 		panic(err)
77 78
 	}
78 79
 }
79 80
new file mode 100644
... ...
@@ -0,0 +1,448 @@
0
+package docker
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+	"log"
6
+	"net/http"
7
+	"net/url"
8
+	"os"
9
+	"runtime"
10
+	"strings"
11
+)
12
+
13
+func (srv *Server) DockerVersion() ApiVersion {
14
+	return ApiVersion{VERSION, GIT_COMMIT, srv.runtime.capabilities.MemoryLimit, srv.runtime.capabilities.SwapLimit}
15
+}
16
+
17
+func (srv *Server) ContainerKill(name string) error {
18
+	if container := srv.runtime.Get(name); container != nil {
19
+		if err := container.Kill(); err != nil {
20
+			return fmt.Errorf("Error restarting container %s: %s", name, err.Error())
21
+		}
22
+	} else {
23
+		return fmt.Errorf("No such container: %s", name)
24
+	}
25
+	return nil
26
+}
27
+
28
+func (srv *Server) ContainerExport(name string, file *os.File) error {
29
+	if container := srv.runtime.Get(name); container != nil {
30
+
31
+		data, err := container.Export()
32
+		if err != nil {
33
+			return err
34
+		}
35
+
36
+		// Stream the entire contents of the container (basically a volatile snapshot)
37
+		if _, err := io.Copy(file, data); err != nil {
38
+			return err
39
+		}
40
+		return nil
41
+	}
42
+	return fmt.Errorf("No such container: %s", name)
43
+}
44
+
45
+func (srv *Server) Images(all, filter, quiet string) ([]ApiImages, error) {
46
+	var allImages map[string]*Image
47
+	var err error
48
+	if all == "1" {
49
+		allImages, err = srv.runtime.graph.Map()
50
+	} else {
51
+		allImages, err = srv.runtime.graph.Heads()
52
+	}
53
+	if err != nil {
54
+		return nil, err
55
+	}
56
+	var outs []ApiImages = []ApiImages{} //produce [] when empty instead of 'null'
57
+	for name, repository := range srv.runtime.repositories.Repositories {
58
+		if filter != "" && name != filter {
59
+			continue
60
+		}
61
+		for tag, id := range repository {
62
+			var out ApiImages
63
+			image, err := srv.runtime.graph.Get(id)
64
+			if err != nil {
65
+				log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
66
+				continue
67
+			}
68
+			delete(allImages, id)
69
+			if quiet != "1" {
70
+				out.Repository = name
71
+				out.Tag = tag
72
+				out.Id = TruncateId(id)
73
+				out.Created = image.Created.Unix()
74
+			} else {
75
+				out.Id = image.ShortId()
76
+			}
77
+			outs = append(outs, out)
78
+		}
79
+	}
80
+	// Display images which aren't part of a
81
+	if filter == "" {
82
+		for id, image := range allImages {
83
+			var out ApiImages
84
+			if quiet != "1" {
85
+				out.Repository = "<none>"
86
+				out.Tag = "<none>"
87
+				out.Id = TruncateId(id)
88
+				out.Created = image.Created.Unix()
89
+			} else {
90
+				out.Id = image.ShortId()
91
+			}
92
+			outs = append(outs, out)
93
+		}
94
+	}
95
+	return outs, nil
96
+}
97
+
98
+func (srv *Server) DockerInfo() ApiInfo {
99
+	images, _ := srv.runtime.graph.All()
100
+	var imgcount int
101
+	if images == nil {
102
+		imgcount = 0
103
+	} else {
104
+		imgcount = len(images)
105
+	}
106
+	var out ApiInfo
107
+	out.Containers = len(srv.runtime.List())
108
+	out.Version = VERSION
109
+	out.Images = imgcount
110
+	if os.Getenv("DEBUG") == "1" {
111
+		out.Debug = true
112
+		out.NFd = getTotalUsedFds()
113
+		out.NGoroutines = runtime.NumGoroutine()
114
+	}
115
+	return out
116
+}
117
+
118
+func (srv *Server) ImageHistory(name string) ([]ApiHistory, error) {
119
+	image, err := srv.runtime.repositories.LookupImage(name)
120
+	if err != nil {
121
+		return nil, err
122
+	}
123
+
124
+	var outs []ApiHistory = []ApiHistory{} //produce [] when empty instead of 'null'
125
+	err = image.WalkHistory(func(img *Image) error {
126
+		var out ApiHistory
127
+		out.Id = srv.runtime.repositories.ImageName(img.ShortId())
128
+		out.Created = img.Created.Unix()
129
+		out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
130
+		outs = append(outs, out)
131
+		return nil
132
+	})
133
+	return outs, nil
134
+
135
+}
136
+
137
+func (srv *Server) ContainerChanges(name string) ([]string, error) {
138
+	if container := srv.runtime.Get(name); container != nil {
139
+		changes, err := container.Changes()
140
+		if err != nil {
141
+			return nil, err
142
+		}
143
+		var changesStr []string
144
+		for _, name := range changes {
145
+			changesStr = append(changesStr, name.String())
146
+		}
147
+		return changesStr, nil
148
+	}
149
+	return nil, fmt.Errorf("No such container: %s", name)
150
+}
151
+
152
+func (srv *Server) ContainerPort(name, privatePort string) (string, error) {
153
+	if container := srv.runtime.Get(name); container != nil {
154
+		if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; exists {
155
+			return frontend, nil
156
+		}
157
+		return "", fmt.Errorf("No private port '%s' allocated on %s", privatePort, name)
158
+	}
159
+	return "", fmt.Errorf("No such container: %s", name)
160
+}
161
+
162
+func (srv *Server) Containers(all, notrunc, quiet string, n int) []ApiContainers {
163
+	var outs []ApiContainers = []ApiContainers{} //produce [] when empty instead of 'null'
164
+	for i, container := range srv.runtime.List() {
165
+		if !container.State.Running && all != "1" && n == -1 {
166
+			continue
167
+		}
168
+		if i == n {
169
+			break
170
+		}
171
+		var out ApiContainers
172
+		out.Id = container.ShortId()
173
+		if quiet != "1" {
174
+			command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
175
+			if notrunc != "1" {
176
+				command = Trunc(command, 20)
177
+			}
178
+			out.Image = srv.runtime.repositories.ImageName(container.Image)
179
+			out.Command = command
180
+			out.Created = container.Created.Unix()
181
+			out.Status = container.State.String()
182
+			out.Ports = container.NetworkSettings.PortMappingHuman()
183
+		}
184
+		outs = append(outs, out)
185
+	}
186
+	return outs
187
+}
188
+
189
+func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, config *Config) (string, error) {
190
+	img, err := srv.runtime.Commit(name, repo, tag, comment, author, config)
191
+	if err != nil {
192
+		return "", err
193
+	}
194
+	return img.ShortId(), err
195
+}
196
+
197
+func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
198
+	if err := srv.runtime.repositories.Set(repo, tag, name, force); err != nil {
199
+		return err
200
+	}
201
+	return nil
202
+}
203
+
204
+func (srv *Server) ImagePull(name string, file *os.File) error {
205
+	if srv.runtime.graph.LookupRemoteImage(name, srv.runtime.authConfig) {
206
+		if err := srv.runtime.graph.PullImage(file, name, srv.runtime.authConfig); err != nil {
207
+			return err
208
+		}
209
+	}
210
+	if err := srv.runtime.graph.PullRepository(file, name, "", srv.runtime.repositories, srv.runtime.authConfig); err != nil {
211
+		return err
212
+	}
213
+	return nil
214
+}
215
+
216
+func (srv *Server) ImageImport(src, repo, tag string, file *os.File) error {
217
+	var archive io.Reader
218
+	var resp *http.Response
219
+
220
+	if src == "-" {
221
+		r, w := io.Pipe()
222
+		go func() {
223
+			defer w.Close()
224
+			defer Debugf("Closing buffered stdin pipe")
225
+			io.Copy(w, file)
226
+		}()
227
+		archive = r
228
+	} else {
229
+		u, err := url.Parse(src)
230
+		if err != nil {
231
+			fmt.Fprintln(file, "Error: "+err.Error())
232
+		}
233
+		if u.Scheme == "" {
234
+			u.Scheme = "http"
235
+			u.Host = src
236
+			u.Path = ""
237
+		}
238
+		fmt.Fprintln(file, "Downloading from", u)
239
+		// Download with curl (pretty progress bar)                                                                            
240
+		// If curl is not available, fallback to http.Get()                                                                    
241
+		resp, err = Download(u.String(), file)
242
+		if err != nil {
243
+			return err
244
+		}
245
+		archive = ProgressReader(resp.Body, int(resp.ContentLength), file, "Importing %v/%v (%v)")
246
+	}
247
+	img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
248
+	if err != nil {
249
+		return err
250
+	}
251
+	// Optionally register the image at REPO/TAG                                                                                   
252
+	if repo != "" {
253
+		if err := srv.runtime.repositories.Set(repo, tag, img.Id, true); err != nil {
254
+			return err
255
+		}
256
+	}
257
+	fmt.Fprintln(file, img.ShortId())
258
+	return nil
259
+}
260
+
261
+func (srv *Server) ContainerCreate(config Config) (string, bool, bool, error) {
262
+	var memoryW, swapW bool
263
+
264
+	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
265
+		memoryW = true
266
+		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
267
+		config.Memory = 0
268
+	}
269
+
270
+	if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit {
271
+		swapW = true
272
+		log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.")
273
+		config.MemorySwap = -1
274
+	}
275
+	container, err := srv.runtime.Create(&config)
276
+	if err != nil {
277
+		if srv.runtime.graph.IsNotExist(err) {
278
+			return "", false, false, fmt.Errorf("No such image: %s", config.Image)
279
+		}
280
+		return "", false, false, err
281
+	}
282
+	return container.ShortId(), memoryW, swapW, nil
283
+}
284
+
285
+func (srv *Server) ContainerRestart(name string, t int) error {
286
+	if container := srv.runtime.Get(name); container != nil {
287
+		if err := container.Restart(t); err != nil {
288
+			return fmt.Errorf("Error restarting container %s: %s", name, err.Error())
289
+		}
290
+	} else {
291
+		return fmt.Errorf("No such container: %s", name)
292
+	}
293
+	return nil
294
+}
295
+
296
+func (srv *Server) ContainerDestroy(name string) error {
297
+	if container := srv.runtime.Get(name); container != nil {
298
+		if err := srv.runtime.Destroy(container); err != nil {
299
+			return fmt.Errorf("Error destroying container %s: %s", name, err.Error())
300
+		}
301
+	} else {
302
+		return fmt.Errorf("No such container: %s", name)
303
+	}
304
+	return nil
305
+}
306
+
307
+func (srv *Server) ImageDelete(name string) error {
308
+	img, err := srv.runtime.repositories.LookupImage(name)
309
+	if err != nil {
310
+		return fmt.Errorf("No such image: %s", name)
311
+	} else {
312
+		if err := srv.runtime.graph.Delete(img.Id); err != nil {
313
+			return fmt.Errorf("Error deleteing image %s: %s", name, err.Error())
314
+		}
315
+	}
316
+	return nil
317
+}
318
+
319
+func (srv *Server) ContainerStart(name string) error {
320
+	if container := srv.runtime.Get(name); container != nil {
321
+		if err := container.Start(); err != nil {
322
+			return fmt.Errorf("Error starting container %s: %s", name, err.Error())
323
+		}
324
+	} else {
325
+		return fmt.Errorf("No such container: %s", name)
326
+	}
327
+	return nil
328
+}
329
+
330
+func (srv *Server) ContainerStop(name string, t int) error {
331
+	if container := srv.runtime.Get(name); container != nil {
332
+		if err := container.Stop(t); err != nil {
333
+			return fmt.Errorf("Error stopping container %s: %s", name, err.Error())
334
+		}
335
+	} else {
336
+		return fmt.Errorf("No such container: %s", name)
337
+	}
338
+	return nil
339
+}
340
+
341
+func (srv *Server) ContainerWait(name string) (int, error) {
342
+	if container := srv.runtime.Get(name); container != nil {
343
+		return container.Wait(), nil
344
+	}
345
+	return 0, fmt.Errorf("No such container: %s", name)
346
+}
347
+
348
+func (srv *Server) ContainerAttach(name, logs, stream, stdin, stdout, stderr string, file *os.File) error {
349
+	if container := srv.runtime.Get(name); container != nil {
350
+		//logs
351
+		if logs == "1" {
352
+			if stdout == "1" {
353
+				cLog, err := container.ReadLog("stdout")
354
+				if err != nil {
355
+					Debugf(err.Error())
356
+				} else if _, err := io.Copy(file, cLog); err != nil {
357
+					Debugf(err.Error())
358
+				}
359
+			}
360
+			if stderr == "1" {
361
+				cLog, err := container.ReadLog("stderr")
362
+				if err != nil {
363
+					Debugf(err.Error())
364
+				} else if _, err := io.Copy(file, cLog); err != nil {
365
+					Debugf(err.Error())
366
+				}
367
+			}
368
+		}
369
+
370
+		//stream
371
+		if stream == "1" {
372
+			if container.State.Ghost {
373
+				return fmt.Errorf("Impossible to attach to a ghost container")
374
+			}
375
+
376
+			if container.Config.Tty {
377
+				oldState, err := SetRawTerminal()
378
+				if err != nil {
379
+					if os.Getenv("DEBUG") != "" {
380
+						log.Printf("Can't set the terminal in raw mode: %s", err)
381
+					}
382
+				} else {
383
+					defer RestoreTerminal(oldState)
384
+				}
385
+			}
386
+			var (
387
+				cStdin           io.ReadCloser
388
+				cStdout, cStderr io.Writer
389
+				cStdinCloser     io.Closer
390
+			)
391
+
392
+			if stdin == "1" {
393
+				r, w := io.Pipe()
394
+				go func() {
395
+					defer w.Close()
396
+					defer Debugf("Closing buffered stdin pipe")
397
+					io.Copy(w, file)
398
+				}()
399
+				cStdin = r
400
+				cStdinCloser = file
401
+			}
402
+			if stdout == "1" {
403
+				cStdout = file
404
+			}
405
+			if stderr == "1" {
406
+				cStderr = file
407
+			}
408
+
409
+			<-container.Attach(cStdin, cStdinCloser, cStdout, cStderr)
410
+		}
411
+	} else {
412
+		return fmt.Errorf("No such container: %s", name)
413
+	}
414
+	return nil
415
+}
416
+
417
+func (srv *Server) ContainerInspect(name string) (*Container, error) {
418
+	if container := srv.runtime.Get(name); container != nil {
419
+		return container, nil
420
+	}
421
+	return nil, fmt.Errorf("No such container: %s", name)
422
+}
423
+
424
+func (srv *Server) ImageInspect(name string) (*Image, error) {
425
+	if image, err := srv.runtime.repositories.LookupImage(name); err == nil && image != nil {
426
+		return image, nil
427
+	}
428
+	return nil, fmt.Errorf("No such image: %s", name)
429
+}
430
+
431
+func NewServer(autoRestart bool) (*Server, error) {
432
+	if runtime.GOARCH != "amd64" {
433
+		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
434
+	}
435
+	runtime, err := NewRuntime(autoRestart)
436
+	if err != nil {
437
+		return nil, err
438
+	}
439
+	srv := &Server{
440
+		runtime: runtime,
441
+	}
442
+	return srv, nil
443
+}
444
+
445
+type Server struct {
446
+	runtime *Runtime
447
+}