Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)
| 1 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,1149 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "bytes" |
|
| 6 |
- "code.google.com/p/go.net/websocket" |
|
| 7 |
- "encoding/base64" |
|
| 8 |
- "encoding/json" |
|
| 9 |
- "expvar" |
|
| 10 |
- "fmt" |
|
| 11 |
- "github.com/dotcloud/docker/auth" |
|
| 12 |
- "github.com/dotcloud/docker/engine" |
|
| 13 |
- "github.com/dotcloud/docker/pkg/systemd" |
|
| 14 |
- "github.com/dotcloud/docker/utils" |
|
| 15 |
- "github.com/gorilla/mux" |
|
| 16 |
- "io" |
|
| 17 |
- "io/ioutil" |
|
| 18 |
- "log" |
|
| 19 |
- "mime" |
|
| 20 |
- "net" |
|
| 21 |
- "net/http" |
|
| 22 |
- "net/http/pprof" |
|
| 23 |
- "os" |
|
| 24 |
- "regexp" |
|
| 25 |
- "strconv" |
|
| 26 |
- "strings" |
|
| 27 |
- "syscall" |
|
| 28 |
-) |
|
| 29 |
- |
|
| 30 |
-const ( |
|
| 31 |
- APIVERSION = 1.9 |
|
| 32 |
- DEFAULTHTTPHOST = "127.0.0.1" |
|
| 33 |
- DEFAULTHTTPPORT = 4243 |
|
| 34 |
- DEFAULTUNIXSOCKET = "/var/run/docker.sock" |
|
| 35 |
-) |
|
| 36 |
- |
|
| 37 |
-type HttpApiFunc func(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error |
|
| 38 |
- |
|
| 39 |
-func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
|
| 40 |
- conn, _, err := w.(http.Hijacker).Hijack() |
|
| 41 |
- if err != nil {
|
|
| 42 |
- return nil, nil, err |
|
| 43 |
- } |
|
| 44 |
- // Flush the options to make sure the client sets the raw mode |
|
| 45 |
- conn.Write([]byte{})
|
|
| 46 |
- return conn, conn, nil |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-//If we don't do this, POST method without Content-type (even with empty body) will fail |
|
| 50 |
-func parseForm(r *http.Request) error {
|
|
| 51 |
- if r == nil {
|
|
| 52 |
- return nil |
|
| 53 |
- } |
|
| 54 |
- if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
|
|
| 55 |
- return err |
|
| 56 |
- } |
|
| 57 |
- return nil |
|
| 58 |
-} |
|
| 59 |
- |
|
| 60 |
-func parseMultipartForm(r *http.Request) error {
|
|
| 61 |
- if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
|
|
| 62 |
- return err |
|
| 63 |
- } |
|
| 64 |
- return nil |
|
| 65 |
-} |
|
| 66 |
- |
|
| 67 |
-func httpError(w http.ResponseWriter, err error) {
|
|
| 68 |
- statusCode := http.StatusInternalServerError |
|
| 69 |
- // FIXME: this is brittle and should not be necessary. |
|
| 70 |
- // If we need to differentiate between different possible error types, we should |
|
| 71 |
- // create appropriate error types with clearly defined meaning. |
|
| 72 |
- if strings.Contains(err.Error(), "No such") {
|
|
| 73 |
- statusCode = http.StatusNotFound |
|
| 74 |
- } else if strings.Contains(err.Error(), "Bad parameter") {
|
|
| 75 |
- statusCode = http.StatusBadRequest |
|
| 76 |
- } else if strings.Contains(err.Error(), "Conflict") {
|
|
| 77 |
- statusCode = http.StatusConflict |
|
| 78 |
- } else if strings.Contains(err.Error(), "Impossible") {
|
|
| 79 |
- statusCode = http.StatusNotAcceptable |
|
| 80 |
- } else if strings.Contains(err.Error(), "Wrong login/password") {
|
|
| 81 |
- statusCode = http.StatusUnauthorized |
|
| 82 |
- } else if strings.Contains(err.Error(), "hasn't been activated") {
|
|
| 83 |
- statusCode = http.StatusForbidden |
|
| 84 |
- } |
|
| 85 |
- |
|
| 86 |
- if err != nil {
|
|
| 87 |
- utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
|
|
| 88 |
- http.Error(w, err.Error(), statusCode) |
|
| 89 |
- } |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 |
-func writeJSON(w http.ResponseWriter, code int, v engine.Env) error {
|
|
| 93 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 94 |
- w.WriteHeader(code) |
|
| 95 |
- return v.Encode(w) |
|
| 96 |
-} |
|
| 97 |
- |
|
| 98 |
-func getBoolParam(value string) (bool, error) {
|
|
| 99 |
- if value == "" {
|
|
| 100 |
- return false, nil |
|
| 101 |
- } |
|
| 102 |
- ret, err := strconv.ParseBool(value) |
|
| 103 |
- if err != nil {
|
|
| 104 |
- return false, fmt.Errorf("Bad parameter")
|
|
| 105 |
- } |
|
| 106 |
- return ret, nil |
|
| 107 |
-} |
|
| 108 |
- |
|
| 109 |
-func matchesContentType(contentType, expectedType string) bool {
|
|
| 110 |
- mimetype, _, err := mime.ParseMediaType(contentType) |
|
| 111 |
- if err != nil {
|
|
| 112 |
- utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
|
|
| 113 |
- } |
|
| 114 |
- return err == nil && mimetype == expectedType |
|
| 115 |
-} |
|
| 116 |
- |
|
| 117 |
-func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 118 |
- var ( |
|
| 119 |
- authConfig, err = ioutil.ReadAll(r.Body) |
|
| 120 |
- job = srv.Eng.Job("auth")
|
|
| 121 |
- status string |
|
| 122 |
- ) |
|
| 123 |
- if err != nil {
|
|
| 124 |
- return err |
|
| 125 |
- } |
|
| 126 |
- job.Setenv("authConfig", string(authConfig))
|
|
| 127 |
- job.Stdout.AddString(&status) |
|
| 128 |
- if err = job.Run(); err != nil {
|
|
| 129 |
- return err |
|
| 130 |
- } |
|
| 131 |
- if status != "" {
|
|
| 132 |
- var env engine.Env |
|
| 133 |
- env.Set("Status", status)
|
|
| 134 |
- return writeJSON(w, http.StatusOK, env) |
|
| 135 |
- } |
|
| 136 |
- w.WriteHeader(http.StatusNoContent) |
|
| 137 |
- return nil |
|
| 138 |
-} |
|
| 139 |
- |
|
| 140 |
-func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 141 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 142 |
- srv.Eng.ServeHTTP(w, r) |
|
| 143 |
- return nil |
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 147 |
- if vars == nil {
|
|
| 148 |
- return fmt.Errorf("Missing parameter")
|
|
| 149 |
- } |
|
| 150 |
- if err := parseForm(r); err != nil {
|
|
| 151 |
- return err |
|
| 152 |
- } |
|
| 153 |
- job := srv.Eng.Job("kill", vars["name"])
|
|
| 154 |
- if sig := r.Form.Get("signal"); sig != "" {
|
|
| 155 |
- job.Args = append(job.Args, sig) |
|
| 156 |
- } |
|
| 157 |
- if err := job.Run(); err != nil {
|
|
| 158 |
- return err |
|
| 159 |
- } |
|
| 160 |
- w.WriteHeader(http.StatusNoContent) |
|
| 161 |
- return nil |
|
| 162 |
-} |
|
| 163 |
- |
|
| 164 |
-func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 165 |
- if vars == nil {
|
|
| 166 |
- return fmt.Errorf("Missing parameter")
|
|
| 167 |
- } |
|
| 168 |
- job := srv.Eng.Job("export", vars["name"])
|
|
| 169 |
- job.Stdout.Add(w) |
|
| 170 |
- if err := job.Run(); err != nil {
|
|
| 171 |
- return err |
|
| 172 |
- } |
|
| 173 |
- return nil |
|
| 174 |
-} |
|
| 175 |
- |
|
| 176 |
-func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 177 |
- if err := parseForm(r); err != nil {
|
|
| 178 |
- return err |
|
| 179 |
- } |
|
| 180 |
- |
|
| 181 |
- var ( |
|
| 182 |
- err error |
|
| 183 |
- outs *engine.Table |
|
| 184 |
- job = srv.Eng.Job("images")
|
|
| 185 |
- ) |
|
| 186 |
- |
|
| 187 |
- job.Setenv("filter", r.Form.Get("filter"))
|
|
| 188 |
- job.Setenv("all", r.Form.Get("all"))
|
|
| 189 |
- |
|
| 190 |
- if version > 1.8 {
|
|
| 191 |
- job.Stdout.Add(w) |
|
| 192 |
- } else if outs, err = job.Stdout.AddListTable(); err != nil {
|
|
| 193 |
- return err |
|
| 194 |
- } |
|
| 195 |
- |
|
| 196 |
- if err := job.Run(); err != nil {
|
|
| 197 |
- return err |
|
| 198 |
- } |
|
| 199 |
- |
|
| 200 |
- if version < 1.8 && outs != nil { // Convert to legacy format
|
|
| 201 |
- outsLegacy := engine.NewTable("Created", 0)
|
|
| 202 |
- for _, out := range outs.Data {
|
|
| 203 |
- for _, repoTag := range out.GetList("RepoTags") {
|
|
| 204 |
- parts := strings.Split(repoTag, ":") |
|
| 205 |
- outLegacy := &engine.Env{}
|
|
| 206 |
- outLegacy.Set("Repository", parts[0])
|
|
| 207 |
- outLegacy.Set("Tag", parts[1])
|
|
| 208 |
- outLegacy.Set("ID", out.Get("ID"))
|
|
| 209 |
- outLegacy.SetInt64("Created", out.GetInt64("Created"))
|
|
| 210 |
- outLegacy.SetInt64("Size", out.GetInt64("Size"))
|
|
| 211 |
- outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize"))
|
|
| 212 |
- outsLegacy.Add(outLegacy) |
|
| 213 |
- } |
|
| 214 |
- } |
|
| 215 |
- if _, err := outsLegacy.WriteListTo(w); err != nil {
|
|
| 216 |
- return err |
|
| 217 |
- } |
|
| 218 |
- } |
|
| 219 |
- return nil |
|
| 220 |
-} |
|
| 221 |
- |
|
| 222 |
-func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 223 |
- if version > 1.6 {
|
|
| 224 |
- w.WriteHeader(http.StatusNotFound) |
|
| 225 |
- return fmt.Errorf("This is now implemented in the client.")
|
|
| 226 |
- } |
|
| 227 |
- srv.Eng.ServeHTTP(w, r) |
|
| 228 |
- return nil |
|
| 229 |
-} |
|
| 230 |
- |
|
| 231 |
-func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 232 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 233 |
- srv.Eng.ServeHTTP(w, r) |
|
| 234 |
- return nil |
|
| 235 |
-} |
|
| 236 |
- |
|
| 237 |
-func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 238 |
- if err := parseForm(r); err != nil {
|
|
| 239 |
- return err |
|
| 240 |
- } |
|
| 241 |
- |
|
| 242 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 243 |
- var job = srv.Eng.Job("events", r.RemoteAddr)
|
|
| 244 |
- job.Stdout.Add(utils.NewWriteFlusher(w)) |
|
| 245 |
- job.Setenv("since", r.Form.Get("since"))
|
|
| 246 |
- return job.Run() |
|
| 247 |
-} |
|
| 248 |
- |
|
| 249 |
-func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 250 |
- if vars == nil {
|
|
| 251 |
- return fmt.Errorf("Missing parameter")
|
|
| 252 |
- } |
|
| 253 |
- |
|
| 254 |
- var job = srv.Eng.Job("history", vars["name"])
|
|
| 255 |
- job.Stdout.Add(w) |
|
| 256 |
- |
|
| 257 |
- if err := job.Run(); err != nil {
|
|
| 258 |
- return err |
|
| 259 |
- } |
|
| 260 |
- return nil |
|
| 261 |
-} |
|
| 262 |
- |
|
| 263 |
-func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 264 |
- if vars == nil {
|
|
| 265 |
- return fmt.Errorf("Missing parameter")
|
|
| 266 |
- } |
|
| 267 |
- var job = srv.Eng.Job("changes", vars["name"])
|
|
| 268 |
- job.Stdout.Add(w) |
|
| 269 |
- |
|
| 270 |
- return job.Run() |
|
| 271 |
-} |
|
| 272 |
- |
|
| 273 |
-func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 274 |
- if version < 1.4 {
|
|
| 275 |
- return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
|
|
| 276 |
- } |
|
| 277 |
- if vars == nil {
|
|
| 278 |
- return fmt.Errorf("Missing parameter")
|
|
| 279 |
- } |
|
| 280 |
- if err := parseForm(r); err != nil {
|
|
| 281 |
- return err |
|
| 282 |
- } |
|
| 283 |
- |
|
| 284 |
- job := srv.Eng.Job("top", vars["name"], r.Form.Get("ps_args"))
|
|
| 285 |
- job.Stdout.Add(w) |
|
| 286 |
- return job.Run() |
|
| 287 |
-} |
|
| 288 |
- |
|
| 289 |
-func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 290 |
- if err := parseForm(r); err != nil {
|
|
| 291 |
- return err |
|
| 292 |
- } |
|
| 293 |
- var ( |
|
| 294 |
- err error |
|
| 295 |
- outs *engine.Table |
|
| 296 |
- job = srv.Eng.Job("containers")
|
|
| 297 |
- ) |
|
| 298 |
- |
|
| 299 |
- job.Setenv("all", r.Form.Get("all"))
|
|
| 300 |
- job.Setenv("size", r.Form.Get("size"))
|
|
| 301 |
- job.Setenv("since", r.Form.Get("since"))
|
|
| 302 |
- job.Setenv("before", r.Form.Get("before"))
|
|
| 303 |
- job.Setenv("limit", r.Form.Get("limit"))
|
|
| 304 |
- |
|
| 305 |
- if version > 1.5 {
|
|
| 306 |
- job.Stdout.Add(w) |
|
| 307 |
- } else if outs, err = job.Stdout.AddTable(); err != nil {
|
|
| 308 |
- return err |
|
| 309 |
- } |
|
| 310 |
- if err = job.Run(); err != nil {
|
|
| 311 |
- return err |
|
| 312 |
- } |
|
| 313 |
- if version < 1.5 { // Convert to legacy format
|
|
| 314 |
- for _, out := range outs.Data {
|
|
| 315 |
- ports := engine.NewTable("", 0)
|
|
| 316 |
- ports.ReadListFrom([]byte(out.Get("Ports")))
|
|
| 317 |
- out.Set("Ports", displayablePorts(ports))
|
|
| 318 |
- } |
|
| 319 |
- if _, err = outs.WriteListTo(w); err != nil {
|
|
| 320 |
- return err |
|
| 321 |
- } |
|
| 322 |
- } |
|
| 323 |
- return nil |
|
| 324 |
-} |
|
| 325 |
- |
|
| 326 |
-func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 327 |
- if err := parseForm(r); err != nil {
|
|
| 328 |
- return err |
|
| 329 |
- } |
|
| 330 |
- if vars == nil {
|
|
| 331 |
- return fmt.Errorf("Missing parameter")
|
|
| 332 |
- } |
|
| 333 |
- |
|
| 334 |
- job := srv.Eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
|
|
| 335 |
- job.Setenv("force", r.Form.Get("force"))
|
|
| 336 |
- if err := job.Run(); err != nil {
|
|
| 337 |
- return err |
|
| 338 |
- } |
|
| 339 |
- w.WriteHeader(http.StatusCreated) |
|
| 340 |
- return nil |
|
| 341 |
-} |
|
| 342 |
- |
|
| 343 |
-func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 344 |
- if err := parseForm(r); err != nil {
|
|
| 345 |
- return err |
|
| 346 |
- } |
|
| 347 |
- var ( |
|
| 348 |
- config engine.Env |
|
| 349 |
- env engine.Env |
|
| 350 |
- job = srv.Eng.Job("commit", r.Form.Get("container"))
|
|
| 351 |
- ) |
|
| 352 |
- if err := config.Import(r.Body); err != nil {
|
|
| 353 |
- utils.Errorf("%s", err)
|
|
| 354 |
- } |
|
| 355 |
- |
|
| 356 |
- job.Setenv("repo", r.Form.Get("repo"))
|
|
| 357 |
- job.Setenv("tag", r.Form.Get("tag"))
|
|
| 358 |
- job.Setenv("author", r.Form.Get("author"))
|
|
| 359 |
- job.Setenv("comment", r.Form.Get("comment"))
|
|
| 360 |
- job.SetenvSubEnv("config", &config)
|
|
| 361 |
- |
|
| 362 |
- var id string |
|
| 363 |
- job.Stdout.AddString(&id) |
|
| 364 |
- if err := job.Run(); err != nil {
|
|
| 365 |
- return err |
|
| 366 |
- } |
|
| 367 |
- env.Set("Id", id)
|
|
| 368 |
- return writeJSON(w, http.StatusCreated, env) |
|
| 369 |
-} |
|
| 370 |
- |
|
| 371 |
-// Creates an image from Pull or from Import |
|
| 372 |
-func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 373 |
- if err := parseForm(r); err != nil {
|
|
| 374 |
- return err |
|
| 375 |
- } |
|
| 376 |
- |
|
| 377 |
- var ( |
|
| 378 |
- image = r.Form.Get("fromImage")
|
|
| 379 |
- tag = r.Form.Get("tag")
|
|
| 380 |
- job *engine.Job |
|
| 381 |
- ) |
|
| 382 |
- authEncoded := r.Header.Get("X-Registry-Auth")
|
|
| 383 |
- authConfig := &auth.AuthConfig{}
|
|
| 384 |
- if authEncoded != "" {
|
|
| 385 |
- authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) |
|
| 386 |
- if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
|
| 387 |
- // for a pull it is not an error if no auth was given |
|
| 388 |
- // to increase compatibility with the existing api it is defaulting to be empty |
|
| 389 |
- authConfig = &auth.AuthConfig{}
|
|
| 390 |
- } |
|
| 391 |
- } |
|
| 392 |
- if version > 1.0 {
|
|
| 393 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 394 |
- } |
|
| 395 |
- if image != "" { //pull
|
|
| 396 |
- metaHeaders := map[string][]string{}
|
|
| 397 |
- for k, v := range r.Header {
|
|
| 398 |
- if strings.HasPrefix(k, "X-Meta-") {
|
|
| 399 |
- metaHeaders[k] = v |
|
| 400 |
- } |
|
| 401 |
- } |
|
| 402 |
- job = srv.Eng.Job("pull", r.Form.Get("fromImage"), tag)
|
|
| 403 |
- job.SetenvBool("parallel", version > 1.3)
|
|
| 404 |
- job.SetenvJson("metaHeaders", metaHeaders)
|
|
| 405 |
- job.SetenvJson("authConfig", authConfig)
|
|
| 406 |
- } else { //import
|
|
| 407 |
- job = srv.Eng.Job("import", r.Form.Get("fromSrc"), r.Form.Get("repo"), tag)
|
|
| 408 |
- job.Stdin.Add(r.Body) |
|
| 409 |
- } |
|
| 410 |
- |
|
| 411 |
- job.SetenvBool("json", version > 1.0)
|
|
| 412 |
- job.Stdout.Add(utils.NewWriteFlusher(w)) |
|
| 413 |
- if err := job.Run(); err != nil {
|
|
| 414 |
- if !job.Stdout.Used() {
|
|
| 415 |
- return err |
|
| 416 |
- } |
|
| 417 |
- sf := utils.NewStreamFormatter(version > 1.0) |
|
| 418 |
- w.Write(sf.FormatError(err)) |
|
| 419 |
- } |
|
| 420 |
- |
|
| 421 |
- return nil |
|
| 422 |
-} |
|
| 423 |
- |
|
| 424 |
-func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 425 |
- if err := parseForm(r); err != nil {
|
|
| 426 |
- return err |
|
| 427 |
- } |
|
| 428 |
- var ( |
|
| 429 |
- authEncoded = r.Header.Get("X-Registry-Auth")
|
|
| 430 |
- authConfig = &auth.AuthConfig{}
|
|
| 431 |
- metaHeaders = map[string][]string{}
|
|
| 432 |
- ) |
|
| 433 |
- |
|
| 434 |
- if authEncoded != "" {
|
|
| 435 |
- authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) |
|
| 436 |
- if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
|
| 437 |
- // for a search it is not an error if no auth was given |
|
| 438 |
- // to increase compatibility with the existing api it is defaulting to be empty |
|
| 439 |
- authConfig = &auth.AuthConfig{}
|
|
| 440 |
- } |
|
| 441 |
- } |
|
| 442 |
- for k, v := range r.Header {
|
|
| 443 |
- if strings.HasPrefix(k, "X-Meta-") {
|
|
| 444 |
- metaHeaders[k] = v |
|
| 445 |
- } |
|
| 446 |
- } |
|
| 447 |
- |
|
| 448 |
- var job = srv.Eng.Job("search", r.Form.Get("term"))
|
|
| 449 |
- job.SetenvJson("metaHeaders", metaHeaders)
|
|
| 450 |
- job.SetenvJson("authConfig", authConfig)
|
|
| 451 |
- job.Stdout.Add(w) |
|
| 452 |
- |
|
| 453 |
- return job.Run() |
|
| 454 |
-} |
|
| 455 |
- |
|
| 456 |
-func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 457 |
- if err := parseForm(r); err != nil {
|
|
| 458 |
- return err |
|
| 459 |
- } |
|
| 460 |
- if vars == nil {
|
|
| 461 |
- return fmt.Errorf("Missing parameter")
|
|
| 462 |
- } |
|
| 463 |
- if version > 1.0 {
|
|
| 464 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 465 |
- } |
|
| 466 |
- |
|
| 467 |
- job := srv.Eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path"))
|
|
| 468 |
- job.SetenvBool("json", version > 1.0)
|
|
| 469 |
- job.Stdout.Add(w) |
|
| 470 |
- if err := job.Run(); err != nil {
|
|
| 471 |
- if !job.Stdout.Used() {
|
|
| 472 |
- return err |
|
| 473 |
- } |
|
| 474 |
- sf := utils.NewStreamFormatter(version > 1.0) |
|
| 475 |
- w.Write(sf.FormatError(err)) |
|
| 476 |
- } |
|
| 477 |
- |
|
| 478 |
- return nil |
|
| 479 |
-} |
|
| 480 |
- |
|
| 481 |
-func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 482 |
- if vars == nil {
|
|
| 483 |
- return fmt.Errorf("Missing parameter")
|
|
| 484 |
- } |
|
| 485 |
- |
|
| 486 |
- metaHeaders := map[string][]string{}
|
|
| 487 |
- for k, v := range r.Header {
|
|
| 488 |
- if strings.HasPrefix(k, "X-Meta-") {
|
|
| 489 |
- metaHeaders[k] = v |
|
| 490 |
- } |
|
| 491 |
- } |
|
| 492 |
- if err := parseForm(r); err != nil {
|
|
| 493 |
- return err |
|
| 494 |
- } |
|
| 495 |
- authConfig := &auth.AuthConfig{}
|
|
| 496 |
- |
|
| 497 |
- authEncoded := r.Header.Get("X-Registry-Auth")
|
|
| 498 |
- if authEncoded != "" {
|
|
| 499 |
- // the new format is to handle the authConfig as a header |
|
| 500 |
- authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) |
|
| 501 |
- if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
|
| 502 |
- // to increase compatibility to existing api it is defaulting to be empty |
|
| 503 |
- authConfig = &auth.AuthConfig{}
|
|
| 504 |
- } |
|
| 505 |
- } else {
|
|
| 506 |
- // the old format is supported for compatibility if there was no authConfig header |
|
| 507 |
- if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
|
|
| 508 |
- return err |
|
| 509 |
- } |
|
| 510 |
- } |
|
| 511 |
- |
|
| 512 |
- if version > 1.0 {
|
|
| 513 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 514 |
- } |
|
| 515 |
- job := srv.Eng.Job("push", vars["name"])
|
|
| 516 |
- job.SetenvJson("metaHeaders", metaHeaders)
|
|
| 517 |
- job.SetenvJson("authConfig", authConfig)
|
|
| 518 |
- job.SetenvBool("json", version > 1.0)
|
|
| 519 |
- job.Stdout.Add(utils.NewWriteFlusher(w)) |
|
| 520 |
- |
|
| 521 |
- if err := job.Run(); err != nil {
|
|
| 522 |
- if !job.Stdout.Used() {
|
|
| 523 |
- return err |
|
| 524 |
- } |
|
| 525 |
- sf := utils.NewStreamFormatter(version > 1.0) |
|
| 526 |
- w.Write(sf.FormatError(err)) |
|
| 527 |
- } |
|
| 528 |
- return nil |
|
| 529 |
-} |
|
| 530 |
- |
|
| 531 |
-func getImagesGet(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 532 |
- if vars == nil {
|
|
| 533 |
- return fmt.Errorf("Missing parameter")
|
|
| 534 |
- } |
|
| 535 |
- if version > 1.0 {
|
|
| 536 |
- w.Header().Set("Content-Type", "application/x-tar")
|
|
| 537 |
- } |
|
| 538 |
- job := srv.Eng.Job("image_export", vars["name"])
|
|
| 539 |
- job.Stdout.Add(w) |
|
| 540 |
- return job.Run() |
|
| 541 |
-} |
|
| 542 |
- |
|
| 543 |
-func postImagesLoad(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 544 |
- job := srv.Eng.Job("load")
|
|
| 545 |
- job.Stdin.Add(r.Body) |
|
| 546 |
- return job.Run() |
|
| 547 |
-} |
|
| 548 |
- |
|
| 549 |
-func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 550 |
- if err := parseForm(r); err != nil {
|
|
| 551 |
- return nil |
|
| 552 |
- } |
|
| 553 |
- var ( |
|
| 554 |
- out engine.Env |
|
| 555 |
- job = srv.Eng.Job("create", r.Form.Get("name"))
|
|
| 556 |
- outWarnings []string |
|
| 557 |
- outId string |
|
| 558 |
- warnings = bytes.NewBuffer(nil) |
|
| 559 |
- ) |
|
| 560 |
- if err := job.DecodeEnv(r.Body); err != nil {
|
|
| 561 |
- return err |
|
| 562 |
- } |
|
| 563 |
- // Read container ID from the first line of stdout |
|
| 564 |
- job.Stdout.AddString(&outId) |
|
| 565 |
- // Read warnings from stderr |
|
| 566 |
- job.Stderr.Add(warnings) |
|
| 567 |
- if err := job.Run(); err != nil {
|
|
| 568 |
- return err |
|
| 569 |
- } |
|
| 570 |
- // Parse warnings from stderr |
|
| 571 |
- scanner := bufio.NewScanner(warnings) |
|
| 572 |
- for scanner.Scan() {
|
|
| 573 |
- outWarnings = append(outWarnings, scanner.Text()) |
|
| 574 |
- } |
|
| 575 |
- out.Set("Id", outId)
|
|
| 576 |
- out.SetList("Warnings", outWarnings)
|
|
| 577 |
- return writeJSON(w, http.StatusCreated, out) |
|
| 578 |
-} |
|
| 579 |
- |
|
| 580 |
-func postContainersRestart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 581 |
- if err := parseForm(r); err != nil {
|
|
| 582 |
- return err |
|
| 583 |
- } |
|
| 584 |
- if vars == nil {
|
|
| 585 |
- return fmt.Errorf("Missing parameter")
|
|
| 586 |
- } |
|
| 587 |
- job := srv.Eng.Job("restart", vars["name"])
|
|
| 588 |
- job.Setenv("t", r.Form.Get("t"))
|
|
| 589 |
- if err := job.Run(); err != nil {
|
|
| 590 |
- return err |
|
| 591 |
- } |
|
| 592 |
- w.WriteHeader(http.StatusNoContent) |
|
| 593 |
- return nil |
|
| 594 |
-} |
|
| 595 |
- |
|
| 596 |
-func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 597 |
- if err := parseForm(r); err != nil {
|
|
| 598 |
- return err |
|
| 599 |
- } |
|
| 600 |
- if vars == nil {
|
|
| 601 |
- return fmt.Errorf("Missing parameter")
|
|
| 602 |
- } |
|
| 603 |
- job := srv.Eng.Job("container_delete", vars["name"])
|
|
| 604 |
- job.Setenv("removeVolume", r.Form.Get("v"))
|
|
| 605 |
- job.Setenv("removeLink", r.Form.Get("link"))
|
|
| 606 |
- if err := job.Run(); err != nil {
|
|
| 607 |
- return err |
|
| 608 |
- } |
|
| 609 |
- w.WriteHeader(http.StatusNoContent) |
|
| 610 |
- return nil |
|
| 611 |
-} |
|
| 612 |
- |
|
| 613 |
-func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 614 |
- if err := parseForm(r); err != nil {
|
|
| 615 |
- return err |
|
| 616 |
- } |
|
| 617 |
- if vars == nil {
|
|
| 618 |
- return fmt.Errorf("Missing parameter")
|
|
| 619 |
- } |
|
| 620 |
- var job = srv.Eng.Job("image_delete", vars["name"])
|
|
| 621 |
- job.Stdout.Add(w) |
|
| 622 |
- job.SetenvBool("autoPrune", version > 1.1)
|
|
| 623 |
- |
|
| 624 |
- return job.Run() |
|
| 625 |
-} |
|
| 626 |
- |
|
| 627 |
-func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 628 |
- if vars == nil {
|
|
| 629 |
- return fmt.Errorf("Missing parameter")
|
|
| 630 |
- } |
|
| 631 |
- name := vars["name"] |
|
| 632 |
- job := srv.Eng.Job("start", name)
|
|
| 633 |
- // allow a nil body for backwards compatibility |
|
| 634 |
- if r.Body != nil {
|
|
| 635 |
- if matchesContentType(r.Header.Get("Content-Type"), "application/json") {
|
|
| 636 |
- if err := job.DecodeEnv(r.Body); err != nil {
|
|
| 637 |
- return err |
|
| 638 |
- } |
|
| 639 |
- } |
|
| 640 |
- } |
|
| 641 |
- if err := job.Run(); err != nil {
|
|
| 642 |
- return err |
|
| 643 |
- } |
|
| 644 |
- w.WriteHeader(http.StatusNoContent) |
|
| 645 |
- return nil |
|
| 646 |
-} |
|
| 647 |
- |
|
| 648 |
-func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 649 |
- if err := parseForm(r); err != nil {
|
|
| 650 |
- return err |
|
| 651 |
- } |
|
| 652 |
- if vars == nil {
|
|
| 653 |
- return fmt.Errorf("Missing parameter")
|
|
| 654 |
- } |
|
| 655 |
- job := srv.Eng.Job("stop", vars["name"])
|
|
| 656 |
- job.Setenv("t", r.Form.Get("t"))
|
|
| 657 |
- if err := job.Run(); err != nil {
|
|
| 658 |
- return err |
|
| 659 |
- } |
|
| 660 |
- w.WriteHeader(http.StatusNoContent) |
|
| 661 |
- return nil |
|
| 662 |
-} |
|
| 663 |
- |
|
| 664 |
-func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 665 |
- if vars == nil {
|
|
| 666 |
- return fmt.Errorf("Missing parameter")
|
|
| 667 |
- } |
|
| 668 |
- var ( |
|
| 669 |
- env engine.Env |
|
| 670 |
- status string |
|
| 671 |
- job = srv.Eng.Job("wait", vars["name"])
|
|
| 672 |
- ) |
|
| 673 |
- job.Stdout.AddString(&status) |
|
| 674 |
- if err := job.Run(); err != nil {
|
|
| 675 |
- return err |
|
| 676 |
- } |
|
| 677 |
- // Parse a 16-bit encoded integer to map typical unix exit status. |
|
| 678 |
- _, err := strconv.ParseInt(status, 10, 16) |
|
| 679 |
- if err != nil {
|
|
| 680 |
- return err |
|
| 681 |
- } |
|
| 682 |
- env.Set("StatusCode", status)
|
|
| 683 |
- return writeJSON(w, http.StatusOK, env) |
|
| 684 |
-} |
|
| 685 |
- |
|
| 686 |
-func postContainersResize(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 687 |
- if err := parseForm(r); err != nil {
|
|
| 688 |
- return err |
|
| 689 |
- } |
|
| 690 |
- if vars == nil {
|
|
| 691 |
- return fmt.Errorf("Missing parameter")
|
|
| 692 |
- } |
|
| 693 |
- if err := srv.Eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
|
|
| 694 |
- return err |
|
| 695 |
- } |
|
| 696 |
- return nil |
|
| 697 |
-} |
|
| 698 |
- |
|
| 699 |
-func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 700 |
- if err := parseForm(r); err != nil {
|
|
| 701 |
- return err |
|
| 702 |
- } |
|
| 703 |
- if vars == nil {
|
|
| 704 |
- return fmt.Errorf("Missing parameter")
|
|
| 705 |
- } |
|
| 706 |
- |
|
| 707 |
- var ( |
|
| 708 |
- job = srv.Eng.Job("inspect", vars["name"], "container")
|
|
| 709 |
- c, err = job.Stdout.AddEnv() |
|
| 710 |
- ) |
|
| 711 |
- if err != nil {
|
|
| 712 |
- return err |
|
| 713 |
- } |
|
| 714 |
- if err = job.Run(); err != nil {
|
|
| 715 |
- return err |
|
| 716 |
- } |
|
| 717 |
- |
|
| 718 |
- inStream, outStream, err := hijackServer(w) |
|
| 719 |
- if err != nil {
|
|
| 720 |
- return err |
|
| 721 |
- } |
|
| 722 |
- defer func() {
|
|
| 723 |
- if tcpc, ok := inStream.(*net.TCPConn); ok {
|
|
| 724 |
- tcpc.CloseWrite() |
|
| 725 |
- } else {
|
|
| 726 |
- inStream.Close() |
|
| 727 |
- } |
|
| 728 |
- }() |
|
| 729 |
- defer func() {
|
|
| 730 |
- if tcpc, ok := outStream.(*net.TCPConn); ok {
|
|
| 731 |
- tcpc.CloseWrite() |
|
| 732 |
- } else if closer, ok := outStream.(io.Closer); ok {
|
|
| 733 |
- closer.Close() |
|
| 734 |
- } |
|
| 735 |
- }() |
|
| 736 |
- |
|
| 737 |
- var errStream io.Writer |
|
| 738 |
- |
|
| 739 |
- fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") |
|
| 740 |
- |
|
| 741 |
- if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version >= 1.6 {
|
|
| 742 |
- errStream = utils.NewStdWriter(outStream, utils.Stderr) |
|
| 743 |
- outStream = utils.NewStdWriter(outStream, utils.Stdout) |
|
| 744 |
- } else {
|
|
| 745 |
- errStream = outStream |
|
| 746 |
- } |
|
| 747 |
- |
|
| 748 |
- job = srv.Eng.Job("attach", vars["name"])
|
|
| 749 |
- job.Setenv("logs", r.Form.Get("logs"))
|
|
| 750 |
- job.Setenv("stream", r.Form.Get("stream"))
|
|
| 751 |
- job.Setenv("stdin", r.Form.Get("stdin"))
|
|
| 752 |
- job.Setenv("stdout", r.Form.Get("stdout"))
|
|
| 753 |
- job.Setenv("stderr", r.Form.Get("stderr"))
|
|
| 754 |
- job.Stdin.Add(inStream) |
|
| 755 |
- job.Stdout.Add(outStream) |
|
| 756 |
- job.Stderr.Set(errStream) |
|
| 757 |
- if err := job.Run(); err != nil {
|
|
| 758 |
- fmt.Fprintf(outStream, "Error: %s\n", err) |
|
| 759 |
- |
|
| 760 |
- } |
|
| 761 |
- return nil |
|
| 762 |
-} |
|
| 763 |
- |
|
| 764 |
-func wsContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 765 |
- if err := parseForm(r); err != nil {
|
|
| 766 |
- return err |
|
| 767 |
- } |
|
| 768 |
- if vars == nil {
|
|
| 769 |
- return fmt.Errorf("Missing parameter")
|
|
| 770 |
- } |
|
| 771 |
- |
|
| 772 |
- if err := srv.Eng.Job("inspect", vars["name"], "container").Run(); err != nil {
|
|
| 773 |
- return err |
|
| 774 |
- } |
|
| 775 |
- |
|
| 776 |
- h := websocket.Handler(func(ws *websocket.Conn) {
|
|
| 777 |
- defer ws.Close() |
|
| 778 |
- job := srv.Eng.Job("attach", vars["name"])
|
|
| 779 |
- job.Setenv("logs", r.Form.Get("logs"))
|
|
| 780 |
- job.Setenv("stream", r.Form.Get("stream"))
|
|
| 781 |
- job.Setenv("stdin", r.Form.Get("stdin"))
|
|
| 782 |
- job.Setenv("stdout", r.Form.Get("stdout"))
|
|
| 783 |
- job.Setenv("stderr", r.Form.Get("stderr"))
|
|
| 784 |
- job.Stdin.Add(ws) |
|
| 785 |
- job.Stdout.Add(ws) |
|
| 786 |
- job.Stderr.Set(ws) |
|
| 787 |
- if err := job.Run(); err != nil {
|
|
| 788 |
- utils.Errorf("Error: %s", err)
|
|
| 789 |
- } |
|
| 790 |
- }) |
|
| 791 |
- h.ServeHTTP(w, r) |
|
| 792 |
- |
|
| 793 |
- return nil |
|
| 794 |
-} |
|
| 795 |
- |
|
| 796 |
-func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 797 |
- if vars == nil {
|
|
| 798 |
- return fmt.Errorf("Missing parameter")
|
|
| 799 |
- } |
|
| 800 |
- var job = srv.Eng.Job("inspect", vars["name"], "container")
|
|
| 801 |
- job.Stdout.Add(w) |
|
| 802 |
- job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
|
|
| 803 |
- return job.Run() |
|
| 804 |
-} |
|
| 805 |
- |
|
| 806 |
-func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 807 |
- if vars == nil {
|
|
| 808 |
- return fmt.Errorf("Missing parameter")
|
|
| 809 |
- } |
|
| 810 |
- var job = srv.Eng.Job("inspect", vars["name"], "image")
|
|
| 811 |
- job.Stdout.Add(w) |
|
| 812 |
- job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
|
|
| 813 |
- return job.Run() |
|
| 814 |
-} |
|
| 815 |
- |
|
| 816 |
-func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 817 |
- if version < 1.3 {
|
|
| 818 |
- return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
|
|
| 819 |
- } |
|
| 820 |
- var ( |
|
| 821 |
- authEncoded = r.Header.Get("X-Registry-Auth")
|
|
| 822 |
- authConfig = &auth.AuthConfig{}
|
|
| 823 |
- configFileEncoded = r.Header.Get("X-Registry-Config")
|
|
| 824 |
- configFile = &auth.ConfigFile{}
|
|
| 825 |
- job = srv.Eng.Job("build")
|
|
| 826 |
- ) |
|
| 827 |
- |
|
| 828 |
- // This block can be removed when API versions prior to 1.9 are deprecated. |
|
| 829 |
- // Both headers will be parsed and sent along to the daemon, but if a non-empty |
|
| 830 |
- // ConfigFile is present, any value provided as an AuthConfig directly will |
|
| 831 |
- // be overridden. See BuildFile::CmdFrom for details. |
|
| 832 |
- if version < 1.9 && authEncoded != "" {
|
|
| 833 |
- authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) |
|
| 834 |
- if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
|
| 835 |
- // for a pull it is not an error if no auth was given |
|
| 836 |
- // to increase compatibility with the existing api it is defaulting to be empty |
|
| 837 |
- authConfig = &auth.AuthConfig{}
|
|
| 838 |
- } |
|
| 839 |
- } |
|
| 840 |
- |
|
| 841 |
- if configFileEncoded != "" {
|
|
| 842 |
- configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded)) |
|
| 843 |
- if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
|
|
| 844 |
- // for a pull it is not an error if no auth was given |
|
| 845 |
- // to increase compatibility with the existing api it is defaulting to be empty |
|
| 846 |
- configFile = &auth.ConfigFile{}
|
|
| 847 |
- } |
|
| 848 |
- } |
|
| 849 |
- |
|
| 850 |
- if version >= 1.8 {
|
|
| 851 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 852 |
- job.SetenvBool("json", true)
|
|
| 853 |
- } |
|
| 854 |
- |
|
| 855 |
- job.Stdout.Add(utils.NewWriteFlusher(w)) |
|
| 856 |
- job.Stdin.Add(r.Body) |
|
| 857 |
- job.Setenv("remote", r.FormValue("remote"))
|
|
| 858 |
- job.Setenv("t", r.FormValue("t"))
|
|
| 859 |
- job.Setenv("q", r.FormValue("q"))
|
|
| 860 |
- job.Setenv("nocache", r.FormValue("nocache"))
|
|
| 861 |
- job.Setenv("rm", r.FormValue("rm"))
|
|
| 862 |
- |
|
| 863 |
- if err := job.Run(); err != nil {
|
|
| 864 |
- if !job.Stdout.Used() {
|
|
| 865 |
- return err |
|
| 866 |
- } |
|
| 867 |
- sf := utils.NewStreamFormatter(version >= 1.8) |
|
| 868 |
- w.Write(sf.FormatError(err)) |
|
| 869 |
- } |
|
| 870 |
- return nil |
|
| 871 |
-} |
|
| 872 |
- |
|
| 873 |
-func postContainersCopy(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 874 |
- if vars == nil {
|
|
| 875 |
- return fmt.Errorf("Missing parameter")
|
|
| 876 |
- } |
|
| 877 |
- |
|
| 878 |
- var copyData engine.Env |
|
| 879 |
- |
|
| 880 |
- if contentType := r.Header.Get("Content-Type"); contentType == "application/json" {
|
|
| 881 |
- if err := copyData.Decode(r.Body); err != nil {
|
|
| 882 |
- return err |
|
| 883 |
- } |
|
| 884 |
- } else {
|
|
| 885 |
- return fmt.Errorf("Content-Type not supported: %s", contentType)
|
|
| 886 |
- } |
|
| 887 |
- |
|
| 888 |
- if copyData.Get("Resource") == "" {
|
|
| 889 |
- return fmt.Errorf("Path cannot be empty")
|
|
| 890 |
- } |
|
| 891 |
- if copyData.Get("Resource")[0] == '/' {
|
|
| 892 |
- copyData.Set("Resource", copyData.Get("Resource")[1:])
|
|
| 893 |
- } |
|
| 894 |
- |
|
| 895 |
- job := srv.Eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
|
|
| 896 |
- job.Stdout.Add(w) |
|
| 897 |
- if err := job.Run(); err != nil {
|
|
| 898 |
- utils.Errorf("%s", err.Error())
|
|
| 899 |
- } |
|
| 900 |
- return nil |
|
| 901 |
-} |
|
| 902 |
- |
|
| 903 |
-func optionsHandler(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 904 |
- w.WriteHeader(http.StatusOK) |
|
| 905 |
- return nil |
|
| 906 |
-} |
|
| 907 |
-func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
|
|
| 908 |
- w.Header().Add("Access-Control-Allow-Origin", "*")
|
|
| 909 |
- w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
|
|
| 910 |
- w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
|
|
| 911 |
-} |
|
| 912 |
- |
|
| 913 |
-func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool) http.HandlerFunc {
|
|
| 914 |
- return func(w http.ResponseWriter, r *http.Request) {
|
|
| 915 |
- // log the request |
|
| 916 |
- utils.Debugf("Calling %s %s", localMethod, localRoute)
|
|
| 917 |
- |
|
| 918 |
- if logging {
|
|
| 919 |
- log.Println(r.Method, r.RequestURI) |
|
| 920 |
- } |
|
| 921 |
- |
|
| 922 |
- if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
|
|
| 923 |
- userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
|
|
| 924 |
- if len(userAgent) == 2 && userAgent[1] != VERSION {
|
|
| 925 |
- utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION)
|
|
| 926 |
- } |
|
| 927 |
- } |
|
| 928 |
- version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64) |
|
| 929 |
- if err != nil {
|
|
| 930 |
- version = APIVERSION |
|
| 931 |
- } |
|
| 932 |
- if enableCors {
|
|
| 933 |
- writeCorsHeaders(w, r) |
|
| 934 |
- } |
|
| 935 |
- |
|
| 936 |
- if version == 0 || version > APIVERSION {
|
|
| 937 |
- http.Error(w, fmt.Errorf("client and server don't have same version (client : %g, server: %g)", version, APIVERSION).Error(), http.StatusNotFound)
|
|
| 938 |
- return |
|
| 939 |
- } |
|
| 940 |
- |
|
| 941 |
- if err := handlerFunc(srv, version, w, r, mux.Vars(r)); err != nil {
|
|
| 942 |
- utils.Errorf("Error: %s", err)
|
|
| 943 |
- httpError(w, err) |
|
| 944 |
- } |
|
| 945 |
- } |
|
| 946 |
-} |
|
| 947 |
- |
|
| 948 |
-// Replicated from expvar.go as not public. |
|
| 949 |
-func expvarHandler(w http.ResponseWriter, r *http.Request) {
|
|
| 950 |
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
| 951 |
- fmt.Fprintf(w, "{\n")
|
|
| 952 |
- first := true |
|
| 953 |
- expvar.Do(func(kv expvar.KeyValue) {
|
|
| 954 |
- if !first {
|
|
| 955 |
- fmt.Fprintf(w, ",\n") |
|
| 956 |
- } |
|
| 957 |
- first = false |
|
| 958 |
- fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) |
|
| 959 |
- }) |
|
| 960 |
- fmt.Fprintf(w, "\n}\n") |
|
| 961 |
-} |
|
| 962 |
- |
|
| 963 |
-func AttachProfiler(router *mux.Router) {
|
|
| 964 |
- router.HandleFunc("/debug/vars", expvarHandler)
|
|
| 965 |
- router.HandleFunc("/debug/pprof/", pprof.Index)
|
|
| 966 |
- router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
|
| 967 |
- router.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
|
| 968 |
- router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
|
| 969 |
- router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
|
|
| 970 |
- router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
|
|
| 971 |
- router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
|
|
| 972 |
-} |
|
| 973 |
- |
|
| 974 |
-func createRouter(srv *Server, logging, enableCors bool) (*mux.Router, error) {
|
|
| 975 |
- r := mux.NewRouter() |
|
| 976 |
- if os.Getenv("DEBUG") != "" {
|
|
| 977 |
- AttachProfiler(r) |
|
| 978 |
- } |
|
| 979 |
- m := map[string]map[string]HttpApiFunc{
|
|
| 980 |
- "GET": {
|
|
| 981 |
- "/events": getEvents, |
|
| 982 |
- "/info": getInfo, |
|
| 983 |
- "/version": getVersion, |
|
| 984 |
- "/images/json": getImagesJSON, |
|
| 985 |
- "/images/viz": getImagesViz, |
|
| 986 |
- "/images/search": getImagesSearch, |
|
| 987 |
- "/images/{name:.*}/get": getImagesGet,
|
|
| 988 |
- "/images/{name:.*}/history": getImagesHistory,
|
|
| 989 |
- "/images/{name:.*}/json": getImagesByName,
|
|
| 990 |
- "/containers/ps": getContainersJSON, |
|
| 991 |
- "/containers/json": getContainersJSON, |
|
| 992 |
- "/containers/{name:.*}/export": getContainersExport,
|
|
| 993 |
- "/containers/{name:.*}/changes": getContainersChanges,
|
|
| 994 |
- "/containers/{name:.*}/json": getContainersByName,
|
|
| 995 |
- "/containers/{name:.*}/top": getContainersTop,
|
|
| 996 |
- "/containers/{name:.*}/attach/ws": wsContainersAttach,
|
|
| 997 |
- }, |
|
| 998 |
- "POST": {
|
|
| 999 |
- "/auth": postAuth, |
|
| 1000 |
- "/commit": postCommit, |
|
| 1001 |
- "/build": postBuild, |
|
| 1002 |
- "/images/create": postImagesCreate, |
|
| 1003 |
- "/images/{name:.*}/insert": postImagesInsert,
|
|
| 1004 |
- "/images/load": postImagesLoad, |
|
| 1005 |
- "/images/{name:.*}/push": postImagesPush,
|
|
| 1006 |
- "/images/{name:.*}/tag": postImagesTag,
|
|
| 1007 |
- "/containers/create": postContainersCreate, |
|
| 1008 |
- "/containers/{name:.*}/kill": postContainersKill,
|
|
| 1009 |
- "/containers/{name:.*}/restart": postContainersRestart,
|
|
| 1010 |
- "/containers/{name:.*}/start": postContainersStart,
|
|
| 1011 |
- "/containers/{name:.*}/stop": postContainersStop,
|
|
| 1012 |
- "/containers/{name:.*}/wait": postContainersWait,
|
|
| 1013 |
- "/containers/{name:.*}/resize": postContainersResize,
|
|
| 1014 |
- "/containers/{name:.*}/attach": postContainersAttach,
|
|
| 1015 |
- "/containers/{name:.*}/copy": postContainersCopy,
|
|
| 1016 |
- }, |
|
| 1017 |
- "DELETE": {
|
|
| 1018 |
- "/containers/{name:.*}": deleteContainers,
|
|
| 1019 |
- "/images/{name:.*}": deleteImages,
|
|
| 1020 |
- }, |
|
| 1021 |
- "OPTIONS": {
|
|
| 1022 |
- "": optionsHandler, |
|
| 1023 |
- }, |
|
| 1024 |
- } |
|
| 1025 |
- |
|
| 1026 |
- for method, routes := range m {
|
|
| 1027 |
- for route, fct := range routes {
|
|
| 1028 |
- utils.Debugf("Registering %s, %s", method, route)
|
|
| 1029 |
- // NOTE: scope issue, make sure the variables are local and won't be changed |
|
| 1030 |
- localRoute := route |
|
| 1031 |
- localFct := fct |
|
| 1032 |
- localMethod := method |
|
| 1033 |
- |
|
| 1034 |
- // build the handler function |
|
| 1035 |
- f := makeHttpHandler(srv, logging, localMethod, localRoute, localFct, enableCors) |
|
| 1036 |
- |
|
| 1037 |
- // add the new route |
|
| 1038 |
- if localRoute == "" {
|
|
| 1039 |
- r.Methods(localMethod).HandlerFunc(f) |
|
| 1040 |
- } else {
|
|
| 1041 |
- r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
|
|
| 1042 |
- r.Path(localRoute).Methods(localMethod).HandlerFunc(f) |
|
| 1043 |
- } |
|
| 1044 |
- } |
|
| 1045 |
- } |
|
| 1046 |
- |
|
| 1047 |
- return r, nil |
|
| 1048 |
-} |
|
| 1049 |
- |
|
| 1050 |
-// ServeRequest processes a single http request to the docker remote api. |
|
| 1051 |
-// FIXME: refactor this to be part of Server and not require re-creating a new |
|
| 1052 |
-// router each time. This requires first moving ListenAndServe into Server. |
|
| 1053 |
-func ServeRequest(srv *Server, apiversion float64, w http.ResponseWriter, req *http.Request) error {
|
|
| 1054 |
- router, err := createRouter(srv, false, true) |
|
| 1055 |
- if err != nil {
|
|
| 1056 |
- return err |
|
| 1057 |
- } |
|
| 1058 |
- // Insert APIVERSION into the request as a convenience |
|
| 1059 |
- req.URL.Path = fmt.Sprintf("/v%g%s", apiversion, req.URL.Path)
|
|
| 1060 |
- router.ServeHTTP(w, req) |
|
| 1061 |
- return nil |
|
| 1062 |
-} |
|
| 1063 |
- |
|
| 1064 |
-// ServeFD creates an http.Server and sets it up to serve given a socket activated |
|
| 1065 |
-// argument. |
|
| 1066 |
-func ServeFd(addr string, handle http.Handler) error {
|
|
| 1067 |
- ls, e := systemd.ListenFD(addr) |
|
| 1068 |
- if e != nil {
|
|
| 1069 |
- return e |
|
| 1070 |
- } |
|
| 1071 |
- |
|
| 1072 |
- chErrors := make(chan error, len(ls)) |
|
| 1073 |
- |
|
| 1074 |
- // Since ListenFD will return one or more sockets we have |
|
| 1075 |
- // to create a go func to spawn off multiple serves |
|
| 1076 |
- for i := range ls {
|
|
| 1077 |
- listener := ls[i] |
|
| 1078 |
- go func() {
|
|
| 1079 |
- httpSrv := http.Server{Handler: handle}
|
|
| 1080 |
- chErrors <- httpSrv.Serve(listener) |
|
| 1081 |
- }() |
|
| 1082 |
- } |
|
| 1083 |
- |
|
| 1084 |
- for i := 0; i < len(ls); i += 1 {
|
|
| 1085 |
- err := <-chErrors |
|
| 1086 |
- if err != nil {
|
|
| 1087 |
- return err |
|
| 1088 |
- } |
|
| 1089 |
- } |
|
| 1090 |
- |
|
| 1091 |
- return nil |
|
| 1092 |
-} |
|
| 1093 |
- |
|
| 1094 |
-// ListenAndServe sets up the required http.Server and gets it listening for |
|
| 1095 |
-// each addr passed in and does protocol specific checking. |
|
| 1096 |
-func ListenAndServe(proto, addr string, srv *Server, logging, enableCors bool) error {
|
|
| 1097 |
- r, err := createRouter(srv, logging, enableCors) |
|
| 1098 |
- if err != nil {
|
|
| 1099 |
- return err |
|
| 1100 |
- } |
|
| 1101 |
- |
|
| 1102 |
- if proto == "fd" {
|
|
| 1103 |
- return ServeFd(addr, r) |
|
| 1104 |
- } |
|
| 1105 |
- |
|
| 1106 |
- if proto == "unix" {
|
|
| 1107 |
- if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
|
|
| 1108 |
- return err |
|
| 1109 |
- } |
|
| 1110 |
- } |
|
| 1111 |
- |
|
| 1112 |
- l, err := net.Listen(proto, addr) |
|
| 1113 |
- if err != nil {
|
|
| 1114 |
- return err |
|
| 1115 |
- } |
|
| 1116 |
- |
|
| 1117 |
- // Basic error and sanity checking |
|
| 1118 |
- switch proto {
|
|
| 1119 |
- case "tcp": |
|
| 1120 |
- if !strings.HasPrefix(addr, "127.0.0.1") {
|
|
| 1121 |
- log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
|
| 1122 |
- } |
|
| 1123 |
- case "unix": |
|
| 1124 |
- if err := os.Chmod(addr, 0660); err != nil {
|
|
| 1125 |
- return err |
|
| 1126 |
- } |
|
| 1127 |
- |
|
| 1128 |
- groups, err := ioutil.ReadFile("/etc/group")
|
|
| 1129 |
- if err != nil {
|
|
| 1130 |
- return err |
|
| 1131 |
- } |
|
| 1132 |
- re := regexp.MustCompile("(^|\n)docker:.*?:([0-9]+)")
|
|
| 1133 |
- if gidMatch := re.FindStringSubmatch(string(groups)); gidMatch != nil {
|
|
| 1134 |
- gid, err := strconv.Atoi(gidMatch[2]) |
|
| 1135 |
- if err != nil {
|
|
| 1136 |
- return err |
|
| 1137 |
- } |
|
| 1138 |
- utils.Debugf("docker group found. gid: %d", gid)
|
|
| 1139 |
- if err := os.Chown(addr, 0, gid); err != nil {
|
|
| 1140 |
- return err |
|
| 1141 |
- } |
|
| 1142 |
- } |
|
| 1143 |
- default: |
|
| 1144 |
- return fmt.Errorf("Invalid protocol format.")
|
|
| 1145 |
- } |
|
| 1146 |
- |
|
| 1147 |
- httpSrv := http.Server{Addr: addr, Handler: r}
|
|
| 1148 |
- return httpSrv.Serve(l) |
|
| 1149 |
-} |
| 1150 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,1162 @@ |
| 0 |
+package api |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bufio" |
|
| 4 |
+ "bytes" |
|
| 5 |
+ "code.google.com/p/go.net/websocket" |
|
| 6 |
+ "encoding/base64" |
|
| 7 |
+ "encoding/json" |
|
| 8 |
+ "expvar" |
|
| 9 |
+ "fmt" |
|
| 10 |
+ "github.com/dotcloud/docker/auth" |
|
| 11 |
+ "github.com/dotcloud/docker/engine" |
|
| 12 |
+ "github.com/dotcloud/docker/pkg/systemd" |
|
| 13 |
+ "github.com/dotcloud/docker/utils" |
|
| 14 |
+ "github.com/gorilla/mux" |
|
| 15 |
+ "io" |
|
| 16 |
+ "io/ioutil" |
|
| 17 |
+ "log" |
|
| 18 |
+ "mime" |
|
| 19 |
+ "net" |
|
| 20 |
+ "net/http" |
|
| 21 |
+ "net/http/pprof" |
|
| 22 |
+ "os" |
|
| 23 |
+ "regexp" |
|
| 24 |
+ "strconv" |
|
| 25 |
+ "strings" |
|
| 26 |
+ "syscall" |
|
| 27 |
+) |
|
| 28 |
+ |
|
| 29 |
+const ( |
|
| 30 |
+ APIVERSION = 1.9 |
|
| 31 |
+ DEFAULTHTTPHOST = "127.0.0.1" |
|
| 32 |
+ DEFAULTHTTPPORT = 4243 |
|
| 33 |
+ DEFAULTUNIXSOCKET = "/var/run/docker.sock" |
|
| 34 |
+) |
|
| 35 |
+ |
|
| 36 |
+type HttpApiFunc func(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error |
|
| 37 |
+ |
|
| 38 |
+func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
|
| 39 |
+ conn, _, err := w.(http.Hijacker).Hijack() |
|
| 40 |
+ if err != nil {
|
|
| 41 |
+ return nil, nil, err |
|
| 42 |
+ } |
|
| 43 |
+ // Flush the options to make sure the client sets the raw mode |
|
| 44 |
+ conn.Write([]byte{})
|
|
| 45 |
+ return conn, conn, nil |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+//If we don't do this, POST method without Content-type (even with empty body) will fail |
|
| 49 |
+func parseForm(r *http.Request) error {
|
|
| 50 |
+ if r == nil {
|
|
| 51 |
+ return nil |
|
| 52 |
+ } |
|
| 53 |
+ if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
|
|
| 54 |
+ return err |
|
| 55 |
+ } |
|
| 56 |
+ return nil |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+func parseMultipartForm(r *http.Request) error {
|
|
| 60 |
+ if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
|
|
| 61 |
+ return err |
|
| 62 |
+ } |
|
| 63 |
+ return nil |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+func httpError(w http.ResponseWriter, err error) {
|
|
| 67 |
+ statusCode := http.StatusInternalServerError |
|
| 68 |
+ // FIXME: this is brittle and should not be necessary. |
|
| 69 |
+ // If we need to differentiate between different possible error types, we should |
|
| 70 |
+ // create appropriate error types with clearly defined meaning. |
|
| 71 |
+ if strings.Contains(err.Error(), "No such") {
|
|
| 72 |
+ statusCode = http.StatusNotFound |
|
| 73 |
+ } else if strings.Contains(err.Error(), "Bad parameter") {
|
|
| 74 |
+ statusCode = http.StatusBadRequest |
|
| 75 |
+ } else if strings.Contains(err.Error(), "Conflict") {
|
|
| 76 |
+ statusCode = http.StatusConflict |
|
| 77 |
+ } else if strings.Contains(err.Error(), "Impossible") {
|
|
| 78 |
+ statusCode = http.StatusNotAcceptable |
|
| 79 |
+ } else if strings.Contains(err.Error(), "Wrong login/password") {
|
|
| 80 |
+ statusCode = http.StatusUnauthorized |
|
| 81 |
+ } else if strings.Contains(err.Error(), "hasn't been activated") {
|
|
| 82 |
+ statusCode = http.StatusForbidden |
|
| 83 |
+ } |
|
| 84 |
+ |
|
| 85 |
+ if err != nil {
|
|
| 86 |
+ utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
|
|
| 87 |
+ http.Error(w, err.Error(), statusCode) |
|
| 88 |
+ } |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+func writeJSON(w http.ResponseWriter, code int, v engine.Env) error {
|
|
| 92 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 93 |
+ w.WriteHeader(code) |
|
| 94 |
+ return v.Encode(w) |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func getBoolParam(value string) (bool, error) {
|
|
| 98 |
+ if value == "" {
|
|
| 99 |
+ return false, nil |
|
| 100 |
+ } |
|
| 101 |
+ ret, err := strconv.ParseBool(value) |
|
| 102 |
+ if err != nil {
|
|
| 103 |
+ return false, fmt.Errorf("Bad parameter")
|
|
| 104 |
+ } |
|
| 105 |
+ return ret, nil |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+//TODO remove, used on < 1.5 in getContainersJSON |
|
| 109 |
+func displayablePorts(ports *engine.Table) string {
|
|
| 110 |
+ result := []string{}
|
|
| 111 |
+ for _, port := range ports.Data {
|
|
| 112 |
+ if port.Get("IP") == "" {
|
|
| 113 |
+ result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PublicPort"), port.Get("Type")))
|
|
| 114 |
+ } else {
|
|
| 115 |
+ result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type")))
|
|
| 116 |
+ } |
|
| 117 |
+ } |
|
| 118 |
+ return strings.Join(result, ", ") |
|
| 119 |
+} |
|
| 120 |
+ |
|
| 121 |
+func MatchesContentType(contentType, expectedType string) bool {
|
|
| 122 |
+ mimetype, _, err := mime.ParseMediaType(contentType) |
|
| 123 |
+ if err != nil {
|
|
| 124 |
+ utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
|
|
| 125 |
+ } |
|
| 126 |
+ return err == nil && mimetype == expectedType |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 130 |
+ var ( |
|
| 131 |
+ authConfig, err = ioutil.ReadAll(r.Body) |
|
| 132 |
+ job = eng.Job("auth")
|
|
| 133 |
+ status string |
|
| 134 |
+ ) |
|
| 135 |
+ if err != nil {
|
|
| 136 |
+ return err |
|
| 137 |
+ } |
|
| 138 |
+ job.Setenv("authConfig", string(authConfig))
|
|
| 139 |
+ job.Stdout.AddString(&status) |
|
| 140 |
+ if err = job.Run(); err != nil {
|
|
| 141 |
+ return err |
|
| 142 |
+ } |
|
| 143 |
+ if status != "" {
|
|
| 144 |
+ var env engine.Env |
|
| 145 |
+ env.Set("Status", status)
|
|
| 146 |
+ return writeJSON(w, http.StatusOK, env) |
|
| 147 |
+ } |
|
| 148 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 149 |
+ return nil |
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+func getVersion(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 153 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 154 |
+ eng.ServeHTTP(w, r) |
|
| 155 |
+ return nil |
|
| 156 |
+} |
|
| 157 |
+ |
|
| 158 |
+func postContainersKill(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 159 |
+ if vars == nil {
|
|
| 160 |
+ return fmt.Errorf("Missing parameter")
|
|
| 161 |
+ } |
|
| 162 |
+ if err := parseForm(r); err != nil {
|
|
| 163 |
+ return err |
|
| 164 |
+ } |
|
| 165 |
+ job := eng.Job("kill", vars["name"])
|
|
| 166 |
+ if sig := r.Form.Get("signal"); sig != "" {
|
|
| 167 |
+ job.Args = append(job.Args, sig) |
|
| 168 |
+ } |
|
| 169 |
+ if err := job.Run(); err != nil {
|
|
| 170 |
+ return err |
|
| 171 |
+ } |
|
| 172 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 173 |
+ return nil |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+func getContainersExport(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 177 |
+ if vars == nil {
|
|
| 178 |
+ return fmt.Errorf("Missing parameter")
|
|
| 179 |
+ } |
|
| 180 |
+ job := eng.Job("export", vars["name"])
|
|
| 181 |
+ job.Stdout.Add(w) |
|
| 182 |
+ if err := job.Run(); err != nil {
|
|
| 183 |
+ return err |
|
| 184 |
+ } |
|
| 185 |
+ return nil |
|
| 186 |
+} |
|
| 187 |
+ |
|
| 188 |
+func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 189 |
+ if err := parseForm(r); err != nil {
|
|
| 190 |
+ return err |
|
| 191 |
+ } |
|
| 192 |
+ |
|
| 193 |
+ var ( |
|
| 194 |
+ err error |
|
| 195 |
+ outs *engine.Table |
|
| 196 |
+ job = eng.Job("images")
|
|
| 197 |
+ ) |
|
| 198 |
+ |
|
| 199 |
+ job.Setenv("filter", r.Form.Get("filter"))
|
|
| 200 |
+ job.Setenv("all", r.Form.Get("all"))
|
|
| 201 |
+ |
|
| 202 |
+ if version > 1.8 {
|
|
| 203 |
+ job.Stdout.Add(w) |
|
| 204 |
+ } else if outs, err = job.Stdout.AddListTable(); err != nil {
|
|
| 205 |
+ return err |
|
| 206 |
+ } |
|
| 207 |
+ |
|
| 208 |
+ if err := job.Run(); err != nil {
|
|
| 209 |
+ return err |
|
| 210 |
+ } |
|
| 211 |
+ |
|
| 212 |
+ if version < 1.8 && outs != nil { // Convert to legacy format
|
|
| 213 |
+ outsLegacy := engine.NewTable("Created", 0)
|
|
| 214 |
+ for _, out := range outs.Data {
|
|
| 215 |
+ for _, repoTag := range out.GetList("RepoTags") {
|
|
| 216 |
+ parts := strings.Split(repoTag, ":") |
|
| 217 |
+ outLegacy := &engine.Env{}
|
|
| 218 |
+ outLegacy.Set("Repository", parts[0])
|
|
| 219 |
+ outLegacy.Set("Tag", parts[1])
|
|
| 220 |
+ outLegacy.Set("ID", out.Get("ID"))
|
|
| 221 |
+ outLegacy.SetInt64("Created", out.GetInt64("Created"))
|
|
| 222 |
+ outLegacy.SetInt64("Size", out.GetInt64("Size"))
|
|
| 223 |
+ outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize"))
|
|
| 224 |
+ outsLegacy.Add(outLegacy) |
|
| 225 |
+ } |
|
| 226 |
+ } |
|
| 227 |
+ if _, err := outsLegacy.WriteListTo(w); err != nil {
|
|
| 228 |
+ return err |
|
| 229 |
+ } |
|
| 230 |
+ } |
|
| 231 |
+ return nil |
|
| 232 |
+} |
|
| 233 |
+ |
|
| 234 |
+func getImagesViz(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 235 |
+ if version > 1.6 {
|
|
| 236 |
+ w.WriteHeader(http.StatusNotFound) |
|
| 237 |
+ return fmt.Errorf("This is now implemented in the client.")
|
|
| 238 |
+ } |
|
| 239 |
+ eng.ServeHTTP(w, r) |
|
| 240 |
+ return nil |
|
| 241 |
+} |
|
| 242 |
+ |
|
| 243 |
+func getInfo(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 244 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 245 |
+ eng.ServeHTTP(w, r) |
|
| 246 |
+ return nil |
|
| 247 |
+} |
|
| 248 |
+ |
|
| 249 |
+func getEvents(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 250 |
+ if err := parseForm(r); err != nil {
|
|
| 251 |
+ return err |
|
| 252 |
+ } |
|
| 253 |
+ |
|
| 254 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 255 |
+ var job = eng.Job("events", r.RemoteAddr)
|
|
| 256 |
+ job.Stdout.Add(utils.NewWriteFlusher(w)) |
|
| 257 |
+ job.Setenv("since", r.Form.Get("since"))
|
|
| 258 |
+ return job.Run() |
|
| 259 |
+} |
|
| 260 |
+ |
|
| 261 |
+func getImagesHistory(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 262 |
+ if vars == nil {
|
|
| 263 |
+ return fmt.Errorf("Missing parameter")
|
|
| 264 |
+ } |
|
| 265 |
+ |
|
| 266 |
+ var job = eng.Job("history", vars["name"])
|
|
| 267 |
+ job.Stdout.Add(w) |
|
| 268 |
+ |
|
| 269 |
+ if err := job.Run(); err != nil {
|
|
| 270 |
+ return err |
|
| 271 |
+ } |
|
| 272 |
+ return nil |
|
| 273 |
+} |
|
| 274 |
+ |
|
| 275 |
+func getContainersChanges(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 276 |
+ if vars == nil {
|
|
| 277 |
+ return fmt.Errorf("Missing parameter")
|
|
| 278 |
+ } |
|
| 279 |
+ var job = eng.Job("changes", vars["name"])
|
|
| 280 |
+ job.Stdout.Add(w) |
|
| 281 |
+ |
|
| 282 |
+ return job.Run() |
|
| 283 |
+} |
|
| 284 |
+ |
|
| 285 |
+func getContainersTop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 286 |
+ if version < 1.4 {
|
|
| 287 |
+ return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
|
|
| 288 |
+ } |
|
| 289 |
+ if vars == nil {
|
|
| 290 |
+ return fmt.Errorf("Missing parameter")
|
|
| 291 |
+ } |
|
| 292 |
+ if err := parseForm(r); err != nil {
|
|
| 293 |
+ return err |
|
| 294 |
+ } |
|
| 295 |
+ |
|
| 296 |
+ job := eng.Job("top", vars["name"], r.Form.Get("ps_args"))
|
|
| 297 |
+ job.Stdout.Add(w) |
|
| 298 |
+ return job.Run() |
|
| 299 |
+} |
|
| 300 |
+ |
|
| 301 |
+func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 302 |
+ if err := parseForm(r); err != nil {
|
|
| 303 |
+ return err |
|
| 304 |
+ } |
|
| 305 |
+ var ( |
|
| 306 |
+ err error |
|
| 307 |
+ outs *engine.Table |
|
| 308 |
+ job = eng.Job("containers")
|
|
| 309 |
+ ) |
|
| 310 |
+ |
|
| 311 |
+ job.Setenv("all", r.Form.Get("all"))
|
|
| 312 |
+ job.Setenv("size", r.Form.Get("size"))
|
|
| 313 |
+ job.Setenv("since", r.Form.Get("since"))
|
|
| 314 |
+ job.Setenv("before", r.Form.Get("before"))
|
|
| 315 |
+ job.Setenv("limit", r.Form.Get("limit"))
|
|
| 316 |
+ |
|
| 317 |
+ if version > 1.5 {
|
|
| 318 |
+ job.Stdout.Add(w) |
|
| 319 |
+ } else if outs, err = job.Stdout.AddTable(); err != nil {
|
|
| 320 |
+ return err |
|
| 321 |
+ } |
|
| 322 |
+ if err = job.Run(); err != nil {
|
|
| 323 |
+ return err |
|
| 324 |
+ } |
|
| 325 |
+ if version < 1.5 { // Convert to legacy format
|
|
| 326 |
+ for _, out := range outs.Data {
|
|
| 327 |
+ ports := engine.NewTable("", 0)
|
|
| 328 |
+ ports.ReadListFrom([]byte(out.Get("Ports")))
|
|
| 329 |
+ out.Set("Ports", displayablePorts(ports))
|
|
| 330 |
+ } |
|
| 331 |
+ if _, err = outs.WriteListTo(w); err != nil {
|
|
| 332 |
+ return err |
|
| 333 |
+ } |
|
| 334 |
+ } |
|
| 335 |
+ return nil |
|
| 336 |
+} |
|
| 337 |
+ |
|
| 338 |
+func postImagesTag(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 339 |
+ if err := parseForm(r); err != nil {
|
|
| 340 |
+ return err |
|
| 341 |
+ } |
|
| 342 |
+ if vars == nil {
|
|
| 343 |
+ return fmt.Errorf("Missing parameter")
|
|
| 344 |
+ } |
|
| 345 |
+ |
|
| 346 |
+ job := eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
|
|
| 347 |
+ job.Setenv("force", r.Form.Get("force"))
|
|
| 348 |
+ if err := job.Run(); err != nil {
|
|
| 349 |
+ return err |
|
| 350 |
+ } |
|
| 351 |
+ w.WriteHeader(http.StatusCreated) |
|
| 352 |
+ return nil |
|
| 353 |
+} |
|
| 354 |
+ |
|
| 355 |
+func postCommit(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 356 |
+ if err := parseForm(r); err != nil {
|
|
| 357 |
+ return err |
|
| 358 |
+ } |
|
| 359 |
+ var ( |
|
| 360 |
+ config engine.Env |
|
| 361 |
+ env engine.Env |
|
| 362 |
+ job = eng.Job("commit", r.Form.Get("container"))
|
|
| 363 |
+ ) |
|
| 364 |
+ if err := config.Import(r.Body); err != nil {
|
|
| 365 |
+ utils.Errorf("%s", err)
|
|
| 366 |
+ } |
|
| 367 |
+ |
|
| 368 |
+ job.Setenv("repo", r.Form.Get("repo"))
|
|
| 369 |
+ job.Setenv("tag", r.Form.Get("tag"))
|
|
| 370 |
+ job.Setenv("author", r.Form.Get("author"))
|
|
| 371 |
+ job.Setenv("comment", r.Form.Get("comment"))
|
|
| 372 |
+ job.SetenvSubEnv("config", &config)
|
|
| 373 |
+ |
|
| 374 |
+ var id string |
|
| 375 |
+ job.Stdout.AddString(&id) |
|
| 376 |
+ if err := job.Run(); err != nil {
|
|
| 377 |
+ return err |
|
| 378 |
+ } |
|
| 379 |
+ env.Set("Id", id)
|
|
| 380 |
+ return writeJSON(w, http.StatusCreated, env) |
|
| 381 |
+} |
|
| 382 |
+ |
|
| 383 |
+// Creates an image from Pull or from Import |
|
| 384 |
+func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 385 |
+ if err := parseForm(r); err != nil {
|
|
| 386 |
+ return err |
|
| 387 |
+ } |
|
| 388 |
+ |
|
| 389 |
+ var ( |
|
| 390 |
+ image = r.Form.Get("fromImage")
|
|
| 391 |
+ tag = r.Form.Get("tag")
|
|
| 392 |
+ job *engine.Job |
|
| 393 |
+ ) |
|
| 394 |
+ authEncoded := r.Header.Get("X-Registry-Auth")
|
|
| 395 |
+ authConfig := &auth.AuthConfig{}
|
|
| 396 |
+ if authEncoded != "" {
|
|
| 397 |
+ authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) |
|
| 398 |
+ if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
|
| 399 |
+ // for a pull it is not an error if no auth was given |
|
| 400 |
+ // to increase compatibility with the existing api it is defaulting to be empty |
|
| 401 |
+ authConfig = &auth.AuthConfig{}
|
|
| 402 |
+ } |
|
| 403 |
+ } |
|
| 404 |
+ if version > 1.0 {
|
|
| 405 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 406 |
+ } |
|
| 407 |
+ if image != "" { //pull
|
|
| 408 |
+ metaHeaders := map[string][]string{}
|
|
| 409 |
+ for k, v := range r.Header {
|
|
| 410 |
+ if strings.HasPrefix(k, "X-Meta-") {
|
|
| 411 |
+ metaHeaders[k] = v |
|
| 412 |
+ } |
|
| 413 |
+ } |
|
| 414 |
+ job = eng.Job("pull", r.Form.Get("fromImage"), tag)
|
|
| 415 |
+ job.SetenvBool("parallel", version > 1.3)
|
|
| 416 |
+ job.SetenvJson("metaHeaders", metaHeaders)
|
|
| 417 |
+ job.SetenvJson("authConfig", authConfig)
|
|
| 418 |
+ } else { //import
|
|
| 419 |
+ job = eng.Job("import", r.Form.Get("fromSrc"), r.Form.Get("repo"), tag)
|
|
| 420 |
+ job.Stdin.Add(r.Body) |
|
| 421 |
+ } |
|
| 422 |
+ |
|
| 423 |
+ job.SetenvBool("json", version > 1.0)
|
|
| 424 |
+ job.Stdout.Add(utils.NewWriteFlusher(w)) |
|
| 425 |
+ if err := job.Run(); err != nil {
|
|
| 426 |
+ if !job.Stdout.Used() {
|
|
| 427 |
+ return err |
|
| 428 |
+ } |
|
| 429 |
+ sf := utils.NewStreamFormatter(version > 1.0) |
|
| 430 |
+ w.Write(sf.FormatError(err)) |
|
| 431 |
+ } |
|
| 432 |
+ |
|
| 433 |
+ return nil |
|
| 434 |
+} |
|
| 435 |
+ |
|
| 436 |
+func getImagesSearch(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 437 |
+ if err := parseForm(r); err != nil {
|
|
| 438 |
+ return err |
|
| 439 |
+ } |
|
| 440 |
+ var ( |
|
| 441 |
+ authEncoded = r.Header.Get("X-Registry-Auth")
|
|
| 442 |
+ authConfig = &auth.AuthConfig{}
|
|
| 443 |
+ metaHeaders = map[string][]string{}
|
|
| 444 |
+ ) |
|
| 445 |
+ |
|
| 446 |
+ if authEncoded != "" {
|
|
| 447 |
+ authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) |
|
| 448 |
+ if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
|
| 449 |
+ // for a search it is not an error if no auth was given |
|
| 450 |
+ // to increase compatibility with the existing api it is defaulting to be empty |
|
| 451 |
+ authConfig = &auth.AuthConfig{}
|
|
| 452 |
+ } |
|
| 453 |
+ } |
|
| 454 |
+ for k, v := range r.Header {
|
|
| 455 |
+ if strings.HasPrefix(k, "X-Meta-") {
|
|
| 456 |
+ metaHeaders[k] = v |
|
| 457 |
+ } |
|
| 458 |
+ } |
|
| 459 |
+ |
|
| 460 |
+ var job = eng.Job("search", r.Form.Get("term"))
|
|
| 461 |
+ job.SetenvJson("metaHeaders", metaHeaders)
|
|
| 462 |
+ job.SetenvJson("authConfig", authConfig)
|
|
| 463 |
+ job.Stdout.Add(w) |
|
| 464 |
+ |
|
| 465 |
+ return job.Run() |
|
| 466 |
+} |
|
| 467 |
+ |
|
| 468 |
+func postImagesInsert(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 469 |
+ if err := parseForm(r); err != nil {
|
|
| 470 |
+ return err |
|
| 471 |
+ } |
|
| 472 |
+ if vars == nil {
|
|
| 473 |
+ return fmt.Errorf("Missing parameter")
|
|
| 474 |
+ } |
|
| 475 |
+ if version > 1.0 {
|
|
| 476 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 477 |
+ } |
|
| 478 |
+ |
|
| 479 |
+ job := eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path"))
|
|
| 480 |
+ job.SetenvBool("json", version > 1.0)
|
|
| 481 |
+ job.Stdout.Add(w) |
|
| 482 |
+ if err := job.Run(); err != nil {
|
|
| 483 |
+ if !job.Stdout.Used() {
|
|
| 484 |
+ return err |
|
| 485 |
+ } |
|
| 486 |
+ sf := utils.NewStreamFormatter(version > 1.0) |
|
| 487 |
+ w.Write(sf.FormatError(err)) |
|
| 488 |
+ } |
|
| 489 |
+ |
|
| 490 |
+ return nil |
|
| 491 |
+} |
|
| 492 |
+ |
|
| 493 |
+func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 494 |
+ if vars == nil {
|
|
| 495 |
+ return fmt.Errorf("Missing parameter")
|
|
| 496 |
+ } |
|
| 497 |
+ |
|
| 498 |
+ metaHeaders := map[string][]string{}
|
|
| 499 |
+ for k, v := range r.Header {
|
|
| 500 |
+ if strings.HasPrefix(k, "X-Meta-") {
|
|
| 501 |
+ metaHeaders[k] = v |
|
| 502 |
+ } |
|
| 503 |
+ } |
|
| 504 |
+ if err := parseForm(r); err != nil {
|
|
| 505 |
+ return err |
|
| 506 |
+ } |
|
| 507 |
+ authConfig := &auth.AuthConfig{}
|
|
| 508 |
+ |
|
| 509 |
+ authEncoded := r.Header.Get("X-Registry-Auth")
|
|
| 510 |
+ if authEncoded != "" {
|
|
| 511 |
+ // the new format is to handle the authConfig as a header |
|
| 512 |
+ authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) |
|
| 513 |
+ if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
|
| 514 |
+ // to increase compatibility to existing api it is defaulting to be empty |
|
| 515 |
+ authConfig = &auth.AuthConfig{}
|
|
| 516 |
+ } |
|
| 517 |
+ } else {
|
|
| 518 |
+ // the old format is supported for compatibility if there was no authConfig header |
|
| 519 |
+ if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
|
|
| 520 |
+ return err |
|
| 521 |
+ } |
|
| 522 |
+ } |
|
| 523 |
+ |
|
| 524 |
+ if version > 1.0 {
|
|
| 525 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 526 |
+ } |
|
| 527 |
+ job := eng.Job("push", vars["name"])
|
|
| 528 |
+ job.SetenvJson("metaHeaders", metaHeaders)
|
|
| 529 |
+ job.SetenvJson("authConfig", authConfig)
|
|
| 530 |
+ job.SetenvBool("json", version > 1.0)
|
|
| 531 |
+ job.Stdout.Add(utils.NewWriteFlusher(w)) |
|
| 532 |
+ |
|
| 533 |
+ if err := job.Run(); err != nil {
|
|
| 534 |
+ if !job.Stdout.Used() {
|
|
| 535 |
+ return err |
|
| 536 |
+ } |
|
| 537 |
+ sf := utils.NewStreamFormatter(version > 1.0) |
|
| 538 |
+ w.Write(sf.FormatError(err)) |
|
| 539 |
+ } |
|
| 540 |
+ return nil |
|
| 541 |
+} |
|
| 542 |
+ |
|
| 543 |
+func getImagesGet(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 544 |
+ if vars == nil {
|
|
| 545 |
+ return fmt.Errorf("Missing parameter")
|
|
| 546 |
+ } |
|
| 547 |
+ if version > 1.0 {
|
|
| 548 |
+ w.Header().Set("Content-Type", "application/x-tar")
|
|
| 549 |
+ } |
|
| 550 |
+ job := eng.Job("image_export", vars["name"])
|
|
| 551 |
+ job.Stdout.Add(w) |
|
| 552 |
+ return job.Run() |
|
| 553 |
+} |
|
| 554 |
+ |
|
| 555 |
+func postImagesLoad(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 556 |
+ job := eng.Job("load")
|
|
| 557 |
+ job.Stdin.Add(r.Body) |
|
| 558 |
+ return job.Run() |
|
| 559 |
+} |
|
| 560 |
+ |
|
| 561 |
+func postContainersCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 562 |
+ if err := parseForm(r); err != nil {
|
|
| 563 |
+ return nil |
|
| 564 |
+ } |
|
| 565 |
+ var ( |
|
| 566 |
+ out engine.Env |
|
| 567 |
+ job = eng.Job("create", r.Form.Get("name"))
|
|
| 568 |
+ outWarnings []string |
|
| 569 |
+ outId string |
|
| 570 |
+ warnings = bytes.NewBuffer(nil) |
|
| 571 |
+ ) |
|
| 572 |
+ if err := job.DecodeEnv(r.Body); err != nil {
|
|
| 573 |
+ return err |
|
| 574 |
+ } |
|
| 575 |
+ // Read container ID from the first line of stdout |
|
| 576 |
+ job.Stdout.AddString(&outId) |
|
| 577 |
+ // Read warnings from stderr |
|
| 578 |
+ job.Stderr.Add(warnings) |
|
| 579 |
+ if err := job.Run(); err != nil {
|
|
| 580 |
+ return err |
|
| 581 |
+ } |
|
| 582 |
+ // Parse warnings from stderr |
|
| 583 |
+ scanner := bufio.NewScanner(warnings) |
|
| 584 |
+ for scanner.Scan() {
|
|
| 585 |
+ outWarnings = append(outWarnings, scanner.Text()) |
|
| 586 |
+ } |
|
| 587 |
+ out.Set("Id", outId)
|
|
| 588 |
+ out.SetList("Warnings", outWarnings)
|
|
| 589 |
+ return writeJSON(w, http.StatusCreated, out) |
|
| 590 |
+} |
|
| 591 |
+ |
|
| 592 |
+func postContainersRestart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 593 |
+ if err := parseForm(r); err != nil {
|
|
| 594 |
+ return err |
|
| 595 |
+ } |
|
| 596 |
+ if vars == nil {
|
|
| 597 |
+ return fmt.Errorf("Missing parameter")
|
|
| 598 |
+ } |
|
| 599 |
+ job := eng.Job("restart", vars["name"])
|
|
| 600 |
+ job.Setenv("t", r.Form.Get("t"))
|
|
| 601 |
+ if err := job.Run(); err != nil {
|
|
| 602 |
+ return err |
|
| 603 |
+ } |
|
| 604 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 605 |
+ return nil |
|
| 606 |
+} |
|
| 607 |
+ |
|
| 608 |
+func deleteContainers(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 609 |
+ if err := parseForm(r); err != nil {
|
|
| 610 |
+ return err |
|
| 611 |
+ } |
|
| 612 |
+ if vars == nil {
|
|
| 613 |
+ return fmt.Errorf("Missing parameter")
|
|
| 614 |
+ } |
|
| 615 |
+ job := eng.Job("container_delete", vars["name"])
|
|
| 616 |
+ job.Setenv("removeVolume", r.Form.Get("v"))
|
|
| 617 |
+ job.Setenv("removeLink", r.Form.Get("link"))
|
|
| 618 |
+ if err := job.Run(); err != nil {
|
|
| 619 |
+ return err |
|
| 620 |
+ } |
|
| 621 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 622 |
+ return nil |
|
| 623 |
+} |
|
| 624 |
+ |
|
| 625 |
+func deleteImages(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 626 |
+ if err := parseForm(r); err != nil {
|
|
| 627 |
+ return err |
|
| 628 |
+ } |
|
| 629 |
+ if vars == nil {
|
|
| 630 |
+ return fmt.Errorf("Missing parameter")
|
|
| 631 |
+ } |
|
| 632 |
+ var job = eng.Job("image_delete", vars["name"])
|
|
| 633 |
+ job.Stdout.Add(w) |
|
| 634 |
+ job.SetenvBool("autoPrune", version > 1.1)
|
|
| 635 |
+ |
|
| 636 |
+ return job.Run() |
|
| 637 |
+} |
|
| 638 |
+ |
|
| 639 |
+func postContainersStart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 640 |
+ if vars == nil {
|
|
| 641 |
+ return fmt.Errorf("Missing parameter")
|
|
| 642 |
+ } |
|
| 643 |
+ name := vars["name"] |
|
| 644 |
+ job := eng.Job("start", name)
|
|
| 645 |
+ // allow a nil body for backwards compatibility |
|
| 646 |
+ if r.Body != nil {
|
|
| 647 |
+ if MatchesContentType(r.Header.Get("Content-Type"), "application/json") {
|
|
| 648 |
+ if err := job.DecodeEnv(r.Body); err != nil {
|
|
| 649 |
+ return err |
|
| 650 |
+ } |
|
| 651 |
+ } |
|
| 652 |
+ } |
|
| 653 |
+ if err := job.Run(); err != nil {
|
|
| 654 |
+ return err |
|
| 655 |
+ } |
|
| 656 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 657 |
+ return nil |
|
| 658 |
+} |
|
| 659 |
+ |
|
| 660 |
+func postContainersStop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 661 |
+ if err := parseForm(r); err != nil {
|
|
| 662 |
+ return err |
|
| 663 |
+ } |
|
| 664 |
+ if vars == nil {
|
|
| 665 |
+ return fmt.Errorf("Missing parameter")
|
|
| 666 |
+ } |
|
| 667 |
+ job := eng.Job("stop", vars["name"])
|
|
| 668 |
+ job.Setenv("t", r.Form.Get("t"))
|
|
| 669 |
+ if err := job.Run(); err != nil {
|
|
| 670 |
+ return err |
|
| 671 |
+ } |
|
| 672 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 673 |
+ return nil |
|
| 674 |
+} |
|
| 675 |
+ |
|
| 676 |
+func postContainersWait(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 677 |
+ if vars == nil {
|
|
| 678 |
+ return fmt.Errorf("Missing parameter")
|
|
| 679 |
+ } |
|
| 680 |
+ var ( |
|
| 681 |
+ env engine.Env |
|
| 682 |
+ status string |
|
| 683 |
+ job = eng.Job("wait", vars["name"])
|
|
| 684 |
+ ) |
|
| 685 |
+ job.Stdout.AddString(&status) |
|
| 686 |
+ if err := job.Run(); err != nil {
|
|
| 687 |
+ return err |
|
| 688 |
+ } |
|
| 689 |
+ // Parse a 16-bit encoded integer to map typical unix exit status. |
|
| 690 |
+ _, err := strconv.ParseInt(status, 10, 16) |
|
| 691 |
+ if err != nil {
|
|
| 692 |
+ return err |
|
| 693 |
+ } |
|
| 694 |
+ env.Set("StatusCode", status)
|
|
| 695 |
+ return writeJSON(w, http.StatusOK, env) |
|
| 696 |
+} |
|
| 697 |
+ |
|
| 698 |
+func postContainersResize(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 699 |
+ if err := parseForm(r); err != nil {
|
|
| 700 |
+ return err |
|
| 701 |
+ } |
|
| 702 |
+ if vars == nil {
|
|
| 703 |
+ return fmt.Errorf("Missing parameter")
|
|
| 704 |
+ } |
|
| 705 |
+ if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
|
|
| 706 |
+ return err |
|
| 707 |
+ } |
|
| 708 |
+ return nil |
|
| 709 |
+} |
|
| 710 |
+ |
|
| 711 |
+func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 712 |
+ if err := parseForm(r); err != nil {
|
|
| 713 |
+ return err |
|
| 714 |
+ } |
|
| 715 |
+ if vars == nil {
|
|
| 716 |
+ return fmt.Errorf("Missing parameter")
|
|
| 717 |
+ } |
|
| 718 |
+ |
|
| 719 |
+ var ( |
|
| 720 |
+ job = eng.Job("inspect", vars["name"], "container")
|
|
| 721 |
+ c, err = job.Stdout.AddEnv() |
|
| 722 |
+ ) |
|
| 723 |
+ if err != nil {
|
|
| 724 |
+ return err |
|
| 725 |
+ } |
|
| 726 |
+ if err = job.Run(); err != nil {
|
|
| 727 |
+ return err |
|
| 728 |
+ } |
|
| 729 |
+ |
|
| 730 |
+ inStream, outStream, err := hijackServer(w) |
|
| 731 |
+ if err != nil {
|
|
| 732 |
+ return err |
|
| 733 |
+ } |
|
| 734 |
+ defer func() {
|
|
| 735 |
+ if tcpc, ok := inStream.(*net.TCPConn); ok {
|
|
| 736 |
+ tcpc.CloseWrite() |
|
| 737 |
+ } else {
|
|
| 738 |
+ inStream.Close() |
|
| 739 |
+ } |
|
| 740 |
+ }() |
|
| 741 |
+ defer func() {
|
|
| 742 |
+ if tcpc, ok := outStream.(*net.TCPConn); ok {
|
|
| 743 |
+ tcpc.CloseWrite() |
|
| 744 |
+ } else if closer, ok := outStream.(io.Closer); ok {
|
|
| 745 |
+ closer.Close() |
|
| 746 |
+ } |
|
| 747 |
+ }() |
|
| 748 |
+ |
|
| 749 |
+ var errStream io.Writer |
|
| 750 |
+ |
|
| 751 |
+ fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") |
|
| 752 |
+ |
|
| 753 |
+ if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version >= 1.6 {
|
|
| 754 |
+ errStream = utils.NewStdWriter(outStream, utils.Stderr) |
|
| 755 |
+ outStream = utils.NewStdWriter(outStream, utils.Stdout) |
|
| 756 |
+ } else {
|
|
| 757 |
+ errStream = outStream |
|
| 758 |
+ } |
|
| 759 |
+ |
|
| 760 |
+ job = eng.Job("attach", vars["name"])
|
|
| 761 |
+ job.Setenv("logs", r.Form.Get("logs"))
|
|
| 762 |
+ job.Setenv("stream", r.Form.Get("stream"))
|
|
| 763 |
+ job.Setenv("stdin", r.Form.Get("stdin"))
|
|
| 764 |
+ job.Setenv("stdout", r.Form.Get("stdout"))
|
|
| 765 |
+ job.Setenv("stderr", r.Form.Get("stderr"))
|
|
| 766 |
+ job.Stdin.Add(inStream) |
|
| 767 |
+ job.Stdout.Add(outStream) |
|
| 768 |
+ job.Stderr.Set(errStream) |
|
| 769 |
+ if err := job.Run(); err != nil {
|
|
| 770 |
+ fmt.Fprintf(outStream, "Error: %s\n", err) |
|
| 771 |
+ |
|
| 772 |
+ } |
|
| 773 |
+ return nil |
|
| 774 |
+} |
|
| 775 |
+ |
|
| 776 |
+func wsContainersAttach(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 777 |
+ if err := parseForm(r); err != nil {
|
|
| 778 |
+ return err |
|
| 779 |
+ } |
|
| 780 |
+ if vars == nil {
|
|
| 781 |
+ return fmt.Errorf("Missing parameter")
|
|
| 782 |
+ } |
|
| 783 |
+ |
|
| 784 |
+ if err := eng.Job("inspect", vars["name"], "container").Run(); err != nil {
|
|
| 785 |
+ return err |
|
| 786 |
+ } |
|
| 787 |
+ |
|
| 788 |
+ h := websocket.Handler(func(ws *websocket.Conn) {
|
|
| 789 |
+ defer ws.Close() |
|
| 790 |
+ job := eng.Job("attach", vars["name"])
|
|
| 791 |
+ job.Setenv("logs", r.Form.Get("logs"))
|
|
| 792 |
+ job.Setenv("stream", r.Form.Get("stream"))
|
|
| 793 |
+ job.Setenv("stdin", r.Form.Get("stdin"))
|
|
| 794 |
+ job.Setenv("stdout", r.Form.Get("stdout"))
|
|
| 795 |
+ job.Setenv("stderr", r.Form.Get("stderr"))
|
|
| 796 |
+ job.Stdin.Add(ws) |
|
| 797 |
+ job.Stdout.Add(ws) |
|
| 798 |
+ job.Stderr.Set(ws) |
|
| 799 |
+ if err := job.Run(); err != nil {
|
|
| 800 |
+ utils.Errorf("Error: %s", err)
|
|
| 801 |
+ } |
|
| 802 |
+ }) |
|
| 803 |
+ h.ServeHTTP(w, r) |
|
| 804 |
+ |
|
| 805 |
+ return nil |
|
| 806 |
+} |
|
| 807 |
+ |
|
| 808 |
+func getContainersByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 809 |
+ if vars == nil {
|
|
| 810 |
+ return fmt.Errorf("Missing parameter")
|
|
| 811 |
+ } |
|
| 812 |
+ var job = eng.Job("inspect", vars["name"], "container")
|
|
| 813 |
+ job.Stdout.Add(w) |
|
| 814 |
+ job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
|
|
| 815 |
+ return job.Run() |
|
| 816 |
+} |
|
| 817 |
+ |
|
| 818 |
+func getImagesByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 819 |
+ if vars == nil {
|
|
| 820 |
+ return fmt.Errorf("Missing parameter")
|
|
| 821 |
+ } |
|
| 822 |
+ var job = eng.Job("inspect", vars["name"], "image")
|
|
| 823 |
+ job.Stdout.Add(w) |
|
| 824 |
+ job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
|
|
| 825 |
+ return job.Run() |
|
| 826 |
+} |
|
| 827 |
+ |
|
| 828 |
+func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 829 |
+ if version < 1.3 {
|
|
| 830 |
+ return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
|
|
| 831 |
+ } |
|
| 832 |
+ var ( |
|
| 833 |
+ authEncoded = r.Header.Get("X-Registry-Auth")
|
|
| 834 |
+ authConfig = &auth.AuthConfig{}
|
|
| 835 |
+ configFileEncoded = r.Header.Get("X-Registry-Config")
|
|
| 836 |
+ configFile = &auth.ConfigFile{}
|
|
| 837 |
+ job = eng.Job("build")
|
|
| 838 |
+ ) |
|
| 839 |
+ |
|
| 840 |
+ // This block can be removed when API versions prior to 1.9 are deprecated. |
|
| 841 |
+ // Both headers will be parsed and sent along to the daemon, but if a non-empty |
|
| 842 |
+ // ConfigFile is present, any value provided as an AuthConfig directly will |
|
| 843 |
+ // be overridden. See BuildFile::CmdFrom for details. |
|
| 844 |
+ if version < 1.9 && authEncoded != "" {
|
|
| 845 |
+ authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) |
|
| 846 |
+ if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
|
| 847 |
+ // for a pull it is not an error if no auth was given |
|
| 848 |
+ // to increase compatibility with the existing api it is defaulting to be empty |
|
| 849 |
+ authConfig = &auth.AuthConfig{}
|
|
| 850 |
+ } |
|
| 851 |
+ } |
|
| 852 |
+ |
|
| 853 |
+ if configFileEncoded != "" {
|
|
| 854 |
+ configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded)) |
|
| 855 |
+ if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
|
|
| 856 |
+ // for a pull it is not an error if no auth was given |
|
| 857 |
+ // to increase compatibility with the existing api it is defaulting to be empty |
|
| 858 |
+ configFile = &auth.ConfigFile{}
|
|
| 859 |
+ } |
|
| 860 |
+ } |
|
| 861 |
+ |
|
| 862 |
+ if version >= 1.8 {
|
|
| 863 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 864 |
+ job.SetenvBool("json", true)
|
|
| 865 |
+ } |
|
| 866 |
+ |
|
| 867 |
+ job.Stdout.Add(utils.NewWriteFlusher(w)) |
|
| 868 |
+ job.Stdin.Add(r.Body) |
|
| 869 |
+ job.Setenv("remote", r.FormValue("remote"))
|
|
| 870 |
+ job.Setenv("t", r.FormValue("t"))
|
|
| 871 |
+ job.Setenv("q", r.FormValue("q"))
|
|
| 872 |
+ job.Setenv("nocache", r.FormValue("nocache"))
|
|
| 873 |
+ job.Setenv("rm", r.FormValue("rm"))
|
|
| 874 |
+ |
|
| 875 |
+ if err := job.Run(); err != nil {
|
|
| 876 |
+ if !job.Stdout.Used() {
|
|
| 877 |
+ return err |
|
| 878 |
+ } |
|
| 879 |
+ sf := utils.NewStreamFormatter(version >= 1.8) |
|
| 880 |
+ w.Write(sf.FormatError(err)) |
|
| 881 |
+ } |
|
| 882 |
+ return nil |
|
| 883 |
+} |
|
| 884 |
+ |
|
| 885 |
+func postContainersCopy(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 886 |
+ if vars == nil {
|
|
| 887 |
+ return fmt.Errorf("Missing parameter")
|
|
| 888 |
+ } |
|
| 889 |
+ |
|
| 890 |
+ var copyData engine.Env |
|
| 891 |
+ |
|
| 892 |
+ if contentType := r.Header.Get("Content-Type"); contentType == "application/json" {
|
|
| 893 |
+ if err := copyData.Decode(r.Body); err != nil {
|
|
| 894 |
+ return err |
|
| 895 |
+ } |
|
| 896 |
+ } else {
|
|
| 897 |
+ return fmt.Errorf("Content-Type not supported: %s", contentType)
|
|
| 898 |
+ } |
|
| 899 |
+ |
|
| 900 |
+ if copyData.Get("Resource") == "" {
|
|
| 901 |
+ return fmt.Errorf("Path cannot be empty")
|
|
| 902 |
+ } |
|
| 903 |
+ if copyData.Get("Resource")[0] == '/' {
|
|
| 904 |
+ copyData.Set("Resource", copyData.Get("Resource")[1:])
|
|
| 905 |
+ } |
|
| 906 |
+ |
|
| 907 |
+ job := eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
|
|
| 908 |
+ job.Stdout.Add(w) |
|
| 909 |
+ if err := job.Run(); err != nil {
|
|
| 910 |
+ utils.Errorf("%s", err.Error())
|
|
| 911 |
+ } |
|
| 912 |
+ return nil |
|
| 913 |
+} |
|
| 914 |
+ |
|
| 915 |
+func optionsHandler(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 916 |
+ w.WriteHeader(http.StatusOK) |
|
| 917 |
+ return nil |
|
| 918 |
+} |
|
| 919 |
+func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
|
|
| 920 |
+ w.Header().Add("Access-Control-Allow-Origin", "*")
|
|
| 921 |
+ w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
|
|
| 922 |
+ w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
|
|
| 923 |
+} |
|
| 924 |
+ |
|
| 925 |
+func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion string) http.HandlerFunc {
|
|
| 926 |
+ return func(w http.ResponseWriter, r *http.Request) {
|
|
| 927 |
+ // log the request |
|
| 928 |
+ utils.Debugf("Calling %s %s", localMethod, localRoute)
|
|
| 929 |
+ |
|
| 930 |
+ if logging {
|
|
| 931 |
+ log.Println(r.Method, r.RequestURI) |
|
| 932 |
+ } |
|
| 933 |
+ |
|
| 934 |
+ if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
|
|
| 935 |
+ userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
|
|
| 936 |
+ if len(userAgent) == 2 && userAgent[1] != dockerVersion {
|
|
| 937 |
+ utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
|
|
| 938 |
+ } |
|
| 939 |
+ } |
|
| 940 |
+ version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64) |
|
| 941 |
+ if err != nil {
|
|
| 942 |
+ version = APIVERSION |
|
| 943 |
+ } |
|
| 944 |
+ if enableCors {
|
|
| 945 |
+ writeCorsHeaders(w, r) |
|
| 946 |
+ } |
|
| 947 |
+ |
|
| 948 |
+ if version == 0 || version > APIVERSION {
|
|
| 949 |
+ http.Error(w, fmt.Errorf("client and server don't have same version (client : %g, server: %g)", version, APIVERSION).Error(), http.StatusNotFound)
|
|
| 950 |
+ return |
|
| 951 |
+ } |
|
| 952 |
+ |
|
| 953 |
+ if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
|
|
| 954 |
+ utils.Errorf("Error: %s", err)
|
|
| 955 |
+ httpError(w, err) |
|
| 956 |
+ } |
|
| 957 |
+ } |
|
| 958 |
+} |
|
| 959 |
+ |
|
| 960 |
+// Replicated from expvar.go as not public. |
|
| 961 |
+func expvarHandler(w http.ResponseWriter, r *http.Request) {
|
|
| 962 |
+ w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
| 963 |
+ fmt.Fprintf(w, "{\n")
|
|
| 964 |
+ first := true |
|
| 965 |
+ expvar.Do(func(kv expvar.KeyValue) {
|
|
| 966 |
+ if !first {
|
|
| 967 |
+ fmt.Fprintf(w, ",\n") |
|
| 968 |
+ } |
|
| 969 |
+ first = false |
|
| 970 |
+ fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) |
|
| 971 |
+ }) |
|
| 972 |
+ fmt.Fprintf(w, "\n}\n") |
|
| 973 |
+} |
|
| 974 |
+ |
|
| 975 |
+func AttachProfiler(router *mux.Router) {
|
|
| 976 |
+ router.HandleFunc("/debug/vars", expvarHandler)
|
|
| 977 |
+ router.HandleFunc("/debug/pprof/", pprof.Index)
|
|
| 978 |
+ router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
|
| 979 |
+ router.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
|
| 980 |
+ router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
|
| 981 |
+ router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
|
|
| 982 |
+ router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
|
|
| 983 |
+ router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
|
|
| 984 |
+} |
|
| 985 |
+ |
|
| 986 |
+func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion string) (*mux.Router, error) {
|
|
| 987 |
+ r := mux.NewRouter() |
|
| 988 |
+ if os.Getenv("DEBUG") != "" {
|
|
| 989 |
+ AttachProfiler(r) |
|
| 990 |
+ } |
|
| 991 |
+ m := map[string]map[string]HttpApiFunc{
|
|
| 992 |
+ "GET": {
|
|
| 993 |
+ "/events": getEvents, |
|
| 994 |
+ "/info": getInfo, |
|
| 995 |
+ "/version": getVersion, |
|
| 996 |
+ "/images/json": getImagesJSON, |
|
| 997 |
+ "/images/viz": getImagesViz, |
|
| 998 |
+ "/images/search": getImagesSearch, |
|
| 999 |
+ "/images/{name:.*}/get": getImagesGet,
|
|
| 1000 |
+ "/images/{name:.*}/history": getImagesHistory,
|
|
| 1001 |
+ "/images/{name:.*}/json": getImagesByName,
|
|
| 1002 |
+ "/containers/ps": getContainersJSON, |
|
| 1003 |
+ "/containers/json": getContainersJSON, |
|
| 1004 |
+ "/containers/{name:.*}/export": getContainersExport,
|
|
| 1005 |
+ "/containers/{name:.*}/changes": getContainersChanges,
|
|
| 1006 |
+ "/containers/{name:.*}/json": getContainersByName,
|
|
| 1007 |
+ "/containers/{name:.*}/top": getContainersTop,
|
|
| 1008 |
+ "/containers/{name:.*}/attach/ws": wsContainersAttach,
|
|
| 1009 |
+ }, |
|
| 1010 |
+ "POST": {
|
|
| 1011 |
+ "/auth": postAuth, |
|
| 1012 |
+ "/commit": postCommit, |
|
| 1013 |
+ "/build": postBuild, |
|
| 1014 |
+ "/images/create": postImagesCreate, |
|
| 1015 |
+ "/images/{name:.*}/insert": postImagesInsert,
|
|
| 1016 |
+ "/images/load": postImagesLoad, |
|
| 1017 |
+ "/images/{name:.*}/push": postImagesPush,
|
|
| 1018 |
+ "/images/{name:.*}/tag": postImagesTag,
|
|
| 1019 |
+ "/containers/create": postContainersCreate, |
|
| 1020 |
+ "/containers/{name:.*}/kill": postContainersKill,
|
|
| 1021 |
+ "/containers/{name:.*}/restart": postContainersRestart,
|
|
| 1022 |
+ "/containers/{name:.*}/start": postContainersStart,
|
|
| 1023 |
+ "/containers/{name:.*}/stop": postContainersStop,
|
|
| 1024 |
+ "/containers/{name:.*}/wait": postContainersWait,
|
|
| 1025 |
+ "/containers/{name:.*}/resize": postContainersResize,
|
|
| 1026 |
+ "/containers/{name:.*}/attach": postContainersAttach,
|
|
| 1027 |
+ "/containers/{name:.*}/copy": postContainersCopy,
|
|
| 1028 |
+ }, |
|
| 1029 |
+ "DELETE": {
|
|
| 1030 |
+ "/containers/{name:.*}": deleteContainers,
|
|
| 1031 |
+ "/images/{name:.*}": deleteImages,
|
|
| 1032 |
+ }, |
|
| 1033 |
+ "OPTIONS": {
|
|
| 1034 |
+ "": optionsHandler, |
|
| 1035 |
+ }, |
|
| 1036 |
+ } |
|
| 1037 |
+ |
|
| 1038 |
+ for method, routes := range m {
|
|
| 1039 |
+ for route, fct := range routes {
|
|
| 1040 |
+ utils.Debugf("Registering %s, %s", method, route)
|
|
| 1041 |
+ // NOTE: scope issue, make sure the variables are local and won't be changed |
|
| 1042 |
+ localRoute := route |
|
| 1043 |
+ localFct := fct |
|
| 1044 |
+ localMethod := method |
|
| 1045 |
+ |
|
| 1046 |
+ // build the handler function |
|
| 1047 |
+ f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, dockerVersion) |
|
| 1048 |
+ |
|
| 1049 |
+ // add the new route |
|
| 1050 |
+ if localRoute == "" {
|
|
| 1051 |
+ r.Methods(localMethod).HandlerFunc(f) |
|
| 1052 |
+ } else {
|
|
| 1053 |
+ r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
|
|
| 1054 |
+ r.Path(localRoute).Methods(localMethod).HandlerFunc(f) |
|
| 1055 |
+ } |
|
| 1056 |
+ } |
|
| 1057 |
+ } |
|
| 1058 |
+ |
|
| 1059 |
+ return r, nil |
|
| 1060 |
+} |
|
| 1061 |
+ |
|
| 1062 |
+// ServeRequest processes a single http request to the docker remote api. |
|
| 1063 |
+// FIXME: refactor this to be part of Server and not require re-creating a new |
|
| 1064 |
+// router each time. This requires first moving ListenAndServe into Server. |
|
| 1065 |
+func ServeRequest(eng *engine.Engine, apiversion float64, w http.ResponseWriter, req *http.Request) error {
|
|
| 1066 |
+ router, err := createRouter(eng, false, true, "") |
|
| 1067 |
+ if err != nil {
|
|
| 1068 |
+ return err |
|
| 1069 |
+ } |
|
| 1070 |
+ // Insert APIVERSION into the request as a convenience |
|
| 1071 |
+ req.URL.Path = fmt.Sprintf("/v%g%s", apiversion, req.URL.Path)
|
|
| 1072 |
+ router.ServeHTTP(w, req) |
|
| 1073 |
+ return nil |
|
| 1074 |
+} |
|
| 1075 |
+ |
|
| 1076 |
+// ServeFD creates an http.Server and sets it up to serve given a socket activated |
|
| 1077 |
+// argument. |
|
| 1078 |
+func ServeFd(addr string, handle http.Handler) error {
|
|
| 1079 |
+ ls, e := systemd.ListenFD(addr) |
|
| 1080 |
+ if e != nil {
|
|
| 1081 |
+ return e |
|
| 1082 |
+ } |
|
| 1083 |
+ |
|
| 1084 |
+ chErrors := make(chan error, len(ls)) |
|
| 1085 |
+ |
|
| 1086 |
+ // Since ListenFD will return one or more sockets we have |
|
| 1087 |
+ // to create a go func to spawn off multiple serves |
|
| 1088 |
+ for i := range ls {
|
|
| 1089 |
+ listener := ls[i] |
|
| 1090 |
+ go func() {
|
|
| 1091 |
+ httpSrv := http.Server{Handler: handle}
|
|
| 1092 |
+ chErrors <- httpSrv.Serve(listener) |
|
| 1093 |
+ }() |
|
| 1094 |
+ } |
|
| 1095 |
+ |
|
| 1096 |
+ for i := 0; i < len(ls); i += 1 {
|
|
| 1097 |
+ err := <-chErrors |
|
| 1098 |
+ if err != nil {
|
|
| 1099 |
+ return err |
|
| 1100 |
+ } |
|
| 1101 |
+ } |
|
| 1102 |
+ |
|
| 1103 |
+ return nil |
|
| 1104 |
+} |
|
| 1105 |
+ |
|
| 1106 |
+// ListenAndServe sets up the required http.Server and gets it listening for |
|
| 1107 |
+// each addr passed in and does protocol specific checking. |
|
| 1108 |
+func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors bool, dockerVersion string) error {
|
|
| 1109 |
+ r, err := createRouter(eng, logging, enableCors, dockerVersion) |
|
| 1110 |
+ if err != nil {
|
|
| 1111 |
+ return err |
|
| 1112 |
+ } |
|
| 1113 |
+ |
|
| 1114 |
+ if proto == "fd" {
|
|
| 1115 |
+ return ServeFd(addr, r) |
|
| 1116 |
+ } |
|
| 1117 |
+ |
|
| 1118 |
+ if proto == "unix" {
|
|
| 1119 |
+ if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) {
|
|
| 1120 |
+ return err |
|
| 1121 |
+ } |
|
| 1122 |
+ } |
|
| 1123 |
+ |
|
| 1124 |
+ l, err := net.Listen(proto, addr) |
|
| 1125 |
+ if err != nil {
|
|
| 1126 |
+ return err |
|
| 1127 |
+ } |
|
| 1128 |
+ |
|
| 1129 |
+ // Basic error and sanity checking |
|
| 1130 |
+ switch proto {
|
|
| 1131 |
+ case "tcp": |
|
| 1132 |
+ if !strings.HasPrefix(addr, "127.0.0.1") {
|
|
| 1133 |
+ log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
|
| 1134 |
+ } |
|
| 1135 |
+ case "unix": |
|
| 1136 |
+ if err := os.Chmod(addr, 0660); err != nil {
|
|
| 1137 |
+ return err |
|
| 1138 |
+ } |
|
| 1139 |
+ |
|
| 1140 |
+ groups, err := ioutil.ReadFile("/etc/group")
|
|
| 1141 |
+ if err != nil {
|
|
| 1142 |
+ return err |
|
| 1143 |
+ } |
|
| 1144 |
+ re := regexp.MustCompile("(^|\n)docker:.*?:([0-9]+)")
|
|
| 1145 |
+ if gidMatch := re.FindStringSubmatch(string(groups)); gidMatch != nil {
|
|
| 1146 |
+ gid, err := strconv.Atoi(gidMatch[2]) |
|
| 1147 |
+ if err != nil {
|
|
| 1148 |
+ return err |
|
| 1149 |
+ } |
|
| 1150 |
+ utils.Debugf("docker group found. gid: %d", gid)
|
|
| 1151 |
+ if err := os.Chown(addr, 0, gid); err != nil {
|
|
| 1152 |
+ return err |
|
| 1153 |
+ } |
|
| 1154 |
+ } |
|
| 1155 |
+ default: |
|
| 1156 |
+ return fmt.Errorf("Invalid protocol format.")
|
|
| 1157 |
+ } |
|
| 1158 |
+ |
|
| 1159 |
+ httpSrv := http.Server{Addr: addr, Handler: r}
|
|
| 1160 |
+ return httpSrv.Serve(l) |
|
| 1161 |
+} |
| 0 | 1162 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,65 @@ |
| 0 |
+package api |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "net/http" |
|
| 5 |
+ "net/http/httptest" |
|
| 6 |
+ "testing" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func TestJsonContentType(t *testing.T) {
|
|
| 10 |
+ if !MatchesContentType("application/json", "application/json") {
|
|
| 11 |
+ t.Fail() |
|
| 12 |
+ } |
|
| 13 |
+ |
|
| 14 |
+ if !MatchesContentType("application/json; charset=utf-8", "application/json") {
|
|
| 15 |
+ t.Fail() |
|
| 16 |
+ } |
|
| 17 |
+ |
|
| 18 |
+ if MatchesContentType("dockerapplication/json", "application/json") {
|
|
| 19 |
+ t.Fail() |
|
| 20 |
+ } |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func TestGetBoolParam(t *testing.T) {
|
|
| 24 |
+ if ret, err := getBoolParam("true"); err != nil || !ret {
|
|
| 25 |
+ t.Fatalf("true -> true, nil | got %t %s", ret, err)
|
|
| 26 |
+ } |
|
| 27 |
+ if ret, err := getBoolParam("True"); err != nil || !ret {
|
|
| 28 |
+ t.Fatalf("True -> true, nil | got %t %s", ret, err)
|
|
| 29 |
+ } |
|
| 30 |
+ if ret, err := getBoolParam("1"); err != nil || !ret {
|
|
| 31 |
+ t.Fatalf("1 -> true, nil | got %t %s", ret, err)
|
|
| 32 |
+ } |
|
| 33 |
+ if ret, err := getBoolParam(""); err != nil || ret {
|
|
| 34 |
+ t.Fatalf("\"\" -> false, nil | got %t %s", ret, err)
|
|
| 35 |
+ } |
|
| 36 |
+ if ret, err := getBoolParam("false"); err != nil || ret {
|
|
| 37 |
+ t.Fatalf("false -> false, nil | got %t %s", ret, err)
|
|
| 38 |
+ } |
|
| 39 |
+ if ret, err := getBoolParam("0"); err != nil || ret {
|
|
| 40 |
+ t.Fatalf("0 -> false, nil | got %t %s", ret, err)
|
|
| 41 |
+ } |
|
| 42 |
+ if ret, err := getBoolParam("faux"); err == nil || ret {
|
|
| 43 |
+ t.Fatalf("faux -> false, err | got %t %s", ret, err)
|
|
| 44 |
+ } |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func TesthttpError(t *testing.T) {
|
|
| 48 |
+ r := httptest.NewRecorder() |
|
| 49 |
+ |
|
| 50 |
+ httpError(r, fmt.Errorf("No such method"))
|
|
| 51 |
+ if r.Code != http.StatusNotFound {
|
|
| 52 |
+ t.Fatalf("Expected %d, got %d", http.StatusNotFound, r.Code)
|
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ httpError(r, fmt.Errorf("This accound hasn't been activated"))
|
|
| 56 |
+ if r.Code != http.StatusForbidden {
|
|
| 57 |
+ t.Fatalf("Expected %d, got %d", http.StatusForbidden, r.Code)
|
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ httpError(r, fmt.Errorf("Some error"))
|
|
| 61 |
+ if r.Code != http.StatusInternalServerError {
|
|
| 62 |
+ t.Fatalf("Expected %d, got %d", http.StatusInternalServerError, r.Code)
|
|
| 63 |
+ } |
|
| 64 |
+} |
| 0 | 65 |
deleted file mode 100644 |
| ... | ... |
@@ -1,19 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "testing" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-func TestJsonContentType(t *testing.T) {
|
|
| 8 |
- if !matchesContentType("application/json", "application/json") {
|
|
| 9 |
- t.Fail() |
|
| 10 |
- } |
|
| 11 |
- |
|
| 12 |
- if !matchesContentType("application/json; charset=utf-8", "application/json") {
|
|
| 13 |
- t.Fail() |
|
| 14 |
- } |
|
| 15 |
- |
|
| 16 |
- if matchesContentType("dockerapplication/json", "application/json") {
|
|
| 17 |
- t.Fail() |
|
| 18 |
- } |
|
| 19 |
-} |
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
"encoding/json" |
| 9 | 9 |
"errors" |
| 10 | 10 |
"fmt" |
| 11 |
+ "github.com/dotcloud/docker/api" |
|
| 11 | 12 |
"github.com/dotcloud/docker/archive" |
| 12 | 13 |
"github.com/dotcloud/docker/auth" |
| 13 | 14 |
"github.com/dotcloud/docker/engine" |
| ... | ... |
@@ -79,7 +80,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
| 79 | 79 |
return nil |
| 80 | 80 |
} |
| 81 | 81 |
} |
| 82 |
- help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[unix://%s]: tcp://host:port to bind/connect to or unix://path/to/socket to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", DEFAULTUNIXSOCKET)
|
|
| 82 |
+ help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[unix://%s]: tcp://host:port to bind/connect to or unix://path/to/socket to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", api.DEFAULTUNIXSOCKET)
|
|
| 83 | 83 |
for _, command := range [][]string{
|
| 84 | 84 |
{"attach", "Attach to a running container"},
|
| 85 | 85 |
{"build", "Build a container from a Dockerfile"},
|
| ... | ... |
@@ -2283,7 +2284,7 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
|
| 2283 | 2283 |
re := regexp.MustCompile("/+")
|
| 2284 | 2284 |
path = re.ReplaceAllString(path, "/") |
| 2285 | 2285 |
|
| 2286 |
- req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params)
|
|
| 2286 |
+ req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", api.APIVERSION, path), params)
|
|
| 2287 | 2287 |
if err != nil {
|
| 2288 | 2288 |
return nil, -1, err |
| 2289 | 2289 |
} |
| ... | ... |
@@ -2360,7 +2361,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h |
| 2360 | 2360 |
re := regexp.MustCompile("/+")
|
| 2361 | 2361 |
path = re.ReplaceAllString(path, "/") |
| 2362 | 2362 |
|
| 2363 |
- req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in)
|
|
| 2363 |
+ req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", api.APIVERSION, path), in)
|
|
| 2364 | 2364 |
if err != nil {
|
| 2365 | 2365 |
return err |
| 2366 | 2366 |
} |
| ... | ... |
@@ -2405,7 +2406,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h |
| 2405 | 2405 |
return fmt.Errorf("Error: %s", bytes.TrimSpace(body))
|
| 2406 | 2406 |
} |
| 2407 | 2407 |
|
| 2408 |
- if matchesContentType(resp.Header.Get("Content-Type"), "application/json") {
|
|
| 2408 |
+ if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
|
|
| 2409 | 2409 |
return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal) |
| 2410 | 2410 |
} |
| 2411 | 2411 |
if _, err := io.Copy(out, resp.Body); err != nil {
|
| ... | ... |
@@ -2424,7 +2425,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea |
| 2424 | 2424 |
re := regexp.MustCompile("/+")
|
| 2425 | 2425 |
path = re.ReplaceAllString(path, "/") |
| 2426 | 2426 |
|
| 2427 |
- req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
|
|
| 2427 |
+ req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", api.APIVERSION, path), nil)
|
|
| 2428 | 2428 |
if err != nil {
|
| 2429 | 2429 |
return err |
| 2430 | 2430 |
} |
| ... | ... |
@@ -3,6 +3,7 @@ package main |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"github.com/dotcloud/docker" |
| 6 |
+ "github.com/dotcloud/docker/api" |
|
| 6 | 7 |
"github.com/dotcloud/docker/engine" |
| 7 | 8 |
flag "github.com/dotcloud/docker/pkg/mflag" |
| 8 | 9 |
"github.com/dotcloud/docker/sysinit" |
| ... | ... |
@@ -57,7 +58,7 @@ func main() {
|
| 57 | 57 |
|
| 58 | 58 |
if defaultHost == "" || *flDaemon {
|
| 59 | 59 |
// If we do not have a host, default to unix socket |
| 60 |
- defaultHost = fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)
|
|
| 60 |
+ defaultHost = fmt.Sprintf("unix://%s", api.DEFAULTUNIXSOCKET)
|
|
| 61 | 61 |
} |
| 62 | 62 |
flHosts.Set(defaultHost) |
| 63 | 63 |
} |
| 64 | 64 |
deleted file mode 100644 |
| ... | ... |
@@ -1,51 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "net/http" |
|
| 6 |
- "net/http/httptest" |
|
| 7 |
- "testing" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-func TestGetBoolParam(t *testing.T) {
|
|
| 11 |
- if ret, err := getBoolParam("true"); err != nil || !ret {
|
|
| 12 |
- t.Fatalf("true -> true, nil | got %t %s", ret, err)
|
|
| 13 |
- } |
|
| 14 |
- if ret, err := getBoolParam("True"); err != nil || !ret {
|
|
| 15 |
- t.Fatalf("True -> true, nil | got %t %s", ret, err)
|
|
| 16 |
- } |
|
| 17 |
- if ret, err := getBoolParam("1"); err != nil || !ret {
|
|
| 18 |
- t.Fatalf("1 -> true, nil | got %t %s", ret, err)
|
|
| 19 |
- } |
|
| 20 |
- if ret, err := getBoolParam(""); err != nil || ret {
|
|
| 21 |
- t.Fatalf("\"\" -> false, nil | got %t %s", ret, err)
|
|
| 22 |
- } |
|
| 23 |
- if ret, err := getBoolParam("false"); err != nil || ret {
|
|
| 24 |
- t.Fatalf("false -> false, nil | got %t %s", ret, err)
|
|
| 25 |
- } |
|
| 26 |
- if ret, err := getBoolParam("0"); err != nil || ret {
|
|
| 27 |
- t.Fatalf("0 -> false, nil | got %t %s", ret, err)
|
|
| 28 |
- } |
|
| 29 |
- if ret, err := getBoolParam("faux"); err == nil || ret {
|
|
| 30 |
- t.Fatalf("faux -> false, err | got %t %s", ret, err)
|
|
| 31 |
- } |
|
| 32 |
-} |
|
| 33 |
- |
|
| 34 |
-func TesthttpError(t *testing.T) {
|
|
| 35 |
- r := httptest.NewRecorder() |
|
| 36 |
- |
|
| 37 |
- httpError(r, fmt.Errorf("No such method"))
|
|
| 38 |
- if r.Code != http.StatusNotFound {
|
|
| 39 |
- t.Fatalf("Expected %d, got %d", http.StatusNotFound, r.Code)
|
|
| 40 |
- } |
|
| 41 |
- |
|
| 42 |
- httpError(r, fmt.Errorf("This accound hasn't been activated"))
|
|
| 43 |
- if r.Code != http.StatusForbidden {
|
|
| 44 |
- t.Fatalf("Expected %d, got %d", http.StatusForbidden, r.Code)
|
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- httpError(r, fmt.Errorf("Some error"))
|
|
| 48 |
- if r.Code != http.StatusInternalServerError {
|
|
| 49 |
- t.Fatalf("Expected %d, got %d", http.StatusInternalServerError, r.Code)
|
|
| 50 |
- } |
|
| 51 |
-} |
| ... | ... |
@@ -7,6 +7,7 @@ import ( |
| 7 | 7 |
"encoding/json" |
| 8 | 8 |
"fmt" |
| 9 | 9 |
"github.com/dotcloud/docker" |
| 10 |
+ "github.com/dotcloud/docker/api" |
|
| 10 | 11 |
"github.com/dotcloud/docker/engine" |
| 11 | 12 |
"github.com/dotcloud/docker/utils" |
| 12 | 13 |
"io" |
| ... | ... |
@@ -21,7 +22,6 @@ import ( |
| 21 | 21 |
func TestGetVersion(t *testing.T) {
|
| 22 | 22 |
eng := NewTestEngine(t) |
| 23 | 23 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 24 |
- srv := mkServerFromEngine(eng, t) |
|
| 25 | 24 |
|
| 26 | 25 |
var err error |
| 27 | 26 |
r := httptest.NewRecorder() |
| ... | ... |
@@ -31,7 +31,7 @@ func TestGetVersion(t *testing.T) {
|
| 31 | 31 |
t.Fatal(err) |
| 32 | 32 |
} |
| 33 | 33 |
// FIXME getting the version should require an actual running Server |
| 34 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 34 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 35 | 35 |
t.Fatal(err) |
| 36 | 36 |
} |
| 37 | 37 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -58,7 +58,6 @@ func TestGetVersion(t *testing.T) {
|
| 58 | 58 |
func TestGetInfo(t *testing.T) {
|
| 59 | 59 |
eng := NewTestEngine(t) |
| 60 | 60 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 61 |
- srv := mkServerFromEngine(eng, t) |
|
| 62 | 61 |
|
| 63 | 62 |
job := eng.Job("images")
|
| 64 | 63 |
initialImages, err := job.Stdout.AddListTable() |
| ... | ... |
@@ -74,7 +73,7 @@ func TestGetInfo(t *testing.T) {
|
| 74 | 74 |
} |
| 75 | 75 |
r := httptest.NewRecorder() |
| 76 | 76 |
|
| 77 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 77 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 78 | 78 |
t.Fatal(err) |
| 79 | 79 |
} |
| 80 | 80 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -122,7 +121,7 @@ func TestGetEvents(t *testing.T) {
|
| 122 | 122 |
|
| 123 | 123 |
r := httptest.NewRecorder() |
| 124 | 124 |
setTimeout(t, "", 500*time.Millisecond, func() {
|
| 125 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 125 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 126 | 126 |
t.Fatal(err) |
| 127 | 127 |
} |
| 128 | 128 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -146,7 +145,6 @@ func TestGetEvents(t *testing.T) {
|
| 146 | 146 |
func TestGetImagesJSON(t *testing.T) {
|
| 147 | 147 |
eng := NewTestEngine(t) |
| 148 | 148 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 149 |
- srv := mkServerFromEngine(eng, t) |
|
| 150 | 149 |
|
| 151 | 150 |
job := eng.Job("images")
|
| 152 | 151 |
initialImages, err := job.Stdout.AddListTable() |
| ... | ... |
@@ -164,7 +162,7 @@ func TestGetImagesJSON(t *testing.T) {
|
| 164 | 164 |
|
| 165 | 165 |
r := httptest.NewRecorder() |
| 166 | 166 |
|
| 167 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 167 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 168 | 168 |
t.Fatal(err) |
| 169 | 169 |
} |
| 170 | 170 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -199,7 +197,7 @@ func TestGetImagesJSON(t *testing.T) {
|
| 199 | 199 |
if err != nil {
|
| 200 | 200 |
t.Fatal(err) |
| 201 | 201 |
} |
| 202 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r2, req2); err != nil {
|
|
| 202 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r2, req2); err != nil {
|
|
| 203 | 203 |
t.Fatal(err) |
| 204 | 204 |
} |
| 205 | 205 |
assertHttpNotError(r2, t) |
| ... | ... |
@@ -232,7 +230,7 @@ func TestGetImagesJSON(t *testing.T) {
|
| 232 | 232 |
t.Fatal(err) |
| 233 | 233 |
} |
| 234 | 234 |
|
| 235 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r3, req3); err != nil {
|
|
| 235 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r3, req3); err != nil {
|
|
| 236 | 236 |
t.Fatal(err) |
| 237 | 237 |
} |
| 238 | 238 |
assertHttpNotError(r3, t) |
| ... | ... |
@@ -250,7 +248,6 @@ func TestGetImagesJSON(t *testing.T) {
|
| 250 | 250 |
func TestGetImagesHistory(t *testing.T) {
|
| 251 | 251 |
eng := NewTestEngine(t) |
| 252 | 252 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 253 |
- srv := mkServerFromEngine(eng, t) |
|
| 254 | 253 |
|
| 255 | 254 |
r := httptest.NewRecorder() |
| 256 | 255 |
|
| ... | ... |
@@ -258,7 +255,7 @@ func TestGetImagesHistory(t *testing.T) {
|
| 258 | 258 |
if err != nil {
|
| 259 | 259 |
t.Fatal(err) |
| 260 | 260 |
} |
| 261 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 261 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 262 | 262 |
t.Fatal(err) |
| 263 | 263 |
} |
| 264 | 264 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -275,7 +272,6 @@ func TestGetImagesHistory(t *testing.T) {
|
| 275 | 275 |
func TestGetImagesByName(t *testing.T) {
|
| 276 | 276 |
eng := NewTestEngine(t) |
| 277 | 277 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 278 |
- srv := mkServerFromEngine(eng, t) |
|
| 279 | 278 |
|
| 280 | 279 |
req, err := http.NewRequest("GET", "/images/"+unitTestImageName+"/json", nil)
|
| 281 | 280 |
if err != nil {
|
| ... | ... |
@@ -283,7 +279,7 @@ func TestGetImagesByName(t *testing.T) {
|
| 283 | 283 |
} |
| 284 | 284 |
|
| 285 | 285 |
r := httptest.NewRecorder() |
| 286 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 286 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 287 | 287 |
t.Fatal(err) |
| 288 | 288 |
} |
| 289 | 289 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -300,7 +296,6 @@ func TestGetImagesByName(t *testing.T) {
|
| 300 | 300 |
func TestGetContainersJSON(t *testing.T) {
|
| 301 | 301 |
eng := NewTestEngine(t) |
| 302 | 302 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 303 |
- srv := mkServerFromEngine(eng, t) |
|
| 304 | 303 |
|
| 305 | 304 |
job := eng.Job("containers")
|
| 306 | 305 |
job.SetenvBool("all", true)
|
| ... | ... |
@@ -328,7 +323,7 @@ func TestGetContainersJSON(t *testing.T) {
|
| 328 | 328 |
} |
| 329 | 329 |
|
| 330 | 330 |
r := httptest.NewRecorder() |
| 331 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 331 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 332 | 332 |
t.Fatal(err) |
| 333 | 333 |
} |
| 334 | 334 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -347,7 +342,6 @@ func TestGetContainersJSON(t *testing.T) {
|
| 347 | 347 |
func TestGetContainersExport(t *testing.T) {
|
| 348 | 348 |
eng := NewTestEngine(t) |
| 349 | 349 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 350 |
- srv := mkServerFromEngine(eng, t) |
|
| 351 | 350 |
|
| 352 | 351 |
// Create a container and remove a file |
| 353 | 352 |
containerID := createTestContainer(eng, |
| ... | ... |
@@ -365,7 +359,7 @@ func TestGetContainersExport(t *testing.T) {
|
| 365 | 365 |
if err != nil {
|
| 366 | 366 |
t.Fatal(err) |
| 367 | 367 |
} |
| 368 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 368 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 369 | 369 |
t.Fatal(err) |
| 370 | 370 |
} |
| 371 | 371 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -396,7 +390,6 @@ func TestGetContainersExport(t *testing.T) {
|
| 396 | 396 |
func TestGetContainersChanges(t *testing.T) {
|
| 397 | 397 |
eng := NewTestEngine(t) |
| 398 | 398 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 399 |
- srv := mkServerFromEngine(eng, t) |
|
| 400 | 399 |
|
| 401 | 400 |
// Create a container and remove a file |
| 402 | 401 |
containerID := createTestContainer(eng, |
| ... | ... |
@@ -413,7 +406,7 @@ func TestGetContainersChanges(t *testing.T) {
|
| 413 | 413 |
if err != nil {
|
| 414 | 414 |
t.Fatal(err) |
| 415 | 415 |
} |
| 416 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 416 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 417 | 417 |
t.Fatal(err) |
| 418 | 418 |
} |
| 419 | 419 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -437,7 +430,6 @@ func TestGetContainersChanges(t *testing.T) {
|
| 437 | 437 |
func TestGetContainersTop(t *testing.T) {
|
| 438 | 438 |
eng := NewTestEngine(t) |
| 439 | 439 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 440 |
- srv := mkServerFromEngine(eng, t) |
|
| 441 | 440 |
|
| 442 | 441 |
containerID := createTestContainer(eng, |
| 443 | 442 |
&docker.Config{
|
| ... | ... |
@@ -481,7 +473,7 @@ func TestGetContainersTop(t *testing.T) {
|
| 481 | 481 |
if err != nil {
|
| 482 | 482 |
t.Fatal(err) |
| 483 | 483 |
} |
| 484 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 484 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 485 | 485 |
t.Fatal(err) |
| 486 | 486 |
} |
| 487 | 487 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -514,7 +506,6 @@ func TestGetContainersTop(t *testing.T) {
|
| 514 | 514 |
func TestGetContainersByName(t *testing.T) {
|
| 515 | 515 |
eng := NewTestEngine(t) |
| 516 | 516 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 517 |
- srv := mkServerFromEngine(eng, t) |
|
| 518 | 517 |
|
| 519 | 518 |
// Create a container and remove a file |
| 520 | 519 |
containerID := createTestContainer(eng, |
| ... | ... |
@@ -530,7 +521,7 @@ func TestGetContainersByName(t *testing.T) {
|
| 530 | 530 |
if err != nil {
|
| 531 | 531 |
t.Fatal(err) |
| 532 | 532 |
} |
| 533 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 533 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 534 | 534 |
t.Fatal(err) |
| 535 | 535 |
} |
| 536 | 536 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -565,7 +556,7 @@ func TestPostCommit(t *testing.T) {
|
| 565 | 565 |
} |
| 566 | 566 |
|
| 567 | 567 |
r := httptest.NewRecorder() |
| 568 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 568 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 569 | 569 |
t.Fatal(err) |
| 570 | 570 |
} |
| 571 | 571 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -585,7 +576,6 @@ func TestPostCommit(t *testing.T) {
|
| 585 | 585 |
func TestPostContainersCreate(t *testing.T) {
|
| 586 | 586 |
eng := NewTestEngine(t) |
| 587 | 587 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 588 |
- srv := mkServerFromEngine(eng, t) |
|
| 589 | 588 |
|
| 590 | 589 |
configJSON, err := json.Marshal(&docker.Config{
|
| 591 | 590 |
Image: unitTestImageID, |
| ... | ... |
@@ -602,7 +592,7 @@ func TestPostContainersCreate(t *testing.T) {
|
| 602 | 602 |
} |
| 603 | 603 |
|
| 604 | 604 |
r := httptest.NewRecorder() |
| 605 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 605 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 606 | 606 |
t.Fatal(err) |
| 607 | 607 |
} |
| 608 | 608 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -627,7 +617,6 @@ func TestPostContainersCreate(t *testing.T) {
|
| 627 | 627 |
func TestPostContainersKill(t *testing.T) {
|
| 628 | 628 |
eng := NewTestEngine(t) |
| 629 | 629 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 630 |
- srv := mkServerFromEngine(eng, t) |
|
| 631 | 630 |
|
| 632 | 631 |
containerID := createTestContainer(eng, |
| 633 | 632 |
&docker.Config{
|
| ... | ... |
@@ -652,7 +641,7 @@ func TestPostContainersKill(t *testing.T) {
|
| 652 | 652 |
if err != nil {
|
| 653 | 653 |
t.Fatal(err) |
| 654 | 654 |
} |
| 655 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 655 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 656 | 656 |
t.Fatal(err) |
| 657 | 657 |
} |
| 658 | 658 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -667,7 +656,6 @@ func TestPostContainersKill(t *testing.T) {
|
| 667 | 667 |
func TestPostContainersRestart(t *testing.T) {
|
| 668 | 668 |
eng := NewTestEngine(t) |
| 669 | 669 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 670 |
- srv := mkServerFromEngine(eng, t) |
|
| 671 | 670 |
|
| 672 | 671 |
containerID := createTestContainer(eng, |
| 673 | 672 |
&docker.Config{
|
| ... | ... |
@@ -692,7 +680,7 @@ func TestPostContainersRestart(t *testing.T) {
|
| 692 | 692 |
t.Fatal(err) |
| 693 | 693 |
} |
| 694 | 694 |
r := httptest.NewRecorder() |
| 695 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 695 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 696 | 696 |
t.Fatal(err) |
| 697 | 697 |
} |
| 698 | 698 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -713,7 +701,6 @@ func TestPostContainersRestart(t *testing.T) {
|
| 713 | 713 |
func TestPostContainersStart(t *testing.T) {
|
| 714 | 714 |
eng := NewTestEngine(t) |
| 715 | 715 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 716 |
- srv := mkServerFromEngine(eng, t) |
|
| 717 | 716 |
|
| 718 | 717 |
containerID := createTestContainer( |
| 719 | 718 |
eng, |
| ... | ... |
@@ -735,7 +722,7 @@ func TestPostContainersStart(t *testing.T) {
|
| 735 | 735 |
req.Header.Set("Content-Type", "application/json")
|
| 736 | 736 |
|
| 737 | 737 |
r := httptest.NewRecorder() |
| 738 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 738 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 739 | 739 |
t.Fatal(err) |
| 740 | 740 |
} |
| 741 | 741 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -752,7 +739,7 @@ func TestPostContainersStart(t *testing.T) {
|
| 752 | 752 |
} |
| 753 | 753 |
|
| 754 | 754 |
r = httptest.NewRecorder() |
| 755 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 755 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 756 | 756 |
t.Fatal(err) |
| 757 | 757 |
} |
| 758 | 758 |
// Starting an already started container should return an error |
| ... | ... |
@@ -767,7 +754,6 @@ func TestPostContainersStart(t *testing.T) {
|
| 767 | 767 |
func TestRunErrorBindMountRootSource(t *testing.T) {
|
| 768 | 768 |
eng := NewTestEngine(t) |
| 769 | 769 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 770 |
- srv := mkServerFromEngine(eng, t) |
|
| 771 | 770 |
|
| 772 | 771 |
containerID := createTestContainer( |
| 773 | 772 |
eng, |
| ... | ... |
@@ -791,7 +777,7 @@ func TestRunErrorBindMountRootSource(t *testing.T) {
|
| 791 | 791 |
req.Header.Set("Content-Type", "application/json")
|
| 792 | 792 |
|
| 793 | 793 |
r := httptest.NewRecorder() |
| 794 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 794 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 795 | 795 |
t.Fatal(err) |
| 796 | 796 |
} |
| 797 | 797 |
if r.Code != http.StatusInternalServerError {
|
| ... | ... |
@@ -803,7 +789,6 @@ func TestRunErrorBindMountRootSource(t *testing.T) {
|
| 803 | 803 |
func TestPostContainersStop(t *testing.T) {
|
| 804 | 804 |
eng := NewTestEngine(t) |
| 805 | 805 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 806 |
- srv := mkServerFromEngine(eng, t) |
|
| 807 | 806 |
|
| 808 | 807 |
containerID := createTestContainer(eng, |
| 809 | 808 |
&docker.Config{
|
| ... | ... |
@@ -829,7 +814,7 @@ func TestPostContainersStop(t *testing.T) {
|
| 829 | 829 |
t.Fatal(err) |
| 830 | 830 |
} |
| 831 | 831 |
r := httptest.NewRecorder() |
| 832 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 832 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 833 | 833 |
t.Fatal(err) |
| 834 | 834 |
} |
| 835 | 835 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -844,7 +829,6 @@ func TestPostContainersStop(t *testing.T) {
|
| 844 | 844 |
func TestPostContainersWait(t *testing.T) {
|
| 845 | 845 |
eng := NewTestEngine(t) |
| 846 | 846 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 847 |
- srv := mkServerFromEngine(eng, t) |
|
| 848 | 847 |
|
| 849 | 848 |
containerID := createTestContainer(eng, |
| 850 | 849 |
&docker.Config{
|
| ... | ... |
@@ -862,7 +846,7 @@ func TestPostContainersWait(t *testing.T) {
|
| 862 | 862 |
if err != nil {
|
| 863 | 863 |
t.Fatal(err) |
| 864 | 864 |
} |
| 865 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 865 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 866 | 866 |
t.Fatal(err) |
| 867 | 867 |
} |
| 868 | 868 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -883,7 +867,6 @@ func TestPostContainersWait(t *testing.T) {
|
| 883 | 883 |
func TestPostContainersAttach(t *testing.T) {
|
| 884 | 884 |
eng := NewTestEngine(t) |
| 885 | 885 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 886 |
- srv := mkServerFromEngine(eng, t) |
|
| 887 | 886 |
|
| 888 | 887 |
containerID := createTestContainer(eng, |
| 889 | 888 |
&docker.Config{
|
| ... | ... |
@@ -921,7 +904,7 @@ func TestPostContainersAttach(t *testing.T) {
|
| 921 | 921 |
t.Fatal(err) |
| 922 | 922 |
} |
| 923 | 923 |
|
| 924 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 924 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 925 | 925 |
t.Fatal(err) |
| 926 | 926 |
} |
| 927 | 927 |
assertHttpNotError(r.ResponseRecorder, t) |
| ... | ... |
@@ -962,7 +945,6 @@ func TestPostContainersAttach(t *testing.T) {
|
| 962 | 962 |
func TestPostContainersAttachStderr(t *testing.T) {
|
| 963 | 963 |
eng := NewTestEngine(t) |
| 964 | 964 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 965 |
- srv := mkServerFromEngine(eng, t) |
|
| 966 | 965 |
|
| 967 | 966 |
containerID := createTestContainer(eng, |
| 968 | 967 |
&docker.Config{
|
| ... | ... |
@@ -1000,7 +982,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
|
| 1000 | 1000 |
t.Fatal(err) |
| 1001 | 1001 |
} |
| 1002 | 1002 |
|
| 1003 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 1003 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 1004 | 1004 |
t.Fatal(err) |
| 1005 | 1005 |
} |
| 1006 | 1006 |
assertHttpNotError(r.ResponseRecorder, t) |
| ... | ... |
@@ -1044,7 +1026,6 @@ func TestPostContainersAttachStderr(t *testing.T) {
|
| 1044 | 1044 |
func TestDeleteContainers(t *testing.T) {
|
| 1045 | 1045 |
eng := NewTestEngine(t) |
| 1046 | 1046 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 1047 |
- srv := mkServerFromEngine(eng, t) |
|
| 1048 | 1047 |
|
| 1049 | 1048 |
containerID := createTestContainer(eng, |
| 1050 | 1049 |
&docker.Config{
|
| ... | ... |
@@ -1058,7 +1039,7 @@ func TestDeleteContainers(t *testing.T) {
|
| 1058 | 1058 |
t.Fatal(err) |
| 1059 | 1059 |
} |
| 1060 | 1060 |
r := httptest.NewRecorder() |
| 1061 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 1061 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 1062 | 1062 |
t.Fatal(err) |
| 1063 | 1063 |
} |
| 1064 | 1064 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -1071,13 +1052,13 @@ func TestDeleteContainers(t *testing.T) {
|
| 1071 | 1071 |
func TestOptionsRoute(t *testing.T) {
|
| 1072 | 1072 |
eng := NewTestEngine(t) |
| 1073 | 1073 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 1074 |
- srv := mkServerFromEngine(eng, t) |
|
| 1074 |
+ |
|
| 1075 | 1075 |
r := httptest.NewRecorder() |
| 1076 | 1076 |
req, err := http.NewRequest("OPTIONS", "/", nil)
|
| 1077 | 1077 |
if err != nil {
|
| 1078 | 1078 |
t.Fatal(err) |
| 1079 | 1079 |
} |
| 1080 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 1080 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 1081 | 1081 |
t.Fatal(err) |
| 1082 | 1082 |
} |
| 1083 | 1083 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -1089,14 +1070,14 @@ func TestOptionsRoute(t *testing.T) {
|
| 1089 | 1089 |
func TestGetEnabledCors(t *testing.T) {
|
| 1090 | 1090 |
eng := NewTestEngine(t) |
| 1091 | 1091 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 1092 |
- srv := mkServerFromEngine(eng, t) |
|
| 1092 |
+ |
|
| 1093 | 1093 |
r := httptest.NewRecorder() |
| 1094 | 1094 |
|
| 1095 | 1095 |
req, err := http.NewRequest("GET", "/version", nil)
|
| 1096 | 1096 |
if err != nil {
|
| 1097 | 1097 |
t.Fatal(err) |
| 1098 | 1098 |
} |
| 1099 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 1099 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 1100 | 1100 |
t.Fatal(err) |
| 1101 | 1101 |
} |
| 1102 | 1102 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -1122,7 +1103,6 @@ func TestGetEnabledCors(t *testing.T) {
|
| 1122 | 1122 |
func TestDeleteImages(t *testing.T) {
|
| 1123 | 1123 |
eng := NewTestEngine(t) |
| 1124 | 1124 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 1125 |
- srv := mkServerFromEngine(eng, t) |
|
| 1126 | 1125 |
|
| 1127 | 1126 |
initialImages := getImages(eng, t, true, "") |
| 1128 | 1127 |
|
| ... | ... |
@@ -1142,7 +1122,7 @@ func TestDeleteImages(t *testing.T) {
|
| 1142 | 1142 |
} |
| 1143 | 1143 |
|
| 1144 | 1144 |
r := httptest.NewRecorder() |
| 1145 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 1145 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 1146 | 1146 |
t.Fatal(err) |
| 1147 | 1147 |
} |
| 1148 | 1148 |
if r.Code != http.StatusConflict {
|
| ... | ... |
@@ -1155,7 +1135,7 @@ func TestDeleteImages(t *testing.T) {
|
| 1155 | 1155 |
} |
| 1156 | 1156 |
|
| 1157 | 1157 |
r2 := httptest.NewRecorder() |
| 1158 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r2, req2); err != nil {
|
|
| 1158 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r2, req2); err != nil {
|
|
| 1159 | 1159 |
t.Fatal(err) |
| 1160 | 1160 |
} |
| 1161 | 1161 |
assertHttpNotError(r2, t) |
| ... | ... |
@@ -1180,7 +1160,6 @@ func TestDeleteImages(t *testing.T) {
|
| 1180 | 1180 |
func TestPostContainersCopy(t *testing.T) {
|
| 1181 | 1181 |
eng := NewTestEngine(t) |
| 1182 | 1182 |
defer mkRuntimeFromEngine(eng, t).Nuke() |
| 1183 |
- srv := mkServerFromEngine(eng, t) |
|
| 1184 | 1183 |
|
| 1185 | 1184 |
// Create a container and remove a file |
| 1186 | 1185 |
containerID := createTestContainer(eng, |
| ... | ... |
@@ -1208,7 +1187,7 @@ func TestPostContainersCopy(t *testing.T) {
|
| 1208 | 1208 |
t.Fatal(err) |
| 1209 | 1209 |
} |
| 1210 | 1210 |
req.Header.Add("Content-Type", "application/json")
|
| 1211 |
- if err := docker.ServeRequest(srv, docker.APIVERSION, r, req); err != nil {
|
|
| 1211 |
+ if err := api.ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
|
| 1212 | 1212 |
t.Fatal(err) |
| 1213 | 1213 |
} |
| 1214 | 1214 |
assertHttpNotError(r, t) |
| ... | ... |
@@ -2,6 +2,7 @@ package docker |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
+ "github.com/dotcloud/docker/api" |
|
| 5 | 6 |
"github.com/dotcloud/docker/utils" |
| 6 | 7 |
"os" |
| 7 | 8 |
"path/filepath" |
| ... | ... |
@@ -129,7 +130,7 @@ func ValidateEnv(val string) (string, error) {
|
| 129 | 129 |
} |
| 130 | 130 |
|
| 131 | 131 |
func ValidateHost(val string) (string, error) {
|
| 132 |
- host, err := utils.ParseHost(DEFAULTHTTPHOST, DEFAULTHTTPPORT, DEFAULTUNIXSOCKET, val) |
|
| 132 |
+ host, err := utils.ParseHost(api.DEFAULTHTTPHOST, api.DEFAULTHTTPPORT, api.DEFAULTUNIXSOCKET, val) |
|
| 133 | 133 |
if err != nil {
|
| 134 | 134 |
return val, err |
| 135 | 135 |
} |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
"errors" |
| 6 | 6 |
"fmt" |
| 7 |
+ "github.com/dotcloud/docker/api" |
|
| 7 | 8 |
"github.com/dotcloud/docker/archive" |
| 8 | 9 |
"github.com/dotcloud/docker/auth" |
| 9 | 10 |
"github.com/dotcloud/docker/engine" |
| ... | ... |
@@ -122,7 +123,7 @@ func (srv *Server) ListenAndServe(job *engine.Job) engine.Status {
|
| 122 | 122 |
protoAddrParts := strings.SplitN(protoAddr, "://", 2) |
| 123 | 123 |
go func() {
|
| 124 | 124 |
log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
|
| 125 |
- chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], srv, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"))
|
|
| 125 |
+ chErrors <- api.ListenAndServe(protoAddrParts[0], protoAddrParts[1], srv.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), VERSION)
|
|
| 126 | 126 |
}() |
| 127 | 127 |
} |
| 128 | 128 |
|