| ... | ... |
@@ -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 |
+} |