@@ -2,14 +2,18 @@ package docker
22
33import (
44 "encoding/json"
5- _ "fmt"
5+ "fmt"
66 "github.com/gorilla/mux"
7+ "io/ioutil"
78 "log"
89 "net/http"
10+ "os"
11+ "runtime"
12+ "strings"
913 "time"
1014)
1115
12- func ListenAndServe (addr string , runtime * Runtime ) error {
16+ func ListenAndServe (addr string , rtime * Runtime ) error {
1317 r := mux .NewRouter ()
1418 log .Printf ("Listening for HTTP on %s\n " , addr )
1519
@@ -32,7 +36,7 @@ func ListenAndServe(addr string, runtime *Runtime) error {
3236
3337 var ret SimpleMessage
3438 for _ , name := range ids {
35- container := runtime .Get (name )
39+ container := rtime .Get (name )
3640 if container == nil {
3741 ret .Message = "No such container: " + name + "\n "
3842 break
@@ -63,22 +67,22 @@ func ListenAndServe(addr string, runtime *Runtime) error {
6367 var allImages map [string ]* Image
6468 var err error
6569 if in .All {
66- allImages , err = runtime .graph .Map ()
70+ allImages , err = rtime .graph .Map ()
6771 } else {
68- allImages , err = runtime .graph .Heads ()
72+ allImages , err = rtime .graph .Heads ()
6973 }
7074 if err != nil {
7175 w .WriteHeader (500 )
7276 return
7377 }
7478 var outs []ImagesOut
75- for name , repository := range runtime .repositories .Repositories {
79+ for name , repository := range rtime .repositories .Repositories {
7680 if in .NameFilter != "" && name != in .NameFilter {
7781 continue
7882 }
7983 for tag , id := range repository {
8084 var out ImagesOut
81- image , err := runtime .graph .Get (id )
85+ image , err := rtime .graph .Get (id )
8286 if err != nil {
8387 log .Printf ("Warning: couldn't load %s from %s/%s: %s" , id , name , tag , err )
8488 continue
@@ -113,7 +117,310 @@ func ListenAndServe(addr string, runtime *Runtime) error {
113117
114118 b , err := json .Marshal (outs )
115119 if err != nil {
116- w .WriteHeader (500 )
120+ http .Error (w , err .Error (), http .StatusInternalServerError )
121+ } else {
122+ w .Write (b )
123+ }
124+
125+ })
126+
127+ r .Path ("/info" ).Methods ("GET" , "POST" ).HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
128+ images , _ := rtime .graph .All ()
129+ var imgcount int
130+ if images == nil {
131+ imgcount = 0
132+ } else {
133+ imgcount = len (images )
134+ }
135+ var out InfoOut
136+ out .Containers = len (rtime .List ())
137+ out .Version = VERSION
138+ out .Images = imgcount
139+ if os .Getenv ("DEBUG" ) == "1" {
140+ out .Debug = true
141+ out .NFd = getTotalUsedFds ()
142+ out .NGoroutines = runtime .NumGoroutine ()
143+ }
144+ b , err := json .Marshal (out )
145+ if err != nil {
146+ http .Error (w , err .Error (), http .StatusInternalServerError )
147+ } else {
148+ w .Write (b )
149+ }
150+ })
151+
152+ r .Path ("/history" ).Methods ("GET" , "POST" ).HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
153+ log .Println (r .RequestURI )
154+
155+ var in HistoryIn
156+ json .NewDecoder (r .Body ).Decode (& in )
157+
158+ image , err := rtime .repositories .LookupImage (in .Name )
159+ if err != nil {
160+ http .Error (w , err .Error (), http .StatusInternalServerError )
161+ return
162+ }
163+ var outs []HistoryOut
164+ err = image .WalkHistory (func (img * Image ) error {
165+ var out HistoryOut
166+ out .Id = rtime .repositories .ImageName (img .ShortId ())
167+ out .Created = HumanDuration (time .Now ().Sub (img .Created )) + " ago"
168+ out .CreatedBy = strings .Join (img .ContainerConfig .Cmd , " " )
169+ return nil
170+ })
171+
172+ b , err := json .Marshal (outs )
173+ if err != nil {
174+ http .Error (w , err .Error (), http .StatusInternalServerError )
175+ } else {
176+ w .Write (b )
177+ }
178+
179+ })
180+
181+ r .Path ("/logs" ).Methods ("GET" , "POST" ).HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
182+
183+ var in LogsIn
184+ json .NewDecoder (r .Body ).Decode (& in )
185+
186+ if container := rtime .Get (in .Name ); container != nil {
187+ var out LogsOut
188+
189+ logStdout , err := container .ReadLog ("stdout" )
190+ if err != nil {
191+ http .Error (w , err .Error (), http .StatusInternalServerError )
192+ return
193+ }
194+ logStderr , err := container .ReadLog ("stderr" )
195+ if err != nil {
196+ http .Error (w , err .Error (), http .StatusInternalServerError )
197+ return
198+ }
199+
200+ stdout , errStdout := ioutil .ReadAll (logStdout )
201+ if errStdout != nil {
202+ http .Error (w , errStdout .Error (), http .StatusInternalServerError )
203+ return
204+ } else {
205+ out .Stdout = fmt .Sprintf ("%s" , stdout )
206+ }
207+ stderr , errStderr := ioutil .ReadAll (logStderr )
208+ if errStderr != nil {
209+ http .Error (w , errStderr .Error (), http .StatusInternalServerError )
210+ return
211+ } else {
212+ out .Stderr = fmt .Sprintf ("%s" , stderr )
213+ }
214+
215+ b , err := json .Marshal (out )
216+ if err != nil {
217+ http .Error (w , err .Error (), http .StatusInternalServerError )
218+ } else {
219+ w .Write (b )
220+ }
221+
222+ } else {
223+ http .Error (w , "No such container: " + in .Name , http .StatusInternalServerError )
224+ }
225+ })
226+
227+ r .Path ("/ps" ).Methods ("GET" , "POST" ).HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
228+ var in PsIn
229+ json .NewDecoder (r .Body ).Decode (& in )
230+
231+ var outs []PsOut
232+
233+ for i , container := range rtime .List () {
234+ if ! container .State .Running && ! in .All && in .Last == - 1 {
235+ continue
236+ }
237+ if i == in .Last {
238+ break
239+ }
240+ var out PsOut
241+ out .Id = container .ShortId ()
242+ if ! in .Quiet {
243+ command := fmt .Sprintf ("%s %s" , container .Path , strings .Join (container .Args , " " ))
244+ if ! in .Full {
245+ command = Trunc (command , 20 )
246+ }
247+ out .Image = rtime .repositories .ImageName (container .Image )
248+ out .Command = command
249+ out .Created = HumanDuration (time .Now ().Sub (container .Created )) + " ago"
250+ out .Status = container .State .String ()
251+ }
252+ outs = append (outs , out )
253+ }
254+
255+ b , err := json .Marshal (outs )
256+ if err != nil {
257+ http .Error (w , err .Error (), http .StatusInternalServerError )
258+ } else {
259+ w .Write (b )
260+ }
261+
262+ })
263+
264+ r .Path ("/restart" ).Methods ("GET" , "POST" ).HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
265+ var ins , outs []string
266+ json .NewDecoder (r .Body ).Decode (& ins )
267+
268+ for _ , name := range ins {
269+ if container := rtime .Get (name ); container != nil {
270+ if err := container .Restart (); err != nil {
271+ http .Error (w , "Error restaring container " + name + ": " + err .Error (), http .StatusInternalServerError )
272+ return
273+ }
274+ outs = append (outs , container .ShortId ())
275+ } else {
276+ http .Error (w , "No such container: " + name , http .StatusInternalServerError )
277+ return
278+ }
279+ }
280+ b , err := json .Marshal (outs )
281+ if err != nil {
282+ http .Error (w , err .Error (), http .StatusInternalServerError )
283+ } else {
284+ w .Write (b )
285+ }
286+ })
287+
288+ r .Path ("/rm" ).Methods ("GET" , "POST" ).HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
289+ var ins , outs []string
290+ json .NewDecoder (r .Body ).Decode (& ins )
291+
292+ for _ , name := range ins {
293+ if container := rtime .Get (name ); container != nil {
294+ if err := rtime .Destroy (container ); err != nil {
295+ http .Error (w , "Error destroying container " + name + ": " + err .Error (), http .StatusInternalServerError )
296+ return
297+ }
298+ outs = append (outs , container .ShortId ())
299+ } else {
300+ http .Error (w , "No such container: " + name , http .StatusInternalServerError )
301+ return
302+ }
303+ }
304+ b , err := json .Marshal (outs )
305+ if err != nil {
306+ http .Error (w , err .Error (), http .StatusInternalServerError )
307+ } else {
308+ w .Write (b )
309+ }
310+
311+ })
312+
313+ r .Path ("/rmi" ).Methods ("GET" , "POST" ).HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
314+ var ins , outs []string
315+ json .NewDecoder (r .Body ).Decode (& ins )
316+
317+ for _ , name := range ins {
318+ img , err := rtime .repositories .LookupImage (name )
319+ if err != nil {
320+ http .Error (w , "No such image: " + name , http .StatusInternalServerError )
321+ return
322+ } else {
323+ if err := rtime .graph .Delete (img .Id ); err != nil {
324+ http .Error (w , "Error deleting image " + name + ": " + err .Error (), http .StatusInternalServerError )
325+ return
326+ }
327+ outs = append (outs , img .ShortId ())
328+ }
329+ }
330+ b , err := json .Marshal (outs )
331+ if err != nil {
332+ http .Error (w , err .Error (), http .StatusInternalServerError )
333+ } else {
334+ w .Write (b )
335+ }
336+
337+ })
338+
339+ r .Path ("/run" ).Methods ("GET" , "POST" ).HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
340+
341+ var config Config
342+ json .NewDecoder (r .Body ).Decode (& config )
343+
344+ hj , ok := w .(http.Hijacker )
345+ if ! ok {
346+ http .Error (w , "webserver doesn't support hijacking" , http .StatusInternalServerError )
347+ return
348+ }
349+ conn , bufrw , err := hj .Hijack ()
350+ if err != nil {
351+ http .Error (w , err .Error (), http .StatusInternalServerError )
352+ return
353+ }
354+ defer conn .Close ()
355+
356+ //TODO config.Tty
357+
358+ // Create new container
359+ container , err := rtime .Create (& config )
360+ if err != nil {
361+ // If container not found, try to pull it
362+ if rtime .graph .IsNotExist (err ) {
363+ bufrw .WriteString ("Image " + config .Image + " not found, trying to pull it from registry.\r \n " )
364+ bufrw .Flush ()
365+ //TODO if err = srv.CmdPull(stdin, stdout, config.Image); err != nil {
366+ //return err
367+ //}
368+ if container , err = rtime .Create (& config ); err != nil {
369+ http .Error (w , err .Error (), http .StatusInternalServerError )
370+ return
371+ }
372+ } else {
373+ http .Error (w , err .Error (), http .StatusInternalServerError )
374+ return
375+ }
376+ }
377+ container = container
378+ })
379+
380+ r .Path ("/start" ).Methods ("GET" , "POST" ).HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
381+ var ins , outs []string
382+ json .NewDecoder (r .Body ).Decode (& ins )
383+
384+ for _ , name := range ins {
385+ if container := rtime .Get (name ); container != nil {
386+ if err := container .Start (); err != nil {
387+ http .Error (w , "Error starting container " + name + ": " + err .Error (), http .StatusInternalServerError )
388+ return
389+ }
390+ outs = append (outs , container .ShortId ())
391+ } else {
392+ http .Error (w , "No such container: " + name , http .StatusInternalServerError )
393+ return
394+ }
395+ }
396+ b , err := json .Marshal (outs )
397+ if err != nil {
398+ http .Error (w , err .Error (), http .StatusInternalServerError )
399+ } else {
400+ w .Write (b )
401+ }
402+
403+ })
404+
405+ r .Path ("/stop" ).Methods ("GET" , "POST" ).HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
406+ var ins , outs []string
407+ json .NewDecoder (r .Body ).Decode (& ins )
408+
409+ for _ , name := range ins {
410+ if container := rtime .Get (name ); container != nil {
411+ if err := container .Stop (); err != nil {
412+ http .Error (w , "Error stopping container " + name + ": " + err .Error (), http .StatusInternalServerError )
413+ return
414+ }
415+ outs = append (outs , container .ShortId ())
416+ } else {
417+ http .Error (w , "No such container: " + name , http .StatusInternalServerError )
418+ return
419+ }
420+ }
421+ b , err := json .Marshal (outs )
422+ if err != nil {
423+ http .Error (w , err .Error (), http .StatusInternalServerError )
117424 } else {
118425 w .Write (b )
119426 }
0 commit comments